Use warnings framework for c division.
authorRobert Bradshaw <robertwb@math.washington.edu>
Sat, 28 Mar 2009 09:22:07 +0000 (02:22 -0700)
committerRobert Bradshaw <robertwb@math.washington.edu>
Sat, 28 Mar 2009 09:22:07 +0000 (02:22 -0700)
Cython/Compiler/ExprNodes.py
Cython/Compiler/ModuleNode.py
Cython/Compiler/Naming.py
Cython/Compiler/Nodes.py
tests/run/cdivision_CEP_516.pyx

index f9a825d20600b466e7720cb8a342691ab3710260..282ecb3c7dd356bcc75a2a8040356158cacbc5bd 100644 (file)
@@ -4235,6 +4235,12 @@ class DivNode(NumBinopNode):
     
     cdivision = None
     cdivision_warnings = False
+    
+    def analyse_types(self, env):
+        NumBinopNode.analyse_types(self, env)
+        if not self.type.is_pyobject and env.directives['cdivision_warnings']:
+            self.operand1 = self.operand1.coerce_to_simple(env)
+            self.operand2 = self.operand2.coerce_to_simple(env)
 
     def generate_evaluation_code(self, code):
         if not self.type.is_pyobject:
@@ -4242,22 +4248,24 @@ class DivNode(NumBinopNode):
                 self.cdivision = (code.globalstate.directives['cdivision'] 
                                     or not self.type.signed
                                     or self.type.is_float)
-            if code.globalstate.directives['cdivision_warnings']:
-                self.cdivision_warnings = True
-                code.globalstate.use_utility_code(div_mod_print_warning_utility_code)
-                code.globalstate.use_utility_code(div_warn_utility_code.specialize(self.type))
-            elif not self.cdivision:
+            if not self.cdivision:
                 code.globalstate.use_utility_code(div_utility_code.specialize(self.type))
         NumBinopNode.generate_evaluation_code(self, code)
+        if not self.type.is_pyobject and code.globalstate.directives['cdivision_warnings']:
+            self.generate_div_warning_code(code)
+    
+    def generate_div_warning_code(self, code):
+        code.globalstate.use_utility_code(cdivision_warning_utility_code)
+        code.putln("if ((%s < 0) ^ (%s < 0)) {" % (
+                        self.operand1.result(),
+                        self.operand2.result()))
+        code.putln(code.set_error_info(self.pos));
+        code.put("if (__Pyx_cdivision_warning()) ")
+        code.put_goto(code.error_label)
+        code.putln("}")
     
     def calculate_result_code(self):
-        if self.cdivision_warnings:
-            return "__Pyx_div_warn_%s(%s, %s, %s)" % (
-                    self.type.specalization_name(),
-                    self.operand1.result(), 
-                    self.operand2.result(),
-                    int(self.cdivision))
-        elif self.cdivision:
+        if self.cdivision:
             return "(%s / %s)" % (
                 self.operand1.result(), 
                 self.operand2.result())
@@ -4280,27 +4288,19 @@ class ModNode(DivNode):
         if not self.type.is_pyobject:
             if self.cdivision is None:
                 self.cdivision = code.globalstate.directives['cdivision'] or not self.type.signed
-            math_h_modifier = getattr(self.type, 'math_h_modifier', '__Pyx_INT')
-            if code.globalstate.directives['cdivision_warnings']:
-                self.cdivision_warnings = True
-                if self.type.is_int:
-                    code.globalstate.use_utility_code(mod_int_helper_macro)
-                code.globalstate.use_utility_code(div_mod_print_warning_utility_code)
-                code.globalstate.use_utility_code(mod_warn_utility_code.specialize(self.type, math_h_modifier=math_h_modifier))
-            elif not self.cdivision:
+            if not self.cdivision:
                 if self.type.is_int:
                     code.globalstate.use_utility_code(mod_int_helper_macro)
+                    math_h_modifier = '__Pyx_INT'
+                else:
+                    math_h_modifier = self.type.math_h_modifier
                 code.globalstate.use_utility_code(mod_utility_code.specialize(self.type, math_h_modifier=math_h_modifier))
         NumBinopNode.generate_evaluation_code(self, code)
+        if not self.type.is_pyobject and code.globalstate.directives['cdivision_warnings']:
+            self.generate_div_warning_code(code)
     
     def calculate_result_code(self):
-        if self.cdivision_warnings:
-            return "__Pyx_mod_warn_%s(%s, %s, %s)" % (
-                    self.type.specalization_name(),
-                    self.operand1.result(), 
-                    self.operand2.result(),
-                    int(self.cdivision))
-        elif self.cdivision:
+        if self.cdivision:
             if self.type.is_float:
                 return "fmod%s(%s, %s)" % (
                     self.type.math_h_modifier,
@@ -5713,7 +5713,7 @@ static INLINE %(type)s __Pyx_mod_%(type_name)s(%(type)s, %(type)s); /* proto */
 impl="""
 static INLINE %(type)s __Pyx_mod_%(type_name)s(%(type)s a, %(type)s b) {
     %(type)s res = fmod%(math_h_modifier)s(a, b);
-    res += (res * b < 0) * b;
+    res += ((res < 0) ^ (b < 0)) * b;
     return res;
 }
 """)
@@ -5730,56 +5730,21 @@ static INLINE %(type)s __Pyx_div_%(type_name)s(%(type)s a, %(type)s b) {
 }
 """)
 
-mod_warn_utility_code = UtilityCode(
+cdivision_warning_utility_code = UtilityCode(
 proto="""
-static INLINE %(type)s __Pyx_mod_warn_%(type_name)s(%(type)s, %(type)s, int cdivision); /* proto */
+static int __Pyx_cdivision_warning(void); /* proto */
 """,
 impl="""
-static INLINE %(type)s __Pyx_mod_warn_%(type_name)s(%(type)s a, %(type)s b, int cdivision) {
-    %(type)s res = fmod%(math_h_modifier)s(a, b);
-    if (res * b < 0) {
-        __Pyx_div_mod_warning();
-        if (!cdivision) res += b;
-    }
-    return res;
+static int __Pyx_cdivision_warning(void) {
+    return PyErr_WarnExplicit(PyExc_RuntimeWarning, 
+                              "division with oppositely signed operands, C and Python semantics differ",
+                              %(FILENAME)s, 
+                              %(LINENO)s,
+                              %(MODULENAME)s,
+                              NULL);
 }
-""")
-
-div_warn_utility_code = UtilityCode(
-proto="""
-static INLINE %(type)s __Pyx_div_warn_%(type_name)s(%(type)s, %(type)s, int cdivision); /* proto */
-""",
-impl="""
-static INLINE %(type)s __Pyx_div_warn_%(type_name)s(%(type)s a, %(type)s b, int cdivision) {
-    %(type)s res = a / b;
-    if (res < 0) {
-        if (__Pyx_div_mod_warning()) ;
-        if (!cdivision) res -= 1;
-    }
-    return res;
-}
-""")
-
-
-div_mod_print_warning_utility_code = UtilityCode(
-proto="""
-static int __Pyx_div_mod_warning(void); /* proto */
-""",
-impl="""
-static int __Pyx_div_mod_warning(void) {
-    int r;
-    PyObject* s;
-    #if PY_MAJOR_VERSION < 3
-    s = PyString_FromString("Warning: division with oppositely signed operands, C and Python semantics differ");
-    #else
-    s = PyUnicode_FromString("Warning: division with oppositely signed operands, C and Python semantics differ");
-    #endif
-    if (s) {
-        r = __Pyx_PrintOne(s);
-        Py_DECREF(s);
-    }
-    else r = -1;
-    return r;
-}
-""",
-requires=[Nodes.printing_one_utility_code])
+""" % {
+    'FILENAME': Naming.filename_cname,
+    'MODULENAME': Naming.modulename_cname,
+    'LINENO':  Naming.lineno_cname,
+})
index e3cb4b6f652ebbd29344afcc5025ab24adf85ada..21c62a8e296dfb61c56654e3ce5d6c28ff099925 100644 (file)
@@ -253,6 +253,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
 
         code.globalstate.use_utility_code(refcount_utility_code)
 
+        code.putln('const char *%s = "%s";' % (Naming.modulename_cname, self.full_module_name))
         code.putln("")
         code.putln("/* Implementation of %s */" % env.qualified_name)
         self.generate_const_definitions(env, code)
index bfed035e0d224687d934ca4ba6dbd3cfbfcb9627..7864efdb61f1319223bf16df17a26d7fb8852f59 100644 (file)
@@ -52,6 +52,7 @@ preimport_cname  = pyrex_prefix + "i"
 moddict_cname    = pyrex_prefix + "d"
 dummy_cname      = pyrex_prefix + "dummy"
 filename_cname   = pyrex_prefix + "filename"
+modulename_cname = pyrex_prefix + "modulename"
 filetable_cname  = pyrex_prefix + "f"
 filenames_cname  = pyrex_prefix + "filenames"
 fileinit_cname   = pyrex_prefix + "init_filenames"
index 71638ffcdd0b23a7d735c61f944fcc1761294652..a5154ead662a2fb337d1fc4a6c20be582cc6df7e 100644 (file)
@@ -5342,32 +5342,6 @@ bad:
 
 #------------------------------------------------------------------------------------
 
-unraisable_exception_utility_code = UtilityCode(
-proto = """
-static void __Pyx_WriteUnraisable(const char *name); /*proto*/
-""",
-impl = """
-static void __Pyx_WriteUnraisable(const char *name) {
-    PyObject *old_exc, *old_val, *old_tb;
-    PyObject *ctx;
-    __Pyx_ErrFetch(&old_exc, &old_val, &old_tb);
-    #if PY_MAJOR_VERSION < 3
-    ctx = PyString_FromString(name);
-    #else
-    ctx = PyUnicode_FromString(name);
-    #endif
-    __Pyx_ErrRestore(old_exc, old_val, old_tb);
-    if (!ctx) {
-        PyErr_WriteUnraisable(Py_None);
-    } else {
-        PyErr_WriteUnraisable(ctx);
-        Py_DECREF(ctx);
-    }
-}
-""")
-
-#------------------------------------------------------------------------------------
-
 traceback_utility_code = UtilityCode(
 proto = """
 static void __Pyx_AddTraceback(const char *funcname); /*proto*/
@@ -5512,6 +5486,33 @@ static INLINE void __Pyx_ErrFetch(PyObject **type, PyObject **value, PyObject **
 
 #------------------------------------------------------------------------------------
 
+unraisable_exception_utility_code = UtilityCode(
+proto = """
+static void __Pyx_WriteUnraisable(const char *name); /*proto*/
+""",
+impl = """
+static void __Pyx_WriteUnraisable(const char *name) {
+    PyObject *old_exc, *old_val, *old_tb;
+    PyObject *ctx;
+    __Pyx_ErrFetch(&old_exc, &old_val, &old_tb);
+    #if PY_MAJOR_VERSION < 3
+    ctx = PyString_FromString(name);
+    #else
+    ctx = PyUnicode_FromString(name);
+    #endif
+    __Pyx_ErrRestore(old_exc, old_val, old_tb);
+    if (!ctx) {
+        PyErr_WriteUnraisable(Py_None);
+    } else {
+        PyErr_WriteUnraisable(ctx);
+        Py_DECREF(ctx);
+    }
+}
+""",
+requires=[restore_exception_utility_code])
+
+#------------------------------------------------------------------------------------
+
 set_vtable_utility_code = UtilityCode(
 proto = """
 static int __Pyx_SetVtable(PyObject *dict, void *vtable); /*proto*/
index ca857cc80579c8d8ac6406a3431c30bfa1131c10..292690015d7fde169ec67e879906618aa1eec274 100644 (file)
@@ -27,12 +27,23 @@ True
 >>> [test_cdiv_cmod(a, b) for a, b in v]
 [(1, 7), (-1, -7), (1, -7), (-1, 7)]
 
->>> mod_int_py_warn(-17, 10)
-Warning: division with oppositely signed operands, C and Python semantics differ
+
+>>> def simple_warn(msg, *args): print msg
+>>> import warnings
+>>> warnings.showwarning = simple_warn
+
+>>> mod_int_c_warn(-17, 10)
+division with oppositely signed operands, C and Python semantics differ
 -7
->>> div_int_py_warn(-17, 10)
-Warning: division with oppositely signed operands, C and Python semantics differ
+>>> div_int_c_warn(-17, 10)
+division with oppositely signed operands, C and Python semantics differ
 -1
+>>> complex_expression(-150, 20, 20, -7)
+verbose_call(-150)
+division with oppositely signed operands, C and Python semantics differ
+verbose_call(20)
+division with oppositely signed operands, C and Python semantics differ
+-2
 """
 
 cimport cython
@@ -83,10 +94,19 @@ def test_cdiv_cmod(short a, short b):
 
 @cython.cdivision(True)
 @cython.cdivision_warnings(True)
-def mod_int_py_warn(int a, int b):
+def mod_int_c_warn(int a, int b):
     return a % b
 
 @cython.cdivision(True)
 @cython.cdivision_warnings(True)
-def div_int_py_warn(int a, int b):
+def div_int_c_warn(int a, int b):
     return a // b
+
+@cython.cdivision(False)
+@cython.cdivision_warnings(True)
+def complex_expression(int a, int b, int c, int d):
+    return (verbose_call(a) // b) % (verbose_call(c) // d)
+
+cdef int verbose_call(int x):
+    print "verbose_call(%s)" % x
+    return x