Load module to invoke its decorators

2019-07-26 15:35发布

问题:

I have a program consistring of several modules specifying the respective web application handlers and one, specifying the respective router.
The library I use can be found here.

Excerpt from webapp.service (there are more such modules):

from webapp.router import ROUTER

@ROUTER.route('/service/[id:int]')
class ServicePermissions(AuthenticatedService):
    """Handles service permissions."""

    NODE = 'services'
    NAME = 'services manager'
    DESCRIPTION = 'Manages services permissions'
    PROMOTE = False

webapp.router:

ROUTER = Router()

When I import the webapp.router module, the webapp.service module does obviously not run. Hence, the @ROUTER.route('/service/[id:int]') decorator is not run and my web aplication will fail with the message, that the respective route is not available.

What is the best practice in that case to run the code in webapp.service to "run" the decorators? I do not really need to import the module itself or any of its members.

回答1:

As stated in the comments fot the question, you simply have to import the modules. As for linter complaints, those are the lesser of your problems. Linters are there to help - if they get into the way, just don't listen to them.

So, the simple way just to get your things working is, at the end of your __main__.py or __init__.py, depending on your app structure, to import explicitly all the modules that make use of the view decorator.

If you have a linter, check how to silence it on the import lines - that is usually accomplished with a special comment on the import line.

Python's introspection is fantastic, but it can't find instances of a class, or subclasses, if those are defined in modules that are not imported: such a module is just a text file sitting on the disk, like any data file.

What some frameworks offer as an approach is to have a "discovery" utility that will silently import all "py" files in the project folders. That way your views can "come into existence" without explicit imports.

You could use a function like:

import os

def discover(caller_file):
    caller_folder = os.path.dirname(caller_file)
    for current, folders, files in os.walk(caller_folder):
        if current == "__pycache__":
            continue
        for file in files:
            if file.endswith(".py"):
                __import__(os.path.join(current, file))

And call it on your main module with discover(__file__)