Skip to content

[3.8] bpo-39562: Prevent collision of future and compiler flags (GH-19230) #19835

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Doc/whatsnew/3.8.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2234,3 +2234,8 @@ Notable changes in Python 3.8.2
Fixed a regression with the ``ignore`` callback of :func:`shutil.copytree`.
The argument types are now str and List[str] again.
(Contributed by Manuel Barkhau and Giampaolo Rodola in :issue:`39390`.)

The constant values of future flags in the :mod:`__future__` module
are updated in order to prevent collision with compiler flags. Previously
``PyCF_ALLOW_TOP_LEVEL_AWAIT`` was clashing with ``CO_FUTURE_DIVISION``.
(Contributed by Batuhan Taskaya in :issue:`39562`)
26 changes: 13 additions & 13 deletions Include/code.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,19 +88,19 @@ typedef struct {
#define CO_ITERABLE_COROUTINE 0x0100
#define CO_ASYNC_GENERATOR 0x0200

/* These are no longer used. */
#if 0
#define CO_GENERATOR_ALLOWED 0x1000
#endif
#define CO_FUTURE_DIVISION 0x2000
#define CO_FUTURE_ABSOLUTE_IMPORT 0x4000 /* do absolute imports by default */
#define CO_FUTURE_WITH_STATEMENT 0x8000
#define CO_FUTURE_PRINT_FUNCTION 0x10000
#define CO_FUTURE_UNICODE_LITERALS 0x20000

#define CO_FUTURE_BARRY_AS_BDFL 0x40000
#define CO_FUTURE_GENERATOR_STOP 0x80000
#define CO_FUTURE_ANNOTATIONS 0x100000
/* bpo-39562: These constant values are changed in Python 3.9
to prevent collision with compiler flags. CO_FUTURE_ and PyCF_
constants must be kept unique. PyCF_ constants can use bits from
0x0100 to 0x10000. CO_FUTURE_ constants use bits starting at 0x20000. */
#define CO_FUTURE_DIVISION 0x20000
#define CO_FUTURE_ABSOLUTE_IMPORT 0x40000 /* do absolute imports by default */
#define CO_FUTURE_WITH_STATEMENT 0x80000
#define CO_FUTURE_PRINT_FUNCTION 0x100000
#define CO_FUTURE_UNICODE_LITERALS 0x200000

#define CO_FUTURE_BARRY_AS_BDFL 0x400000
#define CO_FUTURE_GENERATOR_STOP 0x800000
#define CO_FUTURE_ANNOTATIONS 0x1000000

/* This value is found in the co_cell2arg array when the associated cell
variable does not correspond to an argument. */
Expand Down
6 changes: 6 additions & 0 deletions Include/compile.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,18 @@ PyAPI_FUNC(PyCodeObject *) PyNode_Compile(struct _node *, const char *);
CO_FUTURE_UNICODE_LITERALS | CO_FUTURE_BARRY_AS_BDFL | \
CO_FUTURE_GENERATOR_STOP | CO_FUTURE_ANNOTATIONS)
#define PyCF_MASK_OBSOLETE (CO_NESTED)

/* bpo-39562: CO_FUTURE_ and PyCF_ constants must be kept unique.
PyCF_ constants can use bits from 0x0100 to 0x10000.
CO_FUTURE_ constants use bits starting at 0x20000. */
#define PyCF_SOURCE_IS_UTF8 0x0100
#define PyCF_DONT_IMPLY_DEDENT 0x0200
#define PyCF_ONLY_AST 0x0400
#define PyCF_IGNORE_COOKIE 0x0800
#define PyCF_TYPE_COMMENTS 0x1000
#define PyCF_ALLOW_TOP_LEVEL_AWAIT 0x2000
#define PyCF_COMPILE_MASK (PyCF_ONLY_AST | PyCF_ALLOW_TOP_LEVEL_AWAIT | \
PyCF_TYPE_COMMENTS | PyCF_DONT_IMPLY_DEDENT)

#ifndef Py_LIMITED_API
typedef struct {
Expand Down
16 changes: 8 additions & 8 deletions Lib/__future__.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,14 @@
# this module.
CO_NESTED = 0x0010 # nested_scopes
CO_GENERATOR_ALLOWED = 0 # generators (obsolete, was 0x1000)
CO_FUTURE_DIVISION = 0x2000 # division
CO_FUTURE_ABSOLUTE_IMPORT = 0x4000 # perform absolute imports by default
CO_FUTURE_WITH_STATEMENT = 0x8000 # with statement
CO_FUTURE_PRINT_FUNCTION = 0x10000 # print function
CO_FUTURE_UNICODE_LITERALS = 0x20000 # unicode string literals
CO_FUTURE_BARRY_AS_BDFL = 0x40000
CO_FUTURE_GENERATOR_STOP = 0x80000 # StopIteration becomes RuntimeError in generators
CO_FUTURE_ANNOTATIONS = 0x100000 # annotations become strings at runtime
CO_FUTURE_DIVISION = 0x20000 # division
CO_FUTURE_ABSOLUTE_IMPORT = 0x40000 # perform absolute imports by default
CO_FUTURE_WITH_STATEMENT = 0x80000 # with statement
CO_FUTURE_PRINT_FUNCTION = 0x100000 # print function
CO_FUTURE_UNICODE_LITERALS = 0x200000 # unicode string literals
CO_FUTURE_BARRY_AS_BDFL = 0x400000
CO_FUTURE_GENERATOR_STOP = 0x800000 # StopIteration becomes RuntimeError in generators
CO_FUTURE_ANNOTATIONS = 0x1000000 # annotations become strings at runtime

class _Feature:
def __init__(self, optionalRelease, mandatoryRelease, compiler_flag):
Expand Down
17 changes: 17 additions & 0 deletions Lib/test/test_future.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Test various flavors of legal and illegal future statements

import __future__
import ast
import unittest
from test import support
from textwrap import dedent
Expand Down Expand Up @@ -74,6 +76,21 @@ def test_badfuture10(self):
from test import badsyntax_future10
self.check_syntax_error(cm.exception, "badsyntax_future10", 3)

def test_ensure_flags_dont_clash(self):
# bpo-39562: test that future flags and compiler flags doesn't clash

# obtain future flags (CO_FUTURE_***) from the __future__ module
flags = {
f"CO_FUTURE_{future.upper()}": getattr(__future__, future).compiler_flag
for future in __future__.all_feature_names
}
# obtain some of the exported compiler flags (PyCF_***) from the ast module
flags.update({
flag: getattr(ast, flag)
for flag in dir(ast) if flag.startswith("PyCF_")
})
self.assertCountEqual(set(flags.values()), flags.values())

def test_parserhack(self):
# test that the parser.c::future_hack function works as expected
# Note: although this test must pass, it's not testing the original
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
The constant values of future flags in the :mod:`__future__` module are
updated in order to prevent collision with compiler flags. Previously
``PyCF_ALLOW_TOP_LEVEL_AWAIT`` was clashing with ``CO_FUTURE_DIVISION``.
2 changes: 1 addition & 1 deletion Python/bltinmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -738,7 +738,7 @@ builtin_compile_impl(PyObject *module, PyObject *source, PyObject *filename,
}

if (flags &
~(PyCF_MASK | PyCF_MASK_OBSOLETE | PyCF_DONT_IMPLY_DEDENT | PyCF_ONLY_AST | PyCF_TYPE_COMMENTS))
~(PyCF_MASK | PyCF_MASK_OBSOLETE | PyCF_COMPILE_MASK))
{
PyErr_SetString(PyExc_ValueError,
"compile(): unrecognised flags");
Expand Down