# # 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 `_ #: 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