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
9 changes: 9 additions & 0 deletions crates/by_transforms/src/runtime/_by_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -396,17 +396,26 @@ def __iter__(self):
# `None` a field nothing lowered, which keeps the interpolation python built and so
# stays byte-identical to what python would have reported
#
# the author's text keeps the whitespace before the `=`, conversion, format spec or
# `}` that ends the field, which is what python reports from 3.14.8 on. 3.14.0 to
# 3.14.7 dropped it (python/cpython#154719), so there it is dropped here too, or a
# lowered field would report different text from an untouched field beside it
#
# the import is made here rather than at the top of the file because this module is
# shared with runtimes that have no `string.templatelib`. only a t-string can reach
# this helper, and a t-string needs 3.14
def _by_template(_t, _exprs):
from string.templatelib import Interpolation, Template
from sys import version_info

_drops_trailing_space = version_info < (3, 14, 8)
_strings = _t.strings
_parts = []
for _index, _field in enumerate(_t.interpolations):
_parts.append(_strings[_index])
_expr = _exprs[_index]
if _expr is not None and _drops_trailing_space:
_expr = _expr.rstrip()
_parts.append(
_field
if _expr is None
Expand Down
27 changes: 18 additions & 9 deletions crates/by_transforms/src/transforms/ast_driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,15 @@ use super::{
decl_site_variance, decorated_binding, decorator_keyword, dedent_string, destructure,
django_lookup, dynamic_keyword, empty_declarations, export_import, extension, flexible_keyword,
float_const, force_unwrap, frameworks, generic_call, generics, grapheme_string, identity_swap,
if_let, implicit_receiver, implicit_typing, inferred_annotation, init_method, just_float,
kw_subscript, literal_string, literal_types, local_once, main_function, match_type, modifiers,
module_api, mutable_defaults, none_chain, optional_type, overload, parametric_is,
postfix_await, propagate, properties, protocol_type, raises_clause, reified_class,
reified_generic, repeated_underscore, return_value_use, runtime_union, sentinel, some_ctor,
soundness, statement_expression, static_resource, string_tag, super_keyword, symbolic_type_op,
template_expression, template_type, top_star, trailing_lambda, tuple_index, type_fn, type_is,
type_reification, typed_dict_literal, typed_lambda, typeof_keyword, unique_loop_bindings,
unpack, use_site_variance, visibility_rename,
if_let, implicit_receiver, implicit_typing, inferred_annotation, inferred_base_arguments,
init_method, just_float, kw_subscript, literal_string, literal_types, local_once,
main_function, match_type, modifiers, module_api, mutable_defaults, none_chain, optional_type,
overload, parametric_is, postfix_await, propagate, properties, protocol_type, raises_clause,
reified_class, reified_generic, repeated_underscore, return_value_use, runtime_union, sentinel,
some_ctor, soundness, statement_expression, static_resource, string_tag, super_keyword,
symbolic_type_op, template_expression, template_type, top_star, trailing_lambda, tuple_index,
type_fn, type_is, type_reification, typed_dict_literal, typed_lambda, typeof_keyword,
unique_loop_bindings, unpack, use_site_variance, visibility_rename,
};
use crate::Config;
use crate::source_map::Replacement;
Expand Down Expand Up @@ -1415,6 +1415,11 @@ pub(crate) fn run_against_source<'a>(
let implicit_typing_pass = implicit_typing::ImplicitTypingPass::new();
let inferred_annotation_pass =
inferred_annotation::InferredAnnotationPass::new(written, config.min_version);
let inferred_base_arguments_pass = inferred_base_arguments::InferredBaseArgumentsPass::new(
source_ref,
written,
config.clone(),
);
let template_type_pass = template_type::TemplateTypePass { written };
let tuple_types_pass =
annotation::TupleLiteralTypePass::new(source_ref, written, config.clone());
Expand Down Expand Up @@ -1675,6 +1680,10 @@ pub(crate) fn run_against_source<'a>(
// (`class A: a = 1` → `a: int = 1`); a zero-width insertion at the
// target name, disjoint from the value-position lowerings above
&inferred_annotation_pass,
// write the type arguments a generic base takes from the class body
// (`class B(A)` → `class B(A[int])`); a zero-width insertion after the
// base expression
&inferred_base_arguments_pass,
// an f-string in a type position replaces its whole span with the
// python spelling of the type ty resolved it to. it runs before the
// type-position passes below so its wide edit claims the holes, whose
Expand Down
19 changes: 11 additions & 8 deletions crates/by_transforms/src/transforms/extension.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,15 +269,18 @@ impl<'a> ExtensionBlockPass<'a> {
}
first_member = false;

let kind = member_kind(func, source);
// real decorators other than `property` are kept (dedented to the
// module level); `property` and the synthetic modifier markers are
// consumed by the lowering itself
// the kind every call of the member was resolved as, so the backing function takes
// the receiver those calls pass it
let kind = types
.extension_member_kind(class, func.name.as_str())
.unwrap_or_else(|| member_kind(func, source));
// real decorators are kept (dedented to the module level), except one that decides
// how the member binds its receiver: the backing function is called with the
// receiver already chosen, so `@classmethod` there would make it uncallable. those
// and the synthetic modifier markers are consumed by the lowering itself
for decorator in &func.decorator_list {
if is_synthetic_decorator(source, decorator) {
continue;
}
if matches!(&decorator.expression, Expr::Name(name) if name.id.as_str() == "property")
if is_synthetic_decorator(source, decorator)
|| types.decides_receiver_binding(decorator)
{
continue;
}
Expand Down
2 changes: 1 addition & 1 deletion crates/by_transforms/src/transforms/generics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1600,7 +1600,7 @@ impl PolyfilledTypeParams {

/// what each parameter of `type_params` is renamed to, or `None` when the list is kept as
/// native syntax
fn renames(
pub(crate) fn renames(
&self,
type_params: &ruff_python_ast::TypeParams,
) -> Option<HashMap<String, String>> {
Expand Down
21 changes: 21 additions & 0 deletions crates/by_transforms/src/transforms/inferred_annotation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,27 @@ mod tests {
);
}

/// a class nested in another class of the same module is reached through the outer one,
/// which is still being made where a class-body annotation is evaluated, so the annotation
/// is a string rather than an import of the module it is written in
#[test]
fn a_nested_class_is_reached_through_its_outer_class() {
check(
indoc! {"
class D:
class Inner: ...

x = Inner()
"},
indoc! {r#"
class D:
class Inner: ...

x: "D.Inner" = Inner()
"#},
);
}

/// `var` and a modifier chain declare no type, so the member is the bare assignment
/// it lowers to and gets the same annotation
#[test]
Expand Down
Loading
Loading