Detect if route is not outermost / wrong “order” of decorators in Flask












2














Since the @route decorator has to register the view with the current callback given to the decorator, it has to be the outermost decorator to receive the correct function to invoke when handling a request.



This creates a possible situation where a view has been decorated, but since the decorators are in the wrong order, the decorated function is not invoked. If used for decorating views that require the user to be logged in, have a certain role or have a specific flag, the check will be left out silently.



Our current fix is to have the standard action be to deny access to the resource, then requiring a decorator to allow access. In that case, if the decorator isn't invoked when the request is being handled, the request will fail.



But there are use-cases where this becomes cumbersome since it requires you to decorate all views, except for those few that should be exempt. For a pure hierarchical layout this may work, but for checking single flags the structure can get complicated.



Is there a proper way to detect that we're being invoked in a useful place in the decoratory hierarchy? I.e. can we detect that there hasn't already been a route decorator applied to function we get to wrap?



# wrapped in wrong order - @require_administrator should be after @app.route
@require_administrator
@app.route('/users', methods=['GET'])


Implemented as:



def require_administrator(func):
@functools.wraps(func)
def has_administrator(*args, **kwargs):
if not getattr(g, 'user') or not g.user.is_administrator:
abort(403)

return func(*args, **kwargs)

return has_administrator


Here I'd like to detect if my custom decorator is being wrapped after @app.route, and thus, never will be invoked when the request is handled.



Using functools.wraps replaces the wrapped function with the new one in all ways, so looking at __name__ of the function to be wrapped will fail. This also happens at each step of the decorator wrapping process.



I've tried looking at both traceback and inspect, but haven't found any decent way of determining if the sequence is correct.



Update



My currently best solution is to check the called function name against the set of registered endpoints. But since a Route() decorator can change the name of the endpoint, I'll have to support that for my decorator as well in that case, and it'll silently pass if a different function has used the same endpoint name as the current function.



It also have to iterate the set of registered endpoints since I weren't able to find a simple way to check if just the endpoint name exists (possibly more efficient by attempting to build an URL with it and catch the exception).



def require_administrator_checked(func):
for rule in app.url_map.iter_rules():
if func.__name__ == rule.endpoint:
raise DecoratorOrderError(f"Wrapped endpoint '{rule.endpoint}' has already been registered - wrong order of decorators?")

# as above ..









share|improve this question





























    2














    Since the @route decorator has to register the view with the current callback given to the decorator, it has to be the outermost decorator to receive the correct function to invoke when handling a request.



    This creates a possible situation where a view has been decorated, but since the decorators are in the wrong order, the decorated function is not invoked. If used for decorating views that require the user to be logged in, have a certain role or have a specific flag, the check will be left out silently.



    Our current fix is to have the standard action be to deny access to the resource, then requiring a decorator to allow access. In that case, if the decorator isn't invoked when the request is being handled, the request will fail.



    But there are use-cases where this becomes cumbersome since it requires you to decorate all views, except for those few that should be exempt. For a pure hierarchical layout this may work, but for checking single flags the structure can get complicated.



    Is there a proper way to detect that we're being invoked in a useful place in the decoratory hierarchy? I.e. can we detect that there hasn't already been a route decorator applied to function we get to wrap?



    # wrapped in wrong order - @require_administrator should be after @app.route
    @require_administrator
    @app.route('/users', methods=['GET'])


    Implemented as:



    def require_administrator(func):
    @functools.wraps(func)
    def has_administrator(*args, **kwargs):
    if not getattr(g, 'user') or not g.user.is_administrator:
    abort(403)

    return func(*args, **kwargs)

    return has_administrator


    Here I'd like to detect if my custom decorator is being wrapped after @app.route, and thus, never will be invoked when the request is handled.



    Using functools.wraps replaces the wrapped function with the new one in all ways, so looking at __name__ of the function to be wrapped will fail. This also happens at each step of the decorator wrapping process.



    I've tried looking at both traceback and inspect, but haven't found any decent way of determining if the sequence is correct.



    Update



    My currently best solution is to check the called function name against the set of registered endpoints. But since a Route() decorator can change the name of the endpoint, I'll have to support that for my decorator as well in that case, and it'll silently pass if a different function has used the same endpoint name as the current function.



    It also have to iterate the set of registered endpoints since I weren't able to find a simple way to check if just the endpoint name exists (possibly more efficient by attempting to build an URL with it and catch the exception).



    def require_administrator_checked(func):
    for rule in app.url_map.iter_rules():
    if func.__name__ == rule.endpoint:
    raise DecoratorOrderError(f"Wrapped endpoint '{rule.endpoint}' has already been registered - wrong order of decorators?")

    # as above ..









    share|improve this question



























      2












      2








      2







      Since the @route decorator has to register the view with the current callback given to the decorator, it has to be the outermost decorator to receive the correct function to invoke when handling a request.



      This creates a possible situation where a view has been decorated, but since the decorators are in the wrong order, the decorated function is not invoked. If used for decorating views that require the user to be logged in, have a certain role or have a specific flag, the check will be left out silently.



      Our current fix is to have the standard action be to deny access to the resource, then requiring a decorator to allow access. In that case, if the decorator isn't invoked when the request is being handled, the request will fail.



      But there are use-cases where this becomes cumbersome since it requires you to decorate all views, except for those few that should be exempt. For a pure hierarchical layout this may work, but for checking single flags the structure can get complicated.



      Is there a proper way to detect that we're being invoked in a useful place in the decoratory hierarchy? I.e. can we detect that there hasn't already been a route decorator applied to function we get to wrap?



      # wrapped in wrong order - @require_administrator should be after @app.route
      @require_administrator
      @app.route('/users', methods=['GET'])


      Implemented as:



      def require_administrator(func):
      @functools.wraps(func)
      def has_administrator(*args, **kwargs):
      if not getattr(g, 'user') or not g.user.is_administrator:
      abort(403)

      return func(*args, **kwargs)

      return has_administrator


      Here I'd like to detect if my custom decorator is being wrapped after @app.route, and thus, never will be invoked when the request is handled.



      Using functools.wraps replaces the wrapped function with the new one in all ways, so looking at __name__ of the function to be wrapped will fail. This also happens at each step of the decorator wrapping process.



      I've tried looking at both traceback and inspect, but haven't found any decent way of determining if the sequence is correct.



      Update



      My currently best solution is to check the called function name against the set of registered endpoints. But since a Route() decorator can change the name of the endpoint, I'll have to support that for my decorator as well in that case, and it'll silently pass if a different function has used the same endpoint name as the current function.



      It also have to iterate the set of registered endpoints since I weren't able to find a simple way to check if just the endpoint name exists (possibly more efficient by attempting to build an URL with it and catch the exception).



      def require_administrator_checked(func):
      for rule in app.url_map.iter_rules():
      if func.__name__ == rule.endpoint:
      raise DecoratorOrderError(f"Wrapped endpoint '{rule.endpoint}' has already been registered - wrong order of decorators?")

      # as above ..









      share|improve this question















      Since the @route decorator has to register the view with the current callback given to the decorator, it has to be the outermost decorator to receive the correct function to invoke when handling a request.



      This creates a possible situation where a view has been decorated, but since the decorators are in the wrong order, the decorated function is not invoked. If used for decorating views that require the user to be logged in, have a certain role or have a specific flag, the check will be left out silently.



      Our current fix is to have the standard action be to deny access to the resource, then requiring a decorator to allow access. In that case, if the decorator isn't invoked when the request is being handled, the request will fail.



      But there are use-cases where this becomes cumbersome since it requires you to decorate all views, except for those few that should be exempt. For a pure hierarchical layout this may work, but for checking single flags the structure can get complicated.



      Is there a proper way to detect that we're being invoked in a useful place in the decoratory hierarchy? I.e. can we detect that there hasn't already been a route decorator applied to function we get to wrap?



      # wrapped in wrong order - @require_administrator should be after @app.route
      @require_administrator
      @app.route('/users', methods=['GET'])


      Implemented as:



      def require_administrator(func):
      @functools.wraps(func)
      def has_administrator(*args, **kwargs):
      if not getattr(g, 'user') or not g.user.is_administrator:
      abort(403)

      return func(*args, **kwargs)

      return has_administrator


      Here I'd like to detect if my custom decorator is being wrapped after @app.route, and thus, never will be invoked when the request is handled.



      Using functools.wraps replaces the wrapped function with the new one in all ways, so looking at __name__ of the function to be wrapped will fail. This also happens at each step of the decorator wrapping process.



      I've tried looking at both traceback and inspect, but haven't found any decent way of determining if the sequence is correct.



      Update



      My currently best solution is to check the called function name against the set of registered endpoints. But since a Route() decorator can change the name of the endpoint, I'll have to support that for my decorator as well in that case, and it'll silently pass if a different function has used the same endpoint name as the current function.



      It also have to iterate the set of registered endpoints since I weren't able to find a simple way to check if just the endpoint name exists (possibly more efficient by attempting to build an URL with it and catch the exception).



      def require_administrator_checked(func):
      for rule in app.url_map.iter_rules():
      if func.__name__ == rule.endpoint:
      raise DecoratorOrderError(f"Wrapped endpoint '{rule.endpoint}' has already been registered - wrong order of decorators?")

      # as above ..






      python flask python-decorators






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Nov 14 '18 at 11:20

























      asked Nov 14 '18 at 10:02









      MatsLindh

      24.7k22241




      24.7k22241
























          2 Answers
          2






          active

          oldest

          votes


















          2














          Update 2: See my other answer for a more reusable, less hack-y solution.



          Update:
          Here is an decidedly less hack-y solution. However, it requires you to use a
          custom function instead of app.route. It takes an arbitrary number of decorators, and applies them in the order given, and then makes sure that app.route is called as the final function.
          This requires that you use only this decorator for every function.



          def safe_route(rule, app, *decorators, **options):
          def _route(func):
          for decorator in decorators:
          func = decorator(func)
          return app.route(rule, **options)(func)
          return _route


          You can then use it like this:



          def require_administrator(func):
          @functools.wraps(func)
          def has_administrator(*args, **kwargs):
          print("Would check admin now")

          return func(*args, **kwargs)

          return has_administrator

          @safe_route("/", app, require_administrator, methods=["GET"])
          def test2():
          return "foo"

          test2()
          print(test2.__name__)


          This prints:



          Would check admin now
          foo
          test2


          So if all supplied decorators use functools.wraps, this also conserves the test2 name.



          Old answer:
          If you are OK with an admittedly hack-y solution, you can roll your own inspection by reading the file line by line. Here is a very rough function that does this. You can refine this quite a bit, e.g. at the moment it relies on the app being called "app",
          function definitions having at least one empty line before them (normal PEP-8 behavior, but still might be an issue), ...



          Here is the complete code I used to test it.



          import flask
          import functools
          from itertools import groupby


          class DecoratorOrderError(TypeError):
          pass


          app = flask.Flask(__name__)


          def require_administrator(func):
          @functools.wraps(func)
          def has_administrator(*args, **kwargs):
          print("Would check admin now")

          return func(*args, **kwargs)

          return has_administrator


          @require_administrator # Will raise a custom exception
          @app.route("/", methods=["GET"])
          def test():
          return "ok"


          def check_route_is_topmost_decorator():
          # Read own source
          with open(__file__) as f:
          content = [line.strip() for line in f.readlines()]

          # Split source code on line breaks
          split_by_lines = [list(group) for k, group in groupby(content, lambda x: x == "") if not k]

          # Find consecutive decorators
          decorator_groups = dict()
          for line_group in split_by_lines:
          decorators =
          for line in line_group:
          if line.startswith("@"):
          decorators.append(line)
          elif decorators:
          decorator_groups[line] = decorators
          break
          else:
          break

          # Check if app.route is the last one (if it exists)
          for func_def, decorators in decorator_groups.items():
          is_route = [dec.startswith("@app.route") for dec in decorators]
          if sum(is_route) > 1 or (sum(is_route) == 1 and not decorators[0].startswith("@app.route")):
          raise DecoratorOrderError(f"@app.route is not the topmost decorator for '{func_def}'")


          check_route_is_topmost_decorator()


          This snippet will give you the following error:



          Traceback (most recent call last):
          File "/home/vXYZ/test_sso.py", line 51, in <module>
          check_route_is_topmost_decorator()
          File "/home/vXYZ/test_sso.py", line 48, in check_route_is_topmost_decorator
          raise DecoratorOrderError(f"@app.route is not the topmost decorator for '{func_def}'")
          __main__.DecoratorOrderError: @app.route is not the topmost decorator for 'def test():'


          If you switch the order of the decorator for the test() function, it simply does nothing.



          One downside is that you have to call this method explicitly in every file.
          I don't exactly know how reliable this is, I admit it is pretty ugly, and I won't take any responsibility if it breaks, but it's a start! I am sure there must be a better way.






          share|improve this answer























          • Nice! Slightly heavy on the hacky side, though :-)
            – MatsLindh
            Nov 14 '18 at 11:02










          • Yes, I am aware, and I would not deploy this in production. But it's an interesting problem and I just wanted to provide some kind of solution.
            – RunOrVeith
            Nov 14 '18 at 11:04










          • I've found one slightly less hacky way of doing it for now, but it's still not ideal (and there are situations where it'll fail). Thanks for your time and possible solution!
            – MatsLindh
            Nov 14 '18 at 11:21










          • I'd be interested in your solution!
            – RunOrVeith
            Nov 14 '18 at 12:01










          • I've added it as an update to the question :-) It involves checking if the endpoint name (by default the name of the function) has been registered in the url_map as an endpoint already. If it has, the Route() decorator has been evaluated, and we can throw the DecoratorOrderError.
            – MatsLindh
            Nov 14 '18 at 12:05





















          1














          I am adding another answer, because now I have something that is the least amount of hacky (read: I am using inspect to read to source code of a given function instead of reading the whole file myself), works across modules, and can be reused for any other decorators that should always be the last one. You also do not have to use a different syntax for app.route as in the update of my other answer.



          Here is how to do this (Warning: This is quite a closure-inception):



          import flask
          import inspect


          class DecoratorOrderError(TypeError):
          pass


          def assert_last_decorator(final_decorator):
          """
          Converts a decorator so that an exception is raised when it is not the last decorator to be used on a function.
          This only works for decorator syntax, not if somebody explicitly uses the decorator, e.g.
          final_decorator = some_other_decorator(final_decorator) will still work without an exception.

          :param final_decorator: The decorator that should be made final.
          :return: The same decorator, but it checks that it is the last one before calling the inner function.
          """
          def check_decorator_order(func):
          # Use inspect to read the code of the function
          code, _ = inspect.getsourcelines(func)
          decorators =
          for line in code:
          if line.startswith("@"):
          decorators.append(line)
          else:
          break

          # Remove the "@", function calls, and any object calls, such as "app.route". We just want the name of the decorator function (e.g. "route")
          decorator_names_only = [dec.replace("@", "").split("(")[0].split(".")[-1] for dec in decorators]
          is_final_decorator = [final_decorator.__name__ == name for name in decorator_names_only]
          num_finals = sum(is_final_decorator)

          if num_finals > 1 or (num_finals == 1 and not is_final_decorator[0]):
          raise DecoratorOrderError(f"'{final_decorator.__name__}' is not the topmost decorator of function '{func.__name__}'")

          return func

          def handle_arguments(*args, **kwargs):
          # Used to pass the arguments to the final decorator

          def handle_function(f):
          # Which function should be decorated by the final decorator?
          return final_decorator(*args, **kwargs)(check_decorator_order(f))

          return handle_function

          return handle_arguments


          You can now replace the app.route function with this function, applied to the app.route function. This is important and has to be done before any use of the app.route decorator, so I suggest to just do it when creating the app.



          app = flask.Flask(__name__)
          app.route = assert_last_decorator(app.route)


          def require_administrator(func):
          @functools.wraps(func)
          def has_administrator(*args, **kwargs):
          print("Would check admin now")

          return func(*args, **kwargs)

          return has_administrator


          @app.route("/good", methods=["GET"]) # Works
          @require_administrator
          def test_good():
          return "ok"

          @require_administrator
          @app.route("/bad", methods=["GET"]) # Raises an Exception
          def test_bad():
          return "not ok"


          I believe this is pretty much what you wanted in your question.






          share|improve this answer























          • Thank you for your many attempts! This seems promising. I'll keep the question open for a while to see if anyone have any simpler answers as this still requires special app handling.
            – MatsLindh
            Nov 14 '18 at 14:50










          • I don't think you'll be able to do this completely without any modification. At least with this you only need to add one line to "update" app.route, which only has to be added once per app.
            – RunOrVeith
            Nov 14 '18 at 15:10










          • @MatsLindh Did you ever find anything better?
            – RunOrVeith
            Dec 5 '18 at 14:04










          • I've currently gone with the version I added as an update in my question as it worked out neatly for the current project at least
            – MatsLindh
            Dec 5 '18 at 14:32











          Your Answer






          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "1"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: true,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: 10,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53297513%2fdetect-if-route-is-not-outermost-wrong-order-of-decorators-in-flask%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          2 Answers
          2






          active

          oldest

          votes








          2 Answers
          2






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes









          2














          Update 2: See my other answer for a more reusable, less hack-y solution.



          Update:
          Here is an decidedly less hack-y solution. However, it requires you to use a
          custom function instead of app.route. It takes an arbitrary number of decorators, and applies them in the order given, and then makes sure that app.route is called as the final function.
          This requires that you use only this decorator for every function.



          def safe_route(rule, app, *decorators, **options):
          def _route(func):
          for decorator in decorators:
          func = decorator(func)
          return app.route(rule, **options)(func)
          return _route


          You can then use it like this:



          def require_administrator(func):
          @functools.wraps(func)
          def has_administrator(*args, **kwargs):
          print("Would check admin now")

          return func(*args, **kwargs)

          return has_administrator

          @safe_route("/", app, require_administrator, methods=["GET"])
          def test2():
          return "foo"

          test2()
          print(test2.__name__)


          This prints:



          Would check admin now
          foo
          test2


          So if all supplied decorators use functools.wraps, this also conserves the test2 name.



          Old answer:
          If you are OK with an admittedly hack-y solution, you can roll your own inspection by reading the file line by line. Here is a very rough function that does this. You can refine this quite a bit, e.g. at the moment it relies on the app being called "app",
          function definitions having at least one empty line before them (normal PEP-8 behavior, but still might be an issue), ...



          Here is the complete code I used to test it.



          import flask
          import functools
          from itertools import groupby


          class DecoratorOrderError(TypeError):
          pass


          app = flask.Flask(__name__)


          def require_administrator(func):
          @functools.wraps(func)
          def has_administrator(*args, **kwargs):
          print("Would check admin now")

          return func(*args, **kwargs)

          return has_administrator


          @require_administrator # Will raise a custom exception
          @app.route("/", methods=["GET"])
          def test():
          return "ok"


          def check_route_is_topmost_decorator():
          # Read own source
          with open(__file__) as f:
          content = [line.strip() for line in f.readlines()]

          # Split source code on line breaks
          split_by_lines = [list(group) for k, group in groupby(content, lambda x: x == "") if not k]

          # Find consecutive decorators
          decorator_groups = dict()
          for line_group in split_by_lines:
          decorators =
          for line in line_group:
          if line.startswith("@"):
          decorators.append(line)
          elif decorators:
          decorator_groups[line] = decorators
          break
          else:
          break

          # Check if app.route is the last one (if it exists)
          for func_def, decorators in decorator_groups.items():
          is_route = [dec.startswith("@app.route") for dec in decorators]
          if sum(is_route) > 1 or (sum(is_route) == 1 and not decorators[0].startswith("@app.route")):
          raise DecoratorOrderError(f"@app.route is not the topmost decorator for '{func_def}'")


          check_route_is_topmost_decorator()


          This snippet will give you the following error:



          Traceback (most recent call last):
          File "/home/vXYZ/test_sso.py", line 51, in <module>
          check_route_is_topmost_decorator()
          File "/home/vXYZ/test_sso.py", line 48, in check_route_is_topmost_decorator
          raise DecoratorOrderError(f"@app.route is not the topmost decorator for '{func_def}'")
          __main__.DecoratorOrderError: @app.route is not the topmost decorator for 'def test():'


          If you switch the order of the decorator for the test() function, it simply does nothing.



          One downside is that you have to call this method explicitly in every file.
          I don't exactly know how reliable this is, I admit it is pretty ugly, and I won't take any responsibility if it breaks, but it's a start! I am sure there must be a better way.






          share|improve this answer























          • Nice! Slightly heavy on the hacky side, though :-)
            – MatsLindh
            Nov 14 '18 at 11:02










          • Yes, I am aware, and I would not deploy this in production. But it's an interesting problem and I just wanted to provide some kind of solution.
            – RunOrVeith
            Nov 14 '18 at 11:04










          • I've found one slightly less hacky way of doing it for now, but it's still not ideal (and there are situations where it'll fail). Thanks for your time and possible solution!
            – MatsLindh
            Nov 14 '18 at 11:21










          • I'd be interested in your solution!
            – RunOrVeith
            Nov 14 '18 at 12:01










          • I've added it as an update to the question :-) It involves checking if the endpoint name (by default the name of the function) has been registered in the url_map as an endpoint already. If it has, the Route() decorator has been evaluated, and we can throw the DecoratorOrderError.
            – MatsLindh
            Nov 14 '18 at 12:05


















          2














          Update 2: See my other answer for a more reusable, less hack-y solution.



          Update:
          Here is an decidedly less hack-y solution. However, it requires you to use a
          custom function instead of app.route. It takes an arbitrary number of decorators, and applies them in the order given, and then makes sure that app.route is called as the final function.
          This requires that you use only this decorator for every function.



          def safe_route(rule, app, *decorators, **options):
          def _route(func):
          for decorator in decorators:
          func = decorator(func)
          return app.route(rule, **options)(func)
          return _route


          You can then use it like this:



          def require_administrator(func):
          @functools.wraps(func)
          def has_administrator(*args, **kwargs):
          print("Would check admin now")

          return func(*args, **kwargs)

          return has_administrator

          @safe_route("/", app, require_administrator, methods=["GET"])
          def test2():
          return "foo"

          test2()
          print(test2.__name__)


          This prints:



          Would check admin now
          foo
          test2


          So if all supplied decorators use functools.wraps, this also conserves the test2 name.



          Old answer:
          If you are OK with an admittedly hack-y solution, you can roll your own inspection by reading the file line by line. Here is a very rough function that does this. You can refine this quite a bit, e.g. at the moment it relies on the app being called "app",
          function definitions having at least one empty line before them (normal PEP-8 behavior, but still might be an issue), ...



          Here is the complete code I used to test it.



          import flask
          import functools
          from itertools import groupby


          class DecoratorOrderError(TypeError):
          pass


          app = flask.Flask(__name__)


          def require_administrator(func):
          @functools.wraps(func)
          def has_administrator(*args, **kwargs):
          print("Would check admin now")

          return func(*args, **kwargs)

          return has_administrator


          @require_administrator # Will raise a custom exception
          @app.route("/", methods=["GET"])
          def test():
          return "ok"


          def check_route_is_topmost_decorator():
          # Read own source
          with open(__file__) as f:
          content = [line.strip() for line in f.readlines()]

          # Split source code on line breaks
          split_by_lines = [list(group) for k, group in groupby(content, lambda x: x == "") if not k]

          # Find consecutive decorators
          decorator_groups = dict()
          for line_group in split_by_lines:
          decorators =
          for line in line_group:
          if line.startswith("@"):
          decorators.append(line)
          elif decorators:
          decorator_groups[line] = decorators
          break
          else:
          break

          # Check if app.route is the last one (if it exists)
          for func_def, decorators in decorator_groups.items():
          is_route = [dec.startswith("@app.route") for dec in decorators]
          if sum(is_route) > 1 or (sum(is_route) == 1 and not decorators[0].startswith("@app.route")):
          raise DecoratorOrderError(f"@app.route is not the topmost decorator for '{func_def}'")


          check_route_is_topmost_decorator()


          This snippet will give you the following error:



          Traceback (most recent call last):
          File "/home/vXYZ/test_sso.py", line 51, in <module>
          check_route_is_topmost_decorator()
          File "/home/vXYZ/test_sso.py", line 48, in check_route_is_topmost_decorator
          raise DecoratorOrderError(f"@app.route is not the topmost decorator for '{func_def}'")
          __main__.DecoratorOrderError: @app.route is not the topmost decorator for 'def test():'


          If you switch the order of the decorator for the test() function, it simply does nothing.



          One downside is that you have to call this method explicitly in every file.
          I don't exactly know how reliable this is, I admit it is pretty ugly, and I won't take any responsibility if it breaks, but it's a start! I am sure there must be a better way.






          share|improve this answer























          • Nice! Slightly heavy on the hacky side, though :-)
            – MatsLindh
            Nov 14 '18 at 11:02










          • Yes, I am aware, and I would not deploy this in production. But it's an interesting problem and I just wanted to provide some kind of solution.
            – RunOrVeith
            Nov 14 '18 at 11:04










          • I've found one slightly less hacky way of doing it for now, but it's still not ideal (and there are situations where it'll fail). Thanks for your time and possible solution!
            – MatsLindh
            Nov 14 '18 at 11:21










          • I'd be interested in your solution!
            – RunOrVeith
            Nov 14 '18 at 12:01










          • I've added it as an update to the question :-) It involves checking if the endpoint name (by default the name of the function) has been registered in the url_map as an endpoint already. If it has, the Route() decorator has been evaluated, and we can throw the DecoratorOrderError.
            – MatsLindh
            Nov 14 '18 at 12:05
















          2












          2








          2






          Update 2: See my other answer for a more reusable, less hack-y solution.



          Update:
          Here is an decidedly less hack-y solution. However, it requires you to use a
          custom function instead of app.route. It takes an arbitrary number of decorators, and applies them in the order given, and then makes sure that app.route is called as the final function.
          This requires that you use only this decorator for every function.



          def safe_route(rule, app, *decorators, **options):
          def _route(func):
          for decorator in decorators:
          func = decorator(func)
          return app.route(rule, **options)(func)
          return _route


          You can then use it like this:



          def require_administrator(func):
          @functools.wraps(func)
          def has_administrator(*args, **kwargs):
          print("Would check admin now")

          return func(*args, **kwargs)

          return has_administrator

          @safe_route("/", app, require_administrator, methods=["GET"])
          def test2():
          return "foo"

          test2()
          print(test2.__name__)


          This prints:



          Would check admin now
          foo
          test2


          So if all supplied decorators use functools.wraps, this also conserves the test2 name.



          Old answer:
          If you are OK with an admittedly hack-y solution, you can roll your own inspection by reading the file line by line. Here is a very rough function that does this. You can refine this quite a bit, e.g. at the moment it relies on the app being called "app",
          function definitions having at least one empty line before them (normal PEP-8 behavior, but still might be an issue), ...



          Here is the complete code I used to test it.



          import flask
          import functools
          from itertools import groupby


          class DecoratorOrderError(TypeError):
          pass


          app = flask.Flask(__name__)


          def require_administrator(func):
          @functools.wraps(func)
          def has_administrator(*args, **kwargs):
          print("Would check admin now")

          return func(*args, **kwargs)

          return has_administrator


          @require_administrator # Will raise a custom exception
          @app.route("/", methods=["GET"])
          def test():
          return "ok"


          def check_route_is_topmost_decorator():
          # Read own source
          with open(__file__) as f:
          content = [line.strip() for line in f.readlines()]

          # Split source code on line breaks
          split_by_lines = [list(group) for k, group in groupby(content, lambda x: x == "") if not k]

          # Find consecutive decorators
          decorator_groups = dict()
          for line_group in split_by_lines:
          decorators =
          for line in line_group:
          if line.startswith("@"):
          decorators.append(line)
          elif decorators:
          decorator_groups[line] = decorators
          break
          else:
          break

          # Check if app.route is the last one (if it exists)
          for func_def, decorators in decorator_groups.items():
          is_route = [dec.startswith("@app.route") for dec in decorators]
          if sum(is_route) > 1 or (sum(is_route) == 1 and not decorators[0].startswith("@app.route")):
          raise DecoratorOrderError(f"@app.route is not the topmost decorator for '{func_def}'")


          check_route_is_topmost_decorator()


          This snippet will give you the following error:



          Traceback (most recent call last):
          File "/home/vXYZ/test_sso.py", line 51, in <module>
          check_route_is_topmost_decorator()
          File "/home/vXYZ/test_sso.py", line 48, in check_route_is_topmost_decorator
          raise DecoratorOrderError(f"@app.route is not the topmost decorator for '{func_def}'")
          __main__.DecoratorOrderError: @app.route is not the topmost decorator for 'def test():'


          If you switch the order of the decorator for the test() function, it simply does nothing.



          One downside is that you have to call this method explicitly in every file.
          I don't exactly know how reliable this is, I admit it is pretty ugly, and I won't take any responsibility if it breaks, but it's a start! I am sure there must be a better way.






          share|improve this answer














          Update 2: See my other answer for a more reusable, less hack-y solution.



          Update:
          Here is an decidedly less hack-y solution. However, it requires you to use a
          custom function instead of app.route. It takes an arbitrary number of decorators, and applies them in the order given, and then makes sure that app.route is called as the final function.
          This requires that you use only this decorator for every function.



          def safe_route(rule, app, *decorators, **options):
          def _route(func):
          for decorator in decorators:
          func = decorator(func)
          return app.route(rule, **options)(func)
          return _route


          You can then use it like this:



          def require_administrator(func):
          @functools.wraps(func)
          def has_administrator(*args, **kwargs):
          print("Would check admin now")

          return func(*args, **kwargs)

          return has_administrator

          @safe_route("/", app, require_administrator, methods=["GET"])
          def test2():
          return "foo"

          test2()
          print(test2.__name__)


          This prints:



          Would check admin now
          foo
          test2


          So if all supplied decorators use functools.wraps, this also conserves the test2 name.



          Old answer:
          If you are OK with an admittedly hack-y solution, you can roll your own inspection by reading the file line by line. Here is a very rough function that does this. You can refine this quite a bit, e.g. at the moment it relies on the app being called "app",
          function definitions having at least one empty line before them (normal PEP-8 behavior, but still might be an issue), ...



          Here is the complete code I used to test it.



          import flask
          import functools
          from itertools import groupby


          class DecoratorOrderError(TypeError):
          pass


          app = flask.Flask(__name__)


          def require_administrator(func):
          @functools.wraps(func)
          def has_administrator(*args, **kwargs):
          print("Would check admin now")

          return func(*args, **kwargs)

          return has_administrator


          @require_administrator # Will raise a custom exception
          @app.route("/", methods=["GET"])
          def test():
          return "ok"


          def check_route_is_topmost_decorator():
          # Read own source
          with open(__file__) as f:
          content = [line.strip() for line in f.readlines()]

          # Split source code on line breaks
          split_by_lines = [list(group) for k, group in groupby(content, lambda x: x == "") if not k]

          # Find consecutive decorators
          decorator_groups = dict()
          for line_group in split_by_lines:
          decorators =
          for line in line_group:
          if line.startswith("@"):
          decorators.append(line)
          elif decorators:
          decorator_groups[line] = decorators
          break
          else:
          break

          # Check if app.route is the last one (if it exists)
          for func_def, decorators in decorator_groups.items():
          is_route = [dec.startswith("@app.route") for dec in decorators]
          if sum(is_route) > 1 or (sum(is_route) == 1 and not decorators[0].startswith("@app.route")):
          raise DecoratorOrderError(f"@app.route is not the topmost decorator for '{func_def}'")


          check_route_is_topmost_decorator()


          This snippet will give you the following error:



          Traceback (most recent call last):
          File "/home/vXYZ/test_sso.py", line 51, in <module>
          check_route_is_topmost_decorator()
          File "/home/vXYZ/test_sso.py", line 48, in check_route_is_topmost_decorator
          raise DecoratorOrderError(f"@app.route is not the topmost decorator for '{func_def}'")
          __main__.DecoratorOrderError: @app.route is not the topmost decorator for 'def test():'


          If you switch the order of the decorator for the test() function, it simply does nothing.



          One downside is that you have to call this method explicitly in every file.
          I don't exactly know how reliable this is, I admit it is pretty ugly, and I won't take any responsibility if it breaks, but it's a start! I am sure there must be a better way.







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Nov 14 '18 at 14:02

























          answered Nov 14 '18 at 10:59









          RunOrVeith

          1,0961023




          1,0961023












          • Nice! Slightly heavy on the hacky side, though :-)
            – MatsLindh
            Nov 14 '18 at 11:02










          • Yes, I am aware, and I would not deploy this in production. But it's an interesting problem and I just wanted to provide some kind of solution.
            – RunOrVeith
            Nov 14 '18 at 11:04










          • I've found one slightly less hacky way of doing it for now, but it's still not ideal (and there are situations where it'll fail). Thanks for your time and possible solution!
            – MatsLindh
            Nov 14 '18 at 11:21










          • I'd be interested in your solution!
            – RunOrVeith
            Nov 14 '18 at 12:01










          • I've added it as an update to the question :-) It involves checking if the endpoint name (by default the name of the function) has been registered in the url_map as an endpoint already. If it has, the Route() decorator has been evaluated, and we can throw the DecoratorOrderError.
            – MatsLindh
            Nov 14 '18 at 12:05




















          • Nice! Slightly heavy on the hacky side, though :-)
            – MatsLindh
            Nov 14 '18 at 11:02










          • Yes, I am aware, and I would not deploy this in production. But it's an interesting problem and I just wanted to provide some kind of solution.
            – RunOrVeith
            Nov 14 '18 at 11:04










          • I've found one slightly less hacky way of doing it for now, but it's still not ideal (and there are situations where it'll fail). Thanks for your time and possible solution!
            – MatsLindh
            Nov 14 '18 at 11:21










          • I'd be interested in your solution!
            – RunOrVeith
            Nov 14 '18 at 12:01










          • I've added it as an update to the question :-) It involves checking if the endpoint name (by default the name of the function) has been registered in the url_map as an endpoint already. If it has, the Route() decorator has been evaluated, and we can throw the DecoratorOrderError.
            – MatsLindh
            Nov 14 '18 at 12:05


















          Nice! Slightly heavy on the hacky side, though :-)
          – MatsLindh
          Nov 14 '18 at 11:02




          Nice! Slightly heavy on the hacky side, though :-)
          – MatsLindh
          Nov 14 '18 at 11:02












          Yes, I am aware, and I would not deploy this in production. But it's an interesting problem and I just wanted to provide some kind of solution.
          – RunOrVeith
          Nov 14 '18 at 11:04




          Yes, I am aware, and I would not deploy this in production. But it's an interesting problem and I just wanted to provide some kind of solution.
          – RunOrVeith
          Nov 14 '18 at 11:04












          I've found one slightly less hacky way of doing it for now, but it's still not ideal (and there are situations where it'll fail). Thanks for your time and possible solution!
          – MatsLindh
          Nov 14 '18 at 11:21




          I've found one slightly less hacky way of doing it for now, but it's still not ideal (and there are situations where it'll fail). Thanks for your time and possible solution!
          – MatsLindh
          Nov 14 '18 at 11:21












          I'd be interested in your solution!
          – RunOrVeith
          Nov 14 '18 at 12:01




          I'd be interested in your solution!
          – RunOrVeith
          Nov 14 '18 at 12:01












          I've added it as an update to the question :-) It involves checking if the endpoint name (by default the name of the function) has been registered in the url_map as an endpoint already. If it has, the Route() decorator has been evaluated, and we can throw the DecoratorOrderError.
          – MatsLindh
          Nov 14 '18 at 12:05






          I've added it as an update to the question :-) It involves checking if the endpoint name (by default the name of the function) has been registered in the url_map as an endpoint already. If it has, the Route() decorator has been evaluated, and we can throw the DecoratorOrderError.
          – MatsLindh
          Nov 14 '18 at 12:05















          1














          I am adding another answer, because now I have something that is the least amount of hacky (read: I am using inspect to read to source code of a given function instead of reading the whole file myself), works across modules, and can be reused for any other decorators that should always be the last one. You also do not have to use a different syntax for app.route as in the update of my other answer.



          Here is how to do this (Warning: This is quite a closure-inception):



          import flask
          import inspect


          class DecoratorOrderError(TypeError):
          pass


          def assert_last_decorator(final_decorator):
          """
          Converts a decorator so that an exception is raised when it is not the last decorator to be used on a function.
          This only works for decorator syntax, not if somebody explicitly uses the decorator, e.g.
          final_decorator = some_other_decorator(final_decorator) will still work without an exception.

          :param final_decorator: The decorator that should be made final.
          :return: The same decorator, but it checks that it is the last one before calling the inner function.
          """
          def check_decorator_order(func):
          # Use inspect to read the code of the function
          code, _ = inspect.getsourcelines(func)
          decorators =
          for line in code:
          if line.startswith("@"):
          decorators.append(line)
          else:
          break

          # Remove the "@", function calls, and any object calls, such as "app.route". We just want the name of the decorator function (e.g. "route")
          decorator_names_only = [dec.replace("@", "").split("(")[0].split(".")[-1] for dec in decorators]
          is_final_decorator = [final_decorator.__name__ == name for name in decorator_names_only]
          num_finals = sum(is_final_decorator)

          if num_finals > 1 or (num_finals == 1 and not is_final_decorator[0]):
          raise DecoratorOrderError(f"'{final_decorator.__name__}' is not the topmost decorator of function '{func.__name__}'")

          return func

          def handle_arguments(*args, **kwargs):
          # Used to pass the arguments to the final decorator

          def handle_function(f):
          # Which function should be decorated by the final decorator?
          return final_decorator(*args, **kwargs)(check_decorator_order(f))

          return handle_function

          return handle_arguments


          You can now replace the app.route function with this function, applied to the app.route function. This is important and has to be done before any use of the app.route decorator, so I suggest to just do it when creating the app.



          app = flask.Flask(__name__)
          app.route = assert_last_decorator(app.route)


          def require_administrator(func):
          @functools.wraps(func)
          def has_administrator(*args, **kwargs):
          print("Would check admin now")

          return func(*args, **kwargs)

          return has_administrator


          @app.route("/good", methods=["GET"]) # Works
          @require_administrator
          def test_good():
          return "ok"

          @require_administrator
          @app.route("/bad", methods=["GET"]) # Raises an Exception
          def test_bad():
          return "not ok"


          I believe this is pretty much what you wanted in your question.






          share|improve this answer























          • Thank you for your many attempts! This seems promising. I'll keep the question open for a while to see if anyone have any simpler answers as this still requires special app handling.
            – MatsLindh
            Nov 14 '18 at 14:50










          • I don't think you'll be able to do this completely without any modification. At least with this you only need to add one line to "update" app.route, which only has to be added once per app.
            – RunOrVeith
            Nov 14 '18 at 15:10










          • @MatsLindh Did you ever find anything better?
            – RunOrVeith
            Dec 5 '18 at 14:04










          • I've currently gone with the version I added as an update in my question as it worked out neatly for the current project at least
            – MatsLindh
            Dec 5 '18 at 14:32
















          1














          I am adding another answer, because now I have something that is the least amount of hacky (read: I am using inspect to read to source code of a given function instead of reading the whole file myself), works across modules, and can be reused for any other decorators that should always be the last one. You also do not have to use a different syntax for app.route as in the update of my other answer.



          Here is how to do this (Warning: This is quite a closure-inception):



          import flask
          import inspect


          class DecoratorOrderError(TypeError):
          pass


          def assert_last_decorator(final_decorator):
          """
          Converts a decorator so that an exception is raised when it is not the last decorator to be used on a function.
          This only works for decorator syntax, not if somebody explicitly uses the decorator, e.g.
          final_decorator = some_other_decorator(final_decorator) will still work without an exception.

          :param final_decorator: The decorator that should be made final.
          :return: The same decorator, but it checks that it is the last one before calling the inner function.
          """
          def check_decorator_order(func):
          # Use inspect to read the code of the function
          code, _ = inspect.getsourcelines(func)
          decorators =
          for line in code:
          if line.startswith("@"):
          decorators.append(line)
          else:
          break

          # Remove the "@", function calls, and any object calls, such as "app.route". We just want the name of the decorator function (e.g. "route")
          decorator_names_only = [dec.replace("@", "").split("(")[0].split(".")[-1] for dec in decorators]
          is_final_decorator = [final_decorator.__name__ == name for name in decorator_names_only]
          num_finals = sum(is_final_decorator)

          if num_finals > 1 or (num_finals == 1 and not is_final_decorator[0]):
          raise DecoratorOrderError(f"'{final_decorator.__name__}' is not the topmost decorator of function '{func.__name__}'")

          return func

          def handle_arguments(*args, **kwargs):
          # Used to pass the arguments to the final decorator

          def handle_function(f):
          # Which function should be decorated by the final decorator?
          return final_decorator(*args, **kwargs)(check_decorator_order(f))

          return handle_function

          return handle_arguments


          You can now replace the app.route function with this function, applied to the app.route function. This is important and has to be done before any use of the app.route decorator, so I suggest to just do it when creating the app.



          app = flask.Flask(__name__)
          app.route = assert_last_decorator(app.route)


          def require_administrator(func):
          @functools.wraps(func)
          def has_administrator(*args, **kwargs):
          print("Would check admin now")

          return func(*args, **kwargs)

          return has_administrator


          @app.route("/good", methods=["GET"]) # Works
          @require_administrator
          def test_good():
          return "ok"

          @require_administrator
          @app.route("/bad", methods=["GET"]) # Raises an Exception
          def test_bad():
          return "not ok"


          I believe this is pretty much what you wanted in your question.






          share|improve this answer























          • Thank you for your many attempts! This seems promising. I'll keep the question open for a while to see if anyone have any simpler answers as this still requires special app handling.
            – MatsLindh
            Nov 14 '18 at 14:50










          • I don't think you'll be able to do this completely without any modification. At least with this you only need to add one line to "update" app.route, which only has to be added once per app.
            – RunOrVeith
            Nov 14 '18 at 15:10










          • @MatsLindh Did you ever find anything better?
            – RunOrVeith
            Dec 5 '18 at 14:04










          • I've currently gone with the version I added as an update in my question as it worked out neatly for the current project at least
            – MatsLindh
            Dec 5 '18 at 14:32














          1












          1








          1






          I am adding another answer, because now I have something that is the least amount of hacky (read: I am using inspect to read to source code of a given function instead of reading the whole file myself), works across modules, and can be reused for any other decorators that should always be the last one. You also do not have to use a different syntax for app.route as in the update of my other answer.



          Here is how to do this (Warning: This is quite a closure-inception):



          import flask
          import inspect


          class DecoratorOrderError(TypeError):
          pass


          def assert_last_decorator(final_decorator):
          """
          Converts a decorator so that an exception is raised when it is not the last decorator to be used on a function.
          This only works for decorator syntax, not if somebody explicitly uses the decorator, e.g.
          final_decorator = some_other_decorator(final_decorator) will still work without an exception.

          :param final_decorator: The decorator that should be made final.
          :return: The same decorator, but it checks that it is the last one before calling the inner function.
          """
          def check_decorator_order(func):
          # Use inspect to read the code of the function
          code, _ = inspect.getsourcelines(func)
          decorators =
          for line in code:
          if line.startswith("@"):
          decorators.append(line)
          else:
          break

          # Remove the "@", function calls, and any object calls, such as "app.route". We just want the name of the decorator function (e.g. "route")
          decorator_names_only = [dec.replace("@", "").split("(")[0].split(".")[-1] for dec in decorators]
          is_final_decorator = [final_decorator.__name__ == name for name in decorator_names_only]
          num_finals = sum(is_final_decorator)

          if num_finals > 1 or (num_finals == 1 and not is_final_decorator[0]):
          raise DecoratorOrderError(f"'{final_decorator.__name__}' is not the topmost decorator of function '{func.__name__}'")

          return func

          def handle_arguments(*args, **kwargs):
          # Used to pass the arguments to the final decorator

          def handle_function(f):
          # Which function should be decorated by the final decorator?
          return final_decorator(*args, **kwargs)(check_decorator_order(f))

          return handle_function

          return handle_arguments


          You can now replace the app.route function with this function, applied to the app.route function. This is important and has to be done before any use of the app.route decorator, so I suggest to just do it when creating the app.



          app = flask.Flask(__name__)
          app.route = assert_last_decorator(app.route)


          def require_administrator(func):
          @functools.wraps(func)
          def has_administrator(*args, **kwargs):
          print("Would check admin now")

          return func(*args, **kwargs)

          return has_administrator


          @app.route("/good", methods=["GET"]) # Works
          @require_administrator
          def test_good():
          return "ok"

          @require_administrator
          @app.route("/bad", methods=["GET"]) # Raises an Exception
          def test_bad():
          return "not ok"


          I believe this is pretty much what you wanted in your question.






          share|improve this answer














          I am adding another answer, because now I have something that is the least amount of hacky (read: I am using inspect to read to source code of a given function instead of reading the whole file myself), works across modules, and can be reused for any other decorators that should always be the last one. You also do not have to use a different syntax for app.route as in the update of my other answer.



          Here is how to do this (Warning: This is quite a closure-inception):



          import flask
          import inspect


          class DecoratorOrderError(TypeError):
          pass


          def assert_last_decorator(final_decorator):
          """
          Converts a decorator so that an exception is raised when it is not the last decorator to be used on a function.
          This only works for decorator syntax, not if somebody explicitly uses the decorator, e.g.
          final_decorator = some_other_decorator(final_decorator) will still work without an exception.

          :param final_decorator: The decorator that should be made final.
          :return: The same decorator, but it checks that it is the last one before calling the inner function.
          """
          def check_decorator_order(func):
          # Use inspect to read the code of the function
          code, _ = inspect.getsourcelines(func)
          decorators =
          for line in code:
          if line.startswith("@"):
          decorators.append(line)
          else:
          break

          # Remove the "@", function calls, and any object calls, such as "app.route". We just want the name of the decorator function (e.g. "route")
          decorator_names_only = [dec.replace("@", "").split("(")[0].split(".")[-1] for dec in decorators]
          is_final_decorator = [final_decorator.__name__ == name for name in decorator_names_only]
          num_finals = sum(is_final_decorator)

          if num_finals > 1 or (num_finals == 1 and not is_final_decorator[0]):
          raise DecoratorOrderError(f"'{final_decorator.__name__}' is not the topmost decorator of function '{func.__name__}'")

          return func

          def handle_arguments(*args, **kwargs):
          # Used to pass the arguments to the final decorator

          def handle_function(f):
          # Which function should be decorated by the final decorator?
          return final_decorator(*args, **kwargs)(check_decorator_order(f))

          return handle_function

          return handle_arguments


          You can now replace the app.route function with this function, applied to the app.route function. This is important and has to be done before any use of the app.route decorator, so I suggest to just do it when creating the app.



          app = flask.Flask(__name__)
          app.route = assert_last_decorator(app.route)


          def require_administrator(func):
          @functools.wraps(func)
          def has_administrator(*args, **kwargs):
          print("Would check admin now")

          return func(*args, **kwargs)

          return has_administrator


          @app.route("/good", methods=["GET"]) # Works
          @require_administrator
          def test_good():
          return "ok"

          @require_administrator
          @app.route("/bad", methods=["GET"]) # Raises an Exception
          def test_bad():
          return "not ok"


          I believe this is pretty much what you wanted in your question.







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Nov 14 '18 at 14:04

























          answered Nov 14 '18 at 13:57









          RunOrVeith

          1,0961023




          1,0961023












          • Thank you for your many attempts! This seems promising. I'll keep the question open for a while to see if anyone have any simpler answers as this still requires special app handling.
            – MatsLindh
            Nov 14 '18 at 14:50










          • I don't think you'll be able to do this completely without any modification. At least with this you only need to add one line to "update" app.route, which only has to be added once per app.
            – RunOrVeith
            Nov 14 '18 at 15:10










          • @MatsLindh Did you ever find anything better?
            – RunOrVeith
            Dec 5 '18 at 14:04










          • I've currently gone with the version I added as an update in my question as it worked out neatly for the current project at least
            – MatsLindh
            Dec 5 '18 at 14:32


















          • Thank you for your many attempts! This seems promising. I'll keep the question open for a while to see if anyone have any simpler answers as this still requires special app handling.
            – MatsLindh
            Nov 14 '18 at 14:50










          • I don't think you'll be able to do this completely without any modification. At least with this you only need to add one line to "update" app.route, which only has to be added once per app.
            – RunOrVeith
            Nov 14 '18 at 15:10










          • @MatsLindh Did you ever find anything better?
            – RunOrVeith
            Dec 5 '18 at 14:04










          • I've currently gone with the version I added as an update in my question as it worked out neatly for the current project at least
            – MatsLindh
            Dec 5 '18 at 14:32
















          Thank you for your many attempts! This seems promising. I'll keep the question open for a while to see if anyone have any simpler answers as this still requires special app handling.
          – MatsLindh
          Nov 14 '18 at 14:50




          Thank you for your many attempts! This seems promising. I'll keep the question open for a while to see if anyone have any simpler answers as this still requires special app handling.
          – MatsLindh
          Nov 14 '18 at 14:50












          I don't think you'll be able to do this completely without any modification. At least with this you only need to add one line to "update" app.route, which only has to be added once per app.
          – RunOrVeith
          Nov 14 '18 at 15:10




          I don't think you'll be able to do this completely without any modification. At least with this you only need to add one line to "update" app.route, which only has to be added once per app.
          – RunOrVeith
          Nov 14 '18 at 15:10












          @MatsLindh Did you ever find anything better?
          – RunOrVeith
          Dec 5 '18 at 14:04




          @MatsLindh Did you ever find anything better?
          – RunOrVeith
          Dec 5 '18 at 14:04












          I've currently gone with the version I added as an update in my question as it worked out neatly for the current project at least
          – MatsLindh
          Dec 5 '18 at 14:32




          I've currently gone with the version I added as an update in my question as it worked out neatly for the current project at least
          – MatsLindh
          Dec 5 '18 at 14:32


















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Stack Overflow!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.





          Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


          Please pay close attention to the following guidance:


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53297513%2fdetect-if-route-is-not-outermost-wrong-order-of-decorators-in-flask%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          Guess what letter conforming each word

          Port of Spain

          Run scheduled task as local user group (not BUILTIN)