This repo is not current. Development has moved from Hg to Git. For the latest code use the "Source Code" tab above to go to the "Thun" git repo or navigate to:
https://osdn.net/projects/joypy/scm/git/Thun
Révision | 541f3eb4e9816dac6adb2c5c48af4f7aa0acab63 (tree) |
---|---|
l'heure | 2018-04-22 07:37:19 |
Auteur | ![]() |
Commiter | Simon Forman |
Let the name of wrapped functions appear in tracebacks.
@@ -29,6 +29,7 @@ | ||
29 | 29 | |
30 | 30 | from .parser import text_to_expression, Symbol |
31 | 31 | from .utils.stack import list_to_stack, iter_stack, pick, pushback |
32 | +from .utils.brutal_hackery import rename_code_object | |
32 | 33 | |
33 | 34 | |
34 | 35 | _dictionary = {} |
@@ -167,6 +168,7 @@ | ||
167 | 168 | ''' |
168 | 169 | @FunctionWrapper |
169 | 170 | @wraps(f) |
171 | + @rename_code_object(f.__name__) | |
170 | 172 | def inner(stack, expression, dictionary): |
171 | 173 | return f(stack), expression, dictionary |
172 | 174 | return inner |
@@ -178,6 +180,7 @@ | ||
178 | 180 | ''' |
179 | 181 | @FunctionWrapper |
180 | 182 | @wraps(f) |
183 | + @rename_code_object(f.__name__) | |
181 | 184 | def inner(stack, expression, dictionary): |
182 | 185 | (a, (b, stack)) = stack |
183 | 186 | result = f(b, a) |
@@ -191,6 +194,7 @@ | ||
191 | 194 | ''' |
192 | 195 | @FunctionWrapper |
193 | 196 | @wraps(f) |
197 | + @rename_code_object(f.__name__) | |
194 | 198 | def inner(stack, expression, dictionary): |
195 | 199 | (a, stack) = stack |
196 | 200 | result = f(a) |
@@ -0,0 +1,93 @@ | ||
1 | +# -*- coding: utf-8 -*- | |
2 | +# | |
3 | +# Copyright © 2018 Simon Forman | |
4 | +# | |
5 | +# This file is part of Joypy | |
6 | +# | |
7 | +# Joypy is free software: you can redistribute it and/or modify | |
8 | +# it under the terms of the GNU General Public License as published by | |
9 | +# the Free Software Foundation, either version 3 of the License, or | |
10 | +# (at your option) any later version. | |
11 | +# | |
12 | +# Joypy is distributed in the hope that it will be useful, | |
13 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | +# GNU General Public License for more details. | |
16 | +# | |
17 | +# You should have received a copy of the GNU General Public License | |
18 | +# along with Joypy. If not see <http://www.gnu.org/licenses/>. | |
19 | +# | |
20 | +''' | |
21 | +I really want tracebacks to show which function was being executed when | |
22 | +an error in the wrapper function happens. In order to do that, you have | |
23 | +to do this (the function in this module.) | |
24 | + | |
25 | +Here's what it looks like when you pass too few arguments to e.g. "mul". | |
26 | + | |
27 | + >>> from joy.library import _dictionary | |
28 | + >>> m = _dictionary['*'] | |
29 | + >>> m((), (), {}) | |
30 | + | |
31 | + Traceback (most recent call last): | |
32 | + File "<pyshell#49>", line 1, in <module> | |
33 | + m((), (), {}) | |
34 | + File "joy/library.py", line 185, in mul:inner | |
35 | + (a, (b, stack)) = stack | |
36 | + ValueError: need more than 0 values to unpack | |
37 | + >>> | |
38 | + | |
39 | + | |
40 | +Notice that line 185 in the library.py file is (as of this writing) in | |
41 | +the BinaryBuiltinWrapper's inner() function, but this hacky code has | |
42 | +managed to insert the name of the wrapped function ("mul") along with a | |
43 | +colon into the wrapper function's reported name. | |
44 | + | |
45 | +Normally I would frown on this sort of mad hackery, but... this is in | |
46 | +the service of ease-of-debugging! Very valuable. And note that all the | |
47 | +hideous patching is finished in the module-load-stage, it shouldn't cause | |
48 | +issues of its own at runtime. | |
49 | + | |
50 | +The main problem I see with this is that people coming to this code later | |
51 | +might be mystified if they just see a traceback with a ':' in the | |
52 | +function name! Hopefully they will discover this documentation. | |
53 | +''' | |
54 | + | |
55 | + | |
56 | +def rename_code_object(new_name): | |
57 | + ''' | |
58 | + If you want to wrap a function in another function and have the wrapped | |
59 | + function's name show up in the traceback, you must do this brutal | |
60 | + hackery to change the func.__code__.co_name attribute. See: | |
61 | + | |
62 | + https://stackoverflow.com/questions/29919804/function-decorated-using-functools-wraps-raises-typeerror-with-the-name-of-the-w | |
63 | + | |
64 | + https://stackoverflow.com/questions/29488327/changing-the-name-of-a-generator/29488561#29488561 | |
65 | + | |
66 | + I'm just glad it's possible. | |
67 | + ''' | |
68 | + def inner(func): | |
69 | + name = new_name + ':' + func.__name__ | |
70 | + code_object = func.__code__ | |
71 | + return type(func)( | |
72 | + type(code_object)( | |
73 | + code_object.co_argcount, | |
74 | + code_object.co_nlocals, | |
75 | + code_object.co_stacksize, | |
76 | + code_object.co_flags, | |
77 | + code_object.co_code, | |
78 | + code_object.co_consts, | |
79 | + code_object.co_names, | |
80 | + code_object.co_varnames, | |
81 | + code_object.co_filename, | |
82 | + name, | |
83 | + code_object.co_firstlineno, | |
84 | + code_object.co_lnotab, | |
85 | + code_object.co_freevars, | |
86 | + code_object.co_cellvars | |
87 | + ), | |
88 | + func.__globals__, | |
89 | + name, | |
90 | + func.__defaults__, | |
91 | + func.__closure__ | |
92 | + ) | |
93 | + return inner |