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
* ``@cython.nogil`` is supported as a C-function decorator in Python code.
(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
as fast as looking up C globals.
(Github issue #2313)
......@@ -114,6 +117,14 @@ Bugs fixed
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 NumPy tutorial was also rewritten to simplify the running example.
Contributed by Gabriel de Marmiesse. (Github issue #2245)
......
......@@ -139,7 +139,7 @@ def _populate_unbound(kwds, unbound_symbols, locals=None, globals=None):
def cython_inline(code, get_type=unsafe_type,
lib_dir=os.path.join(get_cython_cache_dir(), 'inline'),
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:
get_type = lambda x: 'object'
......@@ -171,6 +171,11 @@ def cython_inline(code, get_type=unsafe_type,
if not quiet:
# Parsing from strings not fully supported (e.g. cimports).
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 = []
for name, arg in list(kwds.items()):
if arg is cython_module:
......@@ -178,7 +183,7 @@ def cython_inline(code, get_type=unsafe_type,
del kwds[name]
arg_names = sorted(kwds)
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()
if module_name in sys.modules:
......@@ -238,7 +243,7 @@ def __invoke(%(params)s):
build_extension.extensions = cythonize(
[extension],
include_path=cython_include_dirs or ['.'],
compiler_directives=cython_compiler_directives or {},
compiler_directives=cython_compiler_directives,
quiet=quiet)
build_extension.build_temp = os.path.dirname(pyx_file)
build_extension.build_lib = lib_dir
......
......@@ -67,9 +67,10 @@ class Context(object):
# language_level int currently 2 or 3 for Python 2/3
cython_scope = None
language_level = None # warn when not set but default to Py2
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
# an infinite loop.
# Better code organization would fix it.
......@@ -91,7 +92,8 @@ class Context(object):
os.path.join(os.path.dirname(__file__), os.path.pardir, 'Includes')))
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
......@@ -548,9 +550,10 @@ class CompilationOptions(object):
', '.join(unknown_options))
raise ValueError(message)
directive_defaults = Options.get_directive_defaults()
directives = dict(options['compiler_directives']) # copy mutable field
# check for invalid directives
unknown_directives = set(directives) - set(Options.get_directive_defaults())
unknown_directives = set(directives) - set(directive_defaults)
if unknown_directives:
message = "got unknown compiler directive%s: %s" % (
's' if len(unknown_directives) > 1 else '',
......@@ -562,7 +565,9 @@ class CompilationOptions(object):
warnings.warn("C++ mode forced when in Pythran mode!")
options['cplus'] = True
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:
options['formal_grammar'] = directives['formal_grammar']
if options['cache'] is True:
......@@ -824,7 +829,7 @@ default_options = dict(
emit_linenums = False,
relative_path_in_code_position_comments = True,
c_line_in_traceback = True,
language_level = 2,
language_level = None, # warn but default to 2
formal_grammar = False,
gdb_debug = False,
compile_time_env = None,
......
......@@ -197,7 +197,7 @@ _directive_defaults = {
'autotestdict': True,
'autotestdict.cdef': 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.
'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).
......@@ -299,6 +299,7 @@ def normalise_encoding_name(option_name, encoding):
# Override types possibilities above, if needed
directive_types = {
'language_level': int, # values can be None/2/3, where None == 2+warning
'auto_pickle': bool,
'final' : bool, # final cdef classes and methods
'internal' : bool, # cdef class visibility in the module dict
......
......@@ -3662,6 +3662,17 @@ def p_module(s, pxd, full_module_name, ctx=Ctx):
directive_comments = p_compiler_directive_comments(s)
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)
if pxd:
level = 'module_pxd'
......
......@@ -147,6 +147,8 @@ class SourceDescriptor(object):
"""
A SourceDescriptor should be considered immutable.
"""
filename = None
_file_type = 'pyx'
_escaped_description = None
......@@ -274,8 +276,6 @@ class StringSourceDescriptor(SourceDescriptor):
Instances of this class can be used instead of a filenames if the
code originates from a string object.
"""
filename = None
def __init__(self, name, code):
self.name = name
#self.set_file_type_from_name(name)
......
......@@ -29,7 +29,8 @@ class StringParseContext(Main.Context):
include_directories = []
if compiler_directives is None:
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
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):
sys.stderr.write("Disabling forked testing to support XML test output\n")
options.fork = False
if WITH_CYTHON and options.language_level == 3:
sys.stderr.write("Using Cython language level 3.\n")
if WITH_CYTHON:
sys.stderr.write("Using Cython language level %d.\n" % options.language_level)
test_bugs = False
if options.tickets:
......
......@@ -156,8 +156,9 @@ def compile_cython_modules(profile=False, compile_more=False, cython_with_refnan
extensions[-1].sources[0] = pyx_source_file
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:
from Cython.Compiler.Options import get_directive_defaults
get_directive_defaults()['profile'] = True
sys.stderr.write("Enabled profiling for the Cython binary modules\n")
......
......@@ -76,7 +76,7 @@ def exec(code_string, l, g):
old_stderr = sys.stderr
try:
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:
sys.stderr = old_stderr
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