Skip to content
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
127 changes: 109 additions & 18 deletions crates/codegen/src/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,90 @@ enum DoneWithFuture {
Yes,
}

/// A Python `__future__` feature flag imported via `from __future__ import <feature>`.
///
/// # See Also
///
/// - [Python documentation on `__future__`](https://docs.python.org/3.14/library/__future__.html)
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum FutureFeature {
/// ```py
/// from __future__ import absolute_import
/// ```
AbsoluteImport,

/// ```py
/// from __future__ import annotations
/// ```
Annotations,

/// ```py
/// from __future__ import barry_as_FLUFL
/// ```
BarryAsFLUFL,

/// ```py
/// from __future__ import braces
/// ```
Braces,

/// ```py
/// from __future__ import division
/// ```
Division,

/// ```py
/// from __future__ import generator_stop
/// ```
GeneratorStop,

/// ```py
/// from __future__ import generators
/// ```
Generators,

/// ```py
/// from __future__ import nested_scopes
/// ```
NestedScopes,

/// ```py
/// from __future__ import print_function
/// ```
PrintFunction,

/// ```py
/// from __future__ import unicode_literals
/// ```
UnicodeLiterals,

/// ```py
/// from __future__ import with_statement
/// ```
WithStatement,
}

impl TryFrom<&str> for FutureFeature {
type Error = String;

fn try_from(name: &str) -> Result<Self, Self::Error> {
Ok(match name {
"absolute_import" => Self::AbsoluteImport,
"annotations" => Self::Annotations,
"barry_as_FLUFL" => Self::BarryAsFLUFL,
"braces" => Self::Braces,
"division" => Self::Division,
"generator_stop" => Self::GeneratorStop,
"generators" => Self::Generators,
"nested_scopes" => Self::NestedScopes,
"print_function" => Self::PrintFunction,
"unicode_literals" => Self::UnicodeLiterals,
"with_statement" => Self::WithStatement,
_ => return Err(name.into()),
})
}
}

#[derive(Clone, Copy)]
enum ComprehensionSymbolSource {
Child,
Expand Down Expand Up @@ -11144,33 +11228,40 @@ impl<'warnings> Compiler<'warnings> {
if let DoneWithFuture::Yes = self.done_with_future_stmts {
return Err(self.error(CodegenErrorType::InvalidFuturePlacement));
}

self.done_with_future_stmts = DoneWithFuture::DoneWithDoc;

for feature in features {
match feature.name.as_str() {
// Python 3 features; we've already implemented them by default
"nested_scopes" | "generators" | "division" | "absolute_import"
| "with_statement" | "print_function" | "unicode_literals" | "generator_stop" => {}
// Accept the future feature name, but do not implement
// Barry-as-BDFL parser mode.
"barry_as_FLUFL" => {}
"annotations" => {
let future_feature = feature.name.as_str().try_into().map_err(|name| {
self.error_ranged(CodegenErrorType::InvalidFutureFeature(name), feature.range)
})?;

match future_feature {
FutureFeature::Braces => {
return Err(
self.error_ranged(CodegenErrorType::InvalidFutureBraces, feature.range)
);
}
FutureFeature::Annotations => {
self.future_annotations = true;
self.future_features
.insert(bytecode::CodeFlags::FUTURE_ANNOTATIONS);
self.current_code_info()
.flags
.insert(bytecode::CodeFlags::FUTURE_ANNOTATIONS);
}
"braces" => {
return Err(
self.error_ranged(CodegenErrorType::InvalidFutureBraces, feature.range)
);
}
other => {
return Err(self.error_ranged(
CodegenErrorType::InvalidFutureFeature(other.to_owned()),
feature.range,
));
FutureFeature::BarryAsFLUFL => {
// We do not support Barry-as-BDFL parser mode yet. This is a nop for now.
}
FutureFeature::AbsoluteImport
| FutureFeature::Division
| FutureFeature::GeneratorStop
| FutureFeature::Generators
| FutureFeature::NestedScopes
| FutureFeature::PrintFunction
| FutureFeature::UnicodeLiterals
| FutureFeature::WithStatement => {
// Python 3 features. They are already implemented by default.
}
}
}
Expand Down
50 changes: 32 additions & 18 deletions crates/codegen/src/preprocess.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ use ruff_python_ast::{
visitor::transformer::{self, Transformer},
};
use ruff_text_size::{Ranged, TextRange};

use crate::compile::FutureFeature;
use rustpython_compiler_core::bytecode;

const MAXDIGITS: usize = 3;
Expand Down Expand Up @@ -232,30 +234,41 @@ pub fn checked_future_features_in_body(
..
}) if *level == 0 && module.as_ref().map(|id| id.as_str()) == Some("__future__") => {
for alias in names {
match alias.name.as_str() {
"nested_scopes" | "generators" | "division" | "absolute_import"
| "with_statement" | "print_function" | "unicode_literals"
| "generator_stop" => {}
"annotations" => {
future_features.insert(bytecode::CodeFlags::FUTURE_ANNOTATIONS);
}
// Accept the future feature name, but leave it
// as a RustPython no-op.
"barry_as_FLUFL" => {}
"braces" => {
return Err(FutureFeatureError {
let future_feature =
alias
.name
.as_str()
.try_into()
.map_err(|name| FutureFeatureError {
features: future_features,
range: alias.range,
kind: FutureFeatureErrorKind::InvalidBraces,
});
}
other => {
kind: FutureFeatureErrorKind::InvalidFeature(name),
})?;

match future_feature {
FutureFeature::Braces => {
return Err(FutureFeatureError {
features: future_features,
range: alias.range,
kind: FutureFeatureErrorKind::InvalidFeature(other.to_owned()),
kind: FutureFeatureErrorKind::InvalidBraces,
});
}
FutureFeature::Annotations => {
future_features.insert(bytecode::CodeFlags::FUTURE_ANNOTATIONS)
}
FutureFeature::BarryAsFLUFL => {
// We do not support Barry-as-BDFL parser mode yet. This is a nop for now.
}
FutureFeature::AbsoluteImport
| FutureFeature::Division
| FutureFeature::GeneratorStop
| FutureFeature::Generators
| FutureFeature::NestedScopes
| FutureFeature::PrintFunction
| FutureFeature::UnicodeLiterals
| FutureFeature::WithStatement => {
// Python 3 features. They are already implemented by default.
}
}
}
}
Expand Down Expand Up @@ -298,14 +311,15 @@ pub fn preprocess_mod(
}
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
struct AstPreprocessor {
optimize: u8,
future_annotations: bool,
constant_folding: bool,
}

impl AstPreprocessor {
fn visit_astfold_body(&self, body: &mut ast::Suite) {
fn visit_astfold_body(self, body: &mut ast::Suite) {
let mut docstring = body_starts_with_docstring(body);
if docstring && self.optimize >= 2 {
remove_docstring_from_body(body);
Expand Down
Loading