Index: third_party/cython/src/Cython/Compiler/Code.py |
diff --git a/third_party/cython/src/Cython/Compiler/Code.py b/third_party/cython/src/Cython/Compiler/Code.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..1ef646badd89b15d9bb626f02d1beadad6220da7 |
--- /dev/null |
+++ b/third_party/cython/src/Cython/Compiler/Code.py |
@@ -0,0 +1,2238 @@ |
+# cython: language_level = 2 |
+# |
+# Code output module |
+# |
+ |
+import cython |
+cython.declare(os=object, re=object, operator=object, |
+ Naming=object, Options=object, StringEncoding=object, |
+ Utils=object, SourceDescriptor=object, StringIOTree=object, |
+ DebugFlags=object, basestring=object) |
+ |
+import os |
+import re |
+import sys |
+from string import Template |
+import operator |
+import textwrap |
+ |
+try: |
+ import hashlib |
+except ImportError: |
+ import md5 as hashlib |
+ |
+import Naming |
+import Options |
+import StringEncoding |
+from Cython import Utils |
+from Scanning import SourceDescriptor |
+from Cython.StringIOTree import StringIOTree |
+import DebugFlags |
+ |
+try: |
+ from __builtin__ import basestring |
+except ImportError: |
+ from builtins import str as basestring |
+ |
+KEYWORDS_MUST_BE_BYTES = sys.version_info < (2,7) |
+ |
+ |
+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'), |
+ 'basestring' : ('PY_MAJOR_VERSION >= 3', 'str'), |
+ 'xrange' : ('PY_MAJOR_VERSION >= 3', 'range'), |
+ 'raw_input' : ('PY_MAJOR_VERSION >= 3', 'input'), |
+ 'BaseException' : ('PY_VERSION_HEX < 0x02050000', 'Exception'), |
+ } |
+ |
+basicsize_builtins_map = { |
+ # builtins whose type has a different tp_basicsize than sizeof(...) |
+ 'PyTypeObject' : 'PyHeapTypeObject', |
+ } |
+ |
+uncachable_builtins = [ |
+ # builtin names that cannot be cached because they may or may not |
+ # be available at import time |
+ 'WindowsError', |
+ ] |
+ |
+modifier_output_mapper = { |
+ 'inline': 'CYTHON_INLINE' |
+}.get |
+ |
+is_self_assignment = re.compile(r" *(\w+) = (\1);\s*$").match |
+ |
+ |
+def get_utility_dir(): |
+ # make this a function and not global variables: |
+ # http://trac.cython.org/cython_trac/ticket/475 |
+ Cython_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) |
+ return os.path.join(Cython_dir, "Utility") |
+ |
+ |
+class UtilityCodeBase(object): |
+ """ |
+ Support for loading utility code from a file. |
+ |
+ Code sections in the file can be specified as follows: |
+ |
+ ##### MyUtility.proto ##### |
+ |
+ [proto declarations] |
+ |
+ ##### MyUtility.init ##### |
+ |
+ [code run at module initialization] |
+ |
+ ##### MyUtility ##### |
+ #@requires: MyOtherUtility |
+ #@substitute: naming |
+ |
+ [definitions] |
+ |
+ for prototypes and implementation respectively. For non-python or |
+ -cython files backslashes should be used instead. 5 to 30 comment |
+ characters may be used on either side. |
+ |
+ If the @cname decorator is not used and this is a CythonUtilityCode, |
+ one should pass in the 'name' keyword argument to be used for name |
+ mangling of such entries. |
+ """ |
+ |
+ is_cython_utility = False |
+ requires = None |
+ _utility_cache = {} |
+ |
+ @classmethod |
+ def _add_utility(cls, utility, type, lines, begin_lineno, tags=None): |
+ if utility is None: |
+ return |
+ |
+ code = '\n'.join(lines) |
+ if tags and 'substitute' in tags and tags['substitute'] == set(['naming']): |
+ del tags['substitute'] |
+ try: |
+ code = Template(code).substitute(vars(Naming)) |
+ except (KeyError, ValueError), e: |
+ raise RuntimeError("Error parsing templated utility code of type '%s' at line %d: %s" % ( |
+ type, begin_lineno, e)) |
+ |
+ # remember correct line numbers at least until after templating |
+ code = '\n' * begin_lineno + code |
+ |
+ if type == 'proto': |
+ utility[0] = code |
+ elif type == 'impl': |
+ utility[1] = code |
+ else: |
+ all_tags = utility[2] |
+ if KEYWORDS_MUST_BE_BYTES: |
+ type = type.encode('ASCII') |
+ all_tags[type] = code |
+ |
+ if tags: |
+ all_tags = utility[2] |
+ for name, values in tags.items(): |
+ if KEYWORDS_MUST_BE_BYTES: |
+ name = name.encode('ASCII') |
+ all_tags.setdefault(name, set()).update(values) |
+ |
+ @classmethod |
+ def load_utilities_from_file(cls, path): |
+ utilities = cls._utility_cache.get(path) |
+ if utilities: |
+ return utilities |
+ |
+ filename = os.path.join(get_utility_dir(), path) |
+ _, ext = os.path.splitext(path) |
+ if ext in ('.pyx', '.py', '.pxd', '.pxi'): |
+ comment = '#' |
+ replace_comments = re.compile(r'^\s*#.*').sub |
+ else: |
+ comment = '/' |
+ replace_comments = re.compile(r'^\s*//.*|^\s*/\*[^*]*\*/').sub |
+ match_special = re.compile( |
+ (r'^%(C)s{5,30}\s*(?P<name>(?:\w|\.)+)\s*%(C)s{5,30}|' |
+ r'^%(C)s+@(?P<tag>\w+)\s*:\s*(?P<value>(?:\w|[.:])+)' |
+ ) % {'C':comment}).match |
+ match_type = re.compile('(.+)[.](proto|impl|init|cleanup)$').match |
+ |
+ f = Utils.open_source_file(filename, encoding='UTF-8') |
+ try: |
+ all_lines = f.readlines() |
+ finally: |
+ f.close() |
+ |
+ utilities = {} |
+ lines = [] |
+ tags = {} |
+ utility = type = None |
+ begin_lineno = 0 |
+ |
+ for lineno, line in enumerate(all_lines): |
+ m = match_special(line) |
+ if m: |
+ if m.group('name'): |
+ cls._add_utility(utility, type, lines, begin_lineno, tags) |
+ |
+ begin_lineno = lineno + 1 |
+ del lines[:] |
+ tags.clear() |
+ |
+ name = m.group('name') |
+ mtype = match_type(name) |
+ if mtype: |
+ name, type = mtype.groups() |
+ else: |
+ type = 'impl' |
+ utility = utilities.setdefault(name, [None, None, {}]) |
+ else: |
+ tags.setdefault(m.group('tag'), set()).add(m.group('value')) |
+ lines.append('') # keep line number correct |
+ else: |
+ lines.append(replace_comments('', line).rstrip()) |
+ |
+ if utility is None: |
+ raise ValueError("Empty utility code file") |
+ |
+ # Don't forget to add the last utility code |
+ cls._add_utility(utility, type, lines, begin_lineno, tags) |
+ |
+ cls._utility_cache[path] = utilities |
+ return utilities |
+ |
+ @classmethod |
+ def load(cls, util_code_name, from_file=None, **kwargs): |
+ """ |
+ Load utility code from a file specified by from_file (relative to |
+ Cython/Utility) and name util_code_name. If from_file is not given, |
+ load it from the file util_code_name.*. There should be only one |
+ file matched by this pattern. |
+ """ |
+ if '::' in util_code_name: |
+ from_file, util_code_name = util_code_name.rsplit('::', 1) |
+ if not from_file: |
+ utility_dir = get_utility_dir() |
+ prefix = util_code_name + '.' |
+ try: |
+ listing = os.listdir(utility_dir) |
+ except OSError: |
+ # XXX the code below assumes as 'zipimport.zipimporter' instance |
+ # XXX should be easy to generalize, but too lazy right now to write it |
+ import zipfile |
+ global __loader__ |
+ loader = __loader__ |
+ archive = loader.archive |
+ fileobj = zipfile.ZipFile(archive) |
+ listing = [ os.path.basename(name) |
+ for name in fileobj.namelist() |
+ if os.path.join(archive, name).startswith(utility_dir)] |
+ fileobj.close() |
+ files = [ os.path.join(utility_dir, filename) |
+ for filename in listing |
+ if filename.startswith(prefix) ] |
+ if not files: |
+ raise ValueError("No match found for utility code " + util_code_name) |
+ if len(files) > 1: |
+ raise ValueError("More than one filename match found for utility code " + util_code_name) |
+ from_file = files[0] |
+ |
+ utilities = cls.load_utilities_from_file(from_file) |
+ proto, impl, tags = utilities[util_code_name] |
+ |
+ if tags: |
+ orig_kwargs = kwargs.copy() |
+ for name, values in tags.items(): |
+ if name in kwargs: |
+ continue |
+ # only pass lists when we have to: most argument expect one value or None |
+ if name == 'requires': |
+ if orig_kwargs: |
+ values = [cls.load(dep, from_file, **orig_kwargs) |
+ for dep in sorted(values)] |
+ else: |
+ # dependencies are rarely unique, so use load_cached() when we can |
+ values = [cls.load_cached(dep, from_file) |
+ for dep in sorted(values)] |
+ elif not values: |
+ values = None |
+ elif len(values) == 1: |
+ values = values[0] |
+ kwargs[name] = values |
+ |
+ if proto is not None: |
+ kwargs['proto'] = proto |
+ if impl is not None: |
+ kwargs['impl'] = impl |
+ |
+ if 'name' not in kwargs: |
+ kwargs['name'] = util_code_name |
+ |
+ if 'file' not in kwargs and from_file: |
+ kwargs['file'] = from_file |
+ return cls(**kwargs) |
+ |
+ @classmethod |
+ def load_cached(cls, utility_code_name, from_file=None, __cache={}): |
+ """ |
+ Calls .load(), but using a per-type cache based on utility name and file name. |
+ """ |
+ key = (cls, from_file, utility_code_name) |
+ try: |
+ return __cache[key] |
+ except KeyError: |
+ pass |
+ code = __cache[key] = cls.load(utility_code_name, from_file) |
+ return code |
+ |
+ @classmethod |
+ def load_as_string(cls, util_code_name, from_file=None, **kwargs): |
+ """ |
+ Load a utility code as a string. Returns (proto, implementation) |
+ """ |
+ util = cls.load(util_code_name, from_file, **kwargs) |
+ proto, impl = util.proto, util.impl |
+ return util.format_code(proto), util.format_code(impl) |
+ |
+ def format_code(self, code_string, replace_empty_lines=re.compile(r'\n\n+').sub): |
+ """ |
+ Format a code section for output. |
+ """ |
+ if code_string: |
+ code_string = replace_empty_lines('\n', code_string.strip()) + '\n\n' |
+ return code_string |
+ |
+ def __str__(self): |
+ return "<%s(%s)" % (type(self).__name__, self.name) |
+ |
+ def get_tree(self): |
+ pass |
+ |
+ |
+class UtilityCode(UtilityCodeBase): |
+ """ |
+ Stores utility code to add during code generation. |
+ |
+ See GlobalState.put_utility_code. |
+ |
+ hashes/equals by instance |
+ |
+ proto C prototypes |
+ impl implemenation code |
+ init code to call on module initialization |
+ requires utility code dependencies |
+ proto_block the place in the resulting file where the prototype should |
+ end up |
+ name name of the utility code (or None) |
+ file filename of the utility code file this utility was loaded |
+ from (or None) |
+ """ |
+ |
+ def __init__(self, proto=None, impl=None, init=None, cleanup=None, requires=None, |
+ proto_block='utility_code_proto', name=None, file=None): |
+ # proto_block: Which code block to dump prototype in. See GlobalState. |
+ self.proto = proto |
+ self.impl = impl |
+ self.init = init |
+ self.cleanup = cleanup |
+ self.requires = requires |
+ self._cache = {} |
+ self.specialize_list = [] |
+ self.proto_block = proto_block |
+ self.name = name |
+ self.file = file |
+ |
+ def __hash__(self): |
+ return hash((self.proto, self.impl)) |
+ |
+ def __eq__(self, other): |
+ if self is other: |
+ return True |
+ if not isinstance(other, type(self)): |
+ return False |
+ |
+ self_proto = getattr(self, 'proto', None) |
+ other_proto = getattr(other, 'proto', None) |
+ return (self_proto, self.impl) == (other_proto, other.impl) |
+ |
+ def none_or_sub(self, s, context): |
+ """ |
+ Format a string in this utility code with context. If None, do nothing. |
+ """ |
+ if s is None: |
+ return None |
+ return s % context |
+ |
+ def specialize(self, pyrex_type=None, **data): |
+ # Dicts aren't hashable... |
+ if pyrex_type is not None: |
+ data['type'] = pyrex_type.declaration_code('') |
+ data['type_name'] = pyrex_type.specialization_name() |
+ key = tuple(sorted(data.items())) |
+ try: |
+ return self._cache[key] |
+ except KeyError: |
+ if self.requires is None: |
+ requires = None |
+ else: |
+ requires = [r.specialize(data) for r in self.requires] |
+ |
+ s = self._cache[key] = UtilityCode( |
+ self.none_or_sub(self.proto, data), |
+ self.none_or_sub(self.impl, data), |
+ self.none_or_sub(self.init, data), |
+ self.none_or_sub(self.cleanup, data), |
+ requires, |
+ self.proto_block) |
+ |
+ self.specialize_list.append(s) |
+ return s |
+ |
+ def inject_string_constants(self, impl, output): |
+ """Replace 'PYIDENT("xyz")' by a constant Python identifier cname. |
+ """ |
+ replacements = {} |
+ def externalise(matchobj): |
+ name = matchobj.group(1) |
+ try: |
+ cname = replacements[name] |
+ except KeyError: |
+ cname = replacements[name] = output.get_interned_identifier( |
+ StringEncoding.EncodedString(name)).cname |
+ return cname |
+ |
+ impl = re.sub('PYIDENT\("([^"]+)"\)', externalise, impl) |
+ return bool(replacements), impl |
+ |
+ def put_code(self, output): |
+ if self.requires: |
+ for dependency in self.requires: |
+ output.use_utility_code(dependency) |
+ if self.proto: |
+ output[self.proto_block].put_or_include( |
+ self.format_code(self.proto), |
+ '%s_proto' % self.name) |
+ if self.impl: |
+ impl = self.format_code(self.impl) |
+ is_specialised, impl = self.inject_string_constants(impl, output) |
+ if not is_specialised: |
+ # no module specific adaptations => can be reused |
+ output['utility_code_def'].put_or_include( |
+ impl, '%s_impl' % self.name) |
+ else: |
+ output['utility_code_def'].put(impl) |
+ if self.init: |
+ writer = output['init_globals'] |
+ writer.putln("/* %s.init */" % self.name) |
+ if isinstance(self.init, basestring): |
+ writer.put(self.format_code(self.init)) |
+ else: |
+ self.init(writer, output.module_pos) |
+ writer.putln(writer.error_goto_if_PyErr(output.module_pos)) |
+ writer.putln() |
+ if self.cleanup and Options.generate_cleanup_code: |
+ writer = output['cleanup_globals'] |
+ if isinstance(self.cleanup, basestring): |
+ writer.put_or_include( |
+ self.format_code(self.cleanup), |
+ '%s_cleanup' % self.name) |
+ else: |
+ self.cleanup(writer, output.module_pos) |
+ |
+ |
+def sub_tempita(s, context, file=None, name=None): |
+ "Run tempita on string s with given context." |
+ if not s: |
+ return None |
+ |
+ if file: |
+ context['__name'] = "%s:%s" % (file, name) |
+ elif name: |
+ context['__name'] = name |
+ |
+ from Cython.Tempita import sub |
+ return sub(s, **context) |
+ |
+class TempitaUtilityCode(UtilityCode): |
+ def __init__(self, name=None, proto=None, impl=None, init=None, file=None, context=None, **kwargs): |
+ if context is None: |
+ context = {} |
+ proto = sub_tempita(proto, context, file, name) |
+ impl = sub_tempita(impl, context, file, name) |
+ init = sub_tempita(init, context, file, name) |
+ super(TempitaUtilityCode, self).__init__( |
+ proto, impl, init=init, name=name, file=file, **kwargs) |
+ |
+ def none_or_sub(self, s, context): |
+ """ |
+ Format a string in this utility code with context. If None, do nothing. |
+ """ |
+ if s is None: |
+ return None |
+ return sub_tempita(s, context, self.file, self.name) |
+ |
+ |
+class LazyUtilityCode(UtilityCodeBase): |
+ """ |
+ Utility code that calls a callback with the root code writer when |
+ available. Useful when you only have 'env' but not 'code'. |
+ """ |
+ |
+ def __init__(self, callback): |
+ self.callback = callback |
+ |
+ def put_code(self, globalstate): |
+ utility = self.callback(globalstate.rootwriter) |
+ globalstate.use_utility_code(utility) |
+ |
+ |
+class FunctionState(object): |
+ # return_label string function return point label |
+ # error_label string error catch point label |
+ # continue_label string loop continue point label |
+ # break_label string loop break point label |
+ # return_from_error_cleanup_label string |
+ # label_counter integer counter for naming labels |
+ # in_try_finally boolean inside try of try...finally |
+ # exc_vars (string * 3) exception variables for reraise, or None |
+ # can_trace boolean line tracing is supported in the current context |
+ |
+ # Not used for now, perhaps later |
+ def __init__(self, owner, names_taken=set()): |
+ self.names_taken = names_taken |
+ self.owner = owner |
+ |
+ self.error_label = None |
+ self.label_counter = 0 |
+ self.labels_used = set() |
+ self.return_label = self.new_label() |
+ self.new_error_label() |
+ self.continue_label = None |
+ self.break_label = None |
+ self.yield_labels = [] |
+ |
+ self.in_try_finally = 0 |
+ self.exc_vars = None |
+ self.can_trace = False |
+ |
+ self.temps_allocated = [] # of (name, type, manage_ref, static) |
+ 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 |
+ |
+ # This is used to collect temporaries, useful to find out which temps |
+ # need to be privatized in parallel sections |
+ self.collect_temps_stack = [] |
+ |
+ # This is used for the error indicator, which needs to be local to the |
+ # function. It used to be global, which relies on the GIL being held. |
+ # However, exceptions may need to be propagated through 'nogil' |
+ # sections, in which case we introduce a race condition. |
+ self.should_declare_error_indicator = False |
+ self.uses_error_indicator = False |
+ |
+ # labels |
+ |
+ def new_label(self, name=None): |
+ n = self.label_counter |
+ self.label_counter = n + 1 |
+ label = "%s%d" % (Naming.label_prefix, n) |
+ if name is not None: |
+ label += '_' + name |
+ return label |
+ |
+ def new_yield_label(self): |
+ label = self.new_label('resume_from_yield') |
+ num_and_label = (len(self.yield_labels) + 1, label) |
+ self.yield_labels.append(num_and_label) |
+ return num_and_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("break"))) |
+ return old_labels |
+ |
+ def get_all_labels(self): |
+ return ( |
+ self.continue_label, |
+ self.break_label, |
+ self.return_label, |
+ self.error_label) |
+ |
+ def set_all_labels(self, labels): |
+ (self.continue_label, |
+ self.break_label, |
+ self.return_label, |
+ self.error_label) = labels |
+ |
+ def all_new_labels(self): |
+ old_labels = self.get_all_labels() |
+ new_labels = [] |
+ for old_label, name in zip(old_labels, ['continue', 'break', 'return', 'error']): |
+ if old_label: |
+ new_labels.append(self.new_label(name)) |
+ else: |
+ 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 |
+ |
+ # temp handling |
+ |
+ def allocate_temp(self, type, manage_ref, static=False): |
+ """ |
+ Allocates a temporary (which may create a new one or get a previously |
+ allocated and released one of the same type). Type is simply registered |
+ and handed back, but will usually be a PyrexType. |
+ |
+ If type.is_pyobject, manage_ref comes into play. If manage_ref is set to |
+ True, the temp will be decref-ed on return statements and in exception |
+ handling clauses. Otherwise the caller has to deal with any reference |
+ counting of the variable. |
+ |
+ If not type.is_pyobject, then manage_ref will be ignored, but it |
+ still has to be passed. It is recommended to pass False by convention |
+ if it is known that type will never be a Python object. |
+ |
+ static=True marks the temporary declaration with "static". |
+ This is only used when allocating backing store for a module-level |
+ C array literals. |
+ |
+ A C string referring to the variable is returned. |
+ """ |
+ if type.is_const: |
+ type = type.const_base_type |
+ if not type.is_pyobject and not type.is_memoryviewslice: |
+ # Make manage_ref canonical, so that manage_ref will always mean |
+ # a decref is needed. |
+ manage_ref = False |
+ |
+ freelist = self.temps_free.get((type, manage_ref)) |
+ if freelist is not None and len(freelist) > 0: |
+ result = freelist.pop() |
+ else: |
+ while True: |
+ self.temp_counter += 1 |
+ result = "%s%d" % (Naming.codewriter_temp_prefix, self.temp_counter) |
+ if not result in self.names_taken: break |
+ self.temps_allocated.append((result, type, manage_ref, static)) |
+ self.temps_used_type[result] = (type, manage_ref) |
+ if DebugFlags.debug_temp_code_comments: |
+ self.owner.putln("/* %s allocated */" % result) |
+ |
+ if self.collect_temps_stack: |
+ self.collect_temps_stack[-1].add((result, type)) |
+ |
+ return result |
+ |
+ def release_temp(self, name): |
+ """ |
+ Releases a temporary so that it can be reused by other code needing |
+ a temp of the same type. |
+ """ |
+ type, manage_ref = self.temps_used_type[name] |
+ freelist = self.temps_free.get((type, manage_ref)) |
+ if freelist is None: |
+ freelist = [] |
+ self.temps_free[(type, manage_ref)] = freelist |
+ if name in freelist: |
+ raise RuntimeError("Temp %s freed twice!" % name) |
+ freelist.append(name) |
+ if DebugFlags.debug_temp_code_comments: |
+ self.owner.putln("/* %s released */" % name) |
+ |
+ def temps_in_use(self): |
+ """Return a list of (cname,type,manage_ref) tuples of temp names and their type |
+ that are currently in use. |
+ """ |
+ used = [] |
+ for name, type, manage_ref, static in self.temps_allocated: |
+ freelist = self.temps_free.get((type, manage_ref)) |
+ if freelist is None or name not in freelist: |
+ used.append((name, type, manage_ref and type.is_pyobject)) |
+ return used |
+ |
+ def temps_holding_reference(self): |
+ """Return a list of (cname,type) tuples of temp names and their type |
+ that are currently in use. This includes only temps of a |
+ Python object type which owns its reference. |
+ """ |
+ return [(name, type) |
+ for name, type, manage_ref in self.temps_in_use() |
+ if manage_ref and type.is_pyobject] |
+ |
+ def all_managed_temps(self): |
+ """Return a list of (cname, type) tuples of refcount-managed Python objects. |
+ """ |
+ return [(cname, type) |
+ for cname, type, manage_ref, static in self.temps_allocated |
+ if manage_ref] |
+ |
+ def all_free_managed_temps(self): |
+ """Return a list of (cname, type) tuples of refcount-managed Python |
+ objects that are not currently in use. This is used by |
+ try-except and try-finally blocks to clean up temps in the |
+ error case. |
+ """ |
+ return [(cname, type) |
+ for (type, manage_ref), freelist in self.temps_free.items() |
+ if manage_ref |
+ for cname in freelist] |
+ |
+ def start_collecting_temps(self): |
+ """ |
+ Useful to find out which temps were used in a code block |
+ """ |
+ self.collect_temps_stack.append(set()) |
+ |
+ def stop_collecting_temps(self): |
+ return self.collect_temps_stack.pop() |
+ |
+ def init_closure_temps(self, scope): |
+ self.closure_temps = ClosureTempAllocator(scope) |
+ |
+ |
+class NumConst(object): |
+ """Global info about a Python number constant held by GlobalState. |
+ |
+ cname string |
+ value string |
+ py_type string int, long, float |
+ value_code string evaluation code if different from value |
+ """ |
+ |
+ def __init__(self, cname, value, py_type, value_code=None): |
+ self.cname = cname |
+ self.value = value |
+ self.py_type = py_type |
+ self.value_code = value_code or value |
+ |
+ |
+class PyObjectConst(object): |
+ """Global info about a generic constant held by GlobalState. |
+ """ |
+ # cname string |
+ # type PyrexType |
+ |
+ def __init__(self, cname, type): |
+ self.cname = cname |
+ self.type = type |
+ |
+ |
+cython.declare(possible_unicode_identifier=object, possible_bytes_identifier=object, |
+ replace_identifier=object, find_alphanums=object) |
+possible_unicode_identifier = re.compile(ur"(?![0-9])\w+$", re.U).match |
+possible_bytes_identifier = re.compile(r"(?![0-9])\w+$".encode('ASCII')).match |
+replace_identifier = re.compile(r'[^a-zA-Z0-9_]+').sub |
+find_alphanums = re.compile('([a-zA-Z0-9]+)').findall |
+ |
+class StringConst(object): |
+ """Global info about a C string constant held by GlobalState. |
+ """ |
+ # cname string |
+ # text EncodedString or BytesLiteral |
+ # py_strings {(identifier, encoding) : PyStringConst} |
+ |
+ def __init__(self, cname, text, byte_string): |
+ self.cname = cname |
+ self.text = text |
+ self.escaped_value = StringEncoding.escape_byte_string(byte_string) |
+ self.py_strings = None |
+ self.py_versions = [] |
+ |
+ def add_py_version(self, version): |
+ if not version: |
+ self.py_versions = [2,3] |
+ elif version not in self.py_versions: |
+ self.py_versions.append(version) |
+ |
+ def get_py_string_const(self, encoding, identifier=None, |
+ is_str=False, py3str_cstring=None): |
+ py_strings = self.py_strings |
+ text = self.text |
+ |
+ is_str = bool(identifier or is_str) |
+ is_unicode = encoding is None and not is_str |
+ |
+ if encoding is None: |
+ # unicode string |
+ encoding_key = None |
+ else: |
+ # bytes or str |
+ encoding = encoding.lower() |
+ if encoding in ('utf8', 'utf-8', 'ascii', 'usascii', 'us-ascii'): |
+ encoding = None |
+ encoding_key = None |
+ else: |
+ encoding_key = ''.join(find_alphanums(encoding)) |
+ |
+ key = (is_str, is_unicode, encoding_key, py3str_cstring) |
+ if py_strings is not None: |
+ try: |
+ return py_strings[key] |
+ except KeyError: |
+ pass |
+ else: |
+ 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_prefixes['str'] |
+ else: |
+ prefix = Naming.py_const_prefix |
+ |
+ if encoding_key: |
+ encoding_prefix = '_%s' % encoding_key |
+ else: |
+ encoding_prefix = '' |
+ |
+ pystring_cname = "%s%s%s_%s" % ( |
+ prefix, |
+ (is_str and 's') or (is_unicode and 'u') or 'b', |
+ encoding_prefix, |
+ 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, |
+ 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 |
+ self.intern = intern |
+ |
+ def __lt__(self, other): |
+ return self.cname < other.cname |
+ |
+ |
+class GlobalState(object): |
+ # filename_table {string : int} for finding filename table indexes |
+ # filename_list [string] filenames in filename table order |
+ # input_file_contents dict contents (=list of lines) of any file that was used as input |
+ # to create this output C code. This is |
+ # used to annotate the comments. |
+ # |
+ # utility_codes set IDs of used utility code (to avoid reinsertion) |
+ # |
+ # declared_cnames {string:Entry} used in a transition phase to merge pxd-declared |
+ # constants etc. into the pyx-declared ones (i.e, |
+ # check if constants are already added). |
+ # In time, hopefully the literals etc. will be |
+ # supplied directly instead. |
+ # |
+ # const_cnames_used dict global counter for unique constant identifiers |
+ # |
+ |
+ # parts {string:CCodeWriter} |
+ |
+ |
+ # interned_strings |
+ # consts |
+ # interned_nums |
+ |
+ # directives set Temporary variable used to track |
+ # the current set of directives in the code generation |
+ # process. |
+ |
+ directives = {} |
+ |
+ code_layout = [ |
+ 'h_code', |
+ 'filename_table', |
+ 'utility_code_proto_before_types', |
+ 'numeric_typedefs', # Let these detailed individual parts stay!, |
+ 'complex_type_declarations', # as the proper solution is to make a full DAG... |
+ 'type_declarations', # More coarse-grained blocks would simply hide |
+ 'utility_code_proto', # the ugliness, not fix it |
+ 'module_declarations', |
+ 'typeinfo', |
+ 'before_global_var', |
+ 'global_var', |
+ 'decls', |
+ 'all_the_rest', |
+ 'pystring_table', |
+ 'cached_builtins', |
+ 'cached_constants', |
+ 'init_globals', |
+ 'init_module', |
+ 'cleanup_globals', |
+ 'cleanup_module', |
+ 'main_method', |
+ 'utility_code_def', |
+ 'end' |
+ ] |
+ |
+ |
+ def __init__(self, writer, module_node, emit_linenums=False, common_utility_include_dir=None): |
+ self.filename_table = {} |
+ self.filename_list = [] |
+ self.input_file_contents = {} |
+ self.utility_codes = set() |
+ self.declared_cnames = {} |
+ self.in_utility_code_generation = False |
+ self.emit_linenums = emit_linenums |
+ self.common_utility_include_dir = common_utility_include_dir |
+ self.parts = {} |
+ self.module_node = module_node # because some utility code generation needs it |
+ # (generating backwards-compatible Get/ReleaseBuffer |
+ |
+ self.const_cnames_used = {} |
+ self.string_const_index = {} |
+ self.pyunicode_ptr_const_index = {} |
+ self.num_const_index = {} |
+ self.py_constants = [] |
+ |
+ assert writer.globalstate is None |
+ writer.globalstate = self |
+ self.rootwriter = writer |
+ |
+ def initialize_main_c_code(self): |
+ rootwriter = self.rootwriter |
+ for part in self.code_layout: |
+ self.parts[part] = rootwriter.insertion_point() |
+ |
+ if not Options.cache_builtins: |
+ del self.parts['cached_builtins'] |
+ else: |
+ w = self.parts['cached_builtins'] |
+ w.enter_cfunc_scope() |
+ w.putln("static int __Pyx_InitCachedBuiltins(void) {") |
+ |
+ w = self.parts['cached_constants'] |
+ 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.enter_cfunc_scope() |
+ w.putln("") |
+ w.putln("static int __Pyx_InitGlobals(void) {") |
+ |
+ if not Options.generate_cleanup_code: |
+ del self.parts['cleanup_globals'] |
+ else: |
+ w = self.parts['cleanup_globals'] |
+ w.enter_cfunc_scope() |
+ w.putln("") |
+ w.putln("static void __Pyx_CleanupGlobals(void) {") |
+ |
+ # |
+ # utility_code_def |
+ # |
+ code = self.parts['utility_code_def'] |
+ if self.emit_linenums: |
+ code.write('\n#line 1 "cython_utility"\n') |
+ code.putln("") |
+ code.putln("/* Runtime support code */") |
+ |
+ def finalize_main_c_code(self): |
+ self.close_global_decls() |
+ |
+ # |
+ # utility_code_def |
+ # |
+ code = self.parts['utility_code_def'] |
+ code.put(UtilityCode.load_as_string("TypeConversions", "TypeConversion.c")[1]) |
+ code.putln("") |
+ |
+ def __getitem__(self, key): |
+ return self.parts[key] |
+ |
+ # |
+ # Global constants, interned objects, etc. |
+ # |
+ def close_global_decls(self): |
+ # This is called when it is known that no more global declarations will |
+ # declared. |
+ self.generate_const_declarations() |
+ if Options.cache_builtins: |
+ w = self.parts['cached_builtins'] |
+ w.putln("return 0;") |
+ if w.label_used(w.error_label): |
+ w.put_label(w.error_label) |
+ w.putln("return -1;") |
+ w.putln("}") |
+ w.exit_cfunc_scope() |
+ |
+ w = self.parts['cached_constants'] |
+ w.put_finish_refcount_context() |
+ w.putln("return 0;") |
+ if w.label_used(w.error_label): |
+ w.put_label(w.error_label) |
+ w.put_finish_refcount_context() |
+ w.putln("return -1;") |
+ w.putln("}") |
+ w.exit_cfunc_scope() |
+ |
+ w = self.parts['init_globals'] |
+ w.putln("return 0;") |
+ if w.label_used(w.error_label): |
+ w.put_label(w.error_label) |
+ w.putln("return -1;") |
+ w.putln("}") |
+ w.exit_cfunc_scope() |
+ |
+ if Options.generate_cleanup_code: |
+ w = self.parts['cleanup_globals'] |
+ w.putln("}") |
+ w.exit_cfunc_scope() |
+ |
+ if Options.generate_cleanup_code: |
+ 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) |
+ |
+ # constant handling at code generation time |
+ |
+ def get_cached_constants_writer(self): |
+ return self.parts['cached_constants'] |
+ |
+ def get_int_const(self, str_value, longness=False): |
+ py_type = longness and 'long' or 'int' |
+ try: |
+ c = self.num_const_index[(str_value, py_type)] |
+ except KeyError: |
+ c = self.new_num_const(str_value, py_type) |
+ return c |
+ |
+ def get_float_const(self, str_value, value_code): |
+ try: |
+ c = self.num_const_index[(str_value, 'float')] |
+ except KeyError: |
+ c = self.new_num_const(str_value, 'float', value_code) |
+ return c |
+ |
+ def get_py_const(self, type, prefix='', cleanup_level=None): |
+ # create a new Python object constant |
+ const = self.new_py_const(type, prefix) |
+ if cleanup_level is not None \ |
+ and cleanup_level <= Options.generate_cleanup_code: |
+ cleanup_writer = self.parts['cleanup_globals'] |
+ cleanup_writer.putln('Py_CLEAR(%s);' % const.cname) |
+ return const |
+ |
+ def get_string_const(self, text, py_version=None): |
+ # return a C string constant, creating a new one if necessary |
+ if text.is_unicode: |
+ byte_string = text.utf8encode() |
+ else: |
+ byte_string = text.byteencode() |
+ try: |
+ c = self.string_const_index[byte_string] |
+ except KeyError: |
+ c = self.new_string_const(text, byte_string) |
+ c.add_py_version(py_version) |
+ return c |
+ |
+ def get_pyunicode_ptr_const(self, text): |
+ # return a Py_UNICODE[] constant, creating a new one if necessary |
+ assert text.is_unicode |
+ try: |
+ c = self.pyunicode_ptr_const_index[text] |
+ except KeyError: |
+ c = self.pyunicode_ptr_const_index[text] = self.new_const_cname() |
+ return c |
+ |
+ 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 |
+ 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_version=3) |
+ c_string = self.get_string_const(text, py_version=2) |
+ else: |
+ c_string = self.get_string_const(text) |
+ 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 self.get_py_string_const(text, identifier=True) |
+ |
+ def new_string_const(self, text, byte_string): |
+ cname = self.new_string_const_cname(byte_string) |
+ c = StringConst(cname, text, byte_string) |
+ self.string_const_index[byte_string] = c |
+ return c |
+ |
+ def new_num_const(self, value, py_type, value_code=None): |
+ cname = self.new_num_const_cname(value, py_type) |
+ c = NumConst(cname, value, py_type, value_code) |
+ self.num_const_index[(value, py_type)] = c |
+ return c |
+ |
+ def new_py_const(self, type, prefix=''): |
+ cname = self.new_const_cname(prefix) |
+ c = PyObjectConst(cname, type) |
+ self.py_constants.append(c) |
+ return c |
+ |
+ def new_string_const_cname(self, bytes_value): |
+ # Create a new globally-unique nice name for a C string constant. |
+ value = bytes_value.decode('ASCII', 'ignore') |
+ return self.new_const_cname(value=value) |
+ |
+ def new_num_const_cname(self, value, py_type): |
+ if py_type == 'long': |
+ value += 'L' |
+ py_type = 'int' |
+ prefix = Naming.interned_prefixes[py_type] |
+ cname = "%s%s" % (prefix, value) |
+ cname = cname.replace('+', '_').replace('-', 'neg_').replace('.', '_') |
+ return cname |
+ |
+ def new_const_cname(self, prefix='', value=''): |
+ value = replace_identifier('_', value)[:32].strip('_') |
+ used = self.const_cnames_used |
+ name_suffix = value |
+ while name_suffix in used: |
+ counter = used[value] = used[value] + 1 |
+ name_suffix = '%s_%d' % (value, counter) |
+ used[name_suffix] = 1 |
+ if prefix: |
+ prefix = Naming.interned_prefixes[prefix] |
+ else: |
+ prefix = Naming.const_prefix |
+ return "%s%s" % (prefix, name_suffix) |
+ |
+ def add_cached_builtin_decl(self, entry): |
+ 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'] |
+ 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(replacement), |
+ entry.cname) |
+ w.putln('#else') |
+ self.put_cached_builtin_init( |
+ entry.pos, StringEncoding.EncodedString(entry.name), |
+ entry.cname) |
+ if condition: |
+ w.putln('#endif') |
+ |
+ def put_cached_builtin_init(self, pos, name, cname): |
+ w = self.parts['cached_builtins'] |
+ interned_cname = self.get_interned_identifier(name).cname |
+ self.use_utility_code( |
+ UtilityCode.load_cached("GetBuiltinName", "ObjectHandling.c")) |
+ w.putln('%s = __Pyx_GetBuiltinName(%s); if (!%s) %s' % ( |
+ cname, |
+ interned_cname, |
+ cname, |
+ w.error_goto(pos))) |
+ |
+ def generate_const_declarations(self): |
+ self.generate_string_constants() |
+ self.generate_num_constants() |
+ self.generate_object_constant_decls() |
+ |
+ def generate_object_constant_decls(self): |
+ consts = [ (len(c.cname), c.cname, c) |
+ for c in self.py_constants ] |
+ consts.sort() |
+ decls_writer = self.parts['decls'] |
+ for _, cname, c in consts: |
+ decls_writer.putln( |
+ "static %s;" % c.type.declaration_code(cname)) |
+ |
+ def generate_string_constants(self): |
+ c_consts = [ (len(c.cname), c.cname, c) |
+ for c in self.string_const_index.values() ] |
+ c_consts.sort() |
+ py_strings = [] |
+ |
+ decls_writer = self.parts['decls'] |
+ for _, cname, c in c_consts: |
+ conditional = False |
+ if c.py_versions and (2 not in c.py_versions or 3 not in c.py_versions): |
+ conditional = True |
+ decls_writer.putln("#if PY_MAJOR_VERSION %s 3" % ( |
+ (2 in c.py_versions) and '<' or '>=')) |
+ decls_writer.putln('static char %s[] = "%s";' % ( |
+ cname, StringEncoding.split_string_literal(c.escaped_value))) |
+ if conditional: |
+ decls_writer.putln("#endif") |
+ if c.py_strings is not None: |
+ for py_string in c.py_strings.values(): |
+ py_strings.append((c.cname, len(py_string.cname), py_string)) |
+ |
+ for c, cname in self.pyunicode_ptr_const_index.items(): |
+ utf16_array, utf32_array = StringEncoding.encode_pyunicode_string(c) |
+ if utf16_array: |
+ # Narrow and wide representations differ |
+ decls_writer.putln("#ifdef Py_UNICODE_WIDE") |
+ decls_writer.putln("static Py_UNICODE %s[] = { %s };" % (cname, utf32_array)) |
+ if utf16_array: |
+ decls_writer.putln("#else") |
+ decls_writer.putln("static Py_UNICODE %s[] = { %s };" % (cname, utf16_array)) |
+ decls_writer.putln("#endif") |
+ |
+ if py_strings: |
+ self.use_utility_code(UtilityCode.load_cached("InitStrings", "StringTools.c")) |
+ py_strings.sort() |
+ w = self.parts['pystring_table'] |
+ w.putln("") |
+ w.putln("static __Pyx_StringTabEntry %s[] = {" % |
+ Naming.stringtab_cname) |
+ for c_cname, _, py_string in py_strings: |
+ if not py_string.is_str or not py_string.encoding or \ |
+ py_string.encoding in ('ASCII', 'USASCII', 'US-ASCII', |
+ 'UTF8', 'UTF-8'): |
+ encoding = '0' |
+ else: |
+ encoding = '"%s"' % py_string.encoding.lower() |
+ |
+ 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, |
+ c_cname, |
+ c_cname, |
+ encoding, |
+ py_string.is_unicode, |
+ 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("};") |
+ |
+ init_globals = self.parts['init_globals'] |
+ init_globals.putln( |
+ "if (__Pyx_InitStrings(%s) < 0) %s;" % ( |
+ Naming.stringtab_cname, |
+ init_globals.error_goto(self.module_pos))) |
+ |
+ def generate_num_constants(self): |
+ consts = [(c.py_type, c.value[0] == '-', len(c.value), c.value, c.value_code, c) |
+ for c in self.num_const_index.values()] |
+ consts.sort() |
+ decls_writer = self.parts['decls'] |
+ init_globals = self.parts['init_globals'] |
+ for py_type, _, _, value, value_code, c in consts: |
+ cname = c.cname |
+ decls_writer.putln("static PyObject *%s;" % cname) |
+ if py_type == 'float': |
+ function = 'PyFloat_FromDouble(%s)' |
+ elif py_type == 'long': |
+ function = 'PyLong_FromString((char *)"%s", 0, 0)' |
+ elif Utils.long_literal(value): |
+ function = 'PyInt_FromString((char *)"%s", 0, 0)' |
+ elif len(value.lstrip('-')) > 4: |
+ function = "PyInt_FromLong(%sL)" |
+ else: |
+ function = "PyInt_FromLong(%s)" |
+ init_globals.putln('%s = %s; %s' % ( |
+ cname, function % value_code, |
+ init_globals.error_goto_if_null(cname, self.module_pos))) |
+ |
+ # 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. |
+ |
+ def should_declare(self, cname, entry): |
+ if cname in self.declared_cnames: |
+ other = self.declared_cnames[cname] |
+ assert str(entry.type) == str(other.type) |
+ assert entry.init == other.init |
+ return False |
+ else: |
+ self.declared_cnames[cname] = entry |
+ return True |
+ |
+ # |
+ # File name state |
+ # |
+ |
+ def lookup_filename(self, filename): |
+ try: |
+ index = self.filename_table[filename] |
+ except KeyError: |
+ index = len(self.filename_list) |
+ self.filename_list.append(filename) |
+ self.filename_table[filename] = index |
+ return index |
+ |
+ def commented_file_contents(self, source_desc): |
+ try: |
+ return self.input_file_contents[source_desc] |
+ except KeyError: |
+ pass |
+ source_file = source_desc.get_lines(encoding='ASCII', |
+ error_handling='ignore') |
+ try: |
+ F = [u' * ' + line.rstrip().replace( |
+ u'*/', u'*[inserted by cython to avoid comment closer]/' |
+ ).replace( |
+ u'/*', u'/[inserted by cython to avoid comment start]*' |
+ ) |
+ for line in source_file] |
+ finally: |
+ if hasattr(source_file, 'close'): |
+ source_file.close() |
+ if not F: F.append(u'') |
+ self.input_file_contents[source_desc] = F |
+ return F |
+ |
+ # |
+ # Utility code state |
+ # |
+ |
+ def use_utility_code(self, utility_code): |
+ """ |
+ Adds code to the C file. utility_code should |
+ a) implement __eq__/__hash__ for the purpose of knowing whether the same |
+ code has already been included |
+ b) implement put_code, which takes a globalstate instance |
+ |
+ See UtilityCode. |
+ """ |
+ if utility_code not in self.utility_codes: |
+ self.utility_codes.add(utility_code) |
+ utility_code.put_code(self) |
+ |
+ |
+def funccontext_property(name): |
+ attribute_of = operator.attrgetter(name) |
+ def get(self): |
+ return attribute_of(self.funcstate) |
+ def set(self, value): |
+ setattr(self.funcstate, name, value) |
+ return property(get, set) |
+ |
+ |
+class CCodeWriter(object): |
+ """ |
+ Utility class to output C code. |
+ |
+ When creating an insertion point one must care about the state that is |
+ kept: |
+ - formatting state (level, bol) is cloned and used in insertion points |
+ as well |
+ - labels, temps, exc_vars: One must construct a scope in which these can |
+ exist by calling enter_cfunc_scope/exit_cfunc_scope (these are for |
+ sanity checking and forward compatabilty). Created insertion points |
+ looses this scope and cannot access it. |
+ - marker: Not copied to insertion point |
+ - 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 |
+ # |
+ # 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, 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.bol = 1 |
+ |
+ if create_from is not None: |
+ # Use same global state |
+ self.globalstate = create_from.globalstate |
+ self.funcstate = create_from.funcstate |
+ # Clone formatting state |
+ if copy_formatting: |
+ self.level = create_from.level |
+ self.bol = create_from.bol |
+ self.call_level = create_from.call_level |
+ |
+ if emit_linenums is None and self.globalstate: |
+ 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, |
+ c_line_in_traceback=self.c_line_in_traceback) |
+ return result |
+ |
+ def copyto(self, f): |
+ self.buffer.copyto(f) |
+ |
+ def getvalue(self): |
+ return self.buffer.getvalue() |
+ |
+ def write(self, s): |
+ # 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) |
+ |
+ def insertion_point(self): |
+ other = self.create_new(create_from=self, buffer=self.buffer.insertion_point(), copy_formatting=True) |
+ return other |
+ |
+ def new_writer(self): |
+ """ |
+ Creates a new CCodeWriter connected to the same global state, which |
+ can later be inserted using insert. |
+ """ |
+ return CCodeWriter(create_from=self, c_line_in_traceback=self.c_line_in_traceback) |
+ |
+ def insert(self, writer): |
+ """ |
+ Inserts the contents of another code writer (created with |
+ the same global state) in the current location. |
+ |
+ It is ok to write to the inserted writer also after insertion. |
+ """ |
+ assert writer.globalstate is self.globalstate |
+ self.buffer.insert(writer.buffer) |
+ |
+ # Properties delegated to function scope |
+ label_counter = funccontext_property("label_counter") |
+ return_label = funccontext_property("return_label") |
+ error_label = funccontext_property("error_label") |
+ labels_used = funccontext_property("labels_used") |
+ continue_label = funccontext_property("continue_label") |
+ break_label = funccontext_property("break_label") |
+ return_from_error_cleanup_label = funccontext_property("return_from_error_cleanup_label") |
+ yield_labels = funccontext_property("yield_labels") |
+ |
+ # Functions delegated to function scope |
+ def new_label(self, name=None): return self.funcstate.new_label(name) |
+ def new_error_label(self): return self.funcstate.new_error_label() |
+ def new_yield_label(self): return self.funcstate.new_yield_label() |
+ def get_loop_labels(self): return self.funcstate.get_loop_labels() |
+ def set_loop_labels(self, labels): return self.funcstate.set_loop_labels(labels) |
+ def new_loop_labels(self): return self.funcstate.new_loop_labels() |
+ def get_all_labels(self): return self.funcstate.get_all_labels() |
+ def set_all_labels(self, labels): return self.funcstate.set_all_labels(labels) |
+ def all_new_labels(self): return self.funcstate.all_new_labels() |
+ def use_label(self, lbl): return self.funcstate.use_label(lbl) |
+ def label_used(self, lbl): return self.funcstate.label_used(lbl) |
+ |
+ |
+ def enter_cfunc_scope(self): |
+ self.funcstate = FunctionState(self) |
+ |
+ def exit_cfunc_scope(self): |
+ self.funcstate = None |
+ |
+ # constant handling |
+ |
+ def get_py_int(self, str_value, longness): |
+ return self.globalstate.get_int_const(str_value, longness).cname |
+ |
+ def get_py_float(self, str_value, value_code): |
+ return self.globalstate.get_float_const(str_value, value_code).cname |
+ |
+ def get_py_const(self, type, prefix='', cleanup_level=None): |
+ return self.globalstate.get_py_const(type, prefix, cleanup_level).cname |
+ |
+ def get_string_const(self, text): |
+ return self.globalstate.get_string_const(text).cname |
+ |
+ def get_pyunicode_ptr_const(self, text): |
+ return self.globalstate.get_pyunicode_ptr_const(text) |
+ |
+ 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 |
+ |
+ def intern(self, text): |
+ return self.get_py_string_const(text) |
+ |
+ def intern_identifier(self, text): |
+ return self.get_py_string_const(text, identifier=True) |
+ |
+ def get_cached_constants_writer(self): |
+ return self.globalstate.get_cached_constants_writer() |
+ |
+ # code generation |
+ |
+ def putln(self, code="", safe=False): |
+ if self.marker and self.bol: |
+ 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) |
+ else: |
+ self.put(code) |
+ self.write("\n") |
+ self.bol = 1 |
+ |
+ def emit_marker(self): |
+ self.write("\n") |
+ self.indent() |
+ self.write("/* %s */\n" % self.marker[1]) |
+ if (self.funcstate and self.funcstate.can_trace |
+ and self.globalstate.directives['linetrace']): |
+ self.indent() |
+ self.write('__Pyx_TraceLine(%d)\n' % self.marker[0]) |
+ self.last_marker_line = self.marker[0] |
+ self.marker = None |
+ |
+ def put_safe(self, code): |
+ # put code, but ignore {} |
+ self.write(code) |
+ self.bol = 0 |
+ |
+ def put_or_include(self, code, name): |
+ include_dir = self.globalstate.common_utility_include_dir |
+ if include_dir and len(code) > 1024: |
+ include_file = "%s_%s.h" % ( |
+ name, hashlib.md5(code.encode('utf8')).hexdigest()) |
+ path = os.path.join(include_dir, include_file) |
+ if not os.path.exists(path): |
+ tmp_path = '%s.tmp%s' % (path, os.getpid()) |
+ f = Utils.open_new_file(tmp_path) |
+ try: |
+ f.write(code) |
+ finally: |
+ f.close() |
+ os.rename(tmp_path, path) |
+ code = '#include "%s"\n' % path |
+ self.put(code) |
+ |
+ def put(self, code): |
+ if is_self_assignment(code): |
+ return |
+ fix_indent = False |
+ if "{" in code: |
+ dl = code.count("{") |
+ else: |
+ dl = 0 |
+ if "}" in code: |
+ dl -= code.count("}") |
+ if dl < 0: |
+ self.level += dl |
+ elif dl == 0 and code[0] == "}": |
+ # special cases like "} else {" need a temporary dedent |
+ fix_indent = True |
+ self.level -= 1 |
+ if self.bol: |
+ self.indent() |
+ self.write(code) |
+ self.bol = 0 |
+ if dl > 0: |
+ self.level += dl |
+ elif fix_indent: |
+ self.level += 1 |
+ |
+ def putln_tempita(self, code, **context): |
+ from Cython.Tempita import sub |
+ self.putln(sub(code, **context)) |
+ |
+ def put_tempita(self, code, **context): |
+ from Cython.Tempita import sub |
+ self.put(sub(code, **context)) |
+ |
+ def increase_indent(self): |
+ self.level += 1 |
+ |
+ def decrease_indent(self): |
+ 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) |
+ |
+ def get_py_version_hex(self, pyversion): |
+ return "0x%02X%02X%02X%02X" % (tuple(pyversion) + (0,0,0,0))[:4] |
+ |
+ def mark_pos(self, pos): |
+ if pos is None: |
+ return |
+ source_desc, line, col = pos |
+ if self.last_marker_line == line: |
+ return |
+ assert isinstance(source_desc, SourceDescriptor) |
+ contents = self.globalstate.commented_file_contents(source_desc) |
+ lines = contents[max(0, line-3):line] # line numbers start at 1 |
+ lines[-1] += u' # <<<<<<<<<<<<<<' |
+ lines += contents[line:line+2] |
+ |
+ marker = u'"%s":%d\n%s\n' % ( |
+ source_desc.get_escaped_description(), line, u'\n'.join(lines)) |
+ 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_declaration(self, entry, storage_class="", |
+ dll_linkage=None, definition=True): |
+ #print "Code.put_var_declaration:", entry.name, "definition =", definition ### |
+ if entry.visibility == 'private' and not (definition or entry.defined_in_pxd): |
+ #print "...private and not definition, skipping", entry.cname ### |
+ return |
+ if entry.visibility == "private" and not entry.used: |
+ #print "...private and not used, skipping", entry.cname ### |
+ return |
+ if storage_class: |
+ self.put("%s " % storage_class) |
+ if not entry.cf_used: |
+ self.put('CYTHON_UNUSED ') |
+ self.put(entry.type.declaration_code( |
+ entry.cname, dll_linkage=dll_linkage)) |
+ if entry.init is not None: |
+ self.put_safe(" = %s" % entry.type.literal_code(entry.init)) |
+ elif entry.type.is_pyobject: |
+ self.put(" = NULL") |
+ self.putln(";") |
+ |
+ def put_temp_declarations(self, func_context): |
+ for name, type, manage_ref, static in func_context.temps_allocated: |
+ decl = type.declaration_code(name) |
+ if type.is_pyobject: |
+ self.putln("%s = NULL;" % decl) |
+ elif type.is_memoryviewslice: |
+ import MemoryView |
+ self.putln("%s = %s;" % (decl, MemoryView.memslice_entry_init)) |
+ else: |
+ self.putln("%s%s;" % (static and "static " or "", decl)) |
+ |
+ if func_context.should_declare_error_indicator: |
+ if self.funcstate.uses_error_indicator: |
+ unused = '' |
+ else: |
+ unused = 'CYTHON_UNUSED ' |
+ # Initialize these variables to silence compiler warnings |
+ self.putln("%sint %s = 0;" % (unused, Naming.lineno_cname)) |
+ self.putln("%sconst char *%s = NULL;" % (unused, Naming.filename_cname)) |
+ self.putln("%sint %s = 0;" % (unused, Naming.clineno_cname)) |
+ |
+ 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 |
+ else: |
+ return cond |
+ |
+ def build_function_modifiers(self, modifiers, mapper=modifier_output_mapper): |
+ if not modifiers: |
+ return '' |
+ return '%s ' % ' '.join([mapper(m,m) for m in modifiers]) |
+ |
+ # Python objects and reference counting |
+ |
+ def entry_as_pyobject(self, entry): |
+ type = entry.type |
+ if (not entry.is_self_arg and not entry.type.is_complete() |
+ or entry.type.is_extension_type): |
+ 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) |
+ |
+ def put_xgotref(self, cname): |
+ self.putln("__Pyx_XGOTREF(%s);" % cname) |
+ |
+ def put_incref(self, cname, type, nanny=True): |
+ if nanny: |
+ 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): |
+ self._put_decref(cname, type, nanny, null_check=False, clear=False) |
+ |
+ 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_xgotref(self, entry): |
+ if entry.type.is_pyobject: |
+ self.putln("__Pyx_XGOTREF(%s);" % self.entry_as_pyobject(entry)) |
+ |
+ def put_var_xgiveref(self, entry): |
+ if entry.type.is_pyobject: |
+ self.putln("__Pyx_XGIVEREF(%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, clear_before_decref=False): |
+ self._put_decref(cname, type, nanny, null_check=False, |
+ clear=True, clear_before_decref=clear_before_decref) |
+ |
+ def put_xdecref(self, cname, type, nanny=True, have_gil=True): |
+ self._put_decref(cname, type, nanny, null_check=True, |
+ have_gil=have_gil, clear=False) |
+ |
+ def put_xdecref_clear(self, cname, type, nanny=True, clear_before_decref=False): |
+ self._put_decref(cname, type, nanny, null_check=True, |
+ clear=True, clear_before_decref=clear_before_decref) |
+ |
+ def _put_decref(self, cname, type, nanny=True, null_check=False, |
+ have_gil=True, clear=False, clear_before_decref=False): |
+ if type.is_memoryviewslice: |
+ self.put_xdecref_memoryviewslice(cname, have_gil=have_gil) |
+ return |
+ |
+ prefix = nanny and '__Pyx' or 'Py' |
+ X = null_check and 'X' or '' |
+ |
+ if clear: |
+ if clear_before_decref: |
+ if not nanny: |
+ X = '' # CPython doesn't have a Py_XCLEAR() |
+ self.putln("%s_%sCLEAR(%s);" % (prefix, X, cname)) |
+ else: |
+ self.putln("%s_%sDECREF(%s); %s = 0;" % ( |
+ prefix, X, self.as_pyobject(cname, type), cname)) |
+ else: |
+ self.putln("%s_%sDECREF(%s);" % ( |
+ prefix, X, self.as_pyobject(cname, type))) |
+ |
+ def put_decref_set(self, cname, rhs_cname): |
+ self.putln("__Pyx_DECREF_SET(%s, %s);" % (cname, rhs_cname)) |
+ |
+ def put_xdecref_set(self, cname, rhs_cname): |
+ self.putln("__Pyx_XDECREF_SET(%s, %s);" % (cname, rhs_cname)) |
+ |
+ def put_var_decref(self, entry): |
+ if entry.type.is_pyobject: |
+ self.putln("__Pyx_XDECREF(%s);" % self.entry_as_pyobject(entry)) |
+ |
+ def put_var_xdecref(self, entry): |
+ if entry.type.is_pyobject: |
+ self.putln("__Pyx_XDECREF(%s);" % self.entry_as_pyobject(entry)) |
+ |
+ def put_var_decref_clear(self, entry): |
+ self._put_var_decref_clear(entry, null_check=False) |
+ |
+ def put_var_xdecref_clear(self, entry): |
+ self._put_var_decref_clear(entry, null_check=True) |
+ |
+ def _put_var_decref_clear(self, entry, null_check): |
+ if entry.type.is_pyobject: |
+ if entry.in_closure: |
+ # reset before DECREF to make sure closure state is |
+ # consistent during call to DECREF() |
+ self.putln("__Pyx_%sCLEAR(%s);" % ( |
+ null_check and 'X' or '', |
+ entry.cname)) |
+ else: |
+ self.putln("__Pyx_%sDECREF(%s); %s = 0;" % ( |
+ null_check and 'X' or '', |
+ 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: |
+ if entry.xdecref_cleanup: |
+ 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_incref_memoryviewslice(self, slice_cname, have_gil=False): |
+ import MemoryView |
+ self.globalstate.use_utility_code(MemoryView.memviewslice_init_code) |
+ self.putln("__PYX_INC_MEMVIEW(&%s, %d);" % (slice_cname, int(have_gil))) |
+ |
+ def put_xdecref_memoryviewslice(self, slice_cname, have_gil=False): |
+ import MemoryView |
+ self.globalstate.use_utility_code(MemoryView.memviewslice_init_code) |
+ self.putln("__PYX_XDEC_MEMVIEW(&%s, %d);" % (slice_cname, int(have_gil))) |
+ |
+ def put_xgiveref_memoryviewslice(self, slice_cname): |
+ self.put_xgiveref("%s.memview" % slice_cname) |
+ |
+ 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") |
+ if nanny: |
+ 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__': |
+ if entry.name not in ['__cinit__', '__dealloc__', '__richcmp__', '__next__', '__getreadbuffer__', '__getwritebuffer__', '__getsegcount__', '__getcharbuffer__', '__getbuffer__', '__releasebuffer__']: |
+ if entry.name == '__getattr__' and not self.globalstate.directives['fast_getattr']: |
+ pass |
+ # Python's typeobject.c will automatically fill in our slot |
+ # in add_operators() (called by PyType_Ready) with a value |
+ # that's better than ours. |
+ elif allow_skip: |
+ return |
+ from TypeSlots import method_coexist |
+ if entry.doc: |
+ doc_code = entry.doc_cname |
+ else: |
+ doc_code = 0 |
+ method_flags = entry.signature.method_flags() |
+ if method_flags: |
+ if entry.is_special: |
+ method_flags += [method_coexist] |
+ self.putln( |
+ '{__Pyx_NAMESTR("%s"), (PyCFunction)%s, %s, __Pyx_DOCSTR(%s)}%s' % ( |
+ entry.name, |
+ entry.func_cname, |
+ "|".join(method_flags), |
+ doc_code, |
+ term)) |
+ |
+ # GIL methods |
+ |
+ def put_ensure_gil(self, declare_gilstate=True, variable=None): |
+ """ |
+ Acquire the GIL. The generated code is safe even when no PyThreadState |
+ has been allocated for this thread (for threads not initialized by |
+ using the Python API). Additionally, the code generated by this method |
+ may be called recursively. |
+ """ |
+ self.globalstate.use_utility_code( |
+ UtilityCode.load_cached("ForceInitThreads", "ModuleSetupCode.c")) |
+ self.putln("#ifdef WITH_THREAD") |
+ if not variable: |
+ variable = '__pyx_gilstate_save' |
+ if declare_gilstate: |
+ self.put("PyGILState_STATE ") |
+ self.putln("%s = PyGILState_Ensure();" % variable) |
+ self.putln("#endif") |
+ |
+ def put_release_ensured_gil(self, variable=None): |
+ """ |
+ Releases the GIL, corresponds to `put_ensure_gil`. |
+ """ |
+ if not variable: |
+ variable = '__pyx_gilstate_save' |
+ self.putln("#ifdef WITH_THREAD") |
+ self.putln("PyGILState_Release(%s);" % variable) |
+ self.putln("#endif") |
+ |
+ def put_acquire_gil(self, variable=None): |
+ """ |
+ Acquire the GIL. The thread's thread state must have been initialized |
+ by a previous `put_release_gil` |
+ """ |
+ self.putln("#ifdef WITH_THREAD") |
+ if variable: |
+ self.putln('_save = %s;' % variable) |
+ self.putln("Py_BLOCK_THREADS") |
+ self.putln("#endif") |
+ |
+ def put_release_gil(self, variable=None): |
+ "Release the GIL, corresponds to `put_acquire_gil`." |
+ self.putln("#ifdef WITH_THREAD") |
+ self.putln("PyThreadState *_save;") |
+ self.putln("Py_UNBLOCK_THREADS") |
+ if variable: |
+ self.putln('%s = _save;' % variable) |
+ self.putln("#endif") |
+ |
+ def declare_gilstate(self): |
+ self.putln("#ifdef WITH_THREAD") |
+ self.putln("PyGILState_STATE __pyx_gilstate_save;") |
+ self.putln("#endif") |
+ |
+ # error handling |
+ |
+ def put_error_if_neg(self, pos, value): |
+# return self.putln("if (unlikely(%s < 0)) %s" % (value, self.error_goto(pos))) # TODO this path is almost _never_ taken, yet this macro makes is slower! |
+ return self.putln("if (%s < 0) %s" % (value, self.error_goto(pos))) |
+ |
+ def put_error_if_unbound(self, pos, entry, in_nogil_context=False): |
+ import ExprNodes |
+ if entry.from_closure: |
+ func = '__Pyx_RaiseClosureNameError' |
+ self.globalstate.use_utility_code( |
+ ExprNodes.raise_closure_name_error_utility_code) |
+ elif entry.type.is_memoryviewslice and in_nogil_context: |
+ func = '__Pyx_RaiseUnboundMemoryviewSliceNogil' |
+ self.globalstate.use_utility_code( |
+ ExprNodes.raise_unbound_memoryview_utility_code_nogil) |
+ else: |
+ func = '__Pyx_RaiseUnboundLocalError' |
+ self.globalstate.use_utility_code( |
+ ExprNodes.raise_unbound_local_error_utility_code) |
+ |
+ self.putln('if (unlikely(!%s)) { %s("%s"); %s }' % ( |
+ entry.type.check_for_null_code(entry.cname), |
+ func, |
+ entry.name, |
+ self.error_goto(pos))) |
+ |
+ def set_error_info(self, pos, used=False): |
+ self.funcstate.should_declare_error_indicator = True |
+ if used: |
+ self.funcstate.uses_error_indicator = True |
+ if self.c_line_in_traceback: |
+ cinfo = " %s = %s;" % (Naming.clineno_cname, Naming.line_c_macro) |
+ else: |
+ cinfo = "" |
+ |
+ return "%s = %s[%s]; %s = %s;%s" % ( |
+ Naming.filename_cname, |
+ Naming.filetable_cname, |
+ self.lookup_filename(pos[0]), |
+ Naming.lineno_cname, |
+ pos[1], |
+ cinfo) |
+ |
+ def error_goto(self, pos): |
+ lbl = self.funcstate.error_label |
+ self.funcstate.use_label(lbl) |
+ return "{%s goto %s;}" % ( |
+ self.set_error_info(pos), |
+ 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, acquire_gil=False): |
+ if acquire_gil: |
+ self.globalstate.use_utility_code( |
+ UtilityCode.load_cached("ForceInitThreads", "ModuleSetupCode.c")) |
+ self.putln('__Pyx_RefNannySetupContext("%s", %d);' % (name, acquire_gil and 1 or 0)) |
+ |
+ def put_finish_refcount_context(self): |
+ self.putln("__Pyx_RefNannyFinishContext();") |
+ |
+ def put_add_traceback(self, qualified_name): |
+ """ |
+ Build a Python traceback for propagating exceptions. |
+ |
+ qualified_name should be the qualified name of the function. |
+ """ |
+ format_tuple = ( |
+ qualified_name, |
+ Naming.clineno_cname, |
+ Naming.lineno_cname, |
+ Naming.filename_cname, |
+ ) |
+ self.funcstate.uses_error_indicator = True |
+ self.putln('__Pyx_AddTraceback("%s", %s, %s, %s);' % format_tuple) |
+ |
+ def put_unraisable(self, qualified_name): |
+ """ |
+ Generate code to print a Python warning for an unraisable exception. |
+ |
+ qualified_name should be the qualified name of the function. |
+ """ |
+ format_tuple = ( |
+ qualified_name, |
+ Naming.clineno_cname, |
+ Naming.lineno_cname, |
+ Naming.filename_cname, |
+ int(self.globalstate.directives['unraisable_tracebacks']) |
+ ) |
+ self.funcstate.uses_error_indicator = True |
+ self.putln('__Pyx_WriteUnraisable("%s", %s, %s, %s, %s);' % format_tuple) |
+ self.globalstate.use_utility_code( |
+ UtilityCode.load_cached("WriteUnraisableException", "Exceptions.c")) |
+ |
+ 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 putln_openmp(self, string): |
+ self.putln("#ifdef _OPENMP") |
+ self.putln(string) |
+ self.putln("#endif /* _OPENMP */") |
+ |
+ def undef_builtin_expect(self, cond): |
+ """ |
+ Redefine the macros likely() and unlikely to no-ops, depending on |
+ condition 'cond' |
+ """ |
+ self.putln("#if %s" % cond) |
+ self.putln(" #undef likely") |
+ self.putln(" #undef unlikely") |
+ self.putln(" #define likely(x) (x)") |
+ self.putln(" #define unlikely(x) (x)") |
+ self.putln("#endif") |
+ |
+ def redef_builtin_expect(self, cond): |
+ self.putln("#if %s" % cond) |
+ self.putln(" #undef likely") |
+ self.putln(" #undef unlikely") |
+ self.putln(" #define likely(x) __builtin_expect(!!(x), 1)") |
+ self.putln(" #define unlikely(x) __builtin_expect(!!(x), 0)") |
+ self.putln("#endif") |
+ |
+class PyrexCodeWriter(object): |
+ # f file output file |
+ # level int indentation level |
+ |
+ 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 PyxCodeWriter(object): |
+ """ |
+ Can be used for writing out some Cython code. To use the indenter |
+ functionality, the Cython.Compiler.Importer module will have to be used |
+ to load the code to support python 2.4 |
+ """ |
+ |
+ def __init__(self, buffer=None, indent_level=0, context=None, encoding='ascii'): |
+ self.buffer = buffer or StringIOTree() |
+ self.level = indent_level |
+ self.context = context |
+ self.encoding = encoding |
+ |
+ def indent(self, levels=1): |
+ self.level += levels |
+ return True |
+ |
+ def dedent(self, levels=1): |
+ self.level -= levels |
+ |
+ def indenter(self, line): |
+ """ |
+ Instead of |
+ |
+ with pyx_code.indenter("for i in range(10):"): |
+ pyx_code.putln("print i") |
+ |
+ write |
+ |
+ if pyx_code.indenter("for i in range(10);"): |
+ pyx_code.putln("print i") |
+ pyx_code.dedent() |
+ """ |
+ self.putln(line) |
+ self.indent() |
+ return True |
+ |
+ def getvalue(self): |
+ result = self.buffer.getvalue() |
+ if not isinstance(result, unicode): |
+ result = result.decode(self.encoding) |
+ |
+ return result |
+ |
+ def putln(self, line, context=None): |
+ context = context or self.context |
+ if context: |
+ line = sub_tempita(line, context) |
+ self._putln(line) |
+ |
+ def _putln(self, line): |
+ self.buffer.write("%s%s\n" % (self.level * " ", line)) |
+ |
+ def put_chunk(self, chunk, context=None): |
+ context = context or self.context |
+ if context: |
+ chunk = sub_tempita(chunk, context) |
+ |
+ chunk = textwrap.dedent(chunk) |
+ for line in chunk.splitlines(): |
+ self._putln(line) |
+ |
+ def insertion_point(self): |
+ return PyxCodeWriter(self.buffer.insertion_point(), self.level, |
+ self.context) |
+ |
+ def named_insertion_point(self, name): |
+ setattr(self, name, self.insertion_point()) |
+ |
+ |
+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 |