user1502776
user1502776

Reputation: 562

Override importing module's built-in functions from imported module

I have an application where I would like to override certain functions when a condition occurs, for example:

condition_check.py:

Flag = True
import ctypes  # An included library with Python install.
import inspect
def MsgBox(msg):
    ctypes.windll.user32.MessageBoxA(0, msg, 'MsgBox', 1)
def check():
    global print
    if Flag:
        def print(msg):
                MsgBox(msg)
    else:
        pass

main.py:

## works
from condition_check import *
MsgBox('this is msgbox')
print('this is a print')

## does not work
import condition_check
condition_check.MsgBox('this is msgbox')
print('this is a print')

I understand that the condition_check.py is overriding its own print instead of the main.py's print. I believe inspect library could be used for this purpose but I am not able to lookup an example.

Upvotes: 3

Views: 1796

Answers (1)

zondo
zondo

Reputation: 20336

I am assuming that you are using Python 3. If you are, you need merely set an attribute of the built-in module.

import builtins
import ctypes

original = builtins.print
Flag = True

def MsgBox(msg):
    ctypes.windll.user32.MessageBoxA(0, msg, 'MsgBox', 1)

def check():
    if Flag:
        builtins.print = MsgBox
    else:
        builtins.print = original

I will note, however, a couple things:

  1. Flag is not a good name for two reasons: 1, it isn't at all descriptive. A flag merely means that it is True or False; it doesn't say anything about what it is for. 2, the official style guide for Python (PEP 8) recommends snake_case, not PascalCase for regular variables. PascalCase should be used only for classes.

  2. Wildcard imports (from <module> import *) are not recommended by PEP 8 because they make it unclear which names are present in the namespace, confusing both readers and automated tools. (Almost an exact quotation from the section on Imports.)

  3. You don't need to override the print function. A better way is to override sys.stdout to a stream that controls where it goes:

    import ctypes
    import sys
    
    def MsgBox(msg):
        ctypes.windll.user32.MessageBoxA(0, msg, 'MsgBox', 1)
    
    class Printer:
        def __init__(self, original, alternate, use_alternate):
            self.original = original
            self.alternate = alternate
            self.use_alternate = use_alternate
    
        def write(self, msg):
            if self.use_alternate:
                return self.alternate(msg)
            return self.original(msg)
    
    sys.stdout = printer = Printer(sys.stdout.write, MsgBox, True)
    

    Your flag is then printer.use_alternate. Besides being much easier to control, this is also compatible with Python 2, even though the Python 2 print is a statement. This does have the slight disadvantage of keeping the newline that print adds, but one can always use an alternate of something like lambda msg: MsgBox(msg.strip())

Upvotes: 2

Related Questions