Commit 2bb984a4 authored by scoder's avatar scoder Committed by GitHub

Merge pull request #2608 from scoder/warn_missing_language_level

Warn when no "language_level" is specified.
parents e59d4dcf 286abf81
...@@ -26,6 +26,9 @@ Features added ...@@ -26,6 +26,9 @@ Features added
* ``@cython.nogil`` is supported as a C-function decorator in Python code. * ``@cython.nogil`` is supported as a C-function decorator in Python code.
(Github issue #2557) (Github issue #2557)
* ``cython.inline()`` supports a direct ``language_level`` keyword argument that
was previously only available via a directive.
* In CPython 3.6 and later, looking up globals in the module dict is almost * In CPython 3.6 and later, looking up globals in the module dict is almost
as fast as looking up C globals. as fast as looking up C globals.
(Github issue #2313) (Github issue #2313)
...@@ -114,6 +117,14 @@ Bugs fixed ...@@ -114,6 +117,14 @@ Bugs fixed
Other changes Other changes
------------- -------------
* Cython now emits a warning when no ``language_level`` (2 or 3) is set explicitly,
neither as a ``cythonize()`` option nor as a compiler directive. This is meant
to prepare the transition of the default language level from currently Py2
to Py3, since that is what most new users will expect these days. The next
major release is intended to make that change, so that it will parse all code
that does not request a specific language level as Python 3 code. The language
level 2 will continue to be supported for an indefinite time.
* The documentation was restructured, cleaned up and examples are now tested. * The documentation was restructured, cleaned up and examples are now tested.
The NumPy tutorial was also rewritten to simplify the running example. The NumPy tutorial was also rewritten to simplify the running example.
Contributed by Gabriel de Marmiesse. (Github issue #2245) Contributed by Gabriel de Marmiesse. (Github issue #2245)
......
...@@ -139,7 +139,7 @@ def _populate_unbound(kwds, unbound_symbols, locals=None, globals=None): ...@@ -139,7 +139,7 @@ def _populate_unbound(kwds, unbound_symbols, locals=None, globals=None):
def cython_inline(code, get_type=unsafe_type, def cython_inline(code, get_type=unsafe_type,
lib_dir=os.path.join(get_cython_cache_dir(), 'inline'), lib_dir=os.path.join(get_cython_cache_dir(), 'inline'),
cython_include_dirs=None, cython_compiler_directives=None, cython_include_dirs=None, cython_compiler_directives=None,
force=False, quiet=False, locals=None, globals=None, **kwds): force=False, quiet=False, locals=None, globals=None, language_level=None, **kwds):
if get_type is None: if get_type is None:
get_type = lambda x: 'object' get_type = lambda x: 'object'
...@@ -171,6 +171,11 @@ def cython_inline(code, get_type=unsafe_type, ...@@ -171,6 +171,11 @@ def cython_inline(code, get_type=unsafe_type,
if not quiet: if not quiet:
# Parsing from strings not fully supported (e.g. cimports). # Parsing from strings not fully supported (e.g. cimports).
print("Could not parse code as a string (to extract unbound symbols).") print("Could not parse code as a string (to extract unbound symbols).")
cython_compiler_directives = dict(cython_compiler_directives or {})
if language_level is not None:
cython_compiler_directives['language_level'] = language_level
cimports = [] cimports = []
for name, arg in list(kwds.items()): for name, arg in list(kwds.items()):
if arg is cython_module: if arg is cython_module:
...@@ -178,7 +183,7 @@ def cython_inline(code, get_type=unsafe_type, ...@@ -178,7 +183,7 @@ def cython_inline(code, get_type=unsafe_type,
del kwds[name] del kwds[name]
arg_names = sorted(kwds) arg_names = sorted(kwds)
arg_sigs = tuple([(get_type(kwds[arg], ctx), arg) for arg in arg_names]) arg_sigs = tuple([(get_type(kwds[arg], ctx), arg) for arg in arg_names])
key = orig_code, arg_sigs, sys.version_info, sys.executable, Cython.__version__ key = orig_code, arg_sigs, sys.version_info, sys.executable, language_level, Cython.__version__
module_name = "_cython_inline_" + hashlib.md5(_unicode(key).encode('utf-8')).hexdigest() module_name = "_cython_inline_" + hashlib.md5(_unicode(key).encode('utf-8')).hexdigest()
if module_name in sys.modules: if module_name in sys.modules:
...@@ -238,7 +243,7 @@ def __invoke(%(params)s): ...@@ -238,7 +243,7 @@ def __invoke(%(params)s):
build_extension.extensions = cythonize( build_extension.extensions = cythonize(
[extension], [extension],
include_path=cython_include_dirs or ['.'], include_path=cython_include_dirs or ['.'],
compiler_directives=cython_compiler_directives or {}, compiler_directives=cython_compiler_directives,
quiet=quiet) quiet=quiet)
build_extension.build_temp = os.path.dirname(pyx_file) build_extension.build_temp = os.path.dirname(pyx_file)
build_extension.build_lib = lib_dir build_extension.build_lib = lib_dir
......
...@@ -67,9 +67,10 @@ class Context(object): ...@@ -67,9 +67,10 @@ class Context(object):
# language_level int currently 2 or 3 for Python 2/3 # language_level int currently 2 or 3 for Python 2/3
cython_scope = None cython_scope = None
language_level = None # warn when not set but default to Py2
def __init__(self, include_directories, compiler_directives, cpp=False, def __init__(self, include_directories, compiler_directives, cpp=False,
language_level=2, options=None): language_level=None, options=None):
# cython_scope is a hack, set to False by subclasses, in order to break # cython_scope is a hack, set to False by subclasses, in order to break
# an infinite loop. # an infinite loop.
# Better code organization would fix it. # Better code organization would fix it.
...@@ -91,7 +92,8 @@ class Context(object): ...@@ -91,7 +92,8 @@ class Context(object):
os.path.join(os.path.dirname(__file__), os.path.pardir, 'Includes'))) os.path.join(os.path.dirname(__file__), os.path.pardir, 'Includes')))
self.include_directories = include_directories + [standard_include_path] self.include_directories = include_directories + [standard_include_path]
self.set_language_level(language_level) if language_level is not None:
self.set_language_level(language_level)
self.gdb_debug_outputwriter = None self.gdb_debug_outputwriter = None
...@@ -548,9 +550,10 @@ class CompilationOptions(object): ...@@ -548,9 +550,10 @@ class CompilationOptions(object):
', '.join(unknown_options)) ', '.join(unknown_options))
raise ValueError(message) raise ValueError(message)
directive_defaults = Options.get_directive_defaults()
directives = dict(options['compiler_directives']) # copy mutable field directives = dict(options['compiler_directives']) # copy mutable field
# check for invalid directives # check for invalid directives
unknown_directives = set(directives) - set(Options.get_directive_defaults()) unknown_directives = set(directives) - set(directive_defaults)
if unknown_directives: if unknown_directives:
message = "got unknown compiler directive%s: %s" % ( message = "got unknown compiler directive%s: %s" % (
's' if len(unknown_directives) > 1 else '', 's' if len(unknown_directives) > 1 else '',
...@@ -562,7 +565,9 @@ class CompilationOptions(object): ...@@ -562,7 +565,9 @@ class CompilationOptions(object):
warnings.warn("C++ mode forced when in Pythran mode!") warnings.warn("C++ mode forced when in Pythran mode!")
options['cplus'] = True options['cplus'] = True
if 'language_level' in directives and 'language_level' not in kw: if 'language_level' in directives and 'language_level' not in kw:
options['language_level'] = int(directives['language_level']) options['language_level'] = directives['language_level']
elif not options.get('language_level'):
options['language_level'] = directive_defaults.get('language_level')
if 'formal_grammar' in directives and 'formal_grammar' not in kw: if 'formal_grammar' in directives and 'formal_grammar' not in kw:
options['formal_grammar'] = directives['formal_grammar'] options['formal_grammar'] = directives['formal_grammar']
if options['cache'] is True: if options['cache'] is True:
...@@ -824,7 +829,7 @@ default_options = dict( ...@@ -824,7 +829,7 @@ default_options = dict(
emit_linenums = False, emit_linenums = False,
relative_path_in_code_position_comments = True, relative_path_in_code_position_comments = True,
c_line_in_traceback = True, c_line_in_traceback = True,
language_level = 2, language_level = None, # warn but default to 2
formal_grammar = False, formal_grammar = False,
gdb_debug = False, gdb_debug = False,
compile_time_env = None, compile_time_env = None,
......
...@@ -197,7 +197,7 @@ _directive_defaults = { ...@@ -197,7 +197,7 @@ _directive_defaults = {
'autotestdict': True, 'autotestdict': True,
'autotestdict.cdef': False, 'autotestdict.cdef': False,
'autotestdict.all': False, 'autotestdict.all': False,
'language_level': 2, 'language_level': None,
'fast_getattr': False, # Undocumented until we come up with a better way to handle this everywhere. '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 '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). 'preliminary_late_includes_cy28': False, # Temporary directive in 0.28, to be removed in a later version (see GH#2079).
...@@ -299,6 +299,7 @@ def normalise_encoding_name(option_name, encoding): ...@@ -299,6 +299,7 @@ def normalise_encoding_name(option_name, encoding):
# Override types possibilities above, if needed # Override types possibilities above, if needed
directive_types = { directive_types = {
'language_level': int, # values can be None/2/3, where None == 2+warning
'auto_pickle': bool, 'auto_pickle': bool,
'final' : bool, # final cdef classes and methods 'final' : bool, # final cdef classes and methods
'internal' : bool, # cdef class visibility in the module dict 'internal' : bool, # cdef class visibility in the module dict
......
...@@ -3662,6 +3662,17 @@ def p_module(s, pxd, full_module_name, ctx=Ctx): ...@@ -3662,6 +3662,17 @@ def p_module(s, pxd, full_module_name, ctx=Ctx):
directive_comments = p_compiler_directive_comments(s) directive_comments = p_compiler_directive_comments(s)
s.parse_comments = False s.parse_comments = False
if s.context.language_level is None:
s.context.set_language_level(2)
if pos[0].filename:
import warnings
warnings.warn(
"Cython directive 'language_level' not set, using 2 for now (Py2). "
"This will change in a later release! File: %s" % pos[0].filename,
FutureWarning,
stacklevel=1 if cython.compiled else 2,
)
doc = p_doc_string(s) doc = p_doc_string(s)
if pxd: if pxd:
level = 'module_pxd' level = 'module_pxd'
......
...@@ -147,6 +147,8 @@ class SourceDescriptor(object): ...@@ -147,6 +147,8 @@ class SourceDescriptor(object):
""" """
A SourceDescriptor should be considered immutable. A SourceDescriptor should be considered immutable.
""" """
filename = None
_file_type = 'pyx' _file_type = 'pyx'
_escaped_description = None _escaped_description = None
...@@ -274,8 +276,6 @@ class StringSourceDescriptor(SourceDescriptor): ...@@ -274,8 +276,6 @@ class StringSourceDescriptor(SourceDescriptor):
Instances of this class can be used instead of a filenames if the Instances of this class can be used instead of a filenames if the
code originates from a string object. code originates from a string object.
""" """
filename = None
def __init__(self, name, code): def __init__(self, name, code):
self.name = name self.name = name
#self.set_file_type_from_name(name) #self.set_file_type_from_name(name)
......
...@@ -29,7 +29,8 @@ class StringParseContext(Main.Context): ...@@ -29,7 +29,8 @@ class StringParseContext(Main.Context):
include_directories = [] include_directories = []
if compiler_directives is None: if compiler_directives is None:
compiler_directives = {} compiler_directives = {}
Main.Context.__init__(self, include_directories, compiler_directives, cpp=cpp) # TODO: see if "language_level=3" also works for our internal code here.
Main.Context.__init__(self, include_directories, compiler_directives, cpp=cpp, language_level=2)
self.module_name = name self.module_name = name
def find_module(self, module_name, relative_to=None, pos=None, need_pxd=1, absolute_fallback=True): def find_module(self, module_name, relative_to=None, pos=None, need_pxd=1, absolute_fallback=True):
......
...@@ -2295,8 +2295,8 @@ def runtests(options, cmd_args, coverage=None): ...@@ -2295,8 +2295,8 @@ def runtests(options, cmd_args, coverage=None):
sys.stderr.write("Disabling forked testing to support XML test output\n") sys.stderr.write("Disabling forked testing to support XML test output\n")
options.fork = False options.fork = False
if WITH_CYTHON and options.language_level == 3: if WITH_CYTHON:
sys.stderr.write("Using Cython language level 3.\n") sys.stderr.write("Using Cython language level %d.\n" % options.language_level)
test_bugs = False test_bugs = False
if options.tickets: if options.tickets:
......
...@@ -156,8 +156,9 @@ def compile_cython_modules(profile=False, compile_more=False, cython_with_refnan ...@@ -156,8 +156,9 @@ def compile_cython_modules(profile=False, compile_more=False, cython_with_refnan
extensions[-1].sources[0] = pyx_source_file extensions[-1].sources[0] = pyx_source_file
from Cython.Distutils.build_ext import new_build_ext from Cython.Distutils.build_ext import new_build_ext
from Cython.Compiler.Options import get_directive_defaults
get_directive_defaults()['language_level'] = 2
if profile: if profile:
from Cython.Compiler.Options import get_directive_defaults
get_directive_defaults()['profile'] = True get_directive_defaults()['profile'] = True
sys.stderr.write("Enabled profiling for the Cython binary modules\n") sys.stderr.write("Enabled profiling for the Cython binary modules\n")
......
...@@ -76,7 +76,7 @@ def exec(code_string, l, g): ...@@ -76,7 +76,7 @@ def exec(code_string, l, g):
old_stderr = sys.stderr old_stderr = sys.stderr
try: try:
sys.stderr = StringIO() sys.stderr = StringIO()
ns = inline(code_string, locals=l, globals=g, lib_dir=os.path.dirname(__file__)) ns = inline(code_string, locals=l, globals=g, lib_dir=os.path.dirname(__file__), language_level=3)
finally: finally:
sys.stderr = old_stderr sys.stderr = old_stderr
g.update(ns) g.update(ns)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment