Skip to content

bpo-39562: Prevent collision of future and compiler flags #19230

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 10 commits into from
Apr 22, 2020
4 changes: 4 additions & 0 deletions Doc/whatsnew/3.9.rst
Original file line number Diff line number Diff line change
Expand Up @@ -832,6 +832,10 @@ Changes in the Python API
inherit from it should have this method defined.
(Contributed by Kyle Stanley in :issue:`34037`.)

* The constant values of future flags in the :mod:`__future__` module
is 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`)

CPython bytecode changes
------------------------
Expand Down
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How many bits are free for incoming compiler flags? Two? 0x4000 and 0x8000?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and 0x10000 (now that we removed that old macro)

#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 @@ -75,6 +77,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 |= {
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
2 changes: 1 addition & 1 deletion Python/bltinmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -739,7 +739,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