pub(crate) use python::_ast::module_def;
mod pyast;
use crate::builtins::{PyInt, PyStr};
use crate::stdlib::_ast::module::{Mod, ModFunctionType, ModInteractive};
use crate::stdlib::_ast::node::BoxedSlice;
use crate::{
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyRefExact, PyResult,
TryFromObject, VirtualMachine,
builtins::PyIntRef,
builtins::{PyDict, PyModule, PyType, PyUtf8StrRef},
class::{PyClassImpl, StaticType},
compiler::{CompileError, ParseError},
convert::ToPyObject,
};
use node::Node;
use ruff_python_ast as ast;
use ruff_text_size::{Ranged, TextRange, TextSize};
use rustpython_compiler_core::{
LineIndex, OneIndexed, PositionEncoding, SourceFile, SourceFileBuilder, SourceLocation,
};
#[cfg(feature = "parser")]
use ruff_python_parser as parser;
#[cfg(feature = "codegen")]
use rustpython_codegen as codegen;
pub(crate) use python::_ast::NodeAst;
mod python;
mod repr;
mod validate;
mod argument;
mod basic;
mod constant;
mod elif_else_clause;
mod exception;
mod expression;
mod module;
mod node;
mod operator;
mod other;
mod parameter;
mod pattern;
mod statement;
mod string;
mod type_ignore;
mod type_parameters;
fn singleton_node_to_object(vm: &VirtualMachine, node_type: &'static Py<PyType>) -> PyObjectRef {
if let Some(instance) = node_type.get_attr(vm.ctx.intern_str("_instance")) {
return instance;
}
NodeAst
.into_ref_with_type(vm, node_type.to_owned())
.unwrap()
.into()
}
fn get_node_field(vm: &VirtualMachine, obj: &PyObject, field: &'static str, typ: &str) -> PyResult {
vm.get_attribute_opt(obj.to_owned(), field)?
.ok_or_else(|| vm.new_type_error(format!(r#"required field "{field}" missing from {typ}"#)))
}
fn get_node_field_opt(
vm: &VirtualMachine,
obj: &PyObject,
field: &'static str,
) -> PyResult<Option<PyObjectRef>> {
Ok(vm
.get_attribute_opt(obj.to_owned(), field)?
.filter(|obj| !vm.is_none(obj)))
}
fn get_int_field(
vm: &VirtualMachine,
obj: &PyObject,
field: &'static str,
typ: &str,
) -> PyResult<PyRefExact<PyInt>> {
get_node_field(vm, obj, field, typ)?
.downcast_exact(vm)
.map_err(|_| vm.new_type_error(format!(r#"field "{field}" must have integer type"#)))
}
struct PySourceRange {
start: PySourceLocation,
end: PySourceLocation,
}
pub struct PySourceLocation {
row: Row,
column: Column,
}
impl PySourceLocation {
const fn to_source_location(&self) -> SourceLocation {
SourceLocation {
line: self.row.get_one_indexed(),
character_offset: self.column.get_one_indexed(),
}
}
}
#[derive(Clone, Copy)]
struct Row(OneIndexed);
impl Row {
const fn get(self) -> usize {
self.0.get()
}
const fn get_one_indexed(self) -> OneIndexed {
self.0
}
}
#[derive(Clone, Copy)]
struct Column(TextSize);
impl Column {
const fn get(self) -> usize {
self.0.to_usize()
}
const fn get_one_indexed(self) -> OneIndexed {
OneIndexed::from_zero_indexed(self.get())
}
}
fn text_range_to_source_range(source_file: &SourceFile, text_range: TextRange) -> PySourceRange {
let index = LineIndex::from_source_text(source_file.clone().source_text());
let source = &source_file.source_text();
if source.is_empty() {
return PySourceRange {
start: PySourceLocation {
row: Row(OneIndexed::from_zero_indexed(0)),
column: Column(TextSize::new(0)),
},
end: PySourceLocation {
row: Row(OneIndexed::from_zero_indexed(0)),
column: Column(TextSize::new(0)),
},
};
}
let start_row = index.line_index(text_range.start());
let end_row = index.line_index(text_range.end());
let start_col = text_range.start() - index.line_start(start_row, source);
let end_col = text_range.end() - index.line_start(end_row, source);
PySourceRange {
start: PySourceLocation {
row: Row(start_row),
column: Column(start_col),
},
end: PySourceLocation {
row: Row(end_row),
column: Column(end_col),
},
}
}
fn get_opt_int_field(
vm: &VirtualMachine,
obj: &PyObject,
field: &'static str,
) -> PyResult<Option<PyRefExact<PyInt>>> {
match get_node_field_opt(vm, obj, field)? {
Some(val) => val
.downcast_exact(vm)
.map(Some)
.map_err(|_| vm.new_type_error(format!(r#"field "{field}" must have integer type"#))),
None => Ok(None),
}
}
fn range_from_object(
vm: &VirtualMachine,
source_file: &SourceFile,
object: PyObjectRef,
name: &str,
) -> PyResult<TextRange> {
let start_row = get_int_field(vm, &object, "lineno", name)?;
let start_column = get_int_field(vm, &object, "col_offset", name)?;
let end_row =
get_opt_int_field(vm, &object, "end_lineno")?.unwrap_or_else(|| start_row.clone());
let end_column =
get_opt_int_field(vm, &object, "end_col_offset")?.unwrap_or_else(|| start_column.clone());
let start_row_val: i32 = start_row.try_to_primitive(vm)?;
let end_row_val: i32 = end_row.try_to_primitive(vm)?;
let start_col_val: i32 = start_column.try_to_primitive(vm)?;
let end_col_val: i32 = end_column.try_to_primitive(vm)?;
if start_row_val > end_row_val {
return Err(vm.new_value_error(format!(
"AST node line range ({}, {}) is not valid",
start_row_val, end_row_val
)));
}
if (start_row_val < 0 && end_row_val != start_row_val)
|| (start_col_val < 0 && end_col_val != start_col_val)
{
return Err(vm.new_value_error(format!(
"AST node column range ({}, {}) for line range ({}, {}) is not valid",
start_col_val, end_col_val, start_row_val, end_row_val
)));
}
if start_row_val == end_row_val && start_col_val > end_col_val {
return Err(vm.new_value_error(format!(
"line {}, column {}-{} is not a valid range",
start_row_val, start_col_val, end_col_val
)));
}
let location = PySourceRange {
start: PySourceLocation {
row: Row(if start_row_val > 0 {
OneIndexed::new(start_row_val as usize).unwrap_or(OneIndexed::MIN)
} else {
OneIndexed::MIN
}),
column: Column(TextSize::new(start_col_val.max(0) as u32)),
},
end: PySourceLocation {
row: Row(if end_row_val > 0 {
OneIndexed::new(end_row_val as usize).unwrap_or(OneIndexed::MIN)
} else {
OneIndexed::MIN
}),
column: Column(TextSize::new(end_col_val.max(0) as u32)),
},
};
Ok(source_range_to_text_range(source_file, location))
}
fn source_range_to_text_range(source_file: &SourceFile, location: PySourceRange) -> TextRange {
let index = LineIndex::from_source_text(source_file.clone().source_text());
let source = &source_file.source_text();
if source.is_empty() {
return TextRange::new(TextSize::new(0), TextSize::new(0));
}
let start = index.offset(
location.start.to_source_location(),
source,
PositionEncoding::Utf8,
);
let end = index.offset(
location.end.to_source_location(),
source,
PositionEncoding::Utf8,
);
TextRange::new(start, end)
}
fn node_add_location(
dict: &Py<PyDict>,
range: TextRange,
vm: &VirtualMachine,
source_file: &SourceFile,
) {
let range = text_range_to_source_range(source_file, range);
dict.set_item("lineno", vm.ctx.new_int(range.start.row.get()).into(), vm)
.unwrap();
dict.set_item(
"col_offset",
vm.ctx.new_int(range.start.column.get()).into(),
vm,
)
.unwrap();
dict.set_item("end_lineno", vm.ctx.new_int(range.end.row.get()).into(), vm)
.unwrap();
dict.set_item(
"end_col_offset",
vm.ctx.new_int(range.end.column.get()).into(),
vm,
)
.unwrap();
}
pub(crate) fn mode_type_and_name(mode: &str) -> Option<(PyRef<PyType>, &'static str)> {
match mode {
"exec" => Some((pyast::NodeModModule::make_static_type(), "Module")),
"eval" => Some((pyast::NodeModExpression::make_static_type(), "Expression")),
"single" => Some((pyast::NodeModInteractive::make_static_type(), "Interactive")),
"func_type" => Some((
pyast::NodeModFunctionType::make_static_type(),
"FunctionType",
)),
_ => None,
}
}
fn empty_arguments_object(vm: &VirtualMachine) -> PyObjectRef {
let node = NodeAst
.into_ref_with_type(vm, pyast::NodeArguments::static_type().to_owned())
.unwrap();
let dict = node.as_object().dict().unwrap();
for list_field in [
"posonlyargs",
"args",
"kwonlyargs",
"kw_defaults",
"defaults",
] {
dict.set_item(list_field, vm.ctx.new_list(vec![]).into(), vm)
.unwrap();
}
for none_field in ["vararg", "kwarg"] {
dict.set_item(none_field, vm.ctx.none(), vm).unwrap();
}
node.into()
}
#[cfg(feature = "parser")]
pub(crate) fn parse(
vm: &VirtualMachine,
source: &str,
mode: parser::Mode,
optimize: u8,
target_version: Option<ast::PythonVersion>,
type_comments: bool,
) -> Result<PyObjectRef, CompileError> {
let source_file = SourceFileBuilder::new("".to_owned(), source.to_owned()).finish();
let mut options = parser::ParseOptions::from(mode);
let target_version = target_version.unwrap_or(ast::PythonVersion::PY314);
options = options.with_target_version(target_version);
let parsed = parser::parse(source, options).map_err(|parse_error| {
let range = text_range_to_source_range(&source_file, parse_error.location);
ParseError {
error: parse_error.error,
raw_location: parse_error.location,
location: range.start.to_source_location(),
end_location: range.end.to_source_location(),
source_path: "<unknown>".to_string(),
is_unclosed_bracket: false,
}
})?;
if let Some(error) = parsed.unsupported_syntax_errors().first() {
let range = text_range_to_source_range(&source_file, error.range());
return Err(ParseError {
error: parser::ParseErrorType::OtherError(error.to_string()),
raw_location: error.range(),
location: range.start.to_source_location(),
end_location: range.end.to_source_location(),
source_path: "<unknown>".to_string(),
is_unclosed_bracket: false,
}
.into());
}
let mut top = parsed.into_syntax();
if optimize > 0 {
fold_match_value_constants(&mut top);
}
if optimize >= 2 {
strip_docstrings(&mut top);
}
let top = match top {
ast::Mod::Module(m) => Mod::Module(m),
ast::Mod::Expression(e) => Mod::Expression(e),
};
let obj = top.ast_to_object(vm, &source_file);
if type_comments && obj.class().is(pyast::NodeModModule::static_type()) {
let type_ignores = type_ignores_from_source(vm, source)?;
let dict = obj.as_object().dict().unwrap();
dict.set_item("type_ignores", vm.ctx.new_list(type_ignores).into(), vm)
.unwrap();
}
Ok(obj)
}
#[cfg(feature = "parser")]
pub(crate) fn wrap_interactive(vm: &VirtualMachine, module_obj: PyObjectRef) -> PyResult {
if !module_obj.class().is(pyast::NodeModModule::static_type()) {
return Err(vm.new_type_error("expected Module node"));
}
let body = get_node_field(vm, &module_obj, "body", "Module")?;
let node = NodeAst
.into_ref_with_type(vm, pyast::NodeModInteractive::static_type().to_owned())
.unwrap();
let dict = node.as_object().dict().unwrap();
dict.set_item("body", body, vm).unwrap();
Ok(node.into())
}
#[cfg(feature = "parser")]
pub(crate) fn parse_func_type(
vm: &VirtualMachine,
source: &str,
optimize: u8,
target_version: Option<ast::PythonVersion>,
) -> Result<PyObjectRef, CompileError> {
let _ = optimize;
let _ = target_version;
let source = source.trim();
let mut depth = 0i32;
let mut split_at = None;
let mut chars = source.chars().peekable();
let mut idx = 0usize;
while let Some(ch) = chars.next() {
match ch {
'(' | '[' | '{' => depth += 1,
')' | ']' | '}' => depth -= 1,
'-' if depth == 0 && chars.peek() == Some(&'>') => {
split_at = Some(idx);
break;
}
_ => {}
}
idx += ch.len_utf8();
}
let Some(split_at) = split_at else {
return Err(ParseError {
error: parser::ParseErrorType::OtherError("invalid func_type".to_owned()),
raw_location: TextRange::default(),
location: SourceLocation::default(),
end_location: SourceLocation::default(),
source_path: "<unknown>".to_owned(),
is_unclosed_bracket: false,
}
.into());
};
let left = source[..split_at].trim();
let right = source[split_at + 2..].trim();
let parse_expr = |expr_src: &str| -> Result<ast::Expr, CompileError> {
let source_file = SourceFileBuilder::new("".to_owned(), expr_src.to_owned()).finish();
let parsed = parser::parse_expression(expr_src).map_err(|parse_error| {
let range = text_range_to_source_range(&source_file, parse_error.location);
ParseError {
error: parse_error.error,
raw_location: parse_error.location,
location: range.start.to_source_location(),
end_location: range.end.to_source_location(),
source_path: "<unknown>".to_string(),
is_unclosed_bracket: false,
}
})?;
Ok(*parsed.into_syntax().body)
};
let arg_expr = parse_expr(left)?;
let returns = parse_expr(right)?;
let argtypes: Vec<ast::Expr> = match arg_expr {
ast::Expr::Tuple(tup) => tup.elts,
ast::Expr::Name(_) | ast::Expr::Subscript(_) | ast::Expr::Attribute(_) => vec![arg_expr],
other => vec![other],
};
let func_type = ModFunctionType {
argtypes: argtypes.into_boxed_slice(),
returns,
range: TextRange::default(),
};
let source_file = SourceFileBuilder::new("".to_owned(), source.to_owned()).finish();
Ok(func_type.ast_to_object(vm, &source_file))
}
fn type_ignores_from_source(
vm: &VirtualMachine,
source: &str,
) -> Result<Vec<PyObjectRef>, CompileError> {
let mut ignores = Vec::new();
for (idx, line) in source.lines().enumerate() {
let Some(pos) = line.find("#") else {
continue;
};
let comment = &line[pos + 1..];
let comment = comment.trim_start();
let Some(rest) = comment.strip_prefix("type: ignore") else {
continue;
};
let tag = rest.trim_start();
let tag = if tag.is_empty() { "" } else { tag };
let node = NodeAst
.into_ref_with_type(
vm,
pyast::NodeTypeIgnoreTypeIgnore::static_type().to_owned(),
)
.unwrap();
let dict = node.as_object().dict().unwrap();
let lineno = idx + 1;
dict.set_item("lineno", vm.ctx.new_int(lineno).into(), vm)
.unwrap();
dict.set_item("tag", vm.ctx.new_str(tag).into(), vm)
.unwrap();
ignores.push(node.into());
}
Ok(ignores)
}
#[cfg(feature = "parser")]
fn fold_match_value_constants(top: &mut ast::Mod) {
match top {
ast::Mod::Module(module) => fold_stmts(&mut module.body),
ast::Mod::Expression(_expr) => {}
}
}
#[cfg(feature = "parser")]
fn strip_docstrings(top: &mut ast::Mod) {
match top {
ast::Mod::Module(module) => strip_docstring_in_body(&mut module.body),
ast::Mod::Expression(_expr) => {}
}
}
#[cfg(feature = "parser")]
fn strip_docstring_in_body(body: &mut Vec<ast::Stmt>) {
if let Some(range) = take_docstring(body)
&& body.is_empty()
{
let start_offset = range.start();
let end_offset = start_offset + TextSize::from(4);
let pass_range = TextRange::new(start_offset, end_offset);
body.push(ast::Stmt::Pass(ast::StmtPass {
node_index: Default::default(),
range: pass_range,
}));
}
for stmt in body {
match stmt {
ast::Stmt::FunctionDef(def) => strip_docstring_in_body(&mut def.body),
ast::Stmt::ClassDef(def) => strip_docstring_in_body(&mut def.body),
_ => {}
}
}
}
#[cfg(feature = "parser")]
fn take_docstring(body: &mut Vec<ast::Stmt>) -> Option<TextRange> {
let ast::Stmt::Expr(expr_stmt) = body.first()? else {
return None;
};
if matches!(expr_stmt.value.as_ref(), ast::Expr::StringLiteral(_)) {
let range = expr_stmt.range;
body.remove(0);
return Some(range);
}
None
}
#[cfg(feature = "parser")]
fn fold_stmts(stmts: &mut [ast::Stmt]) {
for stmt in stmts {
fold_stmt(stmt);
}
}
#[cfg(feature = "parser")]
fn fold_stmt(stmt: &mut ast::Stmt) {
use ast::Stmt;
match stmt {
Stmt::FunctionDef(def) => fold_stmts(&mut def.body),
Stmt::ClassDef(def) => fold_stmts(&mut def.body),
Stmt::For(stmt) => {
fold_stmts(&mut stmt.body);
fold_stmts(&mut stmt.orelse);
}
Stmt::While(stmt) => {
fold_stmts(&mut stmt.body);
fold_stmts(&mut stmt.orelse);
}
Stmt::If(stmt) => {
fold_stmts(&mut stmt.body);
for clause in &mut stmt.elif_else_clauses {
fold_stmts(&mut clause.body);
}
}
Stmt::With(stmt) => {
fold_stmts(&mut stmt.body);
}
Stmt::Try(stmt) => {
fold_stmts(&mut stmt.body);
fold_stmts(&mut stmt.orelse);
fold_stmts(&mut stmt.finalbody);
}
Stmt::Match(stmt) => {
for case in &mut stmt.cases {
fold_pattern(&mut case.pattern);
if let Some(expr) = case.guard.as_deref_mut() {
fold_expr(expr);
}
fold_stmts(&mut case.body);
}
}
_ => {}
}
}
#[cfg(feature = "parser")]
fn fold_pattern(pattern: &mut ast::Pattern) {
use ast::Pattern;
match pattern {
Pattern::MatchValue(value) => fold_expr(&mut value.value),
Pattern::MatchSequence(seq) => {
for pattern in &mut seq.patterns {
fold_pattern(pattern);
}
}
Pattern::MatchMapping(mapping) => {
for key in &mut mapping.keys {
fold_expr(key);
}
for pattern in &mut mapping.patterns {
fold_pattern(pattern);
}
}
Pattern::MatchClass(class) => {
for pattern in &mut class.arguments.patterns {
fold_pattern(pattern);
}
for keyword in &mut class.arguments.keywords {
fold_pattern(&mut keyword.pattern);
}
}
Pattern::MatchAs(match_as) => {
if let Some(pattern) = match_as.pattern.as_deref_mut() {
fold_pattern(pattern);
}
}
Pattern::MatchOr(match_or) => {
for pattern in &mut match_or.patterns {
fold_pattern(pattern);
}
}
Pattern::MatchSingleton(_) | Pattern::MatchStar(_) => {}
}
}
#[cfg(feature = "parser")]
fn fold_expr(expr: &mut ast::Expr) {
use ast::Expr;
if let Expr::UnaryOp(unary) = expr {
fold_expr(&mut unary.operand);
if matches!(unary.op, ast::UnaryOp::USub)
&& let Expr::NumberLiteral(number_literal) = unary.operand.as_ref()
{
let number = match &number_literal.value {
ast::Number::Int(value) => {
if *value == ast::Int::ZERO {
Some(ast::Number::Int(ast::Int::ZERO))
} else {
None
}
}
ast::Number::Float(value) => Some(ast::Number::Float(-value)),
ast::Number::Complex { real, imag } => Some(ast::Number::Complex {
real: -real,
imag: -imag,
}),
};
if let Some(number) = number {
*expr = Expr::NumberLiteral(ast::ExprNumberLiteral {
node_index: unary.node_index.clone(),
range: unary.range,
value: number,
});
return;
}
}
}
if let Expr::BinOp(binop) = expr {
fold_expr(&mut binop.left);
fold_expr(&mut binop.right);
let Expr::NumberLiteral(left) = binop.left.as_ref() else {
return;
};
let Expr::NumberLiteral(right) = binop.right.as_ref() else {
return;
};
if let Some(number) = fold_number_binop(&left.value, &binop.op, &right.value) {
*expr = Expr::NumberLiteral(ast::ExprNumberLiteral {
node_index: binop.node_index.clone(),
range: binop.range,
value: number,
});
}
}
}
#[cfg(feature = "parser")]
fn fold_number_binop(
left: &ast::Number,
op: &ast::Operator,
right: &ast::Number,
) -> Option<ast::Number> {
let (left_real, left_imag, left_is_complex) = number_to_complex(left)?;
let (right_real, right_imag, right_is_complex) = number_to_complex(right)?;
if !(left_is_complex || right_is_complex) {
return None;
}
match op {
ast::Operator::Add => Some(ast::Number::Complex {
real: left_real + right_real,
imag: left_imag + right_imag,
}),
ast::Operator::Sub => Some(ast::Number::Complex {
real: left_real - right_real,
imag: left_imag - right_imag,
}),
_ => None,
}
}
#[cfg(feature = "parser")]
fn number_to_complex(number: &ast::Number) -> Option<(f64, f64, bool)> {
match number {
ast::Number::Complex { real, imag } => Some((*real, *imag, true)),
ast::Number::Float(value) => Some((*value, 0.0, false)),
ast::Number::Int(value) => value.as_i64().map(|value| (value as f64, 0.0, false)),
}
}
#[cfg(feature = "codegen")]
pub(crate) fn compile(
vm: &VirtualMachine,
object: PyObjectRef,
filename: &str,
mode: crate::compiler::Mode,
optimize: Option<u8>,
) -> PyResult {
let mut opts = vm.compile_opts();
if let Some(optimize) = optimize {
opts.optimize = optimize;
}
let source_file = SourceFileBuilder::new(filename.to_owned(), "".to_owned()).finish();
let ast: Mod = Node::ast_from_object(vm, &source_file, object)?;
validate::validate_mod(vm, &ast)?;
let ast = match ast {
Mod::Module(m) => ast::Mod::Module(m),
Mod::Interactive(ModInteractive { range, body }) => ast::Mod::Module(ast::ModModule {
node_index: Default::default(),
range,
body,
}),
Mod::Expression(e) => ast::Mod::Expression(e),
Mod::FunctionType(_) => todo!(),
};
let text = "";
let source_file = SourceFileBuilder::new(filename, text).finish();
let code = codegen::compile::compile_top(ast, source_file, mode, opts)
.map_err(|err| vm.new_syntax_error(&err.into(), None))?; Ok(vm.ctx.new_code(code).into())
}
#[cfg(feature = "codegen")]
pub(crate) fn validate_ast_object(vm: &VirtualMachine, object: PyObjectRef) -> PyResult<()> {
let source_file = SourceFileBuilder::new("<ast>".to_owned(), "".to_owned()).finish();
let ast: Mod = Node::ast_from_object(vm, &source_file, object)?;
validate::validate_mod(vm, &ast)?;
Ok(())
}
pub const PY_CF_ONLY_AST: i32 = 0x0400;
pub const PY_CF_SOURCE_IS_UTF8: i32 = 0x0100;
pub const PY_CF_DONT_IMPLY_DEDENT: i32 = 0x200;
pub const PY_CF_IGNORE_COOKIE: i32 = 0x0800;
pub const PY_CF_ALLOW_INCOMPLETE_INPUT: i32 = 0x4000;
pub const PY_CF_OPTIMIZED_AST: i32 = 0x8000 | PY_CF_ONLY_AST;
pub const PY_CF_TYPE_COMMENTS: i32 = 0x1000;
pub const PY_CF_ALLOW_TOP_LEVEL_AWAIT: i32 = 0x2000;
const CO_NESTED: i32 = 0x0010;
const CO_GENERATOR_ALLOWED: i32 = 0;
const CO_FUTURE_DIVISION: i32 = 0x20000;
const CO_FUTURE_ABSOLUTE_IMPORT: i32 = 0x40000;
const CO_FUTURE_WITH_STATEMENT: i32 = 0x80000;
const CO_FUTURE_PRINT_FUNCTION: i32 = 0x100000;
const CO_FUTURE_UNICODE_LITERALS: i32 = 0x200000;
const CO_FUTURE_BARRY_AS_BDFL: i32 = 0x400000;
const CO_FUTURE_GENERATOR_STOP: i32 = 0x800000;
const CO_FUTURE_ANNOTATIONS: i32 = 0x1000000;
pub const PY_COMPILE_FLAGS_MASK: i32 = PY_CF_ONLY_AST
| PY_CF_SOURCE_IS_UTF8
| PY_CF_DONT_IMPLY_DEDENT
| PY_CF_IGNORE_COOKIE
| PY_CF_ALLOW_TOP_LEVEL_AWAIT
| PY_CF_ALLOW_INCOMPLETE_INPUT
| PY_CF_OPTIMIZED_AST
| PY_CF_TYPE_COMMENTS
| CO_NESTED
| CO_GENERATOR_ALLOWED
| CO_FUTURE_DIVISION
| CO_FUTURE_ABSOLUTE_IMPORT
| CO_FUTURE_WITH_STATEMENT
| CO_FUTURE_PRINT_FUNCTION
| CO_FUTURE_UNICODE_LITERALS
| CO_FUTURE_BARRY_AS_BDFL
| CO_FUTURE_GENERATOR_STOP
| CO_FUTURE_ANNOTATIONS;