PyObjects and PyNames
Relevant source files
The following files were used as context for generating this wiki page:
PyObjects and PyNames represent the core data structures used by the Rope refactoring library to model Python source code. While PyObject represents the actual semantic entities in Python (like classes, functions, and modules), PyName acts as a reference or a handle to these objects within a specific scope. This separation allows Rope to track how names are defined, imported, and assigned across different parts of a project.
These structures are used extensively for code analysis, providing the foundation for features such as code completion, refactoring (e.g., renaming), and static object analysis.
PyObject Architecture
The PyObject class is the base for all objects that Rope can identify in Python code. It maintains a reference to its type and provides methods to access attributes and determine its module of origin.
Core Hierarchy
Rope uses several abstract and concrete implementations of PyObject to represent different Python constructs:
- AbstractClass: A base for Python classes.
- AbstractFunction: A base for Python functions and methods.
- AbstractModule: A base for Python modules and packages.
- PyDefinedObject: A specific category of objects whose source code Rope can access and analyze directly.
The diagram above shows the relationship between base PyObjects and their defined counterparts which Rope uses to perform deep code analysis.
Sources: rope/base/pyobjects.py:7-175
Base Types
Rope defines four fundamental base types that categorize all PyObject instances:
| Base Type | Description |
|---|---|
| Type | Represents class types. |
| Function | Represents functions and methods. |
| Module | Represents modules and packages. |
| Unknown | A null-object pattern used when the type of an object cannot be determined. |
Sources: rope/base/pyobjects.py:65-115
PyNames and Scopes
A PyName is the mechanism by which a name in a scope refers to a PyObject. This allows Rope to differentiate between a variable name and the object it points to, which is critical during refactorings like renaming.
Name Resolution Flow
When a tool like code_assist or a refactoring engine needs to identify what a name represents, it traverses scopes to find the corresponding PyName, which then resolves to a PyObject.
The flow shows how Rope resolves a textual name in source code to its semantic representation.
Sources: rope/contrib/codeassist.py:313-345, rope/base/pyobjects.py:11-30
Important PyName Types
The library differentiates between how names are introduced into a scope:
- AssignedName: Names created through assignment (e.g.,
x = 10). - ImportedName: Names brought into a scope via
importstatements. - ParameterName: Names representing function arguments.
- DefinedName: Names of classes or functions defined in the current module.
Sources: rope/contrib/codeassist.py:136-150, rope/refactor/importutils/module_imports.py:380-410
Object Information and Analysis
Rope uses an ObjectInfoManager to persist and retrieve information about PyObjects that is discovered during analysis, such as return types and passed arguments.
Static Object Analysis (SOA)
The library can perform static analysis to infer object relationships. The ObjectInfoManager stores this data in an objectdb.
- Return Values: It tracks what types a function might return based on actual usage or analysis.
- Parameter Objects: It maintains records of what objects are passed as parameters to functions.
This sequence illustrates how inferred information is retrieved to enhance code completion and analysis.
Sources: rope/base/oi/objectinfo.py:15-100
Practical Usage in Code Assist
The CompletionProposal class uses PyNames and PyObjects to provide intelligent completions. It maps the internal representation to human-readable types like 'instance', 'class', or 'function'.
# From rope/contrib/codeassist.py
class CompletionProposal:
@property
def type(self):
pyname = self.pyname
if isinstance(pyname, pynames.ImportedModule):
return "module"
elif isinstance(pyname, (pynames.ImportedName, pynames.DefinedName)):
pyobject = pyname.get_object()
if isinstance(pyobject, pyobjects.AbstractFunction):
return "function"
if isinstance(pyobject, pyobjects.AbstractClass):
return "class"
return "instance"
Sources: rope/contrib/codeassist.py:207-230
Summary
PyObjects and PyNames form the semantic backbone of the Rope library. By decoupling names (references) from objects (definitions), Rope can accurately model Python's dynamic nature. This infrastructure enables the library to perform complex refactorings, provide accurate code completion, and conduct deep static analysis of Python projects.
Sources: rope/base/pyobjects.py, rope/contrib/codeassist.py, rope/base/oi/objectinfo.py