Commits

larry committed 1939d20

Removed Converter.wrap in favor of Converter.converter_init.
Also added text_accumulator() and cleaned up code to use it.
And some minor formatting fixes for (the recently renamed) CRenderData.

Comments (0)

Files changed (3)

Modules/_weakref.c

 
 module _weakref
 
-_weakref.getweakrefcount
+_weakref.getweakrefcount -> Py_ssize_t
 
   object: object
 
 #define _WEAKREF_GETWEAKREFCOUNT_METHODDEF    \
     {"getweakrefcount", (PyCFunction)_weakref_getweakrefcount, METH_VARARGS|METH_KEYWORDS, _weakref_getweakrefcount__doc__}
 
-static PyObject *
+static Py_ssize_t
 _weakref_getweakrefcount_impl(PyObject *self, PyObject *object);
 
 static PyObject *
     PyObject *return_value = NULL;
     static char *_keywords[] = {"object", NULL};
     PyObject *object;
+    Py_ssize_t _return_value;
 
     if (!PyArg_ParseTupleAndKeywords(args, kwargs,
         "O:getweakrefcount", _keywords,
         &object))
         goto exit;
-    return_value = _weakref_getweakrefcount_impl(self, object);
+    _return_value = _weakref_getweakrefcount_impl(self, object);
+    if ((_return_value == -1) && PyErr_Occurred())
+        goto exit;
+    return_value = PyLong_FromSsize_t(_return_value);
 
 exit:
     return return_value;
 }
 
-static PyObject *
+static Py_ssize_t
 _weakref_getweakrefcount_impl(PyObject *self, PyObject *object)
-/*[clinic checksum: d484d2107c7de6d15952f72cf40ab4978fee8993]*/
+/*[clinic checksum: cf88cba4f39d68bd91d814bf690767741697045b]*/
 {
-    PyObject *result = NULL;
+    PyWeakReference **list;
 
-    if (PyType_SUPPORTS_WEAKREFS(Py_TYPE(object))) {
-        PyWeakReference **list = GET_WEAKREFS_LISTPTR(object);
-
-        result = PyLong_FromSsize_t(_PyWeakref_GetWeakrefCount(*list));
-    }
-    else
-        result = PyLong_FromLong(0);
-
-    return result;
+    if (!PyType_SUPPORTS_WEAKREFS(Py_TYPE(object)))
+        return 0;
+    
+    list = GET_WEAKREFS_LISTPTR(object);
+    return _PyWeakref_GetWeakrefCount(*list));
 }
 
 

Modules/posixmodule.c

 /*[python]
 
 @add_converter
-@Converter.wrap
 class path_t_converter(Converter):
 
     type = "path_t"
 
     converter = 'path_converter'
 
-    def __init__(self, *, allow_fd=False, nullable=False):
+    def converter_init(self, *, allow_fd=False, nullable=False):
         def strify(value):
             return str(int(bool(value)))
 
         return "path_cleanup(&" + self.name + ");"
 
 @add_converter
-@Converter.wrap
 class dir_fd_converter(Converter):
     type = 'int'
     converter = 'OS_STAT_DIR_FD_CONVERTER'
 
-    def __init__(self):
+    def converter_init(self):
         if self.default in (unspecified, None):
             self.c_default = 'DEFAULT_DIR_FD'
 
         &fd))
         goto exit;
     _return_value = os_ttyname_impl(self, fd);
-    if (PyErr_Occurred())
-        return NULL;
-
+    if (_return_value == NULL)
+        goto exit;
     return_value = PyUnicode_DecodeFSDefault(_return_value);
 
-
 exit:
     return return_value;
 }
 
 static char *
 os_ttyname_impl(PyObject *self, int fd)
-/*[clinic checksum: 100a025aea036a03e224590d2f9c4ad6a589d4e5]*/
+/*[clinic checksum: 4f9a1a8d6972d4f57ab248604b8977687228f6fb]*/
 {
     char *ret;
 
 import tempfile
 
 
-##
-## Open questions (ask on python-dev):
-##
+#
+# convert the N separate arrays of M object in CRenderData
+# into a single array of M objects that each support N properties?
+#
 # 
-# add "_parse" to the parse functions, instead of "_impl"
-# to the implementation functions?
-#
-# emit code for modules and classes?
-#
-# #ifdef support?!
-#   maybe can avoid that with "#define ENTRY {}," and
-#   let the user #undef and #redefine ENTRY to nothing
-#   if the ifdef is false
-#
-# have impl return a struct so we get success/failure?
-#
-# any opinions about my admittedly-kinda-ucky Converter.wrap
-# magic?
 
 
 _empty = inspect._empty
 NULL = Null()
 
 
+def _text_accumulator():
+    """
+    Creates a simple text accumulator / joiner.
+
+    Returns a pair of callables:
+        append, output
+    "append" appends a string to the accumulator.
+    "output" returns the contents of the accumulator
+       joined together (''.join(accumulator)) and
+       empties the accumulator.
+    """
+    text = []
+    def output():
+        s = ''.join(text)
+        text.clear()
+        return s
+    return text, text.append, output
+
+def text_accumulator():
+    text, append, output = _text_accumulator()
+    return append, output
+
 def fail(*args, filename=None, line_number=None):
     joined = " ".join([str(a) for a in args])
-    text = []
-    add = text.append
+    add, output = text_accumulator()
     add("Error")
     if clinic:
         if filename is None:
         add(" on line " + str(line_number))
     add(':\n')
     add(joined)
-    print("".join(text))
+    print(output())
     sys.exit(-1)
 
 
     return s
 
 def quoted_for_c(s):
-    text = []
-    add = text.append
+    add, output = text_accumulator()
     map = { '\t': '\\\\t', '\n': '\\\\n', '\r': '\\\\r' }
     for c in s:
         if c in map:
         if c in '\\"\'':
             add('\\')
         add(c)
-    return ''.join(text)
+    return output()
 
 c_keywords = set("""
 asm auto break case char const continue default do double else enum
           * A newline will be added to the end.
     """
 
-    text = []
-    add = text.append
+    text, add, output = _text_accumulator()
     for line in s.split('\n'):
         indent, curly, trailing = line.partition('{')
         if not curly:
                 add(indent)
                 add(line)
             add('\n')
-    text.pop()
-    return ''.join(text)
+    text.pop() # strip the trailing \n
+    return output()
 
 
-class RenderData:
+class CRenderData:
     def __init__(self):
 
         # The C statements to declare variables.
+        # Should be full lines with \n eol characters.
         self.declarations = []
 
         # The C statements required to initialize the variables before the parse call.
+        # Should be full lines with \n eol characters.
         self.initializers = []
 
         # The entries for the "keywords" array for PyArg_ParseTuple.
+        # Should be individual strings representing the names.
         self.keywords = []
 
         # The "format units" for PyArg_ParseTuple.
+        # Should be individual strings that will get 
         self.format_units = []
 
         # The varargs arguments for PyArg_ParseTuple.
         self.return_value = "return_value"
 
         # For return converters: the code to convert the return
-        # value from the parse function.
+        # value from the parse function.  This is also where
+        # you should check the _return_value for errors, and
+        # "goto exit" if there are any.
         self.return_conversion = []
 
         # The C statements required to clean up after the impl call.
 
 
 def permute_left_option_groups(l):
+    """
+    Given [1, 2, 3], should yield:
+       ()
+       (3,)
+       (3, 2)
+       (3, 2, 1)
+    """
     yield tuple()
     accumulator = []
     for group in reversed(l):
 
 
 def permute_right_option_groups(l):
+    """
+    Given [1, 2, 3], should yield:
+      ()
+      (1,)
+      (1, 2)
+      (1, 2, 3)
+    """
     yield tuple()
     accumulator = []
     for group in l:
         return self.render_function(function)
 
     def docstring_for_c_string(self, f):
-        text = []
-        add = text.append
+        text, add, output = _text_accumulator()
         # turn docstring into a properly quoted C string
         for line in f.docstring.split('\n'):
             add('"')
         # Clinic prefer prefers groups on the left.  So in the above example,
         # five arguments would map to B+C, not C+D.
 
-        text = []
-        add = text.append
-        def output():
-            s = ''.join(text)
-            text.clear()
-            return s
-
+        add, output = text_accumulator()
         parameters = list(f.parameters.values())
 
         groups = []
         add("}}")
         template_dict['positional_parsing'] = output()
 
-
     def render_function(self, f):
         if not f:
             return ""
 
-        text = []
-        add = text.append
-        def output():
-            s = ''.join(text)
-            text.clear()
-            return s
-
-        data = RenderData()
+        add, output = text_accumulator()
+        data = CRenderData()
 
         if f.kind == STATIC_METHOD:
             meth_flags = 'METH_STATIC'
         template_dict['parse_arguments'] = ', '.join(data.parse_arguments)
         template_dict['impl_parameters'] = ", ".join(data.impl_parameters)
         template_dict['impl_arguments'] = ", ".join(data.impl_arguments)
-        template_dict['return_conversion'] = "\n".join(data.return_conversion)
-        template_dict['cleanup'] = "\n\n".join(data.cleanup)
+        template_dict['return_conversion'] = "".join(data.return_conversion).rstrip()
+        template_dict['cleanup'] = "".join(data.cleanup)
         template_dict['return_value'] = data.return_value
 
         template_dict['impl_prototype'] = self.impl_prototype_template.format(**template_dict)
 
     """
     def __init__(self, input, dsl_name=None, signatures=None, output=None, indent='', preindent=''):
-        if not isinstance(input, str):
-            input = "\n".join(input)
+        assert isinstance(input, str)
         self.input = input
         self.dsl_name = dsl_name
         self.signatures = signatures or []
 
         "language" should be a Language object.
         """
-        self.input = collections.deque(reversed(input.split("\n")))
+        self.input = collections.deque(reversed(input.splitlines(keepends=True)))
         self.block_start_line_number = self.line_number = 0
 
         self.language = language
 
     def _line(self):
         self.line_number += 1
-        return self.input.pop().rstrip()
+        return self.input.pop()
 
     def parse_verbatim_block(self):
-        input = []
+        add, output = text_accumulator()
         self.block_start_line_number = self.line_number
 
         while self.input:
             line = self._line()
             dsl_name = self.is_start_line(line)
             if dsl_name:
-                input.append('')
                 self.dsl_name = dsl_name
                 break
-            input.append(line)
+            add(line)
 
-        return Block(input)
+        return Block(output())
 
     def parse_clinic_block(self, dsl_name):
-        input = []
+        input_add, input_output = text_accumulator()
         self.block_start_line_number = self.line_number + 1
-        stop_line = self.language.stop_line.format(dsl_name=dsl_name)
+        stop_line = self.language.stop_line.format(dsl_name=dsl_name) + '\n'
         body_prefix = self.language.body_prefix.format(dsl_name=dsl_name)
 
         # consume body of program
         while self.input:
             line = self._line()
             if (line == stop_line) or self.is_start_line(line):
-                input.append('')
                 break
             if body_prefix:
                 line = line.lstrip()
                 assert line.startswith(body_prefix)
                 line = line[len(body_prefix):]
-            input.append(line)
+            input_add(line)
 
         # consume output and checksum line, if present.
         if self.last_dsl_name == dsl_name:
             return match.group(1)
 
         # scan forward for checksum line
-        output = []
+        output_add, output_output = text_accumulator()
         checksum = None
         while self.input:
             line = self._line()
             checksum = is_checksum_line(line)
             if checksum:
-                output.append('')
                 break
-            output.append(line)
+            output_add(line)
             if self.is_start_line(line):
                 break
 
         if checksum:
-            output = "\n".join(output)
+            output = output_output()
             if self.verify:
                 computed = compute_checksum(output)
                 if checksum != computed:
+                    print("outpoot", repr(output))
                     fail("Checksum mismatch!\nExpected: {}\nComputed: {}".format(checksum, computed))
         else:
             # put back output
-            self.input.extend(reversed(output))
+            self.input.extend(reversed(output.splitlines(keepends=True)))
             self.line_number -= len(output)
             output = None
 
-        return Block(input, dsl_name, output=output)
+        return Block(input_output(), dsl_name, output=output)
 
 
 class BlockPrinter:
         NULL: "None",
     }
 
-    @staticmethod
-    def wrap(cls):
-        class WrappedConverter(cls, Converter):
-            def __init__(self, name, function, default=unspecified, *, doc_default=None, required=False, **kwargs):
-                super(cls, self).__init__(name, function, default, doc_default=doc_default, required=required)
-                cls.__init__(self, **kwargs)
-        return functools.update_wrapper(WrappedConverter, cls, updated=())
-
-    def __init__(self, name, function, default=unspecified, *, doc_default=None, required=False):
+    def __init__(self, name, function, default=unspecified, *, doc_default=None, required=False, **kwargs):
         self.function = function
         self.name = name
 
         elif doc_default is not None:
             fail(function.fullname + " argument " + name + " specified a 'doc_default' without having a 'default'")
         self.required = required
+        self.converter_init(**kwargs)
+
+    def converter_init(self):
+        pass
 
     def is_optional(self):
         return (self.default is not unspecified) and (not self.required)
     def render(self, parameter, data):
         """
         parameter is a clinic.Parameter instance.
-        data is a RenderData instance.
+        data is a CRenderData instance.
         """
         name = legal_c_identifier(self.name)
 
         # cleanup
         cleanup = self.cleanup()
         if cleanup:
-            data.cleanup.append('/* Cleanup for ' + name + ' */\n' + cleanup.rstrip())
+            data.cleanup.append('/* Cleanup for ' + name + ' */\n' + cleanup.rstrip() + "\n")
 
     # Why is this one broken out separately?
     # For "positional-only" function parsing,
     format_unit = 'i'
 
 @add_converter
-@Converter.wrap
 class bool_converter(int_converter):
     format_unit = 'p'
 
-    def __init__(self):
+    def converter_init(self):
         self.default = bool(self.default)
         self.c_default = str(int(self.default))
 
     format_unit = 'c'
 
 @add_converter
-@Converter.wrap
 class object_converter(Converter):
     type = 'PyObject *'
     format_unit = 'O'
 
-    def __init__(self, *, type=None):
+    def converter_init(self, *, type=None):
         if type:
             assert isinstance(type, str)
             assert type.isidentifier()
 @add_converter
 @add_legacy_converter('s*', zeroes=True)
 @add_legacy_converter('z*', zeroes=True, nullable=True)
-@Converter.wrap
 class Py_buffer_converter(Converter):
     type = 'Py_buffer'
     format_unit = 'y*'
     impl_by_reference = True
 
-    def __init__(self, *, str=False, zeroes=False, nullable=False):
+    def converter_init(self, *, str=False, zeroes=False, nullable=False):
         if not str:
             assert not (zeroes or nullable)
         else:
         data.declarations.append(''.join(line))
         data.return_value = name
 
-    def err_occurred(self, data):
-        data.return_conversion.append('if (PyErr_Occurred())\n    return NULL;\n')
+    def err_occurred_if(self, expr, data):
+        data.return_conversion.append('if (({}) && PyErr_Occurred())\n    goto exit;\n'.format(expr))
+
+    def err_occurred_if_null_pointer(self, variable, data):
+        data.return_conversion.append('if ({} == NULL)\n    goto exit;\n'.format(variable))
 
     def render(self, function, data):
         """
         function is a clinic.Function instance.
-        data is a RenderData instance.
+        data is a CRenderData instance.
         """
         pass
 
 
     def render(self, function, data):
         self.declare(data)
-        self.err_occurred(data)
+        self.err_occurred_if("_return_value == -1", data)
         data.return_conversion.append(
             'return_value = PyLong_FromLong((long)_return_value);\n')
 
 
     def render(self, function, data):
         self.declare(data)
-        self.err_occurred(data)
+        self.err_occurred_if("_return_value == -1", data)
         data.return_conversion.append(
             'return_value = PyLong_FromLong(_return_value);\n')
 
 @add_return_converter
+class Py_ssize_t_return_converter(ReturnConverter):
+    type = 'Py_ssize_t'
+
+    def render(self, function, data):
+        self.declare(data)
+        self.err_occurred_if("_return_value == -1", data)
+        data.return_conversion.append(
+            'return_value = PyLong_FromSsize_t(_return_value);\n')
+
+@add_return_converter
 class DecodeFSDefault_return_converter(ReturnConverter):
     type = 'char *'
 
     def render(self, function, data):
         self.declare(data)
-        self.err_occurred(data)
+        self.err_occurred_if_null_pointer("_return_value", data)
         data.return_conversion.append(
             'return_value = PyUnicode_DecodeFSDefault(_return_value);\n')
 
     def format_docstring(self):
         f = self.function
 
-        text = []
-        add = text.append
-        def output():
-            s = ''.join(text)
-            text.clear()
-            return s
-
+        add, output = text_accumulator()
         parameters = list(f.parameters.values())
 
         ##