Skip to content

Commit 5d94c2b

Browse files
authored
[mypyc] Irbuild docstring and comment updates, and minor refactoring (#8542)
Add and update docstrings and comments in mypyc.irbuild. Also try to make the style of docstrings and comments more consistent and cleaner looking. For example, generally make documentation lines shorter than the max line length, for improved readability. Group some related functions close to each other.
1 parent 05e5d58 commit 5d94c2b

16 files changed

+614
-396
lines changed

mypyc/irbuild/callable_class.py

Lines changed: 56 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""Generate a class that represents a nested function.
22
3-
The class defines __call__ for calling the function and allows access to variables
4-
defined in outer scopes.
3+
The class defines __call__ for calling the function and allows access to
4+
non-local variables defined in outer scopes.
55
"""
66

77
from typing import List
@@ -20,21 +20,28 @@
2020

2121

2222
def setup_callable_class(builder: IRBuilder) -> None:
23-
"""Generates a callable class representing a nested function or a function within a
24-
non-extension class and sets up the 'self' variable for that class.
23+
"""Generate an (incomplete) callable class representing function.
2524
26-
This takes the most recently visited function and returns a ClassIR to represent that
27-
function. Each callable class contains an environment attribute with points to another
28-
ClassIR representing the environment class where some of its variables can be accessed.
29-
Note that its '__call__' method is not yet implemented, and is implemented in the
30-
add_call_to_callable_class function.
25+
This can be a nested function or a function within a non-extension
26+
class. Also set up the 'self' variable for that class.
3127
32-
Returns a newly constructed ClassIR representing the callable class for the nested
33-
function.
34-
"""
28+
This takes the most recently visited function and returns a
29+
ClassIR to represent that function. Each callable class contains
30+
an environment attribute which points to another ClassIR
31+
representing the environment class where some of its variables can
32+
be accessed.
3533
36-
# Check to see that the name has not already been taken. If so, rename the class. We allow
37-
# multiple uses of the same function name because this is valid in if-else blocks. Example:
34+
Note that some methods, such as '__call__', are not yet
35+
created here. Use additional functions, such as
36+
add_call_to_callable_class(), to add them.
37+
38+
Return a newly constructed ClassIR representing the callable
39+
class for the nested function.
40+
"""
41+
# Check to see that the name has not already been taken. If so,
42+
# rename the class. We allow multiple uses of the same function
43+
# name because this is valid in if-else blocks. Example:
44+
#
3845
# if True:
3946
# def foo(): ----> foo_obj()
4047
# return True
@@ -48,12 +55,14 @@ def setup_callable_class(builder: IRBuilder) -> None:
4855
count += 1
4956
builder.callable_class_names.add(name)
5057

51-
# Define the actual callable class ClassIR, and set its environment to point at the
52-
# previously defined environment class.
58+
# Define the actual callable class ClassIR, and set its
59+
# environment to point at the previously defined environment
60+
# class.
5361
callable_class_ir = ClassIR(name, builder.module_name, is_generated=True)
5462

55-
# The functools @wraps decorator attempts to call setattr on nested functions, so
56-
# we create a dict for these nested functions.
63+
# The functools @wraps decorator attempts to call setattr on
64+
# nested functions, so we create a dict for these nested
65+
# functions.
5766
# https://github.com/python/cpython/blob/3.7/Lib/functools.py#L58
5867
if builder.fn_info.is_nested:
5968
callable_class_ir.has_dict = True
@@ -68,8 +77,8 @@ def setup_callable_class(builder: IRBuilder) -> None:
6877
builder.fn_info.callable_class = ImplicitClass(callable_class_ir)
6978
builder.classes.append(callable_class_ir)
7079

71-
# Add a 'self' variable to the callable class' environment, and store that variable in a
72-
# register to be accessed later.
80+
# Add a 'self' variable to the environment of the callable class,
81+
# and store that variable in a register to be accessed later.
7382
self_target = add_self_to_env(builder.environment, callable_class_ir)
7483
builder.fn_info.callable_class.self_reg = builder.read(self_target, builder.fn_info.fitem.line)
7584

@@ -79,13 +88,14 @@ def add_call_to_callable_class(builder: IRBuilder,
7988
sig: FuncSignature,
8089
env: Environment,
8190
fn_info: FuncInfo) -> FuncIR:
82-
"""Generates a '__call__' method for a callable class representing a nested function.
91+
"""Generate a '__call__' method for a callable class representing a nested function.
8392
84-
This takes the blocks, signature, and environment associated with a function definition and
85-
uses those to build the '__call__' method of a given callable class, used to represent that
86-
function. Note that a 'self' parameter is added to its list of arguments, as the nested
87-
function becomes a class method.
93+
This takes the blocks, signature, and environment associated with
94+
a function definition and uses those to build the '__call__'
95+
method of a given callable class, used to represent that
96+
function.
8897
"""
98+
# Since we create a method, we also add a 'self' parameter.
8999
sig = FuncSignature((RuntimeArg(SELF_NAME, object_rprimitive),) + sig.args, sig.ret_type)
90100
call_fn_decl = FuncDecl('__call__', fn_info.callable_class.ir.name, builder.module_name, sig)
91101
call_fn_ir = FuncIR(call_fn_decl, blocks, env,
@@ -95,7 +105,7 @@ def add_call_to_callable_class(builder: IRBuilder,
95105

96106

97107
def add_get_to_callable_class(builder: IRBuilder, fn_info: FuncInfo) -> None:
98-
"""Generates the '__get__' method for a callable class."""
108+
"""Generate the '__get__' method for a callable class."""
99109
line = fn_info.fitem.line
100110
builder.enter(fn_info)
101111

@@ -133,22 +143,30 @@ def add_get_to_callable_class(builder: IRBuilder, fn_info: FuncInfo) -> None:
133143

134144

135145
def instantiate_callable_class(builder: IRBuilder, fn_info: FuncInfo) -> Value:
136-
"""
137-
Assigns a callable class to a register named after the given function definition. Note
138-
that fn_info refers to the function being assigned, whereas builder.fn_info refers to the
139-
function encapsulating the function being turned into a callable class.
146+
"""Create an instance of a callable class for a function.
147+
148+
Calls to the function will actually call this instance.
149+
150+
Note that fn_info refers to the function being assigned, whereas
151+
builder.fn_info refers to the function encapsulating the function
152+
being turned into a callable class.
140153
"""
141154
fitem = fn_info.fitem
142155
func_reg = builder.add(Call(fn_info.callable_class.ir.ctor, [], fitem.line))
143156

144-
# Set the callable class' environment attribute to point at the environment class
145-
# defined in the callable class' immediate outer scope. Note that there are three possible
146-
# environment class registers we may use. If the encapsulating function is:
147-
# - a generator function, then the callable class is instantiated from the generator class'
148-
# __next__' function, and hence the generator class' environment register is used.
149-
# - a nested function, then the callable class is instantiated from the current callable
150-
# class' '__call__' function, and hence the callable class' environment register is used.
151-
# - neither, then we use the environment register of the original function.
157+
# Set the environment attribute of the callable class to point at
158+
# the environment class defined in the callable class' immediate
159+
# outer scope. Note that there are three possible environment
160+
# class registers we may use. This depends on what the encapsulating
161+
# (parent) function is:
162+
#
163+
# - A nested function: the callable class is instantiated
164+
# from the current callable class' '__call__' function, and hence
165+
# the callable class' environment register is used.
166+
# - A generator function: the callable class is instantiated
167+
# from the '__next__' method of the generator class, and hence the
168+
# environment of the generator class is used.
169+
# - Regular function: we use the environment of the original function.
152170
curr_env_reg = None
153171
if builder.fn_info.is_generator:
154172
curr_env_reg = builder.fn_info.generator_class.curr_env_reg

mypyc/irbuild/classdef.py

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
"""Transform class definitions from the mypy AST form to IR."""
2+
13
from typing import List, Optional
24

35
from mypy.nodes import (
@@ -29,6 +31,17 @@
2931

3032

3133
def transform_class_def(builder: IRBuilder, cdef: ClassDef) -> None:
34+
"""Create IR for a class definition.
35+
36+
This can generate both extension (native) and non-extension
37+
classes. These are generated in very different ways. In the
38+
latter case we construct a Python type object at runtime by doing
39+
the equivalent of "type(name, bases, dict)" in IR. Extension
40+
classes are defined via C structs that are generated later in
41+
mypyc.codegen.emitclass.
42+
43+
This is the main entry point to this module.
44+
"""
3245
ir = builder.mapper.type_to_ir[cdef.info]
3346

3447
# We do this check here because the base field of parent
@@ -188,9 +201,9 @@ def allocate_class(builder: IRBuilder, cdef: ClassDef) -> Value:
188201

189202

190203
def populate_non_ext_bases(builder: IRBuilder, cdef: ClassDef) -> Value:
191-
"""
192-
Populate the base-class tuple passed to the metaclass constructor
193-
for non-extension classes.
204+
"""Create base class tuple of a non-extension class.
205+
206+
The tuple is passed to the metaclass constructor.
194207
"""
195208
ir = builder.mapper.type_to_ir[cdef.info]
196209
bases = []
@@ -222,11 +235,10 @@ def setup_non_ext_dict(builder: IRBuilder,
222235
cdef: ClassDef,
223236
metaclass: Value,
224237
bases: Value) -> Value:
225-
"""
226-
Initialize the class dictionary for a non-extension class. This class dictionary
227-
is passed to the metaclass constructor.
228-
"""
238+
"""Initialize the class dictionary for a non-extension class.
229239
240+
This class dictionary is passed to the metaclass constructor.
241+
"""
230242
# Check if the metaclass defines a __prepare__ method, and if so, call it.
231243
has_prepare = builder.primitive_op(py_hasattr_op,
232244
[metaclass,
@@ -252,14 +264,16 @@ def setup_non_ext_dict(builder: IRBuilder,
252264
return non_ext_dict
253265

254266

255-
def add_non_ext_class_attr(builder: IRBuilder, non_ext: NonExtClassInfo, lvalue: NameExpr,
256-
stmt: AssignmentStmt, cdef: ClassDef,
267+
def add_non_ext_class_attr(builder: IRBuilder,
268+
non_ext: NonExtClassInfo,
269+
lvalue: NameExpr,
270+
stmt: AssignmentStmt,
271+
cdef: ClassDef,
257272
attr_to_cache: List[Lvalue]) -> None:
258-
"""
259-
Add a class attribute to __annotations__ of a non-extension class. If the
260-
attribute is assigned to a value, it is also added to __dict__.
261-
"""
273+
"""Add a class attribute to __annotations__ of a non-extension class.
262274
275+
If the attribute is initialized with a value, also add it to __dict__.
276+
"""
263277
# We populate __annotations__ because dataclasses uses it to determine
264278
# which attributes to compute on.
265279
# TODO: Maybe generate more precise types for annotations
@@ -284,7 +298,7 @@ def add_non_ext_class_attr(builder: IRBuilder, non_ext: NonExtClassInfo, lvalue:
284298

285299

286300
def generate_attr_defaults(builder: IRBuilder, cdef: ClassDef) -> None:
287-
"""Generate an initialization method for default attr values (from class vars)"""
301+
"""Generate an initialization method for default attr values (from class vars)."""
288302
cls = builder.mapper.type_to_ir[cdef.info]
289303
if cls.builtin_base:
290304
return
@@ -347,6 +361,7 @@ def generate_attr_defaults(builder: IRBuilder, cdef: ClassDef) -> None:
347361

348362

349363
def create_ne_from_eq(builder: IRBuilder, cdef: ClassDef) -> None:
364+
"""Create a "__ne__" method from a "__eq__" method (if only latter exists)."""
350365
cls = builder.mapper.type_to_ir[cdef.info]
351366
if cls.has_method('__eq__') and not cls.has_method('__ne__'):
352367
f = gen_glue_ne_method(builder, cls, cdef.line)
@@ -356,7 +371,7 @@ def create_ne_from_eq(builder: IRBuilder, cdef: ClassDef) -> None:
356371

357372

358373
def gen_glue_ne_method(builder: IRBuilder, cls: ClassIR, line: int) -> FuncIR:
359-
"""Generate a __ne__ method from a __eq__ method. """
374+
"""Generate a "__ne__" method from a "__eq__" method. """
360375
builder.enter()
361376

362377
rt_args = (RuntimeArg("self", RInstance(cls)), RuntimeArg("rhs", object_rprimitive))
@@ -417,10 +432,13 @@ def load_non_ext_class(builder: IRBuilder,
417432

418433

419434
def load_decorated_class(builder: IRBuilder, cdef: ClassDef, type_obj: Value) -> Value:
420-
"""
421-
Given a decorated ClassDef and a register containing a non-extension representation of the
422-
ClassDef created via the type constructor, applies the corresponding decorator functions
423-
on that decorated ClassDef and returns a register containing the decorated ClassDef.
435+
"""Apply class decorators to create a decorated (non-extension) class object.
436+
437+
Given a decorated ClassDef and a register containing a
438+
non-extension representation of the ClassDef created via the type
439+
constructor, applies the corresponding decorator functions on that
440+
decorated ClassDef and returns a register containing the decorated
441+
ClassDef.
424442
"""
425443
decorators = cdef.decorators
426444
dec_class = type_obj
@@ -432,7 +450,7 @@ def load_decorated_class(builder: IRBuilder, cdef: ClassDef, type_obj: Value) ->
432450

433451

434452
def cache_class_attrs(builder: IRBuilder, attrs_to_cache: List[Lvalue], cdef: ClassDef) -> None:
435-
"""Add class attributes to be cached to the global cache"""
453+
"""Add class attributes to be cached to the global cache."""
436454
typ = builder.load_native_type_object(cdef.fullname)
437455
for lval in attrs_to_cache:
438456
assert isinstance(lval, NameExpr)

mypyc/irbuild/context.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
"""Helpers that store information about functions and the related classes."""
2+
13
from typing import List, Optional, Tuple
24

35
from mypy.nodes import FuncItem
@@ -10,6 +12,7 @@
1012

1113
class FuncInfo:
1214
"""Contains information about functions as they are generated."""
15+
1316
def __init__(self,
1417
fitem: FuncItem = INVALID_FUNC_DEF,
1518
name: str = '',
@@ -87,9 +90,14 @@ def curr_env_reg(self) -> Value:
8790

8891

8992
class ImplicitClass:
90-
"""Contains information regarding classes that are generated as a result of nested functions or
91-
generated functions, but not explicitly defined in the source code.
93+
"""Contains information regarding implicitly generated classes.
94+
95+
Implicit classes are generated for nested functions and generator
96+
functions. They are not explicitly defined in the source code.
97+
98+
NOTE: This is both a concrete class and used as a base class.
9299
"""
100+
93101
def __init__(self, ir: ClassIR) -> None:
94102
# The ClassIR instance associated with this class.
95103
self.ir = ir
@@ -131,6 +139,8 @@ def prev_env_reg(self, reg: Value) -> None:
131139

132140

133141
class GeneratorClass(ImplicitClass):
142+
"""Contains information about implicit generator function classes."""
143+
134144
def __init__(self, ir: ClassIR) -> None:
135145
super().__init__(ir)
136146
# This register holds the label number that the '__next__' function should go to the next

0 commit comments

Comments
 (0)