2

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.

Richard Neumann
  • 2,986
  • 2
  • 25
  • 50
  • 1
    Yes you *do* really need to import the module itself. That is the only way to run the code in it. – Daniel Roseman Nov 16 '17 at 16:08
  • _"When I import the webapp.router module, the webapp.service module does obviously not run."_ What? How is that obvious? If you import a module, your code stops running? Why would that ever be the case? I don't understand the problem at all. – Aran-Fey Nov 16 '17 at 16:11
  • @DanielRoseman But then any linter will complain about unused imports. Is there a more elegant way? – Richard Neumann Nov 16 '17 at 16:11
  • @Rawing Why should code from a module run if it is not imported? – Richard Neumann Nov 16 '17 at 16:12
  • Oh, you're not executing `webapp.service`? I see. In that case, the `webapp` module is designed incorrectly and should be refactored. Importing a module should never be (directly) linked to enabling a certain feature/behavior. – Aran-Fey Nov 16 '17 at 16:15
  • @Rawing I tend to agree. In the given case, I should maybe not use the decorators. It seemed like a good idea first, but now seems to be doomed. – Richard Neumann Nov 16 '17 at 16:16
  • I'd prefer to see things the other way round, where only the Router class was defined in webapp.router and then instantiated at the top of webapp.service before being used in the decorators. Incidentally this is how Django's template tag registration works. – Daniel Roseman Nov 16 '17 at 16:19
  • @DanielRoseman Actually I designed the used framwork with `flask` in mind, which also has the `@app.route()` decorator. – Richard Neumann Nov 16 '17 at 16:21

1 Answers1

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__)

jsbueno
  • 99,910
  • 10
  • 151
  • 209