Spaces:
Build error
Build error
# | |
# Cython - Compilation-wide options and pragma declarations | |
# | |
from __future__ import absolute_import | |
class ShouldBeFromDirective(object): | |
known_directives = [] | |
def __init__(self, options_name, directive_name=None, disallow=False): | |
self.options_name = options_name | |
self.directive_name = directive_name or options_name | |
self.disallow = disallow | |
self.known_directives.append(self) | |
def __nonzero__(self): | |
self._bad_access() | |
def __int__(self): | |
self._bad_access() | |
def _bad_access(self): | |
raise RuntimeError(repr(self)) | |
def __repr__(self): | |
return ( | |
"Illegal access of '%s' from Options module rather than directive '%s'" | |
% (self.options_name, self.directive_name)) | |
""" | |
The members of this module are documented using autodata in | |
Cython/docs/src/reference/compilation.rst. | |
See http://www.sphinx-doc.org/en/master/ext/autodoc.html#directive-autoattribute | |
for how autodata works. | |
Descriptions of those members should start with a #: | |
Donc forget to keep the docs in sync by removing and adding | |
the members in both this file and the .rst file. | |
""" | |
#: Whether or not to include docstring in the Python extension. If False, the binary size | |
#: will be smaller, but the ``__doc__`` attribute of any class or function will be an | |
#: empty string. | |
docstrings = True | |
#: Embed the source code position in the docstrings of functions and classes. | |
embed_pos_in_docstring = False | |
#: Copy the original source code line by line into C code comments | |
#: in the generated code file to help with understanding the output. | |
#: This is also required for coverage analysis. | |
emit_code_comments = True | |
# undocumented | |
pre_import = None | |
#: Decref global variables in each module on exit for garbage collection. | |
#: 0: None, 1+: interned objects, 2+: cdef globals, 3+: types objects | |
#: Mostly for reducing noise in Valgrind as it typically executes at process exit | |
#: (when all memory will be reclaimed anyways). | |
#: Note that directly or indirectly executed cleanup code that makes use of global | |
#: variables or types may no longer be safe when enabling the respective level since | |
#: there is no guaranteed order in which the (reference counted) objects will | |
#: be cleaned up. The order can change due to live references and reference cycles. | |
generate_cleanup_code = False | |
#: Should tp_clear() set object fields to None instead of clearing them to NULL? | |
clear_to_none = True | |
#: Generate an annotated HTML version of the input source files for debugging and optimisation purposes. | |
#: This has the same effect as the ``annotate`` argument in :func:`cythonize`. | |
annotate = False | |
# When annotating source files in HTML, include coverage information from | |
# this file. | |
annotate_coverage_xml = None | |
#: This will abort the compilation on the first error occurred rather than trying | |
#: to keep going and printing further error messages. | |
fast_fail = False | |
#: Turn all warnings into errors. | |
warning_errors = False | |
#: Make unknown names an error. Python raises a NameError when | |
#: encountering unknown names at runtime, whereas this option makes | |
#: them a compile time error. If you want full Python compatibility, | |
#: you should disable this option and also 'cache_builtins'. | |
error_on_unknown_names = True | |
#: Make uninitialized local variable reference a compile time error. | |
#: Python raises UnboundLocalError at runtime, whereas this option makes | |
#: them a compile time error. Note that this option affects only variables | |
#: of "python object" type. | |
error_on_uninitialized = True | |
#: This will convert statements of the form ``for i in range(...)`` | |
#: to ``for i from ...`` when ``i`` is a C integer type, and the direction | |
#: (i.e. sign of step) can be determined. | |
#: WARNING: This may change the semantics if the range causes assignment to | |
#: i to overflow. Specifically, if this option is set, an error will be | |
#: raised before the loop is entered, whereas without this option the loop | |
#: will execute until an overflowing value is encountered. | |
convert_range = True | |
#: Perform lookups on builtin names only once, at module initialisation | |
#: time. This will prevent the module from getting imported if a | |
#: builtin name that it uses cannot be found during initialisation. | |
#: Default is True. | |
#: Note that some legacy builtins are automatically remapped | |
#: from their Python 2 names to their Python 3 names by Cython | |
#: when building in Python 3.x, | |
#: so that they do not get in the way even if this option is enabled. | |
cache_builtins = True | |
#: Generate branch prediction hints to speed up error handling etc. | |
gcc_branch_hints = True | |
#: Enable this to allow one to write ``your_module.foo = ...`` to overwrite the | |
#: definition if the cpdef function foo, at the cost of an extra dictionary | |
#: lookup on every call. | |
#: If this is false it generates only the Python wrapper and no override check. | |
lookup_module_cpdef = False | |
#: Whether or not to embed the Python interpreter, for use in making a | |
#: standalone executable or calling from external libraries. | |
#: This will provide a C function which initialises the interpreter and | |
#: executes the body of this module. | |
#: See `this demo <https://github.com/cython/cython/tree/master/Demos/embed>`_ | |
#: for a concrete example. | |
#: If true, the initialisation function is the C main() function, but | |
#: this option can also be set to a non-empty string to provide a function name explicitly. | |
#: Default is False. | |
embed = None | |
# In previous iterations of Cython, globals() gave the first non-Cython module | |
# globals in the call stack. Sage relies on this behavior for variable injection. | |
old_style_globals = ShouldBeFromDirective('old_style_globals') | |
#: Allows cimporting from a pyx file without a pxd file. | |
cimport_from_pyx = False | |
#: Maximum number of dimensions for buffers -- set lower than number of | |
#: dimensions in numpy, as | |
#: slices are passed by value and involve a lot of copying. | |
buffer_max_dims = 8 | |
#: Number of function closure instances to keep in a freelist (0: no freelists) | |
closure_freelist_size = 8 | |
def get_directive_defaults(): | |
# To add an item to this list, all accesses should be changed to use the new | |
# directive, and the global option itself should be set to an instance of | |
# ShouldBeFromDirective. | |
for old_option in ShouldBeFromDirective.known_directives: | |
value = globals().get(old_option.options_name) | |
assert old_option.directive_name in _directive_defaults | |
if not isinstance(value, ShouldBeFromDirective): | |
if old_option.disallow: | |
raise RuntimeError( | |
"Option '%s' must be set from directive '%s'" % ( | |
old_option.option_name, old_option.directive_name)) | |
else: | |
# Warn? | |
_directive_defaults[old_option.directive_name] = value | |
return _directive_defaults | |
# Declare compiler directives | |
_directive_defaults = { | |
'boundscheck' : True, | |
'nonecheck' : False, | |
'initializedcheck' : True, | |
'embedsignature' : False, | |
'auto_cpdef': False, | |
'auto_pickle': None, | |
'cdivision': False, # was True before 0.12 | |
'cdivision_warnings': False, | |
'c_api_binop_methods': True, | |
'overflowcheck': False, | |
'overflowcheck.fold': True, | |
'always_allow_keywords': False, | |
'allow_none_for_extension_args': True, | |
'wraparound' : True, | |
'ccomplex' : False, # use C99/C++ for complex types and arith | |
'callspec' : "", | |
'nogil' : False, | |
'profile': False, | |
'linetrace': False, | |
'emit_code_comments': True, # copy original source code into C code comments | |
'annotation_typing': True, # read type declarations from Python function annotations | |
'infer_types': None, | |
'infer_types.verbose': False, | |
'autotestdict': True, | |
'autotestdict.cdef': False, | |
'autotestdict.all': False, | |
'language_level': None, | |
'fast_getattr': False, # Undocumented until we come up with a better way to handle this everywhere. | |
'py2_import': False, # For backward compatibility of Cython's source code in Py3 source mode | |
'preliminary_late_includes_cy28': False, # Temporary directive in 0.28, to be removed in a later version (see GH#2079). | |
'iterable_coroutine': False, # Make async coroutines backwards compatible with the old asyncio yield-from syntax. | |
'c_string_type': 'bytes', | |
'c_string_encoding': '', | |
'type_version_tag': True, # enables Py_TPFLAGS_HAVE_VERSION_TAG on extension types | |
'unraisable_tracebacks': True, | |
'old_style_globals': False, | |
'np_pythran': False, | |
'fast_gil': False, | |
# set __file__ and/or __path__ to known source/target path at import time (instead of not having them available) | |
'set_initial_path' : None, # SOURCEFILE or "/full/path/to/module" | |
'warn': None, | |
'warn.undeclared': False, | |
'warn.unreachable': True, | |
'warn.maybe_uninitialized': False, | |
'warn.unused': False, | |
'warn.unused_arg': False, | |
'warn.unused_result': False, | |
'warn.multiple_declarators': True, | |
# optimizations | |
'optimize.inline_defnode_calls': True, | |
'optimize.unpack_method_calls': True, # increases code size when True | |
'optimize.unpack_method_calls_in_pyinit': False, # uselessly increases code size when True | |
'optimize.use_switch': True, | |
# remove unreachable code | |
'remove_unreachable': True, | |
# control flow debug directives | |
'control_flow.dot_output': "", # Graphviz output filename | |
'control_flow.dot_annotate_defs': False, # Annotate definitions | |
# test support | |
'test_assert_path_exists' : [], | |
'test_fail_if_path_exists' : [], | |
# experimental, subject to change | |
'binding': None, | |
'formal_grammar': False, | |
} | |
# Extra warning directives | |
extra_warnings = { | |
'warn.maybe_uninitialized': True, | |
'warn.unreachable': True, | |
'warn.unused': True, | |
} | |
def one_of(*args): | |
def validate(name, value): | |
if value not in args: | |
raise ValueError("%s directive must be one of %s, got '%s'" % ( | |
name, args, value)) | |
else: | |
return value | |
return validate | |
def normalise_encoding_name(option_name, encoding): | |
""" | |
>>> normalise_encoding_name('c_string_encoding', 'ascii') | |
'ascii' | |
>>> normalise_encoding_name('c_string_encoding', 'AsCIi') | |
'ascii' | |
>>> normalise_encoding_name('c_string_encoding', 'us-ascii') | |
'ascii' | |
>>> normalise_encoding_name('c_string_encoding', 'utF8') | |
'utf8' | |
>>> normalise_encoding_name('c_string_encoding', 'utF-8') | |
'utf8' | |
>>> normalise_encoding_name('c_string_encoding', 'deFAuLT') | |
'default' | |
>>> normalise_encoding_name('c_string_encoding', 'default') | |
'default' | |
>>> normalise_encoding_name('c_string_encoding', 'SeriousLyNoSuch--Encoding') | |
'SeriousLyNoSuch--Encoding' | |
""" | |
if not encoding: | |
return '' | |
if encoding.lower() in ('default', 'ascii', 'utf8'): | |
return encoding.lower() | |
import codecs | |
try: | |
decoder = codecs.getdecoder(encoding) | |
except LookupError: | |
return encoding # may exists at runtime ... | |
for name in ('ascii', 'utf8'): | |
if codecs.getdecoder(name) == decoder: | |
return name | |
return encoding | |
# Override types possibilities above, if needed | |
directive_types = { | |
'language_level': str, # values can be None/2/3/'3str', where None == 2+warning | |
'auto_pickle': bool, | |
'locals': dict, | |
'final' : bool, # final cdef classes and methods | |
'nogil' : bool, | |
'internal' : bool, # cdef class visibility in the module dict | |
'infer_types' : bool, # values can be True/None/False | |
'binding' : bool, | |
'cfunc' : None, # decorators do not take directive value | |
'ccall' : None, | |
'inline' : None, | |
'staticmethod' : None, | |
'cclass' : None, | |
'no_gc_clear' : bool, | |
'no_gc' : bool, | |
'returns' : type, | |
'exceptval': type, # actually (type, check=True/False), but has its own parser | |
'set_initial_path': str, | |
'freelist': int, | |
'c_string_type': one_of('bytes', 'bytearray', 'str', 'unicode'), | |
'c_string_encoding': normalise_encoding_name, | |
} | |
for key, val in _directive_defaults.items(): | |
if key not in directive_types: | |
directive_types[key] = type(val) | |
directive_scopes = { # defaults to available everywhere | |
# 'module', 'function', 'class', 'with statement' | |
'auto_pickle': ('module', 'cclass'), | |
'final' : ('cclass', 'function'), | |
'nogil' : ('function', 'with statement'), | |
'inline' : ('function',), | |
'cfunc' : ('function', 'with statement'), | |
'ccall' : ('function', 'with statement'), | |
'returns' : ('function',), | |
'exceptval' : ('function',), | |
'locals' : ('function',), | |
'staticmethod' : ('function',), # FIXME: analysis currently lacks more specific function scope | |
'no_gc_clear' : ('cclass',), | |
'no_gc' : ('cclass',), | |
'internal' : ('cclass',), | |
'cclass' : ('class', 'cclass', 'with statement'), | |
'autotestdict' : ('module',), | |
'autotestdict.all' : ('module',), | |
'autotestdict.cdef' : ('module',), | |
'set_initial_path' : ('module',), | |
'test_assert_path_exists' : ('function', 'class', 'cclass'), | |
'test_fail_if_path_exists' : ('function', 'class', 'cclass'), | |
'freelist': ('cclass',), | |
'emit_code_comments': ('module',), | |
'annotation_typing': ('module',), # FIXME: analysis currently lacks more specific function scope | |
# Avoid scope-specific to/from_py_functions for c_string. | |
'c_string_type': ('module',), | |
'c_string_encoding': ('module',), | |
'type_version_tag': ('module', 'cclass'), | |
'language_level': ('module',), | |
# globals() could conceivably be controlled at a finer granularity, | |
# but that would complicate the implementation | |
'old_style_globals': ('module',), | |
'np_pythran': ('module',), | |
'fast_gil': ('module',), | |
'iterable_coroutine': ('module', 'function'), | |
} | |
def parse_directive_value(name, value, relaxed_bool=False): | |
""" | |
Parses value as an option value for the given name and returns | |
the interpreted value. None is returned if the option does not exist. | |
>>> print(parse_directive_value('nonexisting', 'asdf asdfd')) | |
None | |
>>> parse_directive_value('boundscheck', 'True') | |
True | |
>>> parse_directive_value('boundscheck', 'true') | |
Traceback (most recent call last): | |
... | |
ValueError: boundscheck directive must be set to True or False, got 'true' | |
>>> parse_directive_value('c_string_encoding', 'us-ascii') | |
'ascii' | |
>>> parse_directive_value('c_string_type', 'str') | |
'str' | |
>>> parse_directive_value('c_string_type', 'bytes') | |
'bytes' | |
>>> parse_directive_value('c_string_type', 'bytearray') | |
'bytearray' | |
>>> parse_directive_value('c_string_type', 'unicode') | |
'unicode' | |
>>> parse_directive_value('c_string_type', 'unnicode') | |
Traceback (most recent call last): | |
ValueError: c_string_type directive must be one of ('bytes', 'bytearray', 'str', 'unicode'), got 'unnicode' | |
""" | |
type = directive_types.get(name) | |
if not type: | |
return None | |
orig_value = value | |
if type is bool: | |
value = str(value) | |
if value == 'True': | |
return True | |
if value == 'False': | |
return False | |
if relaxed_bool: | |
value = value.lower() | |
if value in ("true", "yes"): | |
return True | |
elif value in ("false", "no"): | |
return False | |
raise ValueError("%s directive must be set to True or False, got '%s'" % ( | |
name, orig_value)) | |
elif type is int: | |
try: | |
return int(value) | |
except ValueError: | |
raise ValueError("%s directive must be set to an integer, got '%s'" % ( | |
name, orig_value)) | |
elif type is str: | |
return str(value) | |
elif callable(type): | |
return type(name, value) | |
else: | |
assert False | |
def parse_directive_list(s, relaxed_bool=False, ignore_unknown=False, | |
current_settings=None): | |
""" | |
Parses a comma-separated list of pragma options. Whitespace | |
is not considered. | |
>>> parse_directive_list(' ') | |
{} | |
>>> (parse_directive_list('boundscheck=True') == | |
... {'boundscheck': True}) | |
True | |
>>> parse_directive_list(' asdf') | |
Traceback (most recent call last): | |
... | |
ValueError: Expected "=" in option "asdf" | |
>>> parse_directive_list('boundscheck=hey') | |
Traceback (most recent call last): | |
... | |
ValueError: boundscheck directive must be set to True or False, got 'hey' | |
>>> parse_directive_list('unknown=True') | |
Traceback (most recent call last): | |
... | |
ValueError: Unknown option: "unknown" | |
>>> warnings = parse_directive_list('warn.all=True') | |
>>> len(warnings) > 1 | |
True | |
>>> sum(warnings.values()) == len(warnings) # all true. | |
True | |
""" | |
if current_settings is None: | |
result = {} | |
else: | |
result = current_settings | |
for item in s.split(','): | |
item = item.strip() | |
if not item: | |
continue | |
if '=' not in item: | |
raise ValueError('Expected "=" in option "%s"' % item) | |
name, value = [s.strip() for s in item.strip().split('=', 1)] | |
if name not in _directive_defaults: | |
found = False | |
if name.endswith('.all'): | |
prefix = name[:-3] | |
for directive in _directive_defaults: | |
if directive.startswith(prefix): | |
found = True | |
parsed_value = parse_directive_value(directive, value, relaxed_bool=relaxed_bool) | |
result[directive] = parsed_value | |
if not found and not ignore_unknown: | |
raise ValueError('Unknown option: "%s"' % name) | |
else: | |
parsed_value = parse_directive_value(name, value, relaxed_bool=relaxed_bool) | |
result[name] = parsed_value | |
return result | |
def parse_variable_value(value): | |
""" | |
Parses value as an option value for the given name and returns | |
the interpreted value. | |
>>> parse_variable_value('True') | |
True | |
>>> parse_variable_value('true') | |
'true' | |
>>> parse_variable_value('us-ascii') | |
'us-ascii' | |
>>> parse_variable_value('str') | |
'str' | |
>>> parse_variable_value('123') | |
123 | |
>>> parse_variable_value('1.23') | |
1.23 | |
""" | |
if value == "True": | |
return True | |
elif value == "False": | |
return False | |
elif value == "None": | |
return None | |
elif value.isdigit(): | |
return int(value) | |
else: | |
try: | |
value = float(value) | |
except Exception: | |
# Not a float | |
pass | |
return value | |
def parse_compile_time_env(s, current_settings=None): | |
""" | |
Parses a comma-separated list of pragma options. Whitespace | |
is not considered. | |
>>> parse_compile_time_env(' ') | |
{} | |
>>> (parse_compile_time_env('HAVE_OPENMP=True') == | |
... {'HAVE_OPENMP': True}) | |
True | |
>>> parse_compile_time_env(' asdf') | |
Traceback (most recent call last): | |
... | |
ValueError: Expected "=" in option "asdf" | |
>>> parse_compile_time_env('NUM_THREADS=4') == {'NUM_THREADS': 4} | |
True | |
>>> parse_compile_time_env('unknown=anything') == {'unknown': 'anything'} | |
True | |
""" | |
if current_settings is None: | |
result = {} | |
else: | |
result = current_settings | |
for item in s.split(','): | |
item = item.strip() | |
if not item: | |
continue | |
if '=' not in item: | |
raise ValueError('Expected "=" in option "%s"' % item) | |
name, value = [s.strip() for s in item.split('=', 1)] | |
result[name] = parse_variable_value(value) | |
return result | |