Skip to content

Commit 4454057

Browse files
authored
bpo-39562: Prevent collision of future and compiler flags (GH-19230)
The constant values of future flags in the __future__ module is updated in order to prevent collision with compiler flags. Previously PyCF_ALLOW_TOP_LEVEL_AWAIT was clashing with CO_FUTURE_DIVISION.
1 parent 9b49893 commit 4454057

File tree

6 files changed

+49
-22
lines changed

6 files changed

+49
-22
lines changed

Doc/whatsnew/3.9.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -832,6 +832,10 @@ Changes in the Python API
832832
inherit from it should have this method defined.
833833
(Contributed by Kyle Stanley in :issue:`34037`.)
834834

835+
* The constant values of future flags in the :mod:`__future__` module
836+
is updated in order to prevent collision with compiler flags. Previously
837+
``PyCF_ALLOW_TOP_LEVEL_AWAIT`` was clashing with ``CO_FUTURE_DIVISION``.
838+
(Contributed by Batuhan Taskaya in :issue:`39562`)
835839

836840
CPython bytecode changes
837841
------------------------

Include/code.h

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -88,19 +88,19 @@ typedef struct {
8888
#define CO_ITERABLE_COROUTINE 0x0100
8989
#define CO_ASYNC_GENERATOR 0x0200
9090

91-
/* These are no longer used. */
92-
#if 0
93-
#define CO_GENERATOR_ALLOWED 0x1000
94-
#endif
95-
#define CO_FUTURE_DIVISION 0x2000
96-
#define CO_FUTURE_ABSOLUTE_IMPORT 0x4000 /* do absolute imports by default */
97-
#define CO_FUTURE_WITH_STATEMENT 0x8000
98-
#define CO_FUTURE_PRINT_FUNCTION 0x10000
99-
#define CO_FUTURE_UNICODE_LITERALS 0x20000
100-
101-
#define CO_FUTURE_BARRY_AS_BDFL 0x40000
102-
#define CO_FUTURE_GENERATOR_STOP 0x80000
103-
#define CO_FUTURE_ANNOTATIONS 0x100000
91+
/* bpo-39562: These constant values are changed in Python 3.9
92+
to prevent collision with compiler flags. CO_FUTURE_ and PyCF_
93+
constants must be kept unique. PyCF_ constants can use bits from
94+
0x0100 to 0x10000. CO_FUTURE_ constants use bits starting at 0x20000. */
95+
#define CO_FUTURE_DIVISION 0x20000
96+
#define CO_FUTURE_ABSOLUTE_IMPORT 0x40000 /* do absolute imports by default */
97+
#define CO_FUTURE_WITH_STATEMENT 0x80000
98+
#define CO_FUTURE_PRINT_FUNCTION 0x100000
99+
#define CO_FUTURE_UNICODE_LITERALS 0x200000
100+
101+
#define CO_FUTURE_BARRY_AS_BDFL 0x400000
102+
#define CO_FUTURE_GENERATOR_STOP 0x800000
103+
#define CO_FUTURE_ANNOTATIONS 0x1000000
104104

105105
/* This value is found in the co_cell2arg array when the associated cell
106106
variable does not correspond to an argument. */

Include/compile.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,18 @@ PyAPI_FUNC(PyCodeObject *) PyNode_Compile(struct _node *, const char *);
1818
CO_FUTURE_UNICODE_LITERALS | CO_FUTURE_BARRY_AS_BDFL | \
1919
CO_FUTURE_GENERATOR_STOP | CO_FUTURE_ANNOTATIONS)
2020
#define PyCF_MASK_OBSOLETE (CO_NESTED)
21+
22+
/* bpo-39562: CO_FUTURE_ and PyCF_ constants must be kept unique.
23+
PyCF_ constants can use bits from 0x0100 to 0x10000.
24+
CO_FUTURE_ constants use bits starting at 0x20000. */
2125
#define PyCF_SOURCE_IS_UTF8 0x0100
2226
#define PyCF_DONT_IMPLY_DEDENT 0x0200
2327
#define PyCF_ONLY_AST 0x0400
2428
#define PyCF_IGNORE_COOKIE 0x0800
2529
#define PyCF_TYPE_COMMENTS 0x1000
2630
#define PyCF_ALLOW_TOP_LEVEL_AWAIT 0x2000
31+
#define PyCF_COMPILE_MASK (PyCF_ONLY_AST | PyCF_ALLOW_TOP_LEVEL_AWAIT | \
32+
PyCF_TYPE_COMMENTS | PyCF_DONT_IMPLY_DEDENT)
2733

2834
#ifndef Py_LIMITED_API
2935
typedef struct {

Lib/__future__.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -68,14 +68,14 @@
6868
# this module.
6969
CO_NESTED = 0x0010 # nested_scopes
7070
CO_GENERATOR_ALLOWED = 0 # generators (obsolete, was 0x1000)
71-
CO_FUTURE_DIVISION = 0x2000 # division
72-
CO_FUTURE_ABSOLUTE_IMPORT = 0x4000 # perform absolute imports by default
73-
CO_FUTURE_WITH_STATEMENT = 0x8000 # with statement
74-
CO_FUTURE_PRINT_FUNCTION = 0x10000 # print function
75-
CO_FUTURE_UNICODE_LITERALS = 0x20000 # unicode string literals
76-
CO_FUTURE_BARRY_AS_BDFL = 0x40000
77-
CO_FUTURE_GENERATOR_STOP = 0x80000 # StopIteration becomes RuntimeError in generators
78-
CO_FUTURE_ANNOTATIONS = 0x100000 # annotations become strings at runtime
71+
CO_FUTURE_DIVISION = 0x20000 # division
72+
CO_FUTURE_ABSOLUTE_IMPORT = 0x40000 # perform absolute imports by default
73+
CO_FUTURE_WITH_STATEMENT = 0x80000 # with statement
74+
CO_FUTURE_PRINT_FUNCTION = 0x100000 # print function
75+
CO_FUTURE_UNICODE_LITERALS = 0x200000 # unicode string literals
76+
CO_FUTURE_BARRY_AS_BDFL = 0x400000
77+
CO_FUTURE_GENERATOR_STOP = 0x800000 # StopIteration becomes RuntimeError in generators
78+
CO_FUTURE_ANNOTATIONS = 0x1000000 # annotations become strings at runtime
7979

8080
class _Feature:
8181
def __init__(self, optionalRelease, mandatoryRelease, compiler_flag):

Lib/test/test_future.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Test various flavors of legal and illegal future statements
22

3+
import __future__
4+
import ast
35
import unittest
46
from test import support
57
from textwrap import dedent
@@ -75,6 +77,21 @@ def test_badfuture10(self):
7577
from test import badsyntax_future10
7678
self.check_syntax_error(cm.exception, "badsyntax_future10", 3)
7779

80+
def test_ensure_flags_dont_clash(self):
81+
# bpo-39562: test that future flags and compiler flags doesn't clash
82+
83+
# obtain future flags (CO_FUTURE_***) from the __future__ module
84+
flags = {
85+
f"CO_FUTURE_{future.upper()}": getattr(__future__, future).compiler_flag
86+
for future in __future__.all_feature_names
87+
}
88+
# obtain some of the exported compiler flags (PyCF_***) from the ast module
89+
flags |= {
90+
flag: getattr(ast, flag)
91+
for flag in dir(ast) if flag.startswith("PyCF_")
92+
}
93+
self.assertCountEqual(set(flags.values()), flags.values())
94+
7895
def test_parserhack(self):
7996
# test that the parser.c::future_hack function works as expected
8097
# Note: although this test must pass, it's not testing the original

Python/bltinmodule.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -739,7 +739,7 @@ builtin_compile_impl(PyObject *module, PyObject *source, PyObject *filename,
739739
}
740740

741741
if (flags &
742-
~(PyCF_MASK | PyCF_MASK_OBSOLETE | PyCF_DONT_IMPLY_DEDENT | PyCF_ONLY_AST | PyCF_TYPE_COMMENTS))
742+
~(PyCF_MASK | PyCF_MASK_OBSOLETE | PyCF_COMPILE_MASK))
743743
{
744744
PyErr_SetString(PyExc_ValueError,
745745
"compile(): unrecognised flags");

0 commit comments

Comments
 (0)