Skip to content

TypeError: unhashable type: 'types.SimpleNamespace' #4660

@nedbat

Description

@nedbat

I added a hypothesis test to cog, and it caused an error inside Hypothesis. This is kind of my fault, and kind of yours. Cog adds a SimpleNamespace object to sys.modules (for import cog), then you try to make a set of all the "modules".

    @hypothesis.given(st.lists(st.text(max_size=100), max_size=50))
>   def test_common_prefix(strings):
                   ^^^

cogapp/test_whiteutils.py:103:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
.tox/py313/lib/python3.13/site-packages/hypothesis/internal/conjecture/engine.py:967: in run
    self._run()
.tox/py313/lib/python3.13/site-packages/hypothesis/internal/conjecture/engine.py:1540: in _run
    self.generate_new_examples()
.tox/py313/lib/python3.13/site-packages/hypothesis/internal/conjecture/engine.py:1245: in generate_new_examples
    prefix = self.generate_novel_prefix()
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.tox/py313/lib/python3.13/site-packages/hypothesis/internal/conjecture/engine.py:760: in generate_novel_prefix
    return self.tree.generate_novel_prefix(self.random)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.tox/py313/lib/python3.13/site-packages/hypothesis/internal/conjecture/datatree.py:781: in generate_novel_prefix
    node_value = self._draw(
.tox/py313/lib/python3.13/site-packages/hypothesis/internal/conjecture/datatree.py:892: in _draw
    value = draw_choice(choice_type, constraints, random=random)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.tox/py313/lib/python3.13/site-packages/hypothesis/internal/conjecture/data.py:1354: in draw_choice
    return cast(ChoiceT, getattr(cd.provider, f"draw_{choice_type}")(**constraints))
                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.tox/py313/lib/python3.13/site-packages/hypothesis/internal/conjecture/providers.py:938: in draw_string
    constant := self._maybe_draw_constant(
.tox/py313/lib/python3.13/site-packages/hypothesis/internal/conjecture/providers.py:739: in _maybe_draw_constant
    assert self._local_constants is not None
           ^^^^^^^^^^^^^^^^^^^^^
/usr/local/pyenv/pyenv/versions/3.13.12/lib/python3.13/functools.py:1025: in __get__
    val = self.func(instance)
          ^^^^^^^^^^^^^^^^^^^
.tox/py313/lib/python3.13/site-packages/hypothesis/internal/conjecture/providers.py:722: in _local_constants
    return _get_local_constants()
           ^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

    def _get_local_constants() -> Constants:
        global _sys_modules_len, _local_constants

        if sys.platform == "emscripten":  # pragma: no cover
            # pyodide builds bundle the stdlib in a nonstandard location, like
            # `/lib/python312.zip/heapq.py`. To avoid identifying the entirety of
            # the stdlib as local code and slowing down on emscripten, instead return
            # that nothing is local.
            #
            # pyodide may provide some way to distinguish stdlib/third-party/local
            # code. I haven't looked into it. If they do, we should correctly implement
            # ModuleLocation for pyodide instead of this.
            return _local_constants

        count_constants = len(_local_constants)
        # We call this function once per HypothesisProvider instance, i.e. once per
        # input, so it needs to be performant. The logic here is more complicated
        # than necessary because of this.
        #
        # First, we check whether there are any new modules with a very cheap length
        # check. This check can be fooled if a module is added while another module is
        # removed, but the more correct check against tuple(sys.modules.keys()) is
        # substantially more expensive. Such a new module would eventually be discovered
        # if / when the length changes again in the future.
        #
        # If the length has changed, we find just modules we haven't seen before. Of
        # those, we find the ones which correspond to local modules, and extract their
        # constants.

        # careful: store sys.modules length when we first check to avoid race conditions
        # with other threads loading a module before we set _sys_modules_len.
        if (sys_modules_len := len(sys.modules)) != _sys_modules_len:
            # set(_seen_modules) shouldn't typically be required, but I have run into
            # a "set changed size during iteration" error here when running
            # test_provider_conformance_crosshair.
>           new_modules = set(sys.modules.values()) - set(_seen_modules)
                          ^^^^^^^^^^^^^^^^^^^^^^^^^
E           TypeError: unhashable type: 'types.SimpleNamespace'

.tox/py313/lib/python3.13/site-packages/hypothesis/internal/conjecture/providers.py:316: TypeError

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugsomething is clearly wrong hereinterophow to play nicely with other packages

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions