1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593 |
- # cython: language_level = 2
- # cython: auto_pickle=False
- #
- # Code output module
- #
- from __future__ import absolute_import
- import cython
- cython.declare(os=object, re=object, operator=object, textwrap=object,
- Template=object, Naming=object, Options=object, StringEncoding=object,
- Utils=object, SourceDescriptor=object, StringIOTree=object,
- DebugFlags=object, basestring=object, defaultdict=object,
- closing=object, partial=object)
- import os
- import re
- import shutil
- import sys
- import operator
- import textwrap
- from string import Template
- from functools import partial
- from contextlib import closing
- from collections import defaultdict
- try:
- import hashlib
- except ImportError:
- import md5 as hashlib
- from . import Naming
- from . import Options
- from . import DebugFlags
- from . import StringEncoding
- from . import Version
- from .. import Utils
- from .Scanning import SourceDescriptor
- from ..StringIOTree import StringIOTree
- 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'),
- }
- ctypedef_builtins_map = {
- # types of builtins in "ctypedef class" statements which we don't
- # import either because the names conflict with C types or because
- # the type simply is not exposed.
- 'py_int' : '&PyInt_Type',
- 'py_long' : '&PyLong_Type',
- 'py_float' : '&PyFloat_Type',
- 'wrapper_descriptor' : '&PyWrapperDescr_Type',
- }
- basicsize_builtins_map = {
- # builtins whose type has a different tp_basicsize than sizeof(...)
- 'PyTypeObject': 'PyHeapTypeObject',
- }
- uncachable_builtins = [
- # Global/builtin names that cannot be cached because they may or may not
- # be available at import time, for various reasons:
- ## - Py3.7+
- 'breakpoint', # might deserve an implementation in Cython
- ## - Py3.4+
- '__loader__',
- '__spec__',
- ## - Py3+
- 'BlockingIOError',
- 'BrokenPipeError',
- 'ChildProcessError',
- 'ConnectionAbortedError',
- 'ConnectionError',
- 'ConnectionRefusedError',
- 'ConnectionResetError',
- 'FileExistsError',
- 'FileNotFoundError',
- 'InterruptedError',
- 'IsADirectoryError',
- 'ModuleNotFoundError',
- 'NotADirectoryError',
- 'PermissionError',
- 'ProcessLookupError',
- 'RecursionError',
- 'ResourceWarning',
- #'StopAsyncIteration', # backported
- 'TimeoutError',
- '__build_class__',
- 'ascii', # might deserve an implementation in Cython
- #'exec', # implemented in Cython
- ## - Py2.7+
- 'memoryview',
- ## - platform specific
- 'WindowsError',
- ## - others
- '_', # e.g. used by gettext
- ]
- special_py_methods = set([
- '__cinit__', '__dealloc__', '__richcmp__', '__next__',
- '__await__', '__aiter__', '__anext__',
- '__getreadbuffer__', '__getwritebuffer__', '__getsegcount__',
- '__getcharbuffer__', '__getbuffer__', '__releasebuffer__'
- ])
- modifier_output_mapper = {
- 'inline': 'CYTHON_INLINE'
- }.get
- class IncludeCode(object):
- """
- An include file and/or verbatim C code to be included in the
- generated sources.
- """
- # attributes:
- #
- # pieces {order: unicode}: pieces of C code to be generated.
- # For the included file, the key "order" is zero.
- # For verbatim include code, the "order" is the "order"
- # attribute of the original IncludeCode where this piece
- # of C code was first added. This is needed to prevent
- # duplication if the same include code is found through
- # multiple cimports.
- # location int: where to put this include in the C sources, one
- # of the constants INITIAL, EARLY, LATE
- # order int: sorting order (automatically set by increasing counter)
- # Constants for location. If the same include occurs with different
- # locations, the earliest one takes precedense.
- INITIAL = 0
- EARLY = 1
- LATE = 2
- counter = 1 # Counter for "order"
- def __init__(self, include=None, verbatim=None, late=True, initial=False):
- self.order = self.counter
- type(self).counter += 1
- self.pieces = {}
- if include:
- if include[0] == '<' and include[-1] == '>':
- self.pieces[0] = u'#include {0}'.format(include)
- late = False # system include is never late
- else:
- self.pieces[0] = u'#include "{0}"'.format(include)
- if verbatim:
- self.pieces[self.order] = verbatim
- if initial:
- self.location = self.INITIAL
- elif late:
- self.location = self.LATE
- else:
- self.location = self.EARLY
- def dict_update(self, d, key):
- """
- Insert `self` in dict `d` with key `key`. If that key already
- exists, update the attributes of the existing value with `self`.
- """
- if key in d:
- other = d[key]
- other.location = min(self.location, other.location)
- other.pieces.update(self.pieces)
- else:
- d[key] = self
- def sortkey(self):
- return self.order
- def mainpiece(self):
- """
- Return the main piece of C code, corresponding to the include
- file. If there was no include file, return None.
- """
- return self.pieces.get(0)
- def write(self, code):
- # Write values of self.pieces dict, sorted by the keys
- for k in sorted(self.pieces):
- code.putln(self.pieces[k])
- 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
- _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) as 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 = '#'
- strip_comments = partial(re.compile(r'^\s*#(?!\s*cython\s*:).*').sub, '')
- rstrip = StringEncoding._unicode.rstrip
- else:
- comment = '/'
- strip_comments = partial(re.compile(r'^\s*//.*|/\*[^*]*\*/').sub, '')
- rstrip = partial(re.compile(r'\s+(\\?)$').sub, r'\1')
- 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(r'(.+)[.](proto(?:[.]\S+)?|impl|init|cleanup)$').match
- with closing(Utils.open_source_file(filename, encoding='UTF-8')) as f:
- all_lines = f.readlines()
- utilities = defaultdict(lambda: [None, None, {}])
- lines = []
- tags = defaultdict(set)
- 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[name]
- else:
- tags[m.group('tag')].add(m.group('value'))
- lines.append('') # keep line number correct
- else:
- lines.append(rstrip(strip_comments(line)))
- 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)
- utilities = dict(utilities) # un-defaultdict-ify
- 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
- with closing(zipfile.ZipFile(archive)) as fileobj:
- listing = [os.path.basename(name)
- for name in fileobj.namelist()
- if os.path.join(archive, name).startswith(utility_dir)]
- files = [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 = list(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, **kwargs):
- pass
- def __deepcopy__(self, memodict=None):
- # No need to deep-copy utility code since it's essentially immutable.
- return self
- class UtilityCode(UtilityCodeBase):
- """
- Stores utility code to add during code generation.
- See GlobalState.put_utility_code.
- hashes/equals by instance
- proto C prototypes
- impl implementation 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
- self_type, other_type = type(self), type(other)
- if self_type is not other_type and not (isinstance(other, self_type) or isinstance(self, other_type)):
- 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.empty_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.
- """
- if 'PYIDENT(' not in impl and 'PYUNICODE(' not in impl:
- return False, impl
- replacements = {}
- def externalise(matchobj):
- key = matchobj.groups()
- try:
- cname = replacements[key]
- except KeyError:
- str_type, name = key
- cname = replacements[key] = output.get_py_string_const(
- StringEncoding.EncodedString(name), identifier=str_type == 'IDENT').cname
- return cname
- impl = re.sub(r'PY(IDENT|UNICODE)\("([^"]+)"\)', externalise, impl)
- assert 'PYIDENT(' not in impl and 'PYUNICODE(' not in impl
- return True, impl
- def inject_unbound_methods(self, impl, output):
- """Replace 'UNBOUND_METHOD(type, "name")' by a constant Python identifier cname.
- """
- if 'CALL_UNBOUND_METHOD(' not in impl:
- return False, impl
- def externalise(matchobj):
- type_cname, method_name, obj_cname, args = matchobj.groups()
- args = [arg.strip() for arg in args[1:].split(',')] if args else []
- assert len(args) < 3, "CALL_UNBOUND_METHOD() does not support %d call arguments" % len(args)
- return output.cached_unbound_method_call_code(obj_cname, type_cname, method_name, args)
- impl = re.sub(
- r'CALL_UNBOUND_METHOD\('
- r'([a-zA-Z_]+),' # type cname
- r'\s*"([^"]+)",' # method name
- r'\s*([^),]+)' # object cname
- r'((?:,\s*[^),]+)*)' # args*
- r'\)', externalise, impl)
- assert 'CALL_UNBOUND_METHOD(' not in impl
- return True, impl
- def wrap_c_strings(self, impl):
- """Replace CSTRING('''xyz''') by a C compatible string
- """
- if 'CSTRING(' not in impl:
- return impl
- def split_string(matchobj):
- content = matchobj.group(1).replace('"', '\042')
- return ''.join(
- '"%s\\n"\n' % line if not line.endswith('\\') or line.endswith('\\\\') else '"%s"\n' % line[:-1]
- for line in content.splitlines())
- impl = re.sub(r'CSTRING\(\s*"""([^"]*(?:"[^"]+)*)"""\s*\)', split_string, impl)
- assert 'CSTRING(' not in impl
- return impl
- def put_code(self, output):
- if self.requires:
- for dependency in self.requires:
- output.use_utility_code(dependency)
- if self.proto:
- writer = output[self.proto_block]
- writer.putln("/* %s.proto */" % self.name)
- writer.put_or_include(
- self.format_code(self.proto), '%s_proto' % self.name)
- if self.impl:
- impl = self.format_code(self.wrap_c_strings(self.impl))
- is_specialised1, impl = self.inject_string_constants(impl, output)
- is_specialised2, impl = self.inject_unbound_methods(impl, output)
- writer = output['utility_code_def']
- writer.putln("/* %s */" % self.name)
- if not (is_specialised1 or is_specialised2):
- # no module specific adaptations => can be reused
- writer.put_or_include(impl, '%s_impl' % self.name)
- else:
- writer.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']
- writer.putln("/* %s.cleanup */" % self.name)
- 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 ..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)
- @classmethod
- def load_cached(cls, utility_code_name, from_file=None, context=None, __cache={}):
- context_key = tuple(sorted(context.items())) if context else None
- assert hash(context_key) is not None # raise TypeError if not hashable
- key = (cls, from_file, utility_code_name, context_key)
- try:
- return __cache[key]
- except KeyError:
- pass
- code = __cache[key] = cls.load(utility_code_name, from_file, context=context)
- return code
- 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'.
- """
- __name__ = '<lazy>'
- requires = None
- 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
- # scope Scope the scope object of the current function
- # Not used for now, perhaps later
- def __init__(self, owner, names_taken=set(), scope=None):
- self.names_taken = names_taken
- self.owner = owner
- self.scope = scope
- 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.current_except = None
- self.can_trace = False
- self.gil_owned = True
- 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.zombie_temps = set() # temps that must not be reused after release
- 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
- # safety checks
- def validate_exit(self):
- # validate that all allocated temps have been freed
- if self.temps_allocated:
- leftovers = self.temps_in_use()
- if leftovers:
- msg = "TEMPGUARD: Temps left over at end of '%s': %s" % (self.scope.name, ', '.join([
- '%s [%s]' % (name, ctype)
- for name, ctype, is_pytemp in sorted(leftovers)]),
- )
- #print(msg)
- raise RuntimeError(msg)
- # 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, expr_type='yield'):
- label = self.new_label('resume_from_%s' % expr_type)
- 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, reusable=True):
- """
- 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.
- if reusable=False, the temp will not be reused after release.
- A C string referring to the variable is returned.
- """
- if type.is_const and not type.is_reference:
- type = type.const_base_type
- elif type.is_reference and not type.is_fake_reference:
- type = type.ref_base_type
- elif type.is_cfunction:
- from . import PyrexTypes
- type = PyrexTypes.c_ptr_type(type) # A function itself isn't an l-value
- 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 reusable and freelist is not None and freelist[0]:
- result = freelist[0].pop()
- freelist[1].remove(result)
- else:
- while True:
- self.temp_counter += 1
- result = "%s%d" % (Naming.codewriter_temp_prefix, self.temp_counter)
- if result not in self.names_taken: break
- self.temps_allocated.append((result, type, manage_ref, static))
- if not reusable:
- self.zombie_temps.add(result)
- self.temps_used_type[result] = (type, manage_ref)
- if DebugFlags.debug_temp_code_comments:
- self.owner.putln("/* %s allocated (%s)%s */" % (result, type, "" if reusable else " - zombie"))
- 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 = ([], set()) # keep order in list and make lookups in set fast
- self.temps_free[(type, manage_ref)] = freelist
- if name in freelist[1]:
- raise RuntimeError("Temp %s freed twice!" % name)
- if name not in self.zombie_temps:
- freelist[0].append(name)
- freelist[1].add(name)
- if DebugFlags.debug_temp_code_comments:
- self.owner.putln("/* %s released %s*/" % (
- name, " - zombie" if name in self.zombie_temps else ""))
- 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[1]:
- 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 sorted([ # Enforce deterministic order.
- (cname, type)
- for (type, manage_ref), freelist in self.temps_free.items() if manage_ref
- for cname in freelist[0]
- ])
- 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(br"(?![0-9])\w+$".decode('ascii'), 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, bytes):
- intern = bool(possible_bytes_identifier(text))
- else:
- intern = bool(possible_unicode_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',
- 'string_decls',
- 'decls',
- 'late_includes',
- '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, code_config, 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.code_config = code_config
- 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.dedup_const_index = {}
- self.pyunicode_ptr_const_index = {}
- self.num_const_index = {}
- self.py_constants = []
- self.cached_cmethods = {}
- self.initialised_constants = set()
- writer.set_global_state(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 CYTHON_SMALL_CODE int __Pyx_InitCachedBuiltins(void) {")
- w = self.parts['cached_constants']
- w.enter_cfunc_scope()
- w.putln("")
- w.putln("static CYTHON_SMALL_CODE 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 CYTHON_SMALL_CODE 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 CYTHON_SMALL_CODE void __Pyx_CleanupGlobals(void) {")
- code = self.parts['utility_code_proto']
- code.putln("")
- code.putln("/* --- Runtime support code (head) --- */")
- code = self.parts['utility_code_def']
- if self.code_config.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']
- util = TempitaUtilityCode.load_cached("TypeConversions", "TypeConversion.c")
- code.put(util.format_code(util.impl))
- 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, target=None):
- if target is not None:
- if target in self.initialised_constants:
- # Return None on second/later calls to prevent duplicate creation code.
- return None
- self.initialised_constants.add(target)
- 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, dedup_key=None):
- if dedup_key is not None:
- const = self.dedup_const_index.get(dedup_key)
- if const is not None:
- return const
- # 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)
- if dedup_key is not None:
- self.dedup_const_index[dedup_key] = const
- 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 get_cached_unbound_method(self, type_cname, method_name):
- key = (type_cname, method_name)
- try:
- cname = self.cached_cmethods[key]
- except KeyError:
- cname = self.cached_cmethods[key] = self.new_const_cname(
- 'umethod', '%s_%s' % (type_cname, method_name))
- return cname
- def cached_unbound_method_call_code(self, obj_cname, type_cname, method_name, arg_cnames):
- # admittedly, not the best place to put this method, but it is reused by UtilityCode and ExprNodes ...
- utility_code_name = "CallUnboundCMethod%d" % len(arg_cnames)
- self.use_utility_code(UtilityCode.load_cached(utility_code_name, "ObjectHandling.c"))
- cache_cname = self.get_cached_unbound_method(type_cname, method_name)
- args = [obj_cname] + arg_cnames
- return "__Pyx_%s(&%s, %s)" % (
- utility_code_name,
- cache_cname,
- ', '.join(args),
- )
- 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_cached_methods_decls()
- 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_cached_methods_decls(self):
- if not self.cached_cmethods:
- return
- decl = self.parts['decls']
- init = self.parts['init_globals']
- cnames = []
- for (type_cname, method_name), cname in sorted(self.cached_cmethods.items()):
- cnames.append(cname)
- method_name_cname = self.get_interned_identifier(StringEncoding.EncodedString(method_name)).cname
- decl.putln('static __Pyx_CachedCFunction %s = {0, &%s, 0, 0, 0};' % (
- cname, method_name_cname))
- # split type reference storage as it might not be static
- init.putln('%s.type = (PyObject*)&%s;' % (
- cname, type_cname))
- if Options.generate_cleanup_code:
- cleanup = self.parts['cleanup_globals']
- for cname in cnames:
- cleanup.putln("Py_CLEAR(%s.method);" % 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['string_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 const 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 sorted(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, source_desc):
- entry = source_desc.get_filenametable_entry()
- try:
- index = self.filename_table[entry]
- except KeyError:
- index = len(self.filename_list)
- self.filename_list.append(source_desc)
- self.filename_table[entry] = 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 and utility_code not in self.utility_codes:
- self.utility_codes.add(utility_code)
- utility_code.put_code(self)
- def use_entry_utility_code(self, entry):
- if entry is None:
- return
- if entry.utility_code:
- self.use_utility_code(entry.utility_code)
- if entry.utility_code_definition:
- self.use_utility_code(entry.utility_code_definition)
- def funccontext_property(func):
- name = func.__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 CCodeConfig(object):
- # emit_linenums boolean write #line pragmas?
- # emit_code_comments boolean copy the original code into C comments?
- # c_line_in_traceback boolean append the c file and line number to the traceback for exceptions?
- def __init__(self, emit_linenums=True, emit_code_comments=True, c_line_in_traceback=True):
- self.emit_code_comments = emit_code_comments
- self.emit_linenums = emit_linenums
- self.c_line_in_traceback = c_line_in_traceback
- 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 compatibility). 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.)
- # pyclass_stack list used during recursive code generation to pass information
- # about the current class one is in
- # code_config CCodeConfig configuration options for the C code writer
- @cython.locals(create_from='CCodeWriter')
- def __init__(self, create_from=None, buffer=None, copy_formatting=False):
- if buffer is None: buffer = StringIOTree()
- self.buffer = buffer
- self.last_pos = None
- self.last_marked_pos = None
- self.pyclass_stack = []
- self.funcstate = None
- self.globalstate = None
- self.code_config = None
- self.level = 0
- self.call_level = 0
- self.bol = 1
- if create_from is not None:
- # Use same global state
- self.set_global_state(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
- self.last_pos = create_from.last_pos
- self.last_marked_pos = create_from.last_marked_pos
- 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)
- return result
- def set_global_state(self, global_state):
- assert self.globalstate is None # prevent overwriting once it's set
- self.globalstate = global_state
- self.code_config = global_state.code_config
- 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
- cython_lineno = self.last_marked_pos[1] if self.last_marked_pos else 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)
- 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
- @funccontext_property
- def label_counter(self): pass
- @funccontext_property
- def return_label(self): pass
- @funccontext_property
- def error_label(self): pass
- @funccontext_property
- def labels_used(self): pass
- @funccontext_property
- def continue_label(self): pass
- @funccontext_property
- def break_label(self): pass
- @funccontext_property
- def return_from_error_cleanup_label(self): pass
- @funccontext_property
- def yield_labels(self): pass
- # 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, *args): return self.funcstate.new_yield_label(*args)
- 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, scope=None):
- self.funcstate = FunctionState(self, scope=scope)
- 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, dedup_key=None):
- return self.globalstate.get_py_const(type, prefix, cleanup_level, dedup_key).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, target=None):
- return self.globalstate.get_cached_constants_writer(target)
- # code generation
- def putln(self, code="", safe=False):
- if self.last_pos and self.bol:
- self.emit_marker()
- if self.code_config.emit_linenums and self.last_marked_pos:
- source_desc, line, _ = self.last_marked_pos
- self.write('\n#line %s "%s"\n' % (line, source_desc.get_escaped_description()))
- if code:
- if safe:
- self.put_safe(code)
- else:
- self.put(code)
- self.write("\n")
- self.bol = 1
- def mark_pos(self, pos, trace=True):
- if pos is None:
- return
- if self.last_marked_pos and self.last_marked_pos[:2] == pos[:2]:
- return
- self.last_pos = (pos, trace)
- def emit_marker(self):
- pos, trace = self.last_pos
- self.last_marked_pos = pos
- self.last_pos = None
- self.write("\n")
- if self.code_config.emit_code_comments:
- self.indent()
- self.write("/* %s */\n" % self._build_marker(pos))
- if trace and self.funcstate and self.funcstate.can_trace and self.globalstate.directives['linetrace']:
- self.indent()
- self.write('__Pyx_TraceLine(%d,%d,%s)\n' % (
- pos[1], not self.funcstate.gil_owned, self.error_goto(pos)))
- def _build_marker(self, pos):
- source_desc, line, col = pos
- 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]
- return u'"%s":%d\n%s\n' % (source_desc.get_escaped_description(), line, u'\n'.join(lines))
- 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())
- with closing(Utils.open_new_file(tmp_path)) as f:
- f.write(code)
- shutil.move(tmp_path, path)
- code = '#include "%s"\n' % path
- self.put(code)
- def put(self, code):
- 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 ..Tempita import sub
- self.putln(sub(code, **context))
- def put_tempita(self, code, **context):
- from ..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 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:
- from . 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_generated_by(self):
- self.putln("/* Generated by Cython %s */" % Version.watermark)
- self.putln("")
- 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, nanny=True):
- if entry.type.is_pyobject:
- if nanny:
- self.putln("__Pyx_INCREF(%s);" % self.entry_as_pyobject(entry))
- else:
- self.putln("Py_INCREF(%s);" % self.entry_as_pyobject(entry))
- def put_var_xincref(self, entry):
- if entry.type.is_pyobject:
- self.putln("__Pyx_XINCREF(%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 = '__Pyx' if nanny else 'Py'
- X = 'X' if null_check else ''
- 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, nanny=True):
- if entry.type.is_pyobject:
- if nanny:
- self.putln("__Pyx_XDECREF(%s);" % self.entry_as_pyobject(entry))
- else:
- self.putln("Py_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):
- from . 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):
- from . 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, wrapper_code_writer=None):
- if entry.is_special or entry.name == '__getattribute__':
- if entry.name not in special_py_methods:
- 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
- method_flags = entry.signature.method_flags()
- if not method_flags:
- return
- if entry.is_special:
- from . import TypeSlots
- method_flags += [TypeSlots.method_coexist]
- func_ptr = wrapper_code_writer.put_pymethoddef_wrapper(entry) if wrapper_code_writer else entry.func_cname
- # Add required casts, but try not to shadow real warnings.
- cast = '__Pyx_PyCFunctionFast' if 'METH_FASTCALL' in method_flags else 'PyCFunction'
- if 'METH_KEYWORDS' in method_flags:
- cast += 'WithKeywords'
- if cast != 'PyCFunction':
- func_ptr = '(void*)(%s)%s' % (cast, func_ptr)
- self.putln(
- '{"%s", (PyCFunction)%s, %s, %s}%s' % (
- entry.name,
- func_ptr,
- "|".join(method_flags),
- entry.doc_cname if entry.doc else '0',
- term))
- def put_pymethoddef_wrapper(self, entry):
- func_cname = entry.func_cname
- if entry.is_special:
- method_flags = entry.signature.method_flags()
- if method_flags and 'METH_NOARGS' in method_flags:
- # Special NOARGS methods really take no arguments besides 'self', but PyCFunction expects one.
- func_cname = Naming.method_wrapper_prefix + func_cname
- self.putln("static PyObject *%s(PyObject *self, CYTHON_UNUSED PyObject *arg) {return %s(self);}" % (
- func_cname, entry.func_cname))
- return func_cname
- # 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"))
- if self.globalstate.directives['fast_gil']:
- self.globalstate.use_utility_code(UtilityCode.load_cached("FastGil", "ModuleSetupCode.c"))
- else:
- self.globalstate.use_utility_code(UtilityCode.load_cached("NoFastGil", "ModuleSetupCode.c"))
- self.putln("#ifdef WITH_THREAD")
- if not variable:
- variable = '__pyx_gilstate_save'
- if declare_gilstate:
- self.put("PyGILState_STATE ")
- self.putln("%s = __Pyx_PyGILState_Ensure();" % variable)
- self.putln("#endif")
- def put_release_ensured_gil(self, variable=None):
- """
- Releases the GIL, corresponds to `put_ensure_gil`.
- """
- if self.globalstate.directives['fast_gil']:
- self.globalstate.use_utility_code(UtilityCode.load_cached("FastGil", "ModuleSetupCode.c"))
- else:
- self.globalstate.use_utility_code(UtilityCode.load_cached("NoFastGil", "ModuleSetupCode.c"))
- if not variable:
- variable = '__pyx_gilstate_save'
- self.putln("#ifdef WITH_THREAD")
- self.putln("__Pyx_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`
- """
- if self.globalstate.directives['fast_gil']:
- self.globalstate.use_utility_code(UtilityCode.load_cached("FastGil", "ModuleSetupCode.c"))
- else:
- self.globalstate.use_utility_code(UtilityCode.load_cached("NoFastGil", "ModuleSetupCode.c"))
- self.putln("#ifdef WITH_THREAD")
- self.putln("__Pyx_FastGIL_Forget();")
- 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`."
- if self.globalstate.directives['fast_gil']:
- self.globalstate.use_utility_code(UtilityCode.load_cached("FastGil", "ModuleSetupCode.c"))
- else:
- self.globalstate.use_utility_code(UtilityCode.load_cached("NoFastGil", "ModuleSetupCode.c"))
- self.putln("#ifdef WITH_THREAD")
- self.putln("PyThreadState *_save;")
- self.putln("Py_UNBLOCK_THREADS")
- if variable:
- self.putln('%s = _save;' % variable)
- self.putln("__Pyx_FastGIL_Remember();")
- 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):
- # TODO this path is almost _never_ taken, yet this macro makes is slower!
- # return self.putln("if (unlikely(%s < 0)) %s" % (value, self.error_goto(pos)))
- return self.putln("if (%s < 0) %s" % (value, self.error_goto(pos)))
- def put_error_if_unbound(self, pos, entry, in_nogil_context=False):
- from . 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
- return "__PYX_MARK_ERR_POS(%s, %s)" % (
- self.lookup_filename(pos[0]),
- pos[1])
- def error_goto(self, pos, used=True):
- lbl = self.funcstate.error_label
- self.funcstate.use_label(lbl)
- if pos is None:
- return 'goto %s;' % lbl
- self.funcstate.should_declare_error_indicator = True
- if used:
- self.funcstate.uses_error_indicator = True
- return "__PYX_ERR(%s, %s, %s)" % (
- self.lookup_filename(pos[0]),
- pos[1],
- 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, include_cline=True):
- """
- Build a Python traceback for propagating exceptions.
- qualified_name should be the qualified name of the function.
- """
- format_tuple = (
- qualified_name,
- Naming.clineno_cname if include_cline else 0,
- 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, nogil=False):
- """
- 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,
- self.globalstate.directives['unraisable_tracebacks'],
- nogil,
- )
- self.funcstate.uses_error_indicator = True
- self.putln('__Pyx_WriteUnraisable("%s", %s, %s, %s, %d, %d);' % 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_frame_init(self, codeobj=None):
- if codeobj:
- self.putln('__Pyx_TraceFrameInit(%s)' % codeobj)
- def put_trace_call(self, name, pos, nogil=False):
- self.putln('__Pyx_TraceCall("%s", %s[%s], %s, %d, %s);' % (
- name, Naming.filetable_cname, self.lookup_filename(pos[0]), pos[1], nogil, self.error_goto(pos)))
- def put_trace_exception(self):
- self.putln("__Pyx_TraceException();")
- def put_trace_return(self, retvalue_cname, nogil=False):
- self.putln("__Pyx_TraceReturn(%s, %d);" % (retvalue_cname, nogil))
- 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 isinstance(result, bytes):
- 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 type not 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
|