pytype

A static type analyzer for Python code

Home
Developer guide
Workflow
• Development process
• Python version upgrades
• Supporting new features
Program analysis
• Bytecode
• Directives
• Main loop
• Stack frames
• Typegraph
Data representation
• Abstract values
• Attributes
• Overlays
• Special builtins
• Type annotations
• Type stubs
• TypeVars
Configuration
Style guide
Tools
Documentation debugging

View the Project on GitHub google/pytype

Hosted on GitHub Pages — Theme by orderedlist

Attributes

Introduction

The attribute module handles getting and setting attributes on abstract values. It is mostly used to:

Terminology

We will refer to the object on which an attribute is being get or set as the target object.

Implementation

attribute has two public methods: get_attribute and set_attribute.

set_attribute

We’ll start by looking at set_attribute, since it’s by far the simpler of the two top-level methods:

set_attribute diagram

  1. set_attribute recursively calls itself as needed to unpack the target object. For example, when passed a union, set_attribute iterates through the union’s options and calls itself on each one.
  2. set_attribute then calls _set_member, which first calls _maybe_load_as_instance_attribute to make sure the requested attribute has been lazily loaded into the object’s members dict, then modifies the appropriate members entry.

get_attribute

get_attribute is considerably more complicated, due to the lengths it goes to in order to emulate the behavior of the Python interpreter:

get_attribute diagram

  1. Like set_attribute, get_attribute recursively calls itself to unpack the target object.
  2. Once the object has been unpacked, it is either a module, class instance, class, or super instance (the result of a super() call). The corresponding helper method is called.

    a. _get_module_attribute forwards to _get_instance_attribute.

    b. _get_instance_attribute and _get_class_attribute forward to _get_attribute.

    c. _get_attribute_from_super_instance determines the super class and calls _lookup_from_mro_and_handle_descriptors on it.

  3. Python classes can define a magic method, __getattribute__, that is called unconditionally to implement attribute access, as well as a fallback method, __getattr__, that is only called when normal attribute lookup fails. (See the Python data model documentation for more information.) To mimic this, _get_attribute first calls _get_attribute_computed("__getattribute__"), then calls either _get_member or _lookup_from_mro_and_handle_descriptors depending on whether the target object is a class instance or a class, and finally calls _get_attribute_computed("__getattr__").

  4. Both _get_attribute_computed and _lookup_from_mro_and_handle_descriptors use _lookup_from_mro to do attribute lookup on a class. The latter walks the class’s MRO, calling _get_attribute_flat - which in turn calls _get_member - to check for the attribute on the class and its bases.

  5. Similar to _set_member, _get_member uses _maybe_load_as_instance_attribute to force lazy loading and then checks the members dict for the requested attribute.

get_special_attribute

Occasionally, an abstract class needs to bypass normal attribute lookup. In such cases, the class can implement a get_special_attribute method whose return value will be immediately used. For example, HasSlots returns the custom method implementations in its slots rather than the underlying PyTDFunction objects.

valself

attribute.get_attribute takes an optional valself parameter, a binding to an abstract value related to the target object. When to pass valself and what value to use can be tricky: