OiO.lk Community platform!

Oio.lk is an excellent forum for developers, providing a wide range of resources, discussions, and support for those in the developer community. Join oio.lk today to connect with like-minded professionals, share insights, and stay updated on the latest trends and technologies in the development field.
  You need to log in or register to access the solved answers to this problem.
  • You have reached the maximum number of guest views allowed
  • Please register below to remove this limitation

Wrapped functions of a Python module raise TypeError

  • Thread starter Thread starter Green绿色
  • Start date Start date
G

Green绿色

Guest
I am currently trying to replace a module in a large code base in a certain condition and to figure out when any function of this module is called, I wrap each function/method in the module with a warning that prints a short message with the stack trace. The implementation of this method is as follows (printing the stack trace was omitted):

Code:
def wrap_module_with_warnings(module):
    for fn_name, fn in inspect.getmembers(module):
        if (not (inspect.isfunction(fn) or inspect.ismethod(fn))
                or fn_name.startswith('_')
                or inspect.getmodule(fn) is not module):
            continue

        @functools.wraps(fn)
        def wrapped_fn(*args, **kwargs):
            warnings.warn(f"The function {fn_name} was called.")
            return fn(*args, **kwargs)

        setattr(module, fn_name, wrapped_fn)

Unfortunately, this function doesn't work as I'd expect. My expectation is that it replaces all functions in module with its wrapped variant that just prints a warning and then calls the original function. However, in reality, it throws TypeError: 'module' object is not callable:

Code:
/tmp/question/main.py:22: UserWarning: The function random was called.
  warnings.warn(f"The function {fn_name} was called.")
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
File /tmp/question/main.py:31
     29 print(f"Output before wrapping: {mod.f()}")
     30 wrap_module_with_warnings(mod)
---> 31 print(f"Output after wrapping: {mod.f()}")

File /tmp/question/main.py:23, in wrap_module_with_warnings.<locals>.wrapped_fn(*args, **kwargs)
     20 @functools.wraps(fn)
     21 def wrapped_fn(*args, **kwargs):
     22     warnings.warn(f"The function {fn_name} was called.")
---> 23     return fn(*args, **kwargs)

TypeError: 'module' object is not callable

To demonstrate my problem, I'm using the following main code:

Code:
import mod

if __name__ == '__main__':
    print(f"Output before wrapping: {mod.f()}")
    wrap_module_with_warnings(mod)
    print(f"Output after wrapping: {mod.f()}")

and the example module mod.py:

Code:
import random


def f() -> int:
    return random.randint(0, 1000)

The error suggests that mod.f is a module. Examining the type of mod.f after calling my wrapper method reveals that it's actually a function:

Code:
In [1]: type(mod.f)
Out[1]: function

To examine the original function, I changed the last line of wrap_module_with_warnings to:

Code:
setattr(module, fn_name, (fn, wrapped_fn))

and examine mod.f after calling the wrapper doesn't help much either:

Code:
In [1]: mod.f[0]
Out[1]: <function mod.f() -> int>

In [2]: mod.f[1]
Out[2]: <function mod.f() -> int>

In [3]: type(mod.f[0])
Out[3]: function

In [4]: type(mod.f[1])
Out[4]: function

In [5]: mod.f[0]()
Out[5]: 785

In [6]: mod.f[1]()
/tmp/AscendSpeed/q/main.py:22: UserWarning: The function random was called.
  warnings.warn(f"The function {fn_name} was called.")
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[6], line 1
----> 1 mod.f[1]()

File /tmp/AscendSpeed/q/main.py:23, in wrap_module_with_warnings.<locals>.wrapped_fn(*args, **kwargs)
     20 @functools.wraps(fn)
     21 def wrapped_fn(*args, **kwargs):
     22     warnings.warn(f"The function {fn_name} was called.")
---> 23     return fn(*args, **kwargs)

TypeError: 'module' object is not callable

I'm pretty sure that this code should work, fn should be the function mod.f, but why do I get the TypeError then?
<p>I am currently trying to replace a module in a large code base in a certain condition and to figure out when any function of this module is called, I wrap each function/method in the module with a warning that prints a short message with the stack trace. The implementation of this method is as follows (printing the stack trace was omitted):</p>
<pre><code>def wrap_module_with_warnings(module):
for fn_name, fn in inspect.getmembers(module):
if (not (inspect.isfunction(fn) or inspect.ismethod(fn))
or fn_name.startswith('_')
or inspect.getmodule(fn) is not module):
continue

@functools.wraps(fn)
def wrapped_fn(*args, **kwargs):
warnings.warn(f"The function {fn_name} was called.")
return fn(*args, **kwargs)

setattr(module, fn_name, wrapped_fn)
</code></pre>
<p>Unfortunately, this function doesn't work as I'd expect. My expectation is that it replaces all functions in <code>module</code> with its wrapped variant that just prints a warning and then calls the original function. However, in reality, it throws <code>TypeError: 'module' object is not callable</code>:</p>
<pre><code>/tmp/question/main.py:22: UserWarning: The function random was called.
warnings.warn(f"The function {fn_name} was called.")
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
File /tmp/question/main.py:31
29 print(f"Output before wrapping: {mod.f()}")
30 wrap_module_with_warnings(mod)
---> 31 print(f"Output after wrapping: {mod.f()}")

File /tmp/question/main.py:23, in wrap_module_with_warnings.<locals>.wrapped_fn(*args, **kwargs)
20 @functools.wraps(fn)
21 def wrapped_fn(*args, **kwargs):
22 warnings.warn(f"The function {fn_name} was called.")
---> 23 return fn(*args, **kwargs)

TypeError: 'module' object is not callable
</code></pre>
<p>To demonstrate my problem, I'm using the following main code:</p>
<pre><code>import mod

if __name__ == '__main__':
print(f"Output before wrapping: {mod.f()}")
wrap_module_with_warnings(mod)
print(f"Output after wrapping: {mod.f()}")
</code></pre>
<p>and the example module <code>mod.py</code>:</p>
<pre><code>import random


def f() -> int:
return random.randint(0, 1000)
</code></pre>
<p>The error suggests that <code>mod.f</code> is a module. Examining the type of <code>mod.f</code> after calling my wrapper method reveals that it's actually a function:</p>
<pre><code>In [1]: type(mod.f)
Out[1]: function
</code></pre>
<p>To examine the original function, I changed the last line of <code>wrap_module_with_warnings</code> to:</p>
<pre><code>setattr(module, fn_name, (fn, wrapped_fn))
</code></pre>
<p>and examine <code>mod.f</code> after calling the wrapper doesn't help much either:</p>
<pre><code>In [1]: mod.f[0]
Out[1]: <function mod.f() -> int>

In [2]: mod.f[1]
Out[2]: <function mod.f() -> int>

In [3]: type(mod.f[0])
Out[3]: function

In [4]: type(mod.f[1])
Out[4]: function

In [5]: mod.f[0]()
Out[5]: 785

In [6]: mod.f[1]()
/tmp/AscendSpeed/q/main.py:22: UserWarning: The function random was called.
warnings.warn(f"The function {fn_name} was called.")
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[6], line 1
----> 1 mod.f[1]()

File /tmp/AscendSpeed/q/main.py:23, in wrap_module_with_warnings.<locals>.wrapped_fn(*args, **kwargs)
20 @functools.wraps(fn)
21 def wrapped_fn(*args, **kwargs):
22 warnings.warn(f"The function {fn_name} was called.")
---> 23 return fn(*args, **kwargs)

TypeError: 'module' object is not callable
</code></pre>
<p>I'm pretty sure that this code should work, <code>fn</code> <em>should</em> be the function <code>mod.f</code>, but why do I get the <code>TypeError</code> then?</p>
 

Latest posts

M
Replies
0
Views
1
MOHAMED AMIIN ABDI AADAN
M
Top