-# cython: language_level = 3
+# cython: language_level = 3, py2_import=True
#
# Pyrex - Code output module
#
import cython
cython.declare(re=object, Naming=object, Options=object, StringEncoding=object,
Utils=object, SourceDescriptor=object, StringIOTree=object,
- DebugFlags=object, none_or_sub=object)
+ DebugFlags=object, none_or_sub=object, basestring=object)
import re
import Naming
from Cython.Utils import none_or_sub
try:
- basestring
-except NameError:
- basestring = str
+ from __builtin__ import basestring
+except ImportError:
+ from builtins import str as basestring
+
+
+non_portable_builtins_map = {
+ # builtins that have different names in different Python versions
+ 'bytes' : ('PY_MAJOR_VERSION < 3', 'str'),
+ 'unicode' : ('PY_MAJOR_VERSION >= 3', 'str'),
+ 'xrange' : ('PY_MAJOR_VERSION >= 3', 'range'),
+ 'BaseException' : ('PY_VERSION_HEX < 0x02050000', 'Exception'),
+ }
+
+uncachable_builtins = [
+ # builtin names that cannot be cached because they may or may not
+ # be available at import time
+ 'WindowsError',
+ ]
class UtilityCode(object):
# Stores utility code to add during code generation.
# See GlobalState.put_utility_code.
#
# hashes/equals by instance
-
+
def __init__(self, proto=None, impl=None, init=None, cleanup=None, requires=None,
proto_block='utility_code_proto'):
# proto_block: Which code block to dump prototype in. See GlobalState.
writer.put(self.cleanup)
else:
self.cleanup(writer, output.module_pos)
-
-
+
+
class FunctionState(object):
# return_label string function return point label
def __init__(self, owner, names_taken=cython.set()):
self.names_taken = names_taken
self.owner = owner
-
+
self.error_label = None
self.label_counter = 0
self.labels_used = cython.set()
self.temps_free = {} # (type, manage_ref) -> list of free vars with same type/managed status
self.temps_used_type = {} # name -> (type, manage_ref)
self.temp_counter = 0
+ self.closure_temps = None
# labels
if name is not None:
label += '_' + name
return label
-
+
def new_error_label(self):
old_err_lbl = self.error_label
self.error_label = self.new_label('error')
return old_err_lbl
-
+
def get_loop_labels(self):
return (
self.continue_label,
self.break_label)
-
+
def set_loop_labels(self, labels):
(self.continue_label,
self.break_label) = labels
-
+
def new_loop_labels(self):
old_labels = self.get_loop_labels()
self.set_loop_labels(
- (self.new_label("continue"),
+ (self.new_label("continue"),
self.new_label("break")))
return old_labels
-
+
def get_all_labels(self):
return (
self.continue_label,
new_labels.append(old_label)
self.set_all_labels(new_labels)
return old_labels
-
+
def use_label(self, lbl):
self.labels_used.add(lbl)
-
+
def label_used(self, lbl):
return lbl in self.labels_used
if manage_ref
for cname in freelist]
+ def init_closure_temps(self, scope):
+ self.closure_temps = ClosureTempAllocator(scope)
+
class IntConst(object):
"""Global info about a Python integer constant held by GlobalState.
self.escaped_value = StringEncoding.escape_byte_string(byte_string)
self.py_strings = None
- def get_py_string_const(self, encoding, identifier=None, is_str=False):
+ def get_py_string_const(self, encoding, identifier=None,
+ is_str=False, py3str_cstring=None):
py_strings = self.py_strings
text = self.text
else:
encoding_key = ''.join(find_alphanums(encoding))
- key = (is_str, is_unicode, encoding_key)
- if py_strings is not None and key in py_strings:
- py_string = py_strings[key]
+ key = (is_str, is_unicode, encoding_key, py3str_cstring)
+ if py_strings is not None:
+ try:
+ return py_strings[key]
+ except KeyError:
+ pass
else:
- if py_strings is None:
- self.py_strings = {}
- if identifier:
- intern = True
- elif identifier is None:
- if isinstance(text, unicode):
- intern = bool(possible_unicode_identifier(text))
- else:
- intern = bool(possible_bytes_identifier(text))
- else:
- intern = False
- if intern:
- prefix = Naming.interned_str_prefix
- else:
- prefix = Naming.py_const_prefix
- pystring_cname = "%s%s_%s" % (
- prefix,
- (is_str and 's') or (is_unicode and 'u') or 'b',
- self.cname[len(Naming.const_prefix):])
-
- py_string = PyStringConst(
- pystring_cname, encoding, is_unicode, is_str, intern)
- self.py_strings[key] = py_string
+ self.py_strings = {}
+ if identifier:
+ intern = True
+ elif identifier is None:
+ if isinstance(text, unicode):
+ intern = bool(possible_unicode_identifier(text))
+ else:
+ intern = bool(possible_bytes_identifier(text))
+ else:
+ intern = False
+ if intern:
+ prefix = Naming.interned_str_prefix
+ else:
+ prefix = Naming.py_const_prefix
+ pystring_cname = "%s%s_%s" % (
+ prefix,
+ (is_str and 's') or (is_unicode and 'u') or 'b',
+ self.cname[len(Naming.const_prefix):])
+
+ py_string = PyStringConst(
+ pystring_cname, encoding, is_unicode, is_str, py3str_cstring, intern)
+ self.py_strings[key] = py_string
return py_string
class PyStringConst(object):
"""Global info about a Python string constant held by GlobalState.
"""
# cname string
+ # py3str_cstring string
# encoding string
# intern boolean
# is_unicode boolean
# is_str boolean
- def __init__(self, cname, encoding, is_unicode, is_str=False, intern=False):
+ def __init__(self, cname, encoding, is_unicode, is_str=False,
+ py3str_cstring=None, intern=False):
self.cname = cname
+ self.py3str_cstring = py3str_cstring
self.encoding = encoding
self.is_str = is_str
self.is_unicode = is_unicode
# parts {string:CCodeWriter}
-
+
# interned_strings
# consts
# interned_nums
'utility_code_def',
'end'
]
-
+
def __init__(self, writer, emit_linenums=False):
self.filename_table = {}
w.enter_cfunc_scope()
w.putln("")
w.putln("static int __Pyx_InitCachedConstants(void) {")
+ w.put_declare_refcount_context()
w.put_setup_refcount_context("__Pyx_InitCachedConstants")
w = self.parts['init_globals']
w = self.parts['cleanup_module']
w.putln("}")
w.exit_cfunc_scope()
-
+
def put_pyobject_decl(self, entry):
self['global_var'].putln("static PyObject *%s;" % entry.cname)
c = self.new_string_const(text, byte_string)
return c
- def get_py_string_const(self, text, identifier=None, is_str=False):
+ def get_py_string_const(self, text, identifier=None,
+ is_str=False, unicode_value=None):
# return a Python string constant, creating a new one if necessary
c_string = self.get_string_const(text)
- py_string = c_string.get_py_string_const(text.encoding, identifier, is_str)
+ py3str_cstring = None
+ if is_str and unicode_value is not None \
+ and unicode_value.utf8encode() != text.byteencode():
+ py3str_cstring = self.get_string_const(unicode_value)
+ py_string = c_string.get_py_string_const(
+ text.encoding, identifier, is_str, py3str_cstring)
return py_string
def get_interned_identifier(self, text):
return "%s%s%d" % (Naming.const_prefix, prefix, n)
def add_cached_builtin_decl(self, entry):
- if Options.cache_builtins:
+ if entry.is_builtin and entry.is_const:
if self.should_declare(entry.cname, entry):
self.put_pyobject_decl(entry)
w = self.parts['cached_builtins']
- if entry.name == 'xrange':
- # replaced by range() in Py3
- w.putln('#if PY_MAJOR_VERSION >= 3')
+ condition = None
+ if entry.name in non_portable_builtins_map:
+ condition, replacement = non_portable_builtins_map[entry.name]
+ w.putln('#if %s' % condition)
self.put_cached_builtin_init(
- entry.pos, StringEncoding.EncodedString('range'),
+ entry.pos, StringEncoding.EncodedString(replacement),
entry.cname)
w.putln('#else')
self.put_cached_builtin_init(
entry.pos, StringEncoding.EncodedString(entry.name),
entry.cname)
- if entry.name == 'xrange':
+ if condition:
w.putln('#endif')
def put_cached_builtin_init(self, pos, name, cname):
decls_writer.putln(
"static PyObject *%s;" % py_string.cname)
+ if py_string.py3str_cstring:
+ w.putln("#if PY_MAJOR_VERSION >= 3")
+ w.putln(
+ "{&%s, %s, sizeof(%s), %s, %d, %d, %d}," % (
+ py_string.cname,
+ py_string.py3str_cstring.cname,
+ py_string.py3str_cstring.cname,
+ '0', 1, 0,
+ py_string.intern
+ ))
+ w.putln("#else")
w.putln(
"{&%s, %s, sizeof(%s), %s, %d, %d, %d}," % (
py_string.cname,
py_string.is_str,
py_string.intern
))
+ if py_string.py3str_cstring:
+ w.putln("#endif")
w.putln("{0, 0, 0, 0, 0, 0, 0}")
w.putln("};")
# The functions below are there in a transition phase only
# and will be deprecated. They are called from Nodes.BlockNode.
# The copy&paste duplication is intentional in order to be able
- # to see quickly how BlockNode worked, until this is replaced.
+ # to see quickly how BlockNode worked, until this is replaced.
def should_declare(self, cname, entry):
if cname in self.declared_cnames:
#
# Utility code state
#
-
+
def use_utility_code(self, utility_code):
"""
Adds code to the C file. utility_code should
- filename_table, filename_list, input_file_contents: All codewriters
coming from the same root share the same instances simultaneously.
"""
-
- # f file output file
- # buffer StringIOTree
-
- # level int indentation level
- # bol bool beginning of line?
- # marker string comment to emit before next line
- # funcstate FunctionState contains state local to a C function used for code
- # generation (labels and temps state etc.)
- # globalstate GlobalState contains state global for a C file (input file info,
- # utility code, declared constants etc.)
- # emit_linenums boolean whether or not to write #line pragmas
+
+ # f file output file
+ # buffer StringIOTree
+
+ # level int indentation level
+ # bol bool beginning of line?
+ # marker string comment to emit before next line
+ # funcstate FunctionState contains state local to a C function used for code
+ # generation (labels and temps state etc.)
+ # globalstate GlobalState contains state global for a C file (input file info,
+ # utility code, declared constants etc.)
+ # emit_linenums boolean whether or not to write #line pragmas
#
- # pyclass_stack list used during recursive code generation to pass information
- # about the current class one is in
+ # c_line_in_traceback boolean append the c file and line number to the traceback for exceptions
+ #
+ # pyclass_stack list used during recursive code generation to pass information
+ # about the current class one is in
globalstate = None
-
- def __init__(self, create_from=None, buffer=None, copy_formatting=False, emit_linenums=None):
+
+ def __init__(self, create_from=None, buffer=None, copy_formatting=False, emit_linenums=None, c_line_in_traceback=True):
if buffer is None: buffer = StringIOTree()
self.buffer = buffer
self.marker = None
self.last_marker_line = 0
self.source_desc = ""
self.pyclass_stack = []
-
+
self.funcstate = None
self.level = 0
self.call_level = 0
self.emit_linenums = self.globalstate.emit_linenums
else:
self.emit_linenums = emit_linenums
+ self.c_line_in_traceback = c_line_in_traceback
def create_new(self, create_from, buffer, copy_formatting):
# polymorphic constructor -- very slightly more versatile
# than using __class__
- result = CCodeWriter(create_from, buffer, copy_formatting)
+ result = CCodeWriter(create_from, buffer, copy_formatting, c_line_in_traceback=self.c_line_in_traceback)
return result
def copyto(self, f):
return self.buffer.getvalue()
def write(self, s):
- # also put invalid markers (lineno 0), to indicate that those lines
+ # also put invalid markers (lineno 0), to indicate that those lines
# have no Cython source code correspondence
if self.marker is None:
cython_lineno = self.last_marker_line
else:
cython_lineno = self.marker[0]
-
+
self.buffer.markers.extend([cython_lineno] * s.count('\n'))
self.buffer.write(s)
Creates a new CCodeWriter connected to the same global state, which
can later be inserted using insert.
"""
- return CCodeWriter(create_from=self)
+ return CCodeWriter(create_from=self, c_line_in_traceback=self.c_line_in_traceback)
def insert(self, writer):
"""
def enter_cfunc_scope(self):
self.funcstate = FunctionState(self)
-
+
def exit_cfunc_scope(self):
self.funcstate = None
def get_string_const(self, text):
return self.globalstate.get_string_const(text).cname
- def get_py_string_const(self, text, identifier=None, is_str=False):
- return self.globalstate.get_py_string_const(text, identifier, is_str).cname
+ def get_py_string_const(self, text, identifier=None,
+ is_str=False, unicode_value=None):
+ return self.globalstate.get_py_string_const(
+ text, identifier, is_str, unicode_value).cname
def get_argument_default_const(self, type):
return self.globalstate.get_py_const(type).cname
self.emit_marker()
if self.emit_linenums and self.last_marker_line != 0:
self.write('\n#line %s "%s"\n' % (self.last_marker_line, self.source_desc))
-
+
if code:
if safe:
self.put_safe(code)
self.put(code)
self.write("\n");
self.bol = 1
-
+
def emit_marker(self):
self.write("\n");
self.indent()
def increase_indent(self):
self.level = self.level + 1
-
+
def decrease_indent(self):
self.level = self.level - 1
-
+
def begin_block(self):
self.putln("{")
self.increase_indent()
-
+
def end_block(self):
self.decrease_indent()
self.putln("}")
-
+
def indent(self):
self.write(" " * self.level)
self.marker = (line, marker)
if self.emit_linenums:
self.source_desc = source_desc.get_escaped_description()
-
+
def put_label(self, lbl):
if lbl in self.funcstate.labels_used:
self.putln("%s:;" % lbl)
-
+
def put_goto(self, lbl):
self.funcstate.use_label(lbl)
self.putln("goto %s;" % lbl)
-
+
def put_var_declarations(self, entries, static = 0, dll_linkage = None,
definition = True):
for entry in entries:
if not entry.in_cinclude:
self.put_var_declaration(entry, static, dll_linkage, definition)
-
+
def put_var_declaration(self, entry, static = 0, dll_linkage = None,
definition = True):
#print "Code.put_var_declaration:", entry.name, "definition =", definition ###
def put_h_guard(self, guard):
self.putln("#ifndef %s" % guard)
self.putln("#define %s" % guard)
-
+
def unlikely(self, cond):
if Options.gcc_branch_hints:
return 'unlikely(%s)' % cond
return "(PyObject *)" + entry.cname
else:
return entry.cname
-
+
def as_pyobject(self, cname, type):
from PyrexTypes import py_object_type, typecast
return typecast(py_object_type, type, cname)
-
+
def put_gotref(self, cname):
self.putln("__Pyx_GOTREF(%s);" % cname)
-
+
def put_giveref(self, cname):
self.putln("__Pyx_GIVEREF(%s);" % cname)
-
+
def put_xgiveref(self, cname):
self.putln("__Pyx_XGIVEREF(%s);" % cname)
self.putln("__Pyx_INCREF(%s);" % self.as_pyobject(cname, type))
else:
self.putln("Py_INCREF(%s);" % self.as_pyobject(cname, type))
-
+
def put_decref(self, cname, type, nanny=True):
if nanny:
self.putln("__Pyx_DECREF(%s);" % self.as_pyobject(cname, type))
def put_var_gotref(self, entry):
if entry.type.is_pyobject:
self.putln("__Pyx_GOTREF(%s);" % self.entry_as_pyobject(entry))
-
+
def put_var_giveref(self, entry):
if entry.type.is_pyobject:
self.putln("__Pyx_GIVEREF(%s);" % self.entry_as_pyobject(entry))
def put_var_incref(self, entry):
if entry.type.is_pyobject:
self.putln("__Pyx_INCREF(%s);" % self.entry_as_pyobject(entry))
-
+
def put_decref_clear(self, cname, type, nanny=True):
from PyrexTypes import py_object_type, typecast
if nanny:
else:
self.putln("Py_DECREF(%s); %s = 0;" % (
typecast(py_object_type, type, cname), cname))
-
+
def put_xdecref(self, cname, type, nanny=True):
if nanny:
self.putln("__Pyx_XDECREF(%s);" % self.as_pyobject(cname, type))
else:
self.putln("Py_XDECREF(%s);" % self.as_pyobject(cname, type))
-
+
def put_xdecref_clear(self, cname, type, nanny=True):
if nanny:
self.putln("__Pyx_XDECREF(%s); %s = 0;" % (
self.putln("__Pyx_XDECREF(%s);" % self.entry_as_pyobject(entry))
else:
self.putln("__Pyx_DECREF(%s);" % self.entry_as_pyobject(entry))
-
+
def put_var_decref_clear(self, entry):
if entry.type.is_pyobject:
self.putln("__Pyx_DECREF(%s); %s = 0;" % (
self.entry_as_pyobject(entry), entry.cname))
-
+
def put_var_xdecref(self, entry):
if entry.type.is_pyobject:
self.putln("__Pyx_XDECREF(%s);" % self.entry_as_pyobject(entry))
-
+
def put_var_xdecref_clear(self, entry):
if entry.type.is_pyobject:
self.putln("__Pyx_XDECREF(%s); %s = 0;" % (
self.entry_as_pyobject(entry), entry.cname))
-
+
def put_var_decrefs(self, entries, used_only = 0):
for entry in entries:
if not used_only or entry.used:
self.put_var_xdecref(entry)
else:
self.put_var_decref(entry)
-
+
def put_var_xdecrefs(self, entries):
for entry in entries:
self.put_var_xdecref(entry)
-
+
def put_var_xdecrefs_clear(self, entries):
for entry in entries:
self.put_var_xdecref_clear(entry)
-
+
def put_init_to_py_none(self, cname, type, nanny=True):
from PyrexTypes import py_object_type, typecast
py_none = typecast(type, py_object_type, "Py_None")
self.putln("%s = %s; __Pyx_INCREF(Py_None);" % (cname, py_none))
else:
self.putln("%s = %s; Py_INCREF(Py_None);" % (cname, py_none))
-
+
def put_init_var_to_py_none(self, entry, template = "%s", nanny=True):
code = template % entry.cname
#if entry.type.is_extension_type:
# code = "((PyObject*)%s)" % code
self.put_init_to_py_none(code, entry.type, nanny)
+ if entry.in_closure:
+ self.put_giveref('Py_None')
def put_pymethoddef(self, entry, term, allow_skip=True):
if entry.is_special or entry.name == '__getattribute__':
method_flags += [method_coexist]
self.putln(
'{__Pyx_NAMESTR("%s"), (PyCFunction)%s, %s, __Pyx_DOCSTR(%s)}%s' % (
- entry.name,
+ entry.name,
entry.func_cname,
"|".join(method_flags),
doc_code,
return self.putln("if (%s < 0) %s" % (value, self.error_goto(pos)))
def set_error_info(self, pos):
- if Options.c_line_in_traceback:
+ if self.c_line_in_traceback:
cinfo = " %s = %s;" % (Naming.clineno_cname, Naming.line_c_macro)
else:
cinfo = ""
Naming.lineno_cname,
pos[1],
cinfo)
-
+
def error_goto(self, pos):
lbl = self.funcstate.error_label
self.funcstate.use_label(lbl)
def error_goto_if(self, cond, pos):
return "if (%s) %s" % (self.unlikely(cond), self.error_goto(pos))
-
+
def error_goto_if_null(self, cname, pos):
return self.error_goto_if("!%s" % cname, pos)
-
+
def error_goto_if_neg(self, cname, pos):
return self.error_goto_if("%s < 0" % cname, pos)
-
+
def error_goto_if_PyErr(self, pos):
return self.error_goto_if("PyErr_Occurred()", pos)
-
+
def lookup_filename(self, filename):
return self.globalstate.lookup_filename(filename)
+ def put_declare_refcount_context(self):
+ self.putln('__Pyx_RefNannyDeclarations')
+
def put_setup_refcount_context(self, name):
self.putln('__Pyx_RefNannySetupContext("%s");' % name)
def put_trace_declarations(self):
self.putln('__Pyx_TraceDeclarations');
-
+
def put_trace_call(self, name, pos):
self.putln('__Pyx_TraceCall("%s", %s[%s], %s);' % (name, Naming.filetable_cname, self.lookup_filename(pos[0]), pos[1]));
-
+
def put_trace_exception(self):
self.putln("__Pyx_TraceException();")
-
+
def put_trace_return(self, retvalue_cname):
self.putln("__Pyx_TraceReturn(%s);" % retvalue_cname)
def __init__(self, outfile_name):
self.f = Utils.open_new_file(outfile_name)
self.level = 0
-
+
def putln(self, code):
self.f.write("%s%s\n" % (" " * self.level, code))
-
+
def indent(self):
self.level += 1
-
+
def dedent(self):
self.level -= 1
+
+class ClosureTempAllocator(object):
+ def __init__(self, klass):
+ self.klass = klass
+ self.temps_allocated = {}
+ self.temps_free = {}
+ self.temps_count = 0
+
+ def reset(self):
+ for type, cnames in self.temps_allocated.items():
+ self.temps_free[type] = list(cnames)
+
+ def allocate_temp(self, type):
+ if not type in self.temps_allocated:
+ self.temps_allocated[type] = []
+ self.temps_free[type] = []
+ elif self.temps_free[type]:
+ return self.temps_free[type].pop(0)
+ cname = '%s%d' % (Naming.codewriter_temp_prefix, self.temps_count)
+ self.klass.declare_var(pos=None, name=cname, cname=cname, type=type, is_cdef=True)
+ self.temps_allocated[type].append(cname)
+ self.temps_count += 1
+ return cname