# pylingual: PLDI Tut3
---
## Tutorial preparation
1. Connect to tutorial VM via
```shell=
ssh <userid>@pldi25.pylingual.io
```
2. Clone the repo on the VM and create a virtual environment
```shell==
mkdir $HOME/repos
cd $HOME/repos
# checkout local version of pylingual source
git clone /usr/share/pylingual
cd pylingual
python -m venv venv --system-site-packages
source venv/bin/activate
pip install -e .
```
3. To confirm the correct set-up please run the following command.
```shell=
git status
On branch pldi
Your branch is up to date with 'origin/pldi'.
```
4. (optional) You are recommended to a tmux session for the tutorial session.
```shell=
tmux new-session -t tut3
```
## Control Flow Reconstruction
To reconstruct control flow, PyLingual matches [templates] that model control flow structures found in Python. We attempt to match these templates from the bottom up and once a template is matched we add the appropriate indentation levels.
**Please note that control flow structures may differ across versions.**
### Creating a template
Each template is implemented as a class inheriting from the ControlFlowTemplate class, and each template needs to be registered with the `register_template` decorator to be used.
`register_template(priority, run, version)`
where priority is the priority of your template over other templates, run the run where your template can begin being matched, and version being the python version your template is used for.
Ex.
`@register_template(0,0(3,10))`
### Template setup
Setup your template by adding nodes via the T() function. Create the nodes and add their edges through the N() class. Add the name of the node you wish to connect to as a string.
```python=
template = T(node=N("natural edge", "conditional edge","exception edge")
node2=N("node")
)
```
### Matching nodes
To match nodes each class requires a try_match function. However you can use the `make_try_match` function to create the function for you by providing the nodes of your template into the function. Note down EdgeKind.Falls and EdgeKing.Exceptions. For cases where a node can exist, but does not need to exist.
### To indented source
Once a node its match to_indented_source is called and turns the contents of it's nodes into source code. using the `@to_indented_source` decorator you can use a docstring to create your indented source.
## With statement (3.10)
### Test File 1
Run the control flow reconstructor on the `with` test file:
```shell=
python dev_scripts/cflow.py test_files/with.py -v3.10
```
```python=
=== original file ===
def read_file(filename):
with open(filename, 'r') as file:
content = file.read()
return content
=== reconstructed file ===
def read_file(filename):
# meta: irreducible cflow
pass
=== equivalence report ===
<module>: Success: Equal
<module>.read_file: Failure: Different control flow
```
The control flow reconstructor was not successful. Check the CFGs to see where it got stuck:
```shell=
# generate cfgs with -g flag
python dev_scripts/cflow.py test_files/with.py -v3.10 -g
# you can view the cfgs in your browser using this script,
# at pldi25.pylingual.io:<port>
# where port is 5000 + your user id
# (port 5000 for user00, port 5001 for user01, ...)
python dev_scripts/dotviewer.py
```
```graphviz
digraph "<module>.read_file_10_1.dot" {
graph [bb="0,0,940.5,959.75",
splines=true
];
node [label="\N"];
8074026080507 [fontname="Courier New",
height=0.5,
label="Node 6: <38: RERAISE 1>\l",
labeljust=l,
pos="771.5,109.48",
shape=box,
width=1.9444];
8074026080417 [fontname="Courier New",
height=0.5,
label="MetaTemplate[end]\l",
labeljust=l,
pos="617.5,18",
shape=box,
width=2.1806];
8074026080507 -> 8074026080417 [color=blue,
fontname="Courier New",
kind=meta,
label=meta,
labeljust=l,
lp="677.49,71.236",
pos="e,647.86,36.036 741.07,91.406 716.92,77.06 682.98,56.898 656.78,41.333"];
8074026080441 [fontname="Courier New",
height=0.5,
label="MetaTemplate[start]\l",
labeljust=l,
pos="494.5,941.75",
shape=box,
width=2.4028];
8074026080537 [fontname="Courier New",
height=1.3611,
label="Node 1: BlockTemplate[\l| [2] <0: LOAD_GLOBAL 0 (open)>\l| <2: LOAD_FAST 0 (filename)>\l| <4: LOAD_CONST 1 (\"r\")>\l| <6: CALL_\
FUNCTION 2 (2 positional arguments)>\l| <8: SETUP_WITH 13 (to 34)>]\l",
labeljust=l,
pos="494.5,818.45",
shape=box,
width=5.9583];
8074026080441 -> 8074026080537 [color=blue,
fontname="Courier New",
kind=meta,
label=meta,
labeljust=l,
lp="477.5,903.18",
pos="e,494.5,867.64 494.5,923.56 494.5,911.35 494.5,894.52 494.5,877.91"];
8074026080549 [fontname="Courier New",
height=1.5694,
label="Node 2: BlockTemplate[\l| <10: STORE_FAST 1 (file)>\l| [3] <12: LOAD_FAST 1 (file)>\l| <14: LOAD_METHOD 1 (read)>\l| <16: CALL_\
METHOD 0 (0 positional arguments)>\l| <18: STORE_FAST 2 (content)>\l| <20: POP_BLOCK>]\l",
labeljust=l,
pos="494.5,633.26",
shape=box,
width=5.8472];
8074026080537 -> 8074026080549 [color=black,
fontname="Courier New",
kind=natural,
label=natural,
labeljust=l,
lp="465.5,737.14",
pos="e,494.5,689.85 494.5,769.31 494.5,748.03 494.5,722.78 494.5,699.86",
type=natural];
8074026080552 [fontname="Courier New",
height=1.5694,
label="Node 4: BlockTemplate[\l| <22: LOAD_CONST 0 (None)>\l| <24: DUP_TOP>\l| <26: DUP_TOP>\l| <28: CALL_FUNCTION 3 (3 positional \
arguments)>\l| <30: POP_TOP>\l| <32: JUMP_ABSOLUTE 52 (to 50)>]\l",
labeljust=l,
pos="218.5,437.45",
shape=box,
width=6.0694];
8074026080549 -> 8074026080552 [color=black,
fontname="Courier New",
kind=natural,
label=natural,
labeljust=l,
lp="327.52,542.87",
pos="e,298.15,493.96 414.67,576.62 380.77,552.57 341.14,524.46 306.4,499.81",
type=natural];
8074026080561 [fontname="Courier New",
height=0.73611,
label="Node 3: BlockTemplate[\l| <34: WITH_EXCEPT_START>\l| <36: POP_JUMP_IF_TRUE 21 (to 40)>]\l",
labeljust=l,
pos="771.5,437.45",
shape=box,
width=4.6944];
8074026080549 -> 8074026080561 [color=red,
fontname="Courier New",
kind=exception,
label=exception,
labeljust=l,
lp="616.57,527.96",
pos="e,733.8,464.1 574.62,576.62 623.65,541.96 684.63,498.86 725.62,469.88",
type=exception];
8074026080564 [fontname="Courier New",
height=0.73611,
label="Node 7: BlockTemplate[\l| [4] <50: LOAD_FAST 2 (content)>\l| <52: RETURN_VALUE>]\l",
labeljust=l,
pos="464.5,109.48",
shape=box,
width=4.3611];
8074026080552 -> 8074026080564 [color=black,
fontname="Courier New",
kind=jump,
label=jump,
labeljust=l,
lp="336.13,265.46",
pos="e,444.49,136.16 261.07,380.7 312.08,312.69 395.96,200.85 438.31,144.4",
type=jump];
8074026080561 -> 8074026080507 [color=black,
fontname="Courier New",
kind=natural,
label=natural,
labeljust=l,
lp="742.5,277.19",
pos="e,771.5,127.79 771.5,410.89 771.5,350.04 771.5,200.95 771.5,138.13",
type=natural];
8074026080543 [fontname="Courier New",
height=1.3611,
label="Node 5: BlockTemplate[\l| <40: POP_TOP>\l| <42: POP_TOP>\l| <44: POP_TOP>\l| <46: POP_EXCEPT>\l| <48: POP_TOP>]\l",
labeljust=l,
pos="548.5,252.25",
shape=box,
width=2.6389];
8074026080561 -> 8074026080543 [color=green,
fontname="Courier New",
kind=true_jump,
label=true_jump,
labeljust=l,
lp="636.11,363.65",
pos="e,607.62,301.35 739.55,410.92 707.3,384.14 656.54,341.98 615.44,307.84",
type=true_jump];
8074026080543 -> 8074026080564 [color=black,
fontname="Courier New",
kind=natural,
label=natural,
labeljust=l,
lp="470.7,176.81",
pos="e,480.24,136.23 519.59,203.12 508.36,184.02 495.73,162.57 485.41,145.02",
type=natural];
8074026080564 -> 8074026080417 [color=blue,
fontname="Courier New",
kind=meta,
label=meta,
labeljust=l,
lp="530.97,67.071",
pos="e,587.09,36.185 509.17,82.774 531.08,69.669 557.25,54.026 578.37,41.397"];
}
```
### Reading the Control Flow Graph
Before making a template you must first understand what each node in the control flow graph corresponds to.
#### Node 1 `setup_with`
The bytecode in this node appears to be the setup for the with, it contains the open function call and its parameters. It also has a natural edge to **node 2**.
#### Node 2 `with_body`
The bytecode in this node appears to correspond to our with's body as the bytecode for content = file.read() is in it. This has an exception edge to **node 3** and a natural edge to **node 4**.
#### Node 3 `with_except_start`
This node has bytecode setting up exception handling. With a conditional edge to **node 5** and an exception edge to **node 6**.
#### Node 4 `cleanup`
This node is the natural edge for the with body. The bytecode in this node does not correspond to any source code while preparing the stack. This has one natural edge to **node 7**.
#### Node 5 `pop_top`
This node is cleaning up after an exception is raised. This has one natural edge to **node 7**.
#### Node 6 `reraise`
This is a node that raises an exception that terminates the program.
#### Node 7 `tail`
This is beginning of code beyond the with statement.
### Nodes to code
```python=
# registers the template for version 3.10,
# note your template will not be matched without registering it
@register_template(0, 0, (3, 10))
class With3_10(ControlFlowTemplate):
# node_name = N("natural edge", "conditional edge, exception edge")
template = T(
setup_with=N("with_body"),
with_body=N("cleanup", None, "with_except_start"),
with_except_start=N("reraise", "pop_top"),
cleanup=N("tail"),
pop_top=N("tail"),
reraise=N.tail(),
tail=N.tail(),
)
# add all your nodes into make_try_match to try matching
try_match = make_try_match(
# EdgeKind.Fall means tail doesn't have to exist to match
{EdgeKind.Fall: "tail"},
"setup_with", "with_body", "cleanup", "with_except_start", \
"pop_top", "reraise"
)
# use contents of nodes to create indented source
@to_indented_source
def to_indented_source():
"""
{setup_with}
{with_body}
"""
```
Rerun the control flow reconstructor to see if the change solves the issue:
```shell=
python dev_scripts/cflow.py test_files/with.py -v3.10
```
### CFlow output
```python==
"""original file"""
def read_file(filename):
with open(filename, 'r') as file:
content = file.read()
return content
"""reconstructed file"""
def read_file(filename):
with open(filename, 'r') as file:
content = file.read()
return content
"""equivalence report"""
<module>: Success: Equal
<module>.read_file: Success: Equal
```
<!--
### Test File 2
Now try it on the next test file:
```
python dev_scripts/cflow.py test_files/with2.py -v3.10
```
```
=== original file ===
def compare_file(filename1, filename2):
with open(filename1, 'r') as file1, open(filename2, 'r') as file2:
content1 = file1.read()
content2 = file2.read()
if content1 == content2:
print('files are equal')
else:
print('files are not equal')
=== reconstructed file ===
def compare_file(filename1, filename2):
# meta: irreducible cflow
pass
=== equivalence report ===
<module>: Success: Equal
<module>.compare_file: Failure: Different control flow
```
Replace the with-template with a more versitile one:
```python
class WithCleanup3_10(ControlFlowTemplate):
template = T(
start=~N("reraise", "poptop").with_cond(starting_instructions("WITH_EXCEPT_START")),
reraise=+N().with_cond(exact_instructions("RERAISE")).with_in_deg(1),
poptop=~N("tail.", None).with_cond(starting_instructions("POP_TOP")).with_in_deg(1),
tail=N.tail(),
)
try_match = make_try_match({EdgeKind.Fall: "tail"}, "start", "reraise", "poptop")
@to_indented_source
def to_indented_source():
"""
{poptop}
"""
@register_template(0, 0, (3, 10))
class With3_10(ControlFlowTemplate):
template = T(
setup_with=~N("with_body", None),
with_body=N("normal_cleanup.", None, "exc_cleanup").with_in_deg(1),
exc_cleanup=N.tail().of_subtemplate(WithCleanup3_10).with_in_deg(1),
normal_cleanup=~N.tail(),
)
try_match = make_try_match({EdgeKind.Fall: "normal_cleanup"}, "setup_with", "with_body", "exc_cleanup")
@to_indented_source
def to_indented_source():
"""
{setup_with}
{with_body}
{exc_cleanup}
"""
```
```
=== original file ===
def compare_file(filename1, filename2):
with open(filename1, 'r') as file1, open(filename2, 'r') as file2:
content1 = file1.read()
content2 = file2.read()
if content1 == content2:
print('files are equal')
else:
print('files are not equal')
=== reconstructed file ===
def compare_file(filename1, filename2):
with open(filename1, 'r') as file1, open(filename2, 'r') as file2:
content1 = file1.read()
content2 = file2.read()
if content1 == content2:
print('files are equal')
else:
print('files are not equal')
=== equivalence report ===
<module>: Success: Equal
<module>.compare_file: Success: Equal
```
-->
## Try-Except 3.10
### Test File 1
Run the control flow reconstructor with the `try-except` test file:
```shell=
python dev_scripts/cflow.py test_files/try.py -v3.10 -g
```
```python=
"""original file"""
def division(a, b):
try:
result = a / b
except:
result = None
return result
"""reconstructed file"""
def division(a, b):
# meta: irreducible cflow
pass
"""equivalence report"""
<module>: Success: Equal
<module>.division: Failure: Different control flow
```
The CFG looks like this:
```graphviz
digraph "<module>.division_10_1.dot" {
graph [bb="0,0,678,712.26",
splines=true
];
node [label="\N"];
8740437942198 [fontname="Courier New",
height=0.5,
label="Node 1: [2] <0: SETUP_FINALLY 7 (to 14)>\l",
labeljust=l,
pos="330.75,622.26",
shape=box,
width=3.8889];
8740437942117 [fontname="Courier New",
height=1.4861,
label="Node 2: BlockTemplate[\l| [3] <2: LOAD_FAST 0 (a)>\l| <4: LOAD_FAST 1 (b)>\l| <6: BINARY_TRUE_DIVIDE>\l| <8: STORE_FAST 2 (result)>\l| <\
10: POP_BLOCK>]\l",
labeljust=l,
pos="330.75,489.42",
shape=box,
width=3.7743];
8740437942198 -> 8740437942117 [color=black,
fontname="Courier New",
kind=natural,
label=natural,
labeljust=l,
lp="301.88,581.79",
pos="e,330.75,543.28 330.75,603.79 330.75,590.86 330.75,572.75 330.75,554.74",
type=natural];
8740437942192 [fontname="Courier New",
height=0.5,
label="Node 3: <12: JUMP_ABSOLUTE 28 (to 26)>\l",
labeljust=l,
pos="131.75,283.54",
shape=box,
width=3.6597];
8740437942060 [fontname="Courier New",
height=0.79861,
label="Node 5: BlockTemplate[\l| [6] <26: LOAD_FAST 2 (result)>\l| <28: RETURN_VALUE>]\l",
labeljust=l,
pos="330.75,114.25",
shape=box,
width=4.2326];
8740437942192 -> 8740437942060 [color=black,
fontname="Courier New",
kind=jump,
label=jump,
labeljust=l,
lp="208.22,212.7",
pos="e,296.48,143.4 153.43,265.1 185.59,237.74 246.6,185.84 287.97,150.65",
type=jump];
8740439082412 [fontname="Courier New",
height=0.5,
label="MetaTemplate[start]\l",
labeljust=l,
pos="330.75,694.26",
shape=box,
width=2.3993];
8740439082412 -> 8740437942198 [color=blue,
fontname="Courier New",
kind=meta,
label=meta,
labeljust=l,
lp="314.25,666.89",
pos="e,330.75,640.67 330.75,676.09 330.75,668.81 330.75,660.24 330.75,652.09"];
8740439082409 [fontname="Courier New",
height=0.5,
label="MetaTemplate[end]\l",
labeljust=l,
pos="330.75,18",
shape=box,
width=2.1701];
8740437942117 -> 8740437942192 [color=black,
fontname="Courier New",
kind=natural,
label=natural,
labeljust=l,
lp="185.35,377.12",
pos="e,149.56,301.97 278.73,435.6 239.61,395.13 187.71,341.44 157.2,309.87",
type=natural];
8740437942138 [fontname="Courier New",
height=1.7153,
label="Node 4: BlockTemplate[\l| [4] <14: POP_TOP>\l| <16: POP_TOP>\l| <18: POP_TOP>\l| [5] <20: LOAD_CONST 0 (None)>\l| <22: STORE_\
FAST 2 (result)>\l| <24: POP_EXCEPT>]\l",
labeljust=l,
pos="529.75,283.54",
shape=box,
width=4.1181];
8740437942117 -> 8740437942138 [color=red,
fontname="Courier New",
kind=exception,
label=exception,
labeljust=l,
lp="389.08,398.92",
pos="e,469.93,345.43 382.77,435.6 406.99,410.55 436.1,380.43 462,353.63",
type=exception];
8740437942138 -> 8740437942060 [color=black,
fontname="Courier New",
kind=natural,
label=natural,
labeljust=l,
lp="381.92,190.6",
pos="e,364.99,143.38 456.78,221.46 428.82,197.68 397.85,171.33 373.6,150.7",
type=natural];
8740437942060 -> 8740439082409 [color=blue,
fontname="Courier New",
kind=meta,
label=meta,
labeljust=l,
lp="314.25,69.244",
pos="e,330.75,36.434 330.75,85.111 330.75,73.242 330.75,59.539 330.75,47.622"];
}
```
### Reading the Control Flow Graph
#### Node 1 `try_header`
This node has bytecode that sets up the try block and it has one natural edge to **node 2**.
#### Node 2 `try_body`
This node has bytecode that translates to the source code in the try body of our test case. This node has a natural edge to **node 3** and an exception edge to **node 4**.
#### Node 3 `jump`
This node is a jump with a natural edge to **node 5**.
#### Node 4 `except_body`
This node has bytecode that translates to the source code in the except block of our test case. This has one natural edge to **node 5**
#### Node 5 `tail`
This is the return statement after the try except block.
Add the template to `pylingual/control_flow_reconstructor/templates/Exception.py`:
### Nodes to code
```python=
# register template for 3.10
@register_template(0, 0, (3, 10))
class TryExcept3_10(ControlFlowTemplate):
# node_name=N("natural edge", "conditional edge", "exception edge")
template = T(
try_header=N("try_body"),
try_body=N("jump", None, "except_body"),
jump=N("tail"),
except_body=N("tail"),
tail=N.tail(),
)
## EdgeKind.Fall means that tail does not have to exist to match this template
try_match = make_try_match({EdgeKind.Fall: "tail"}, "try_header",
"try_body", "jump", "except_body")
@to_indented_source
def to_indented_source():
"""
{try_header}
try:
{try_body}
except:
{except_body}
"""
```
### CFlow Result
```python=
""" original file """
def division(a, b):
try:
result = a / b
except:
result = None
return result
""" reconstructed file """
def division(a, b):
try:
result = a / b
except:
result = None
return result
```
<!--
### Test File 2
```
python dev_scripts/cflow.py test_files/try2.py -v3.10 -g
```
```
=== original file ===
def division(a, b):
try:
result = a / b
except Exception:
result = None
return result
=== reconstructed file ===
def division(a, b):
try:
result = a / b
except:
except Exception:
result = None
return result
=== equivalence report ===
Result.CompileError
```
`except:` and `except SomeException:` produce different CFGs:
```graphviz
digraph "<module>.division_10_1.dot" {
graph [bb="0,0,783.25,857.16",
splines=true
];
node [label="\N"];
8746727694301 [fontname="Courier New",
height=0.5,
label="[2] <0: SETUP_FINALLY 7 (to 14)>\l",
labeljust=l,
pos="362.75,767.16",
shape=box,
width=3.8889];
8746727306314 [fontname="Courier New",
height=1.4861,
label="BlockTemplate[\l| [3] <2: LOAD_FAST 0 (a)>\l| <4: LOAD_FAST 1 (b)>\l| <6: BINARY_TRUE_DIVIDE>\l| <8: STORE_FAST 2 (result)>\l| <\
10: POP_BLOCK>]\l",
labeljust=l,
pos="362.75,634.32",
shape=box,
width=3.7743];
8746727694301 -> 8746727306314 [color=black,
fontname="Courier New",
kind=natural,
label=natural,
labeljust=l,
lp="333.88,726.69",
pos="e,362.75,688.18 362.75,748.69 362.75,735.76 362.75,717.65 362.75,699.64",
type=natural];
8746727306446 [fontname="Courier New",
height=0.5,
label="<12: JUMP_ABSOLUTE 34 (to 32)>\l",
labeljust=l,
pos="131.75,465.03",
shape=box,
width=3.6597];
8746727306317 [fontname="Courier New",
height=0.79861,
label="BlockTemplate[\l| [6] <32: LOAD_FAST 2 (result)>\l| <34: RETURN_VALUE>]\l",
labeljust=l,
pos="299.75,114.25",
shape=box,
width=4.2326];
8746727306446 -> 8746727306317 [color=black,
fontname="Courier New",
kind=jump,
label=jump,
labeljust=l,
lp="167.31,295.41",
pos="e,271.74,143.11 135.63,446.77 145.1,404.57 171.84,297.8 215.8,218.09 228.94,194.26 247.69,170.44 264.13,151.64",
type=jump];
8746727306383 [fontname="Courier New",
height=0.5,
label="<36: RERAISE 0>\l",
labeljust=l,
pos="603.75,114.25",
shape=box,
width=1.941];
8746727694328 [fontname="Courier New",
height=0.5,
label="MetaTemplate[end]\l",
labeljust=l,
pos="451.75,18",
shape=box,
width=2.1701];
8746727306383 -> 8746727694328 [color=blue,
fontname="Courier New",
kind=meta,
label=meta,
labeljust=l,
lp="511.12,74.292",
pos="e,480.85,36.426 574.8,95.92 550.9,80.782 516.75,59.159 490.49,42.53"];
8746727694316 [fontname="Courier New",
height=0.5,
label="MetaTemplate[start]\l",
labeljust=l,
pos="362.75,839.16",
shape=box,
width=2.3993];
8746727694316 -> 8746727694301 [color=blue,
fontname="Courier New",
kind=meta,
label=meta,
labeljust=l,
lp="346.25,811.79",
pos="e,362.75,785.57 362.75,820.99 362.75,813.71 362.75,805.14 362.75,796.99"];
8746727306314 -> 8746727306446 [color=black,
fontname="Courier New",
kind=natural,
label=natural,
labeljust=l,
lp="194.36,540.33",
pos="e,156.84,483.42 289.34,580.52 247.98,550.21 198.27,513.78 165.9,490.05",
type=natural];
8746727306347 [fontname="Courier New",
height=1.0278,
label="BlockTemplate[\l| [4] <14: DUP_TOP>\l| <16: LOAD_GLOBAL 0 (Exception)>\l| <18: JUMP_IF_NOT_EXC_MATCH 19 (to 36)>]\l",
labeljust=l,
pos="593.75,465.03",
shape=box,
width=5.2639];
8746727306314 -> 8746727306347 [color=red,
fontname="Courier New",
kind=exception,
label=exception,
labeljust=l,
lp="452.39,549.67",
pos="e,542.84,502.34 436.16,580.52 467.64,557.45 503.97,530.83 533.8,508.96",
type=exception];
8746727306347 -> 8746727306383 [color=green,
fontname="Courier New",
kind=false_jump,
label=false_jump,
labeljust=l,
lp="557.77,288.52",
pos="e,603.23,132.62 594.81,427.82 596.81,357.75 601.09,207.55 602.91,143.82",
type=false_jump];
8746727306311 [fontname="Courier New",
height=1.7153,
label="BlockTemplate[\l| <20: POP_TOP>\l| <22: POP_TOP>\l| <24: POP_TOP>\l| [5] <26: LOAD_CONST 0 (None)>\l| <28: STORE_\
FAST 2 (result)>\l| <30: POP_EXCEPT>]\l",
labeljust=l,
pos="367.75,283.54",
shape=box,
width=4.1181];
8746727306347 -> 8746727306311 [color=black,
fontname="Courier New",
kind=natural,
label=natural,
labeljust=l,
lp="467.01,394.69",
pos="e,444.94,345.53 547.2,427.65 520.27,406.02 485.53,378.12 453.8,352.64",
type=natural];
8746727306311 -> 8746727306317 [color=black,
fontname="Courier New",
kind=natural,
label=natural,
labeljust=l,
lp="298.13,190.35",
pos="e,311.36,143.15 342.82,221.46 333.73,198.85 323.72,173.92 315.63,153.8",
type=natural];
8746727306317 -> 8746727694328 [color=blue,
fontname="Courier New",
kind=meta,
label=meta,
labeljust=l,
lp="367.86,68.924",
pos="e,422.64,36.434 345.77,85.111 367.24,71.512 392.52,55.504 413.01,42.534"];
}
```
Replace the template with this one that checks for both cases:
```python
class Except3_10(ControlFlowTemplate):
@classmethod
def try_match(cls, cfg, node) -> ControlFlowTemplate | None:
if x := ExceptExc3_10.try_match(cfg, node):
return x
if x := BareExcept3_10.try_match(cfg, node):
return x
class ExceptExc3_10(Except3_10):
template = T(
except_header=N("except_body", "no_match"),
except_body=N("tail"),
no_match=N.tail(),
tail=N.tail(),
)
try_match = make_try_match({EdgeKind.Fall: "tail"}, "except_header", "except_body", "no_match")
@to_indented_source
def to_indented_source():
"""
{except_header}
{except_body}
"""
class BareExcept3_10(Except3_10):
template = T(
except_body=N("tail"),
tail=N.tail(),
)
try_match = make_try_match({EdgeKind.Fall: "tail"}, "except_body")
@to_indented_source
def to_indented_source():
"""
except:
{except_body}
"""
@register_template(0, 0, (3, 10))
class Try3_10(ControlFlowTemplate):
template = T(
try_header=N("try_body"),
try_body=N("jump", None, "except_body"),
jump=N("tail"),
except_body=N("tail").of_subtemplate(Except3_10),
tail=N.tail(),
)
try_match = make_try_match({EdgeKind.Fall: "tail"}, "try_header", "try_body", "jump", "except_body")
@to_indented_source
def to_indented_source():
"""
{try_header}
try:
{try_body}
{except_body}
"""
```
```
python dev_scripts/cflow.py test_files/try.py -v3.10
python dev_scripts/cflow.py test_files/try2.py -v3.10
```
```
=== original file ===
def division(a, b):
try:
result = a / b
except:
result = None
return result
=== reconstructed file ===
def division(a, b):
try:
result = a / b
except:
result = None
return result
=== equivalence report ===
<module>: Success: Equal
<module>.division: Success: Equal
=== original file ===
def division(a, b):
try:
result = a / b
except Exception:
result = None
return result
=== reconstructed file ===
def division(a, b):
try:
result = a / b
except Exception:
result = None
return result
=== equivalence report ===
<module>: Success: Equal
<module>.division: Success: Equal
```
Both cases work now.
### Test File 3
```
python dev_scripts/cflow.py test_files/try3.py -v3.10 -g
```
```
=== original file ===
def division1(a, b):
try:
result = a / b
except ZeroDivisionError:
result = a
except Exception:
result = None
return result
def division2(a, b):
try:
result = a / b
except TypeError:
result = 0
except ZeroDivisionError:
result = a
except Exception:
result = 1234
except:
result = None
return result
=== reconstructed file ===
def division1(a, b):
# meta: irreducible cflow
pass
def division2(a, b):
# meta: irreducible cflow
pass
=== equivalence report ===
<module>: Success: Equal
<module>.division1: Failure: Different control flow
<module>.division2: Failure: Different control flow
```
There can be an arbitrary number of `except` statements on a `try` statement. Make the following changes to handle this:
```diff
class Except3_10(ControlFlowTemplate):
@classmethod
def try_match(cls, cfg, node) -> ControlFlowTemplate | None:
+ if [x.opname for x in node.get_instructions()] == ["RERAISE"]:
+ return node
if x := ExceptExc3_10.try_match(cfg, node):
return x
if x := BareExcept3_10.try_match(cfg, node):
return x
class ExceptExc3_10(Except3_10):
template = T(
except_header=N("except_body", "no_match"),
except_body=N("tail"),
- no_match=N.tail(),
+ no_match=N("tail.").of_subtemplate(Except3_10),
tail=N.tail(),
)
try_match = make_try_match({EdgeKind.Fall: "tail"}, "except_header", "except_body", "no_match")
@to_indented_source
def to_indented_source():
"""
{except_header}
{except_body}
+ {no_match}
"""
``` -->
More details [CFG document 1](/s/XbHVeE0WF) and [CFG document 2](/s/Ucm_4um7L).
Have fun!
###### tags: `pldi`,`pylingual`,`standalone`
---
[templates]:https://todo