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: