Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dict views should be introspectable #85067

Closed
rhettinger opened this issue Jun 6, 2020 · 19 comments
Closed

Dict views should be introspectable #85067

rhettinger opened this issue Jun 6, 2020 · 19 comments
Labels
3.10 only security fixes interpreter-core (Objects, Python, Grammar, and Parser dirs) type-bug An unexpected behavior, bug, or error

Comments

@rhettinger
Copy link
Contributor

BPO 40890
Nosy @rhettinger, @jab, @methane, @serhiy-storchaka, @remilapeyre, @sweeneyde
PRs
  • bpo-40890: Add mapping attribute to dict views #20691
  • bpo-40890: Add mapping property to dict views #20749
  • bpo-40890: Fix the signature of the dictview.mapping getter #20873
  • bpo-40890: Fix compiler warning in dictobject.c #20876
  • Add MappingView.mapping for parity with dictview #28892
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = None
    closed_at = <Date 2020-06-15.05:55:24.199>
    created_at = <Date 2020-06-06.17:21:19.846>
    labels = ['interpreter-core', 'type-bug', '3.10']
    title = 'Dict views should be introspectable'
    updated_at = <Date 2021-10-11.23:25:28.222>
    user = 'https://github.com/rhettinger'

    bugs.python.org fields:

    activity = <Date 2021-10-11.23:25:28.222>
    actor = 'pablogsal'
    assignee = 'none'
    closed = True
    closed_date = <Date 2020-06-15.05:55:24.199>
    closer = 'serhiy.storchaka'
    components = ['Interpreter Core']
    creation = <Date 2020-06-06.17:21:19.846>
    creator = 'rhettinger'
    dependencies = []
    files = []
    hgrepos = []
    issue_num = 40890
    keywords = ['patch']
    message_count = 19.0
    messages = ['370841', '370875', '370876', '370877', '370882', '370884', '370885', '370892', '370893', '370914', '371070', '371072', '371081', '371121', '371155', '371337', '371398', '371506', '371517']
    nosy_count = 6.0
    nosy_names = ['rhettinger', 'jab', 'methane', 'serhiy.storchaka', 'remi.lapeyre', 'Dennis Sweeney']
    pr_nums = ['20691', '20749', '20873', '20876', '28892']
    priority = 'normal'
    resolution = 'fixed'
    stage = 'resolved'
    status = 'closed'
    superseder = None
    type = 'behavior'
    url = 'https://bugs.python.org/issue40890'
    versions = ['Python 3.10']

    @rhettinger
    Copy link
    Contributor Author

    Dict views wrap an underlying mapping but don't expose that mapping as an attribute.

    Traditionally, we do expose wrapped objects: property() exposes fget, partial() exposes func, bound methods expose __func__, ChainMap() exposes maps, etc.

    Exposing this attribute would help with introspection, making it possible to write efficient functions that operate on dict views.

    @rhettinger rhettinger added easy 3.10 only security fixes interpreter-core (Objects, Python, Grammar, and Parser dirs) type-bug An unexpected behavior, bug, or error labels Jun 6, 2020
    @sweeneyde
    Copy link
    Member

    I'd be happy to write a PR.

    Method names could be "mapping", "target", "target_mapping", "target_dict", "referent_dict", etc.

    I like the choice of "target_mapping":

        d = dict()
        assert d is d.keys().target_mapping
        assert d is d.values().target_mapping
        assert d is d.items().target_mapping

    @rhettinger
    Copy link
    Contributor Author

    Just "mapping" would suffice. That is variable name used in the ABCs, the name defined in the glossary, and the variable name used in ChainMap (in plural form).

    @sweeneyde
    Copy link
    Member

    I think this will also require typing.MappingProxyType to change a bit, since it would make a proxy's underlying dict accessible:

        >>> d = dict()
        >>> proxy = MappingProxyType(d)
        >>> type(proxy.items()) is type(d.items())  # should be False
        True
        >>> proxy.items().mapping is d  # should be False
        ???

    @sweeneyde
    Copy link
    Member

    Indeed, with PR 20691 applied, the following crashes:

    >> vars(str).items().mapping.clear()
    >> "uh oh"

    @sweeneyde
    Copy link
    Member

    Would the best way to address this be adding new KeysProxy, ValuesProxy, and ItemsProxy types?

    @serhiy-storchaka
    Copy link
    Member

    I am not sure that it is good idea to expose internal details (do you want to expose also internal details of iterators?), but if expose an underlying dict of a dict view, I think it should be either a copy or a read-only proxy. It should be enough to help with introspection, but will not open the hole in integrity.

    @methane
    Copy link
    Member

    methane commented Jun 7, 2020

    Would the best way to address this be adding new KeysProxy, ValuesProxy, and ItemsProxy types?

    I feel it is too much. Until real high need is demonstrated, I don't want to introduce such complexity.

    @remilapeyre
    Copy link
    Mannequin

    remilapeyre mannequin commented Jun 7, 2020

    property, partial, bound methods and ChinMap all do something complex with the underlying object. Dict-views are quite simple by comparison, is there an example where this would be helpful and better than just passing directly the mapping object?

    @rhettinger
    Copy link
    Contributor Author

    Do you see any easy way to have some_mapping_proxy.items().mapping return some_mapping_proxy rather than unmasking the proxied dict?

    @sweeneyde
    Copy link
    Member

    PR 20749 gives each dict view access to a mappingproxy for the original dict, although I don't know if that defeats the original purpose.

    It might be hard to sensibly make MappingProxy(d).items() return something other than d.items(), since this is already the behavior for user-defined classes:

    >>> class A:
        def __getitem__(self, key):
            return "value"
        def items(self):
            return 17
    
    >>> from types import MappingProxyType
    >>> MappingProxyType(A()).items()
    17

    @sweeneyde
    Copy link
    Member

    Here's a workaround that's possible with PR 20749 applied:

    >>> d = {"a":1, "b":2} # fill up the dict...
    >>> DICT = object()
    >>> d[DICT] = d
    >>> items = d.items()
    >>> del d
    >>>
    >>> d = items.mapping[DICT].pop(DICT)
    >>> d
    {'a': 1, 'b': 2}

    @serhiy-storchaka
    Copy link
    Member

    I meant that you can make dict_view.mapping always returning a MappingProxyType.

    @rhettinger
    Copy link
    Contributor Author

    I meant that you can make dict_view.mapping always
    returning a MappingProxyType.

    That would be a reasonable constraint.

    @methane
    Copy link
    Member

    methane commented Jun 10, 2020

    +1.

    @sweeneyde
    Copy link
    Member

    Would it be better to have a dictview.mapping() method rather than an attribute, since it constructs a new object of a different type and since that's what keys(), values(), and items() are?

    @rhettinger
    Copy link
    Contributor Author

    New changeset 3ee0e48 by Dennis Sweeney in branch 'master':
    bpo-40890: Add mapping property to dict views (GH-20749)
    3ee0e48

    @serhiy-storchaka
    Copy link
    Member

    There is a tiny portability issue.

    @pablogsal
    Copy link
    Member

    New changeset 10c3b21 by Pablo Galindo in branch 'master':
    bpo-40890: Fix compiler warning in dictobject.c (GH-20876)
    10c3b21

    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    3.10 only security fixes interpreter-core (Objects, Python, Grammar, and Parser dirs) type-bug An unexpected behavior, bug, or error
    Projects
    None yet
    Development

    No branches or pull requests

    5 participants