Skip to content

functools.wraps can pollute a function's global namespace #242

@pierreglaser

Description

@pierreglaser

using functools.wraps

functools.wraps(f)
def g():
    pass

changes a g's __module__ attribute to f.__module__, but not its __globals__. Because we retrieve globals from state['module'] at unpickling time, this will pollute a depickled version of g with the globals of f.__module__

As an illustration:

import functools
import pickle
import cloudpickle


@functools.wraps(pickle._dumps)
def f():
    pass


depickled_f = pickle.loads(cloudpickle.dumps(f))
assert depickled_f.__globals__ is vars(pickle)

passes.

This is tricky:
On one hand one can spot if a function is wrapping another by checking for a __wrapped__ attribute. On the other hand, once wraps is used, there is no way back, and f's attributes are lost forever.
The side effects are not life threatening, but this may be worth adding a warning at pickling time. I am thinking about (roughly):

if hasattr(func, '__wrapped__'):
    msg = ("This function seems decorated by functools.wraps. This"
           " may cause {}'s __globals__ to be populated with"
           " unnecessary global variables at unpickling time."
           " To fix it, change the assigned argument of"
           " functools.wraps so that is does not include  __module__ ")
    raise UserWarning(msg.format(func.__code__.co_name))

Inside extract_func_data.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions