|
| 1 | +"""Single example exercising many variable-visibility scenarios for value capture. |
| 2 | +
|
| 3 | +Covers: |
| 4 | +- Simple function locals/params |
| 5 | +- Nested functions, nonlocal (closures) |
| 6 | +- Globals read/write |
| 7 | +- Class body scope and metaclass |
| 8 | +- Lambdas and all comprehensions |
| 9 | +- Generators and async coroutines |
| 10 | +- try/except/else/finally and with |
| 11 | +- Decorators and wrappers |
| 12 | +- eval/exec dynamic names |
| 13 | +- Imports at module and function scope |
| 14 | +- Builtins usage |
| 15 | +""" |
| 16 | + |
| 17 | +from __future__ import annotations |
| 18 | + |
| 19 | +#import asyncio |
| 20 | +import math |
| 21 | + |
| 22 | +# 1. Simple function: params and locals |
| 23 | +def simple_function(x: int): |
| 24 | + a = 1 |
| 25 | + b = a + x |
| 26 | + return a, b |
| 27 | + |
| 28 | +# Globals |
| 29 | +GLOBAL_VAL = 10 |
| 30 | +counter = 0 |
| 31 | +setting = "Hello" |
| 32 | +CONSTANT = 42 |
| 33 | + |
| 34 | + |
| 35 | +# 8. Decorator and wrapper (captures free var `setting`) |
| 36 | +def my_decorator(func): |
| 37 | + def wrapper(*args, **kwargs): |
| 38 | + # variables visible here: args, kwargs, setting, func (closed over) |
| 39 | + return func(*args, **kwargs) |
| 40 | + return wrapper |
| 41 | + |
| 42 | + |
| 43 | +@my_decorator |
| 44 | +def greet(name: str) -> str: |
| 45 | + message = f"Hi, {name}" |
| 46 | + return message |
| 47 | + |
| 48 | + |
| 49 | + |
| 50 | + |
| 51 | +# 2. Nested functions and nonlocal |
| 52 | +def outer_func(x: int): |
| 53 | + y = 1 |
| 54 | + |
| 55 | + def inner_func(z: int): |
| 56 | + nonlocal y |
| 57 | + w = x + y + z |
| 58 | + y = w |
| 59 | + return w |
| 60 | + |
| 61 | + total = inner_func(5) |
| 62 | + return y, total |
| 63 | + |
| 64 | + |
| 65 | +# 3. Globals |
| 66 | +def global_test(): |
| 67 | + local_copy = GLOBAL_VAL |
| 68 | + global counter |
| 69 | + counter += 1 |
| 70 | + return local_copy, counter |
| 71 | + |
| 72 | + |
| 73 | +# 4. Class scope and metaclass |
| 74 | +class MetaCounter(type): |
| 75 | + count = 0 |
| 76 | + |
| 77 | + def __init__(cls, name, bases, attrs): |
| 78 | + MetaCounter.count += 1 |
| 79 | + super().__init__(name, bases, attrs) |
| 80 | + |
| 81 | + |
| 82 | +class Sample(metaclass=MetaCounter): |
| 83 | + a = 10 |
| 84 | + b = a + 5 |
| 85 | + c = a + b + CONSTANT |
| 86 | + |
| 87 | + def method(self): |
| 88 | + return self.a + self.b |
| 89 | + |
| 90 | + |
| 91 | +# 6. Generators and async coroutines |
| 92 | +def counter_gen(n: int): |
| 93 | + total = 0 |
| 94 | + for i in range(n): |
| 95 | + total += i |
| 96 | + yield total |
| 97 | + return total |
| 98 | + |
| 99 | + |
| 100 | +# async def async_sum(data: list[int]) -> int: |
| 101 | +# total = 0 |
| 102 | +# for x in data: |
| 103 | +# total += x |
| 104 | +# await asyncio.sleep(0) |
| 105 | +# return total |
| 106 | + |
| 107 | + |
| 108 | +# async def agen(n: int): |
| 109 | +# for i in range(n): |
| 110 | +# yield i + 1 |
| 111 | + |
| 112 | + |
| 113 | +# 7. try/except/finally and with |
| 114 | +def exception_and_with_demo(x: int): |
| 115 | + try: |
| 116 | + inv = 10 / x |
| 117 | + except ZeroDivisionError as e: |
| 118 | + error_msg = f"Error: {e}" |
| 119 | + else: |
| 120 | + inv += 1 |
| 121 | + finally: |
| 122 | + final_flag = True |
| 123 | + |
| 124 | + with open(__file__, "r") as f: |
| 125 | + first_line = f.readline() |
| 126 | + return locals() |
| 127 | + |
| 128 | + |
| 129 | +# 9. eval/exec |
| 130 | +def eval_test(): |
| 131 | + value = 10 |
| 132 | + formula = "value * 2" |
| 133 | + result = eval(formula) |
| 134 | + return result |
| 135 | + |
| 136 | + |
| 137 | +# 10. Imports and visibility |
| 138 | +def import_test(): |
| 139 | + import os |
| 140 | + constant = math.pi |
| 141 | + cwd = os.getcwd() |
| 142 | + return constant, cwd |
| 143 | + |
| 144 | + |
| 145 | +# 11. Builtins |
| 146 | +def builtins_test(seq): |
| 147 | + n = len(seq) |
| 148 | + m = max(seq) |
| 149 | + return n, m |
| 150 | + |
| 151 | + |
| 152 | +def main() -> None: |
| 153 | + 1 |
| 154 | + res1 = simple_function(5) |
| 155 | + |
| 156 | + # 2 |
| 157 | + res2 = outer_func(2) |
| 158 | + |
| 159 | + # 3 |
| 160 | + before = counter |
| 161 | + _local_copy, _ctr = global_test() |
| 162 | + after = counter |
| 163 | + |
| 164 | + # 5. Lambdas and comprehensions |
| 165 | + factor = 2 |
| 166 | + double = lambda y: y * factor # noqa: E731 |
| 167 | + squares = [n ** 2 for n in range(3)] |
| 168 | + scaled_set = {n * factor for n in range(3)} |
| 169 | + mapping = {n: n * factor for n in range(3)} |
| 170 | + gen_exp = (n * factor for n in range(3)) |
| 171 | + result_list = list(gen_exp) |
| 172 | + |
| 173 | + # 6. Generators and async coroutines |
| 174 | + gen = counter_gen(3) |
| 175 | + gen_results = list(gen) |
| 176 | + # coroutine_result = asyncio.run(async_sum([1, 2, 3])) |
| 177 | + |
| 178 | + # async def consume() -> int: |
| 179 | + # acc = 0 |
| 180 | + # async for x in agen(3): |
| 181 | + # acc += x |
| 182 | + # return acc |
| 183 | + |
| 184 | + # async_acc = asyncio.run(consume()) |
| 185 | + |
| 186 | + # 7. try/except/finally and with |
| 187 | + r1 = exception_and_with_demo(0) |
| 188 | + r2 = exception_and_with_demo(5) |
| 189 | + has_e = "error_msg" in r1 |
| 190 | + has_inv = "inv" in r2 |
| 191 | + has_final_flag = r1.get("final_flag", False) and r2.get("final_flag", False) |
| 192 | + |
| 193 | + # 8. Decorator and wrapper |
| 194 | + output = greet("World") |
| 195 | + |
| 196 | + # 9. eval/exec |
| 197 | + expr_code = "dynamic_var = 99" |
| 198 | + exec(expr_code, globals()) |
| 199 | + dynamic_var = globals()["dynamic_var"] |
| 200 | + check = dynamic_var + 1 |
| 201 | + out = eval_test() |
| 202 | + |
| 203 | + # 10. import visibility |
| 204 | + constant, cwd = import_test() |
| 205 | + |
| 206 | + # 11. builtins |
| 207 | + built_n, built_m = builtins_test([5, 3, 7]) |
| 208 | + |
| 209 | + #Aggregate a compact, deterministic summary |
| 210 | + print( |
| 211 | + "ok", |
| 212 | + res1[0] + res1[1], # simple_function sum |
| 213 | + sum(res2), # outer_func sum |
| 214 | + after - before, # global counter increment |
| 215 | + MetaCounter.count, # metaclass incremented classes |
| 216 | + sum(squares), len(scaled_set), len(mapping), sum(result_list), |
| 217 | + sum(gen_results), # generator totals |
| 218 | + #coroutine_result, async_acc, # async results |
| 219 | + has_e, has_inv, has_final_flag, # exception/with signals |
| 220 | + len(output), # decorator + greet result length |
| 221 | + dynamic_var, check, out, # eval/exec values |
| 222 | + f"{constant:.3f}", # math.pi to 3 decimals |
| 223 | + bool(len(cwd)), # cwd non-empty is True |
| 224 | + built_n, built_m, # builtins result |
| 225 | + double(7), # lambda capture |
| 226 | + Sample.c, # class body computed constant |
| 227 | + ) |
| 228 | + |
| 229 | + |
| 230 | +if __name__ == "__main__": |
| 231 | + main() |
0 commit comments