1#
2#  Cython - Compilation-wide options and pragma declarations
3#
4
5from __future__ import absolute_import
6
7
8class ShouldBeFromDirective(object):
9
10    known_directives = []
11
12    def __init__(self, options_name, directive_name=None, disallow=False):
13        self.options_name = options_name
14        self.directive_name = directive_name or options_name
15        self.disallow = disallow
16        self.known_directives.append(self)
17
18    def __nonzero__(self):
19        self._bad_access()
20
21    def __int__(self):
22        self._bad_access()
23
24    def _bad_access(self):
25        raise RuntimeError(repr(self))
26
27    def __repr__(self):
28        return (
29        "Illegal access of '%s' from Options module rather than directive '%s'"
30        % (self.options_name, self.directive_name))
31
32
33"""
34The members of this module are documented using autodata in
35Cython/docs/src/reference/compilation.rst.
36See http://www.sphinx-doc.org/en/master/ext/autodoc.html#directive-autoattribute
37for how autodata works.
38Descriptions of those members should start with a #:
39Donc forget to keep the docs in sync by removing and adding
40the members in both this file and the .rst file.
41"""
42
43#: Whether or not to include docstring in the Python extension. If False, the binary size
44#: will be smaller, but the ``__doc__`` attribute of any class or function will be an
45#: empty string.
46docstrings = True
47
48#: Embed the source code position in the docstrings of functions and classes.
49embed_pos_in_docstring = False
50
51#: Copy the original source code line by line into C code comments
52#: in the generated code file to help with understanding the output.
53#: This is also required for coverage analysis.
54emit_code_comments = True
55
56# undocumented
57pre_import = None
58
59#: Decref global variables in each module on exit for garbage collection.
60#: 0: None, 1+: interned objects, 2+: cdef globals, 3+: types objects
61#: Mostly for reducing noise in Valgrind as it typically executes at process exit
62#: (when all memory will be reclaimed anyways).
63#: Note that directly or indirectly executed cleanup code that makes use of global
64#: variables or types may no longer be safe when enabling the respective level since
65#: there is no guaranteed order in which the (reference counted) objects will
66#: be cleaned up.  The order can change due to live references and reference cycles.
67generate_cleanup_code = False
68
69#: Should tp_clear() set object fields to None instead of clearing them to NULL?
70clear_to_none = True
71
72#: Generate an annotated HTML version of the input source files for debugging and optimisation purposes.
73#: This has the same effect as the ``annotate`` argument in :func:`cythonize`.
74annotate = False
75
76# When annotating source files in HTML, include coverage information from
77# this file.
78annotate_coverage_xml = None
79
80#: This will abort the compilation on the first error occurred rather than trying
81#: to keep going and printing further error messages.
82fast_fail = False
83
84#: Turn all warnings into errors.
85warning_errors = False
86
87#: Make unknown names an error.  Python raises a NameError when
88#: encountering unknown names at runtime, whereas this option makes
89#: them a compile time error.  If you want full Python compatibility,
90#: you should disable this option and also 'cache_builtins'.
91error_on_unknown_names = True
92
93#: Make uninitialized local variable reference a compile time error.
94#: Python raises UnboundLocalError at runtime, whereas this option makes
95#: them a compile time error. Note that this option affects only variables
96#: of "python object" type.
97error_on_uninitialized = True
98
99#: This will convert statements of the form ``for i in range(...)``
100#: to ``for i from ...`` when ``i`` is a C integer type, and the direction
101#: (i.e. sign of step) can be determined.
102#: WARNING: This may change the semantics if the range causes assignment to
103#: i to overflow. Specifically, if this option is set, an error will be
104#: raised before the loop is entered, whereas without this option the loop
105#: will execute until an overflowing value is encountered.
106convert_range = True
107
108#: Perform lookups on builtin names only once, at module initialisation
109#: time.  This will prevent the module from getting imported if a
110#: builtin name that it uses cannot be found during initialisation.
111#: Default is True.
112#: Note that some legacy builtins are automatically remapped
113#: from their Python 2 names to their Python 3 names by Cython
114#: when building in Python 3.x,
115#: so that they do not get in the way even if this option is enabled.
116cache_builtins = True
117
118#: Generate branch prediction hints to speed up error handling etc.
119gcc_branch_hints = True
120
121#: Enable this to allow one to write ``your_module.foo = ...`` to overwrite the
122#: definition if the cpdef function foo, at the cost of an extra dictionary
123#: lookup on every call.
124#: If this is false it generates only the Python wrapper and no override check.
125lookup_module_cpdef = False
126
127#: Whether or not to embed the Python interpreter, for use in making a
128#: standalone executable or calling from external libraries.
129#: This will provide a C function which initialises the interpreter and
130#: executes the body of this module.
131#: See `this demo <https://github.com/cython/cython/tree/master/Demos/embed>`_
132#: for a concrete example.
133#: If true, the initialisation function is the C main() function, but
134#: this option can also be set to a non-empty string to provide a function name explicitly.
135#: Default is False.
136embed = None
137
138# In previous iterations of Cython, globals() gave the first non-Cython module
139# globals in the call stack.  Sage relies on this behavior for variable injection.
140old_style_globals = ShouldBeFromDirective('old_style_globals')
141
142#: Allows cimporting from a pyx file without a pxd file.
143cimport_from_pyx = False
144
145#: Maximum number of dimensions for buffers -- set lower than number of
146#: dimensions in numpy, as
147#: slices are passed by value and involve a lot of copying.
148buffer_max_dims = 8
149
150#: Number of function closure instances to keep in a freelist (0: no freelists)
151closure_freelist_size = 8
152
153
154def get_directive_defaults():
155    # To add an item to this list, all accesses should be changed to use the new
156    # directive, and the global option itself should be set to an instance of
157    # ShouldBeFromDirective.
158    for old_option in ShouldBeFromDirective.known_directives:
159        value = globals().get(old_option.options_name)
160        assert old_option.directive_name in _directive_defaults
161        if not isinstance(value, ShouldBeFromDirective):
162            if old_option.disallow:
163                raise RuntimeError(
164                    "Option '%s' must be set from directive '%s'" % (
165                    old_option.option_name, old_option.directive_name))
166            else:
167                # Warn?
168                _directive_defaults[old_option.directive_name] = value
169    return _directive_defaults
170
171# Declare compiler directives
172_directive_defaults = {
173    'boundscheck' : True,
174    'nonecheck' : False,
175    'initializedcheck' : True,
176    'embedsignature' : False,
177    'auto_cpdef': False,
178    'auto_pickle': None,
179    'cdivision': False,  # was True before 0.12
180    'cdivision_warnings': False,
181    'c_api_binop_methods': True,
182    'overflowcheck': False,
183    'overflowcheck.fold': True,
184    'always_allow_keywords': False,
185    'allow_none_for_extension_args': True,
186    'wraparound' : True,
187    'ccomplex' : False,  # use C99/C++ for complex types and arith
188    'callspec' : "",
189    'nogil' : False,
190    'profile': False,
191    'linetrace': False,
192    'emit_code_comments': True,  # copy original source code into C code comments
193    'annotation_typing': True,  # read type declarations from Python function annotations
194    'infer_types': None,
195    'infer_types.verbose': False,
196    'autotestdict': True,
197    'autotestdict.cdef': False,
198    'autotestdict.all': False,
199    'language_level': None,
200    'fast_getattr': False,  # Undocumented until we come up with a better way to handle this everywhere.
201    'py2_import': False,  # For backward compatibility of Cython's source code in Py3 source mode
202    'preliminary_late_includes_cy28': False,  # Temporary directive in 0.28, to be removed in a later version (see GH#2079).
203    'iterable_coroutine': False,  # Make async coroutines backwards compatible with the old asyncio yield-from syntax.
204    'c_string_type': 'bytes',
205    'c_string_encoding': '',
206    'type_version_tag': True,  # enables Py_TPFLAGS_HAVE_VERSION_TAG on extension types
207    'unraisable_tracebacks': True,
208    'old_style_globals': False,
209    'np_pythran': False,
210    'fast_gil': False,
211
212    # set __file__ and/or __path__ to known source/target path at import time (instead of not having them available)
213    'set_initial_path' : None,  # SOURCEFILE or "/full/path/to/module"
214
215    'warn': None,
216    'warn.undeclared': False,
217    'warn.unreachable': True,
218    'warn.maybe_uninitialized': False,
219    'warn.unused': False,
220    'warn.unused_arg': False,
221    'warn.unused_result': False,
222    'warn.multiple_declarators': True,
223
224# optimizations
225    'optimize.inline_defnode_calls': True,
226    'optimize.unpack_method_calls': True,  # increases code size when True
227    'optimize.unpack_method_calls_in_pyinit': False,  # uselessly increases code size when True
228    'optimize.use_switch': True,
229
230# remove unreachable code
231    'remove_unreachable': True,
232
233# control flow debug directives
234    'control_flow.dot_output': "",  # Graphviz output filename
235    'control_flow.dot_annotate_defs': False,  # Annotate definitions
236
237# test support
238    'test_assert_path_exists' : [],
239    'test_fail_if_path_exists' : [],
240
241# experimental, subject to change
242    'binding': None,
243
244    'formal_grammar': False,
245}
246
247# Extra warning directives
248extra_warnings = {
249    'warn.maybe_uninitialized': True,
250    'warn.unreachable': True,
251    'warn.unused': True,
252}
253
254def one_of(*args):
255    def validate(name, value):
256        if value not in args:
257            raise ValueError("%s directive must be one of %s, got '%s'" % (
258                name, args, value))
259        else:
260            return value
261    return validate
262
263
264def normalise_encoding_name(option_name, encoding):
265    """
266    >>> normalise_encoding_name('c_string_encoding', 'ascii')
267    'ascii'
268    >>> normalise_encoding_name('c_string_encoding', 'AsCIi')
269    'ascii'
270    >>> normalise_encoding_name('c_string_encoding', 'us-ascii')
271    'ascii'
272    >>> normalise_encoding_name('c_string_encoding', 'utF8')
273    'utf8'
274    >>> normalise_encoding_name('c_string_encoding', 'utF-8')
275    'utf8'
276    >>> normalise_encoding_name('c_string_encoding', 'deFAuLT')
277    'default'
278    >>> normalise_encoding_name('c_string_encoding', 'default')
279    'default'
280    >>> normalise_encoding_name('c_string_encoding', 'SeriousLyNoSuch--Encoding')
281    'SeriousLyNoSuch--Encoding'
282    """
283    if not encoding:
284        return ''
285    if encoding.lower() in ('default', 'ascii', 'utf8'):
286        return encoding.lower()
287    import codecs
288    try:
289        decoder = codecs.getdecoder(encoding)
290    except LookupError:
291        return encoding  # may exists at runtime ...
292    for name in ('ascii', 'utf8'):
293        if codecs.getdecoder(name) == decoder:
294            return name
295    return encoding
296
297
298# Override types possibilities above, if needed
299directive_types = {
300    'language_level': str,  # values can be None/2/3/'3str', where None == 2+warning
301    'auto_pickle': bool,
302    'locals': dict,
303    'final' : bool,  # final cdef classes and methods
304    'nogil' : bool,
305    'internal' : bool,  # cdef class visibility in the module dict
306    'infer_types' : bool,  # values can be True/None/False
307    'binding' : bool,
308    'cfunc' : None,  # decorators do not take directive value
309    'ccall' : None,
310    'inline' : None,
311    'staticmethod' : None,
312    'cclass' : None,
313    'no_gc_clear' : bool,
314    'no_gc' : bool,
315    'returns' : type,
316    'exceptval': type,  # actually (type, check=True/False), but has its own parser
317    'set_initial_path': str,
318    'freelist': int,
319    'c_string_type': one_of('bytes', 'bytearray', 'str', 'unicode'),
320    'c_string_encoding': normalise_encoding_name,
321}
322
323for key, val in _directive_defaults.items():
324    if key not in directive_types:
325        directive_types[key] = type(val)
326
327directive_scopes = {  # defaults to available everywhere
328    # 'module', 'function', 'class', 'with statement'
329    'auto_pickle': ('module', 'cclass'),
330    'final' : ('cclass', 'function'),
331    'nogil' : ('function', 'with statement'),
332    'inline' : ('function',),
333    'cfunc' : ('function', 'with statement'),
334    'ccall' : ('function', 'with statement'),
335    'returns' : ('function',),
336    'exceptval' : ('function',),
337    'locals' : ('function',),
338    'staticmethod' : ('function',),  # FIXME: analysis currently lacks more specific function scope
339    'no_gc_clear' : ('cclass',),
340    'no_gc' : ('cclass',),
341    'internal' : ('cclass',),
342    'cclass' : ('class', 'cclass', 'with statement'),
343    'autotestdict' : ('module',),
344    'autotestdict.all' : ('module',),
345    'autotestdict.cdef' : ('module',),
346    'set_initial_path' : ('module',),
347    'test_assert_path_exists' : ('function', 'class', 'cclass'),
348    'test_fail_if_path_exists' : ('function', 'class', 'cclass'),
349    'freelist': ('cclass',),
350    'emit_code_comments': ('module',),
351    'annotation_typing': ('module',),  # FIXME: analysis currently lacks more specific function scope
352    # Avoid scope-specific to/from_py_functions for c_string.
353    'c_string_type': ('module',),
354    'c_string_encoding': ('module',),
355    'type_version_tag': ('module', 'cclass'),
356    'language_level': ('module',),
357    # globals() could conceivably be controlled at a finer granularity,
358    # but that would complicate the implementation
359    'old_style_globals': ('module',),
360    'np_pythran': ('module',),
361    'fast_gil': ('module',),
362    'iterable_coroutine': ('module', 'function'),
363}
364
365
366def parse_directive_value(name, value, relaxed_bool=False):
367    """
368    Parses value as an option value for the given name and returns
369    the interpreted value. None is returned if the option does not exist.
370
371    >>> print(parse_directive_value('nonexisting', 'asdf asdfd'))
372    None
373    >>> parse_directive_value('boundscheck', 'True')
374    True
375    >>> parse_directive_value('boundscheck', 'true')
376    Traceback (most recent call last):
377       ...
378    ValueError: boundscheck directive must be set to True or False, got 'true'
379
380    >>> parse_directive_value('c_string_encoding', 'us-ascii')
381    'ascii'
382    >>> parse_directive_value('c_string_type', 'str')
383    'str'
384    >>> parse_directive_value('c_string_type', 'bytes')
385    'bytes'
386    >>> parse_directive_value('c_string_type', 'bytearray')
387    'bytearray'
388    >>> parse_directive_value('c_string_type', 'unicode')
389    'unicode'
390    >>> parse_directive_value('c_string_type', 'unnicode')
391    Traceback (most recent call last):
392    ValueError: c_string_type directive must be one of ('bytes', 'bytearray', 'str', 'unicode'), got 'unnicode'
393    """
394    type = directive_types.get(name)
395    if not type:
396        return None
397    orig_value = value
398    if type is bool:
399        value = str(value)
400        if value == 'True':
401            return True
402        if value == 'False':
403            return False
404        if relaxed_bool:
405            value = value.lower()
406            if value in ("true", "yes"):
407                return True
408            elif value in ("false", "no"):
409                return False
410        raise ValueError("%s directive must be set to True or False, got '%s'" % (
411            name, orig_value))
412    elif type is int:
413        try:
414            return int(value)
415        except ValueError:
416            raise ValueError("%s directive must be set to an integer, got '%s'" % (
417                name, orig_value))
418    elif type is str:
419        return str(value)
420    elif callable(type):
421        return type(name, value)
422    else:
423        assert False
424
425
426def parse_directive_list(s, relaxed_bool=False, ignore_unknown=False,
427                         current_settings=None):
428    """
429    Parses a comma-separated list of pragma options. Whitespace
430    is not considered.
431
432    >>> parse_directive_list('      ')
433    {}
434    >>> (parse_directive_list('boundscheck=True') ==
435    ... {'boundscheck': True})
436    True
437    >>> parse_directive_list('  asdf')
438    Traceback (most recent call last):
439       ...
440    ValueError: Expected "=" in option "asdf"
441    >>> parse_directive_list('boundscheck=hey')
442    Traceback (most recent call last):
443       ...
444    ValueError: boundscheck directive must be set to True or False, got 'hey'
445    >>> parse_directive_list('unknown=True')
446    Traceback (most recent call last):
447       ...
448    ValueError: Unknown option: "unknown"
449    >>> warnings = parse_directive_list('warn.all=True')
450    >>> len(warnings) > 1
451    True
452    >>> sum(warnings.values()) == len(warnings)  # all true.
453    True
454    """
455    if current_settings is None:
456        result = {}
457    else:
458        result = current_settings
459    for item in s.split(','):
460        item = item.strip()
461        if not item:
462            continue
463        if '=' not in item:
464            raise ValueError('Expected "=" in option "%s"' % item)
465        name, value = [s.strip() for s in item.strip().split('=', 1)]
466        if name not in _directive_defaults:
467            found = False
468            if name.endswith('.all'):
469                prefix = name[:-3]
470                for directive in _directive_defaults:
471                    if directive.startswith(prefix):
472                        found = True
473                        parsed_value = parse_directive_value(directive, value, relaxed_bool=relaxed_bool)
474                        result[directive] = parsed_value
475            if not found and not ignore_unknown:
476                raise ValueError('Unknown option: "%s"' % name)
477        else:
478            parsed_value = parse_directive_value(name, value, relaxed_bool=relaxed_bool)
479            result[name] = parsed_value
480    return result
481
482
483def parse_variable_value(value):
484    """
485    Parses value as an option value for the given name and returns
486    the interpreted value.
487
488    >>> parse_variable_value('True')
489    True
490    >>> parse_variable_value('true')
491    'true'
492    >>> parse_variable_value('us-ascii')
493    'us-ascii'
494    >>> parse_variable_value('str')
495    'str'
496    >>> parse_variable_value('123')
497    123
498    >>> parse_variable_value('1.23')
499    1.23
500
501    """
502    if value == "True":
503        return True
504    elif value == "False":
505        return False
506    elif value == "None":
507        return None
508    elif value.isdigit():
509        return int(value)
510    else:
511        try:
512            value = float(value)
513        except Exception:
514            # Not a float
515            pass
516        return value
517
518
519def parse_compile_time_env(s, current_settings=None):
520    """
521    Parses a comma-separated list of pragma options. Whitespace
522    is not considered.
523
524    >>> parse_compile_time_env('      ')
525    {}
526    >>> (parse_compile_time_env('HAVE_OPENMP=True') ==
527    ... {'HAVE_OPENMP': True})
528    True
529    >>> parse_compile_time_env('  asdf')
530    Traceback (most recent call last):
531       ...
532    ValueError: Expected "=" in option "asdf"
533    >>> parse_compile_time_env('NUM_THREADS=4') == {'NUM_THREADS': 4}
534    True
535    >>> parse_compile_time_env('unknown=anything') == {'unknown': 'anything'}
536    True
537    """
538    if current_settings is None:
539        result = {}
540    else:
541        result = current_settings
542    for item in s.split(','):
543        item = item.strip()
544        if not item:
545            continue
546        if '=' not in item:
547            raise ValueError('Expected "=" in option "%s"' % item)
548        name, value = [s.strip() for s in item.split('=', 1)]
549        result[name] = parse_variable_value(value)
550    return result
551