pythonpython-3.xautocompletepycharmcode-completion

How to enable code completion for a dictionary from method in Python?


I am using Python 3.6 with PyCharm and it's kind of frustrating that there's no support for code completion for special cases of dictionary objects (with fixed key schema).

Say for example I create and try to access a simple dictionary object like this:

inventory = {'name': 'hammer', 'price': 2.3}
inventory['']

When I position my cursor inside the quotes ' ' and hit Ctrl + Space i get code completion and the IDE correctly suggests all the possible keys in the dictionary object. That's great!

But if i try to build it as a utility function that returns this same dict object, say with values that the user provide but with the same dict keys - then I don't get code completion anymore!

def get_inventory(name: str, price: float):
    return {'name': name, 'price': price}

inventory = get_inventory('hammer', 2.3)
inventory['']    # <- Pycharm can't offer any suggestions! 

Any workaround or solution for this? I searched already for similar solutions but I didn't find anything that works. I know I can just convert it into a class Inventory and access the properties that way but I don't want to do it for a couple of reasons:

Any help or solution for how I can get my IDE to assist in code completion by recognizing the possible keys in such a dict object would be greatly appreciated!


Solution

  • It is now mid-2023, and still no ways for PyCharm to detect dynamic dict keys returned from a function def exist.

    However, there is a "sort" of workaround that is now possible. I realized that I forgot to mention it, so including it here.

    Note that I say it's kind of a workaround, because this approach only works for static dict keys, rather than dynamic keys populated within a function scope. But hey, it's better than nothing.

    Using builtin dict

    In Python 3.8+, this can now be achieved using TypedDict, as shown below. Also added a __future__ import to account for annotations that could potentially be undefined at runtime, based on usage below.

    from __future__ import annotations
    
    from typing import TYPE_CHECKING, TypedDict
    
    
    # To avoid creating a class at runtime, for type-hinting alone.
    if TYPE_CHECKING:
    
        # Map the `dict` fields here
        class Inventory(TypedDict):
            name: str
            price: float
    
    
    def get_inventory(name: str, price: float) -> Inventory:
        return {'name': name, 'price': price}
    
    
    inventory = get_inventory('hammer', 2.3)
    
    inventory['']    # <- suggestions now work in PyCharm!
    

    Dot-Access Dict - w/ fast performance

    If dot or attribute access is desirable -- such as a.b.c instead of a['b']['c'] -- then I have created a helper library dotwiz to achieve this, as shown below:

    import dotwiz
    from typing import TYPE_CHECKING
    
    
    # To avoid creating a class at runtime, for type-hinting alone.
    if TYPE_CHECKING:
        from dataclasses import dataclass
    
        # Map the `dict` fields here
        @dataclass
        class Inventory:
            name: str
            price: float
    
    else:
        Inventory = dotwiz.DotWiz
    
    
    def get_inventory(name: str, price: float) -> Inventory:
        return Inventory(name=name, price=price)
    
    
    inventory = get_inventory('hammer', 2.3)
    
    print(inventory)
    assert inventory.price == 2.3
    
    inventory.    # <- suggestions now work in PyCharm!