users = dict() def get_user(name): # Return type of 'get_user' is shown as 'void', try: # it should be unsure(User, None) since it return users[name.lower()] # knows users is a dict of string : User. except KeyError: pass for user in users.values(): # 'user' is shown as mixed when it should know if user.alias.match(name): # that values() returns an iterable of User. return user # Further, the values() return type is shown # as 'list of unknown'. return None class User: def __init__(self, nick): self.nick = nick self._alias = None users[nick.lower()] = self def get_alias(self): return self._alias def set_alias(self, value): self._alias = re.compile(value, re.IGNORECASE) alias = property(get_alias, set_alias) This turns out to be related to parsing order; if you move the class def above the function def things work (except for the values() tooltip being wrong about its return type, which needs a tweak). Here is a reduced testcase: users = dict() def add(): users[""] = "" def get(): dictvalues = users.values() # dictvalues is correctly marked as 'list of string' # ------------- users = dict() def get(): dictvalues = users.values() # dictvalues is marked as 'list of mixed' def add(): users[""] = "" Reproducible: Always