Exa
Exa

Reputation: 181

Module namespace initialisation before execution

I'm trying to dynamically update code during runtime by reloading modules using importlib.reload. However, I need a specific module variable to be set before the module's code is executed. I could easily set it as an attribute after reloading but each module would have already executed its code (e.g., defined its default arguments).

A simple example:

# module.py

def do():
  try:
    print(a)
  except NameError:
    print('failed')
# main.py

import module

module.do() # prints failed

module.a = 'succeeded'

module.do() # prints succeeded

The desired pseudocode:

import_module_without_executing_code module

module.initialise(a = 'succeeded')

module.do()

Is there a way to control module namespace initialisation (like with classes using metaclasses)?

Upvotes: 3

Views: 141

Answers (2)

Ken Bellows
Ken Bellows

Reputation: 6930

Strictly speaking, I don't believe there is a way to do what you're describing in Python natively. However, assuming you own the module you're trying to import, a common approach with Python modules that need some initializing input is to use an init function.

If all you need is some internal variables to be set, like a in you example above, that's easy: just declare some module-global variables and set them in your init function:

Demo: https://repl.it/MyK0

Module:

## mymodule.py

a = None

def do():
  print(a)


def init(_a):
  global a
  a = _a

Main:

## main.py

import mymodule

mymodule.init(123)
mymodule.do()

mymodule.init('foo')
mymodule.do()

Output:

123
foo

Where things can get trickier is if you need to actually redefine some functions because some dynamic internal something is dependent on the input you give. Here's one solution, borrowed from https://stackoverflow.com/a/1676860. Basically, the idea is to grab a reference to the current module by using the magic variable __name__ to index into the system module dictionary, sys.modules, and then define or overwrite the functions that need it. We can define the functions locally as inner functions, then add them to the module:

Demo: https://repl.it/MyHT/2

Module:

## mymodule.py

import sys

def init(a):
  current_module = sys.modules[__name__]
  def _do():
    try:
      print(a)
    except NameError:
      print('failed')
  current_module.do = _do

Upvotes: 0

Davis Herring
Davis Herring

Reputation: 39788

It's not usually a good idea to use reload other than for interactive debugging. For example, it can easily create situations where two objects of type module.A are not the same type.

What you want is execfile. Pass a globals dictionary (you don't need an explicit locals dictionary) to keep each execution isolated; anything you store in it ahead of time acts exactly like the "pre-set" variables you want. If you do want to have a "real" module interface change, you can have a wrapper module that calls (or just holds as an attribute) the most recently loaded function from your changing file.

Of course, since you're using Python 3, you'll have to use one of the replacements for execfile.

Upvotes: 1

Related Questions