Skip to content

Manually setting module to __main__ for callables causes errors starting in 0.3.5 #482

@dwhswenson

Description

@dwhswenson

Context: Our code uses dill to serialize callables only under certain circumstances, including when the callable's module is __main__. We want to be able to test this at an integration level, using dill on a function where func.__module__ == __main__.

Prior to the release of 0.3.5, we did this by manually setting the __module__ attribute. However, our tests have been failing since the release of 0.3.5.

I'm not sure if this is supposed to be an allowed use case. But is there supposed to be another way that I make it look like my functions were defined in __main__ (for the part our code manages) such that dill can also handle it, when really I'm running under pytest or another testing framework?

MCVE: In an environment with pytest and dill installed:

# test_dill.py

import dill

def setup_module():
    print(f"\nDill version: {dill.__version__}")


def external_func_pretending_to_be_main():
    return


def test_dill_lambda():
    print()  # cleaner output
    func = lambda x: x
    func.__module__ = "__main__"
    dill.dumps(func)


def test_dill_func():
    print() # cleaner output
    func = external_func_pretending_to_be_main
    func.__module__ = "__main__"
    dill.dumps(func)
pytest -vs test_dill.py

Output in 0.3.4:

test_dill.py::test_dill_lambda
Dill version: 0.3.4

PASSED
test_dill.py::test_dill_func
PASSED

Output in 0.3.5.1

test_dill.py::test_dill_lambda
Dill version: 0.3.5.1

FAILED
test_dill.py::test_dill_func
FAILED

Tracebacks, as reported by pytest:

test_dill_lambda
../../mambaforge/envs/dill/lib/python3.10/site-packages/dill/_dill.py:364: in dumps
    dump(obj, file, protocol, byref, fmode, recurse, **kwds)#, strictio)
../../mambaforge/envs/dill/lib/python3.10/site-packages/dill/_dill.py:336: in dump
    Pickler(file, protocol, **_kwds).dump(obj)
../../mambaforge/envs/dill/lib/python3.10/site-packages/dill/_dill.py:620: in dump
    StockPickler.dump(self, obj)
../../mambaforge/envs/dill/lib/python3.10/pickle.py:487: in dump
    self.save(obj)
../../mambaforge/envs/dill/lib/python3.10/pickle.py:560: in save
    f(self, obj)  # Call unbound method with explicit self
../../mambaforge/envs/dill/lib/python3.10/site-packages/dill/_dill.py:1963: in save_function
    _save_with_postproc(pickler, (_create_function, (
../../mambaforge/envs/dill/lib/python3.10/site-packages/dill/_dill.py:1154: in _save_with_postproc
    pickler._batch_setitems(iter(source.items()))
../../mambaforge/envs/dill/lib/python3.10/pickle.py:998: in _batch_setitems
    save(v)
../../mambaforge/envs/dill/lib/python3.10/pickle.py:603: in save
    self.save_reduce(obj=obj, *rv)
../../mambaforge/envs/dill/lib/python3.10/pickle.py:717: in save_reduce
    save(state)
../../mambaforge/envs/dill/lib/python3.10/pickle.py:560: in save
    f(self, obj)  # Call unbound method with explicit self
../../mambaforge/envs/dill/lib/python3.10/site-packages/dill/_dill.py:1251: in save_module_dict
    StockPickler.save_dict(pickler, obj)
../../mambaforge/envs/dill/lib/python3.10/pickle.py:972: in save_dict
    self._batch_setitems(obj.items())
../../mambaforge/envs/dill/lib/python3.10/pickle.py:998: in _batch_setitems
    save(v)
../../mambaforge/envs/dill/lib/python3.10/pickle.py:603: in save
    self.save_reduce(obj=obj, *rv)
../../mambaforge/envs/dill/lib/python3.10/pickle.py:717: in save_reduce
    save(state)
../../mambaforge/envs/dill/lib/python3.10/pickle.py:560: in save
    f(self, obj)  # Call unbound method with explicit self
../../mambaforge/envs/dill/lib/python3.10/site-packages/dill/_dill.py:1251: in save_module_dict
    StockPickler.save_dict(pickler, obj)
../../mambaforge/envs/dill/lib/python3.10/pickle.py:972: in save_dict
    self._batch_setitems(obj.items())
../../mambaforge/envs/dill/lib/python3.10/pickle.py:998: in _batch_setitems
    save(v)
../../mambaforge/envs/dill/lib/python3.10/pickle.py:603: in save
    self.save_reduce(obj=obj, *rv)
../../mambaforge/envs/dill/lib/python3.10/pickle.py:687: in save_reduce
    save(cls)
../../mambaforge/envs/dill/lib/python3.10/pickle.py:560: in save
    f(self, obj)  # Call unbound method with explicit self
../../mambaforge/envs/dill/lib/python3.10/site-packages/dill/_dill.py:1838: in save_type
    _save_with_postproc(pickler, (_create_type, (
../../mambaforge/envs/dill/lib/python3.10/site-packages/dill/_dill.py:1140: in _save_with_postproc
    pickler.save_reduce(*reduction, obj=obj)
../../mambaforge/envs/dill/lib/python3.10/pickle.py:692: in save_reduce
    save(args)
../../mambaforge/envs/dill/lib/python3.10/pickle.py:560: in save
    f(self, obj)  # Call unbound method with explicit self
../../mambaforge/envs/dill/lib/python3.10/pickle.py:902: in save_tuple
    save(element)
../../mambaforge/envs/dill/lib/python3.10/pickle.py:560: in save
    f(self, obj)  # Call unbound method with explicit self
../../mambaforge/envs/dill/lib/python3.10/site-packages/dill/_dill.py:1251: in save_module_dict
    StockPickler.save_dict(pickler, obj)
../../mambaforge/envs/dill/lib/python3.10/pickle.py:972: in save_dict
    self._batch_setitems(obj.items())
../../mambaforge/envs/dill/lib/python3.10/pickle.py:998: in _batch_setitems
    save(v)
../../mambaforge/envs/dill/lib/python3.10/pickle.py:560: in save
    f(self, obj)  # Call unbound method with explicit self
../../mambaforge/envs/dill/lib/python3.10/site-packages/dill/_dill.py:1963: in save_function
    _save_with_postproc(pickler, (_create_function, (
../../mambaforge/envs/dill/lib/python3.10/site-packages/dill/_dill.py:1154: in _save_with_postproc
    pickler._batch_setitems(iter(source.items()))
../../mambaforge/envs/dill/lib/python3.10/pickle.py:998: in _batch_setitems
    save(v)
../../mambaforge/envs/dill/lib/python3.10/pickle.py:560: in save
    f(self, obj)  # Call unbound method with explicit self
../../mambaforge/envs/dill/lib/python3.10/site-packages/dill/_dill.py:2004: in save_function
    StockPickler.save_global(pickler, obj, name=name)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <dill._dill.Pickler object at 0x1082f2e90>
obj = <function make_set_closure_cell.<locals>.set_closure_cell at 0x1053c6290>
name = 'make_set_closure_cell.<locals>.set_closure_cell'

    def save_global(self, obj, name=None):
        write = self.write
        memo = self.memo

        if name is None:
            name = getattr(obj, '__qualname__', None)
        if name is None:
            name = obj.__name__

        module_name = whichmodule(obj, name)
        try:
            __import__(module_name, level=0)
            module = sys.modules[module_name]
            obj2, parent = _getattribute(module, name)
        except (ImportError, KeyError, AttributeError):
>           raise PicklingError(
                "Can't pickle %r: it's not found as %s.%s" %
                (obj, module_name, name)) from None
E           _pickle.PicklingError: Can't pickle <function make_set_closure_cell.<locals>.set_closure_cell at 0x1053c6290>: it's not found as attr._compat.make_set_closure_cell.<locals>.set_closure_cell

../../mambaforge/envs/dill/lib/python3.10/pickle.py:1071: PicklingError
test_dill_func
../../mambaforge/envs/dill/lib/python3.10/site-packages/dill/_dill.py:364: in dumps
    dump(obj, file, protocol, byref, fmode, recurse, **kwds)#, strictio)
../../mambaforge/envs/dill/lib/python3.10/site-packages/dill/_dill.py:336: in dump
    Pickler(file, protocol, **_kwds).dump(obj)
../../mambaforge/envs/dill/lib/python3.10/site-packages/dill/_dill.py:620: in dump
    StockPickler.dump(self, obj)
../../mambaforge/envs/dill/lib/python3.10/pickle.py:487: in dump
    self.save(obj)
../../mambaforge/envs/dill/lib/python3.10/pickle.py:560: in save
    f(self, obj)  # Call unbound method with explicit self
../../mambaforge/envs/dill/lib/python3.10/site-packages/dill/_dill.py:1963: in save_function
    _save_with_postproc(pickler, (_create_function, (
../../mambaforge/envs/dill/lib/python3.10/site-packages/dill/_dill.py:1154: in _save_with_postproc
    pickler._batch_setitems(iter(source.items()))
../../mambaforge/envs/dill/lib/python3.10/pickle.py:998: in _batch_setitems
    save(v)
../../mambaforge/envs/dill/lib/python3.10/pickle.py:603: in save
    self.save_reduce(obj=obj, *rv)
../../mambaforge/envs/dill/lib/python3.10/pickle.py:717: in save_reduce
    save(state)
../../mambaforge/envs/dill/lib/python3.10/pickle.py:560: in save
    f(self, obj)  # Call unbound method with explicit self
../../mambaforge/envs/dill/lib/python3.10/site-packages/dill/_dill.py:1251: in save_module_dict
    StockPickler.save_dict(pickler, obj)
../../mambaforge/envs/dill/lib/python3.10/pickle.py:972: in save_dict
    self._batch_setitems(obj.items())
../../mambaforge/envs/dill/lib/python3.10/pickle.py:998: in _batch_setitems
    save(v)
../../mambaforge/envs/dill/lib/python3.10/pickle.py:603: in save
    self.save_reduce(obj=obj, *rv)
../../mambaforge/envs/dill/lib/python3.10/pickle.py:717: in save_reduce
    save(state)
../../mambaforge/envs/dill/lib/python3.10/pickle.py:560: in save
    f(self, obj)  # Call unbound method with explicit self
../../mambaforge/envs/dill/lib/python3.10/site-packages/dill/_dill.py:1251: in save_module_dict
    StockPickler.save_dict(pickler, obj)
../../mambaforge/envs/dill/lib/python3.10/pickle.py:972: in save_dict
    self._batch_setitems(obj.items())
../../mambaforge/envs/dill/lib/python3.10/pickle.py:998: in _batch_setitems
    save(v)
../../mambaforge/envs/dill/lib/python3.10/pickle.py:603: in save
    self.save_reduce(obj=obj, *rv)
../../mambaforge/envs/dill/lib/python3.10/pickle.py:687: in save_reduce
    save(cls)
../../mambaforge/envs/dill/lib/python3.10/pickle.py:560: in save
    f(self, obj)  # Call unbound method with explicit self
../../mambaforge/envs/dill/lib/python3.10/site-packages/dill/_dill.py:1838: in save_type
    _save_with_postproc(pickler, (_create_type, (
../../mambaforge/envs/dill/lib/python3.10/site-packages/dill/_dill.py:1140: in _save_with_postproc
    pickler.save_reduce(*reduction, obj=obj)
../../mambaforge/envs/dill/lib/python3.10/pickle.py:692: in save_reduce
    save(args)
../../mambaforge/envs/dill/lib/python3.10/pickle.py:560: in save
    f(self, obj)  # Call unbound method with explicit self
../../mambaforge/envs/dill/lib/python3.10/pickle.py:902: in save_tuple
    save(element)
../../mambaforge/envs/dill/lib/python3.10/pickle.py:560: in save
    f(self, obj)  # Call unbound method with explicit self
../../mambaforge/envs/dill/lib/python3.10/site-packages/dill/_dill.py:1251: in save_module_dict
    StockPickler.save_dict(pickler, obj)
../../mambaforge/envs/dill/lib/python3.10/pickle.py:972: in save_dict
    self._batch_setitems(obj.items())
../../mambaforge/envs/dill/lib/python3.10/pickle.py:998: in _batch_setitems
    save(v)
../../mambaforge/envs/dill/lib/python3.10/pickle.py:560: in save
    f(self, obj)  # Call unbound method with explicit self
../../mambaforge/envs/dill/lib/python3.10/site-packages/dill/_dill.py:1963: in save_function
    _save_with_postproc(pickler, (_create_function, (
../../mambaforge/envs/dill/lib/python3.10/site-packages/dill/_dill.py:1154: in _save_with_postproc
    pickler._batch_setitems(iter(source.items()))
../../mambaforge/envs/dill/lib/python3.10/pickle.py:998: in _batch_setitems
    save(v)
../../mambaforge/envs/dill/lib/python3.10/pickle.py:560: in save
    f(self, obj)  # Call unbound method with explicit self
../../mambaforge/envs/dill/lib/python3.10/site-packages/dill/_dill.py:2004: in save_function
    StockPickler.save_global(pickler, obj, name=name)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <dill._dill.Pickler object at 0x10849f910>
obj = <function make_set_closure_cell.<locals>.set_closure_cell at 0x1053c6290>
name = 'make_set_closure_cell.<locals>.set_closure_cell'

    def save_global(self, obj, name=None):
        write = self.write
        memo = self.memo

        if name is None:
            name = getattr(obj, '__qualname__', None)
        if name is None:
            name = obj.__name__

        module_name = whichmodule(obj, name)
        try:
            __import__(module_name, level=0)
            module = sys.modules[module_name]
            obj2, parent = _getattribute(module, name)
        except (ImportError, KeyError, AttributeError):
>           raise PicklingError(
                "Can't pickle %r: it's not found as %s.%s" %
                (obj, module_name, name)) from None
E           _pickle.PicklingError: Can't pickle <function make_set_closure_cell.<locals>.set_closure_cell at 0x1053c6290>: it's not found as attr._compat.make_set_closure_cell.<locals>.set_closure_cell

../../mambaforge/envs/dill/lib/python3.10/pickle.py:1071: PicklingError

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions