Skip to content

Non-working code in update() method of weakref.WeakKeyDictionary #124748

Closed

Description

@rigilfanov

Bug report

Bug description:

Edited at 2024.09.30 10:07 AM.

Example of code

import weakref

d = weakref.WeakKeyDictionary()
d.update(x=1)

Traceback

Traceback (most recent call last):
  File "/home/username/example.py", line 4, in <module>
    d.update(x=1)
    ~~~~~~~~^^^^^
  File "/home/username/.pyenv/versions/3.13.0rc2/lib/python3.13/weakref.py", line 518, in update
    self.update(kwargs)
    ~~~~~~~~~~~^^^^^^^^
  File "/home/username/.pyenv/versions/3.13.0rc2/lib/python3.13/weakref.py", line 516, in update
    d[ref(key, self._remove)] = value
      ~~~^^^^^^^^^^^^^^^^^^^
TypeError: cannot create weak reference to 'str' object

Source of error

class WeakKeyDictionary(_collections_abc.MutableMapping):
    ...
    def update(self, dict=None, /, **kwargs):  # `kwargs` always use 'str' for keys
        d = self.data
        if dict is not None:
            if not hasattr(dict, "items"):
                dict = type({})(dict)
            for key, value in dict.items():
                d[ref(key, self._remove)] = value
        if len(kwargs): # If true, then `kwargs` contains a key of `str` type
            self.update(kwargs)  # So this line always raises `TypeError`

Proposals

Option 1

a) Add a clear exception and delete last two lines:

class WeakKeyDictionary(_collections_abc.MutableMapping):
    ...
    def update(self, dict=None, /, **kwargs):
        if kwargs:
            raise TypeError(
                "Keyword arguments are not supported. "
                "Can not create weak references to `str` keys."
            )
          d = self.data
        if dict is not None:
            if not hasattr(dict, "items"):
                dict = type({})(dict)
            for key, value in dict.items():
                d[ref(key, self._remove)] = value

b) Write about this behavior in the documentation.

Option 2

a) Remove the kwargs parameter from WeakKeyDicionary.update().
b) Inherit WeakKeyDicionary from Mapping to avoid violating of the Liskov Substitution Principle with MutableMapping.

class WeakKeyDictionary(_collections_abc.Mapping):
    ...
    def update(self, dict=None):
        d = self.data
        if dict is not None:
            if not hasattr(dict, "items"):
                dict = type({})(dict)
            for key, value in dict.items():
                d[ref(key, self._remove)] = value

Thanks

CPython versions tested on:

3.13

Operating systems tested on:

Linux

Linked PRs

Activity

  1. picnixz commented on Sep 29, 2024

    @picnixz
    Member

    I don't know whether this is a bug or not, namely I'm not sure that adding __weakref__ to a subclass of str should effectively turn that class into a weak-referencable class.

  2. cfbolz commented on Sep 29, 2024

    @cfbolz
    Contributor

    @ri-gilfanov could you explain what you want your code to do? Is your main point that it's strange that update accepts keyword arguments, but using that feature will always raise?

    (imo the use of a str subclass is a distraction here. You would get an identical error for any other object that you pass via keyword arguments to update).

  3. rigilfanov commented on Sep 30, 2024

    @rigilfanov
    ContributorAuthor

    Is your main point that it's strange that update accepts keyword arguments, but using that feature will always raise?

    @cfbolz, yes, almost. The update method also contains the code to handling keyword arguments:

            # 517-518 lines of weakref.py
            if len(kwargs):
                self.update(kwargs)

    My main point is the following. User can call the method with keyword arguments. User can find the source code for handling keyword arguments. But using that feature will always raises a type error. It's looks like a bug.

    (imo the use of a str subclass is a distraction here. You would get an identical error for any other object that you pass via keyword arguments to update).

    You are right. I will update the example a bit later.

    could you explain what you want your code to do?

    I implemented the flyweight pattern for a hashable user objects. I needed a dictionary that would store keys and values ​​as weak references. And I used the source code of WeakKeyDictionary and WeakValueDictionary as a starting point.

    The solution works. However, I decided to cover my code with unit tests. So, I discovered that I inherited non-working code from the WeakKeyDictionary. So, I decided to write a little bug report.

    I don't know whether this is a bug or not, namely I'm not sure that adding weakref to a subclass of str should effectively turn that class into a weak-referencable class.

    @picnixz, adding the __slots__ is a recommended optimization for str subclasses. No more. We can just use subclasses of the dict, list and str types with weak references. Working example:

    from weakref import ref
    
    class StringSubclass(str): ...
    
    strong_reference = StringSubclass('something')
    weak_reference = ref(strong_reference)
    assert strong_reference == weak_reference()
  4. cfbolz commented on Sep 30, 2024

    @cfbolz
    Contributor

    Here's an example without any kind of subclass to talk about your problem:

    >>> import weakref
    >>> d = weakref.WeakKeyDictionary()
    >>> d.update(x=1)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/usr/lib/python3.11/weakref.py", line 518, in update
        self.update(kwargs)
      File "/usr/lib/python3.11/weakref.py", line 516, in update
        d[ref(key, self._remove)] = value
          ^^^^^^^^^^^^^^^^^^^^^^
    TypeError: cannot create weak reference to 'str' object

    I think it would be reasonable to remove the last two lines of update and raise an explicit error instead. Like your Option 1, but the exception should be a TypeError for backwards compatibility reasons.

  5. rigilfanov commented on Sep 30, 2024

    @rigilfanov
    ContributorAuthor

    @cfbolz , thanks. I updated first comment in this issue. Also I updated the error message in option 1.

  6. added 12 commits that reference this issue on Sep 30, 2024
  7. robkarlson104 commented on Feb 16, 2026

    @robkarlson104
  8. added 2 commits that reference this issue on Feb 18, 2026
  9. sergey-miryanov commented on Feb 18, 2026

    @sergey-miryanov
    Contributor

    Completed via #124783

  10. rigilfanov commented on Feb 20, 2026

    @rigilfanov
    ContributorAuthor

    @sergey-miryanov , thank you very much!

  11. added a commit that references this issue on Feb 28, 2026
  12. added a commit that references this issue on Apr 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    interpreter-core(Objects, Python, Grammar, and Parser dirs)type-bugAn unexpected behavior, bug, or error

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions