Bengt Richter bokr at oz.net
Sun Aug 28 23:33:05 CEST 2005

```On Sun, 28 Aug 2005 04:09:10 GMT, bokr at oz.net (Bengt Richter) wrote:

[... a response to the OP's apparent desire to "overload the divide operator" with a call
to his safediv function ...]

The part that rewrote the the AugAssign could only work for plain name Augassign targets, so
I introduced a helper function to generate a suitable assignment target node for attributes,
subscripts, and slices as well. So the test (still very alpha) looks like the following now:

----< testdiv.py >-------------------------------------------------------------------------
def test():
print 1.0/2.0
print 12/3
a=12; b=3
print a/b
print 2**a/(b+1)
try:
print 'print 1/0 =>'
print 1/0
except Exception, e: print '%s: %s' %(e.__class__.__name__, e)
try:
print 'print a/(b*(a-12)) =>'
print a/(b*(a-12))
except Exception, e: print '%s: %s' %(e.__class__.__name__, e)
try:
print 'def foo(x=1/(b-3)): return x =>'
def foo(x=1/(b-3)): return x
print 'print foo() =>'
print foo()
except Exception, e: print '%s: %s' %(e.__class__.__name__, e)
try:
print 'b /= (a-12) =>'
b /= (a-12)
print 'b after b/=(a-12):', b
except Exception, e: print '%s: %s' %(e.__class__.__name__, e)
print 's=[15]; s[0] /= 3; print s =>'
s=[15]; s[0] /= 3; print s
class T(list):
def __getitem__(self, i): print i; return 42
def __setitem__(self, i, v): print i, v
def __div__(self, other): print self, other; return 4242
t = T()
print 't.x=15; t.x /= 3; print t.x =>'
t.x=15; t.x /= 3; print t.x
print 't=T([15, 27]); t /= 3; print t =>'
t=T([15, 27]); t /= 3; print t

def foo(x, y):
"""for dis.dis to show code"""
z = x/y
x /= y
x.a /= y
x[z] /= y
x[:] /= y

if __name__ == '__main__': test()
------------------------------------------------------------------------------------------
Outputfrom run without conversion of / :

----< import_safediv.py >------------------------------------------------------------------
# import_safediv.py
from arborist import import_editing_ast, get_augassign_tgt
from compiler.ast import Div, CallFunc, Name, AugAssign, Assign

def safediv(num, den):
if den==0: result = 0*num
else: result = num/den
print 'safediv(%r, %r) => %r'%(num, den, result)
return result

def div2safediv(divnode):
"""replace Div nodes with CallFunc nodes calling safediv with same args"""
return  CallFunc(Name('safediv'),[divnode.left, divnode.right], None, None, divnode.lineno)

def divaugass2safediv(auganode):
if auganode.op != '/=': return auganode
return Assign([get_augassign_tgt(auganode)],
CallFunc(Name('safediv'),[auganode.node, auganode.expr], None, None, auganode.lineno))

callbacks = {Div:div2safediv, AugAssign:divaugass2safediv}
def import_safediv(modname, verbose=False):
modsafe = import_editing_ast(modname, callbacks, verbose)
modsafe.safediv = safediv
return modsafe

if __name__ == '__main__':
modsafe = import_safediv('testdiv', verbose=True)
modsafe.test()
-------------------------------------------------------------------------------------------

Result from using the above interactively:

[14:21] C:\pywk\ut\ast>py24
Python 2.4b1 (#56, Nov  3 2004, 01:47:27)
[GCC 3.2.3 (mingw special 20030504-1)] on win32

First import as usual and run test()
>>> import testdiv
>>> testdiv.test()
0.5
4
4
1024
print 1/0 =>
ZeroDivisionError: integer division or modulo by zero
print a/(b*(a-12)) =>
ZeroDivisionError: integer division or modulo by zero
def foo(x=1/(b-3)): return x =>
ZeroDivisionError: integer division or modulo by zero
b /= (a-12) =>
ZeroDivisionError: integer division or modulo by zero
s=[15]; s[0] /= 3; print s =>
[5]
t.x=15; t.x /= 3; print t.x =>
5
t=T([15, 27]); t /= 3; print t =>
[15, 27] 3
4242

Now import the import_safediv importer, to import with
conversion of ast to effect translation of divides to safediv calls:

Import and runs test() much as before:

>>> from import_safediv import import_safediv
>>> tdsafe = import_safediv('testdiv')
>>> tdsafe.test()
safediv(1.0, 2.0) => 0.5
0.5
safediv(12, 3) => 4
4
safediv(12, 3) => 4
4
safediv(4096, 4) => 1024
1024
print 1/0 =>
safediv(1, 0) => 0
0
print a/(b*(a-12)) =>
safediv(12, 0) => 0
0
def foo(x=1/(b-3)): return x =>
safediv(1, 0) => 0
print foo() =>
0
b /= (a-12) =>
safediv(3, 0) => 0
b after b/=(a-12): 0
s=[15]; s[0] /= 3; print s =>
safediv(15, 3) => 5
[5]
t.x=15; t.x /= 3; print t.x =>
safediv(15, 3) => 5
5
t=T([15, 27]); t /= 3; print t =>
[15, 27] 3
safediv([15, 27], 3) => 4242
4242

Look at the code generated by normal import first

>>> import dis
>>> dis.dis(testdiv.foo)
6 BINARY_DIVIDE
7 STORE_FAST               2 (z)

16 INPLACE_DIVIDE
17 STORE_FAST               0 (x)

23 DUP_TOP
30 INPLACE_DIVIDE
31 ROT_TWO
32 STORE_ATTR               3 (a)

41 DUP_TOPX                 2
44 BINARY_SUBSCR
48 INPLACE_DIVIDE
49 ROT_THREE
50 STORE_SUBSCR

54 DUP_TOP
55 SLICE+0
59 INPLACE_DIVIDE
60 ROT_TWO
61 STORE_SLICE+0
65 RETURN_VALUE

and then corresponding import_safediv (with same line numbers):

>>> dis.dis(tdsafe.foo)
9 CALL_FUNCTION            2
12 STORE_FAST               3 (z)

24 CALL_FUNCTION            2
27 STORE_FAST               0 (x)

42 CALL_FUNCTION            2
48 STORE_ATTR               4 (a)

60 BINARY_SUBSCR
64 CALL_FUNCTION            2
73 STORE_SUBSCR

80 SLICE+0
84 CALL_FUNCTION            2
90 STORE_SLICE+0
94 RETURN_VALUE

I guess this shows that code munging by AST rewriting is somewhat feasible,
but you can probably expect to have to dig into dark corners ;-)

I need to think through a few things I'm doing by intuition before I go much further...

Regards,
Bengt Richter

```