| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
|
Warning Rate limit exceeded@youknowone has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 18 minutes and 5 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR. We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📥 CommitsReviewing files that changed from the base of the PR and between af2d36c and bd133fd. 📒 Files selected for processing (5)
""" WalkthroughThis update removes expected failure decorators from several Python test methods, rewrites generic alias parameter handling in Rust to support default parameters and tuple expansion, and adds a new constant for __typing_subst__ in the VM context. The changes align parameter substitution logic with CPython's behavior and improve type parameter management. Changes
Sequence Diagram(s)sequenceDiagram
participant PythonTest as Python Test
participant RustVM as Rust VM (genericalias.rs)
participant Context as VM Context
PythonTest->>RustVM: Substitute generic alias parameters
RustVM->>RustVM: Check for __typing_subst__ attribute
alt Has __typing_subst__
RustVM->>RustVM: Call __typing_subst__ on argument
else No __typing_subst__
RustVM->>RustVM: Use subs_tvars (with tuple expansion if needed)
end
RustVM->>Context: Access __typing_subst__ constant
RustVM-->>PythonTest: Return substituted parameters tuple
Suggested reviewers
Poem✨ Finishing Touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. ❤️ Share 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (.coderabbit.yaml)
Documentation and Community
|
Sorry, something went wrong.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (3)vm/src/builtins/genericalias.rs (3)📜 Review details217-257: Consider optimizing vector resizing for better performance.
The current implementation resizes the vector multiple times during execution, which could lead to unnecessary allocations. Consider collecting parameters into a temporary structure first or using a more efficient growth strategy.
You could optimize by first collecting all parameters and then creating the final vector:
pub(crate) fn make_parameters(args: &Py<PyTuple>, vm: &VirtualMachine) -> PyTupleRef { - let mut parameters: Vec<PyObjectRef> = Vec::with_capacity(args.len()); - let mut iparam = 0; + let mut seen = std::collections::HashSet::new(); + let mut parameters = Vec::new(); for arg in args { // We don't want __parameters__ descriptor of a bare Python class. if arg.class().is(vm.ctx.types.type_type) { continue; } // Check for __typing_subst__ attribute (like CPython) if arg.get_attr(identifier!(vm, __typing_subst__), vm).is_ok() { - // Use tuple_add equivalent logic - if tuple_index_vec(¶meters, arg).is_none() { - if iparam >= parameters.len() { - parameters.resize(iparam + 1, vm.ctx.none()); - } - parameters[iparam] = arg.clone(); - iparam += 1; + if seen.insert(arg.as_object().id()) { + parameters.push(arg.clone()); } } else if let Ok(subparams) = arg.get_attr(identifier!(vm, __parameters__), vm) { if let Ok(sub_params) = subparams.try_to_ref::<PyTuple>(vm) { - let len2 = sub_params.len(); - // Resize if needed - if iparam + len2 > parameters.len() { - parameters.resize(iparam + len2, vm.ctx.none()); - } for sub_param in sub_params { - // Use tuple_add equivalent logic - if tuple_index_vec(¶meters[..iparam], sub_param).is_none() { - if iparam >= parameters.len() { - parameters.resize(iparam + 1, vm.ctx.none()); - } - parameters[iparam] = sub_param.clone(); - iparam += 1; + if seen.insert(sub_param.as_object().id()) { + parameters.push(sub_param.clone()); } } } } } - // Resize to actual size - parameters.truncate(iparam); PyTuple::new_ref(parameters, &vm.ctx) }
261-269: Refactor to avoid code duplication between tuple_index and tuple_index_vec.
Both functions perform the same operation of finding an item by identity. Consider creating a generic helper function.
#[inline] -fn tuple_index(tuple: &PyTupleRef, item: &PyObjectRef) -> Option<usize> { - tuple.iter().position(|element| element.is(item)) +fn find_by_identity<'a>(iter: impl Iterator<Item = &'a PyObjectRef>, item: &PyObjectRef) -> Option<usize> { + iter.position(|element| element.is(item)) } #[inline] -fn tuple_index_vec(vec: &[PyObjectRef], item: &PyObjectRef) -> Option<usize> { - vec.iter().position(|element| element.is(item)) +fn tuple_index(tuple: &PyTupleRef, item: &PyObjectRef) -> Option<usize> { + find_by_identity(tuple.iter(), item) +} + +#[inline] +fn tuple_index_vec(vec: &[PyObjectRef], item: &PyObjectRef) -> Option<usize> { + find_by_identity(vec.iter(), item) }
375-392: Clarify error message for unsupported default values.
The comment indicates that CPython doesn't support default values in this context, but the error message doesn't make this clear to users.
} else { // CPython doesn't support default values in this context return Err(vm.new_type_error(format!( - "No argument provided for parameter at index {}", - idx + "No argument provided for parameter '{}' at index {} (default values not supported with __typing_subst__)", + arg.repr(vm).unwrap_or_else(|_| "?".to_string()), + idx ))); }
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Reviewing files that changed from the base of the PR and between 3031d5b and 7bc7887.
📒 Files selected for processing (3)Learnt from: CR PR: RustPython/RustPython#0 File: .github/copilot-instructions.md:0-0 Timestamp: 2025-06-26T12:52:11.109Z Learning: Use the macro system (such as 'pyclass', 'pymodule', 'pyfunction') when implementing Python functionality in Rust for RustPython.
Learnt from: CR PR: RustPython/RustPython#0 File: .github/copilot-instructions.md:0-0 Timestamp: 2025-06-26T12:52:11.109Z Learning: When implementing a Python module in Rust for RustPython, use the #[pymodule] attribute and expose functions and classes using #[pyfunction], #[pyclass], and #[pymethod] as appropriate.
vm/src/vm/context.rs (1)226-226: LGTM!
The addition of __typing_subst__ constant follows the established pattern and is necessary for the enhanced generic alias parameter handling.
vm/src/builtins/genericalias.rs (1)284-309: TypeVarTuple handling looks correct!
The implementation properly handles TypeVarTuple expansion by checking for the iterator slot and expanding tuple elements when appropriate. This aligns with CPython's behavior for type parameter substitution.
Sorry, something went wrong.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)vm/src/builtins/genericalias.rs (1)📜 Review details217-234: Consider simplifying the vector management logic.
The index-based insertion with resize calls could be simplified. The current approach works but may be harder to maintain.
Consider this cleaner approach:
- let mut parameters: Vec<PyObjectRef> = Vec::with_capacity(args.len()); - let mut iparam = 0; - for arg in args { // We don't want __parameters__ descriptor of a bare Python class. if arg.class().is(vm.ctx.types.type_type) { continue; } // Check for __typing_subst__ attribute (like CPython) if arg.get_attr(identifier!(vm, __typing_subst__), vm).is_ok() { - // Use tuple_add equivalent logic - if tuple_index_vec(¶meters, arg).is_none() { - if iparam >= parameters.len() { - parameters.resize(iparam + 1, vm.ctx.none()); - } - parameters[iparam] = arg.clone(); - iparam += 1; + if !parameters.contains(arg) { + parameters.push(arg.clone()); }
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Reviewing files that changed from the base of the PR and between 7bc7887 and b4f4ffa.
📒 Files selected for processing (5)Learnt from: CR PR: RustPython/RustPython#0 File: .github/copilot-instructions.md:0-0 Timestamp: 2025-06-26T12:52:11.109Z Learning: Use the macro system (such as 'pyclass', 'pymodule', 'pyfunction') when implementing Python functionality in Rust for RustPython.
Learnt from: CR PR: RustPython/RustPython#0 File: .github/copilot-instructions.md:0-0 Timestamp: 2025-06-26T12:52:11.109Z Learning: When implementing a Python module in Rust for RustPython, use the #[pymodule] attribute and expose functions and classes using #[pyfunction], #[pyclass], and #[pymethod] as appropriate.
vm/src/builtins/genericalias.rs (5)266-269: LGTM! Clean helper function implementation.
The tuple_index_vec function correctly uses identity comparison with is() which is appropriate for Python object references.
256-258: Good optimization with truncate.
Using truncate instead of shrink_to_fit is more appropriate here since we know the exact final size needed.
235-254: Verify the nested parameter expansion logic.
The recursive parameter expansion with index management is complex and could be error-prone. The logic appears correct but warrants careful testing.
#!/bin/bash # Search for tests that cover nested parameter expansion scenarios rg -A 10 -B 5 "__parameters__.*__parameters__" --type py
284-314: Verify TypeVarTuple detection mechanism.
The TypeVarTuple detection using param.class().slots.iter.load().is_some() is interesting but may not be the most reliable way to identify TypeVarTuple objects.
#!/bin/bash # Search for TypeVarTuple usage patterns and tests rg -A 5 -B 5 "TypeVarTuple" --type py ast-grep --pattern 'class TypeVarTuple($_) { $$$ }'
338-370: ```shell
#!/bin/bash
sed -n '330,370p' vm/src/builtins/genericalias.rs</details> </blockquote></details> </details> <!-- This is an auto-generated comment by CodeRabbit for review status -->
Sorry, something went wrong.
| for arg in args.iter() { | ||
| // Check for __typing_subst__ attribute directly (like CPython) | ||
| if let Ok(subst) = arg.get_attr(identifier!(vm, __typing_subst__), vm) { | ||
| let idx = tuple_index(¶meters, arg).unwrap(); | ||
| if idx < num_items { | ||
| // Call __typing_subst__ with the argument | ||
| let substituted = subst.call((arg_items[idx].clone(),), vm)?; | ||
| new_args.push(substituted); | ||
| } else { | ||
| subs_tvars(arg.clone(), ¶meters, arg_items, vm) | ||
| // CPython doesn't support default values in this context | ||
| return Err(vm.new_type_error(format!( | ||
| "No argument provided for parameter at index {}", | ||
| idx | ||
| ))); | ||
| } | ||
| }) | ||
| .collect::<PyResult<Vec<_>>>()?; | ||
| } else { | ||
| new_args.push(subs_tvars(arg.clone(), ¶meters, arg_items, vm)?); | ||
| } | ||
| } |
There was a problem hiding this comment.
⚠️ Potential issue
Potential issue with missing parameter handling.
The error handling for missing parameters when using __typing_subst__ may be too strict. CPython might handle this case differently, particularly with default values.
} else {
- // CPython doesn't support default values in this context
- return Err(vm.new_type_error(format!(
- "No argument provided for parameter at index {}",
- idx
- )));
+ // Use the parameter itself as default (CPython behavior)
+ new_args.push(arg.clone());
}This needs verification against CPython's actual behavior in similar scenarios.
📝 Committable suggestion‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| for arg in args.iter() { | |
| // Check for __typing_subst__ attribute directly (like CPython) | |
| if let Ok(subst) = arg.get_attr(identifier!(vm, __typing_subst__), vm) { | |
| let idx = tuple_index(¶meters, arg).unwrap(); | |
| if idx < num_items { | |
| // Call __typing_subst__ with the argument | |
| let substituted = subst.call((arg_items[idx].clone(),), vm)?; | |
| new_args.push(substituted); | |
| } else { | |
| subs_tvars(arg.clone(), ¶meters, arg_items, vm) | |
| // CPython doesn't support default values in this context | |
| return Err(vm.new_type_error(format!( | |
| "No argument provided for parameter at index {}", | |
| idx | |
| ))); | |
| } | |
| }) | |
| .collect::<PyResult<Vec<_>>>()?; | |
| } else { | |
| new_args.push(subs_tvars(arg.clone(), ¶meters, arg_items, vm)?); | |
| } | |
| } | |
| for arg in args.iter() { | |
| // Check for __typing_subst__ attribute directly (like CPython) | |
| if let Ok(subst) = arg.get_attr(identifier!(vm, __typing_subst__), vm) { | |
| let idx = tuple_index(¶meters, arg).unwrap(); | |
| if idx < num_items { | |
| // Call __typing_subst__ with the argument | |
| let substituted = subst.call((arg_items[idx].clone(),), vm)?; | |
| new_args.push(substituted); | |
| } else { | |
| // Use the parameter itself as default (CPython behavior) | |
| new_args.push(arg.clone()); | |
| } | |
| } else { | |
| new_args.push(subs_tvars(arg.clone(), ¶meters, arg_items, vm)?); | |
| } | |
| } |
In vm/src/builtins/genericalias.rs between lines 374 and 392, the current code returns a type error if an argument index exceeds the number of items, assuming no default values are supported like CPython. To address the potential issue, verify CPython's behavior regarding default parameters with __typing_subst__ and adjust the error handling accordingly. If CPython allows default values or handles missing parameters differently, modify the code to support default values or skip substitution for those parameters instead of returning an error.
Sorry, something went wrong.
There was a problem hiding this comment.
python3.13 -c "
import typing
from typing import TypeVar, Generic
# Create a test case that would trigger the missing parameter scenario
T = TypeVar('T')
U = TypeVar('U', bound=str)
class MyGeneric(Generic[T, U]):
pass
# Test various substitution scenarios
try:
# This should help us understand how CPython handles missing parameters
result = MyGeneric[int] # Missing second parameter
print('CPython allows partial substitution:', result)
except Exception as e:
print('CPython error:', e)
"
CPython error: Too few arguments for <class '__main__.MyGeneric'>; actual 1, expected at least 2
Sorry, something went wrong.
There was a problem hiding this comment.
@youknowone Thank you for providing that test! Your output clearly shows that CPython raises an error with the format "Too few arguments for...; actual X, expected at least Y" when there are insufficient arguments for a generic type.
Looking at the current implementation in the diff, I can see that the RustPython code actually handles this correctly:
if num_items < min_required {
return Err(vm.new_type_error(format!(
"Too few arguments for {}; actual {}, expected at least {}",
repr(vm)?,
num_items,
min_required
)));
}This matches CPython's behavior exactly. My original suggestion was incorrect - I was concerned that the error handling for the __typing_subst__ case might be too strict, but your test demonstrates that CPython does indeed raise errors for insufficient arguments.
The current implementation correctly:
I retract my original suggestion. The current implementation appears to align properly with CPython's behavior.
🐰💭
Sorry, something went wrong.
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)vm/src/builtins/genericalias.rs (1)🧹 Nitpick comments (2)374-392: This still needs verification against CPython behavior.
The error handling for missing parameters when using __typing_subst__ may still be too strict compared to CPython's actual behavior, as noted in the previous review.
The previous review comment about this exact issue remains valid. Please verify CPython's behavior for this scenario:
#!/bin/bash # Verify CPython's behavior with __typing_subst__ and missing parameters python3 -c " import typing from typing import TypeVar, Generic # Create a test case that would trigger the missing parameter scenario T = TypeVar('T') U = TypeVar('U', bound=str) class MyGeneric(Generic[T, U]): pass # Test various substitution scenarios try: # This should help us understand how CPython handles missing parameters result = MyGeneric[int] # Missing second parameter print('CPython allows partial substitution:', result) except Exception as e: print('CPython error:', e) "
vm/src/builtins/genericalias.rs (2)📜 Review details220-264: Consider simplifying the complex parameter handling logic.
The rewritten make_parameters function introduces significant complexity with vector resizing, index tracking, and deduplication logic. While the approach appears to align with CPython's behavior, the implementation could be error-prone.
Consider these potential improvements:
- Simplify vector management: Instead of frequent resizing, use a Vec that grows naturally and truncate at the end.
- Optimize deduplication: Consider using a HashSet for O(1) duplicate detection instead of linear search via tuple_index.
- Add comprehensive comments: The complex logic would benefit from detailed comments explaining the CPython behavior being mimicked.
pub(crate) fn make_parameters(args: &Py<PyTuple>, vm: &VirtualMachine) -> PyTupleRef { - let mut parameters: Vec<PyObjectRef> = Vec::with_capacity(args.len()); - let mut iparam = 0; + let mut parameters: Vec<PyObjectRef> = Vec::new(); + let mut seen: std::collections::HashSet<*const PyObject> = std::collections::HashSet::new(); for arg in args { // We don't want __parameters__ descriptor of a bare Python class. if arg.class().is(vm.ctx.types.type_type) { continue; } // Check for __typing_subst__ attribute (like CPython) if arg.get_attr(identifier!(vm, __typing_subst__), vm).is_ok() { - // Use tuple_add equivalent logic - if tuple_index(¶meters, arg).is_none() { - if iparam >= parameters.len() { - parameters.resize(iparam + 1, vm.ctx.none()); - } - parameters[iparam] = arg.clone(); - iparam += 1; - } + if seen.insert(arg.as_ptr()) { + parameters.push(arg.clone()); + } } else if let Ok(subparams) = arg.get_attr(identifier!(vm, __parameters__), vm) { // ... similar simplification for sub_params handling } } - // Resize to actual size - parameters.truncate(iparam); PyTuple::new_ref(parameters, &vm.ctx) }
284-308: Good TypeVarTuple handling, but add clarifying comments.
The logic for handling TypeVarTuple expansion is correct, but the implementation would benefit from clearer comments explaining the behavior.
for arg in sub_params.iter() { if let Some(idx) = tuple_index(params.as_slice(), arg) { let param = ¶ms[idx]; let substituted_arg = &arg_items[idx]; - // Check if this is a TypeVarTuple (has tp_iter) + // TypeVarTuple handling: Check if this parameter has iteration support + // and the substituted argument is a tuple that should be expanded if param.class().slots.iter.load().is_some() && substituted_arg.try_to_ref::<PyTuple>(vm).is_ok() { - // TypeVarTuple case - extend with tuple elements + // Expand tuple elements for TypeVarTuple substitution if let Ok(tuple) = substituted_arg.try_to_ref::<PyTuple>(vm) { for elem in tuple.iter() { sub_args.push(elem.clone()); } continue; } }
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Reviewing files that changed from the base of the PR and between 5fd3525 and 433f24e.
📒 Files selected for processing (2)Learnt from: CR PR: RustPython/RustPython#0 File: .github/copilot-instructions.md:0-0 Timestamp: 2025-06-26T12:52:11.109Z Learning: Use the macro system (such as 'pyclass', 'pymodule', 'pyfunction') when implementing Python functionality in Rust for RustPython.
vm/src/builtins/genericalias.rs (3)20-34: LGTM: Appropriate expansion of special attributes.
The addition of __class__, __bases__, __unpacked__, and __typing_unpacked_tuple_args__ to the attribute exceptions list is appropriate for comprehensive generic alias support.
266-269: LGTM: Clean utility function.
The tuple_index function is a straightforward utility that correctly uses identity comparison for Python objects.
338-370: Excellent default parameter handling implementation.
The logic for handling default parameters is well-implemented with proper validation and informative error messages. The approach of counting defaults from the end backwards aligns with Python's parameter handling conventions.
Sorry, something went wrong.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)vm/src/stdlib/typing.rs (1)📜 Review details937-959: Consider caching the typing module import for performance.
The helper function is well-structured and follows good practices for calling typing module functions. However, it imports the typing module on every call.
Consider caching the typing module reference if this function is called frequently:
fn call_typing_args_kwargs( name: &'static str, cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine, ) -> PyResult { - let typing = vm.import("typing", 0)?; + // Consider caching if performance becomes an issue + let typing = vm.import("typing", 0)?; let func = typing.get_attr(name, vm)?; // Prepare arguments: (cls, *args) let mut call_args = vec![cls.into()]; call_args.extend(args.args); // Call with prepared args and original kwargs let func_args = FuncArgs { args: call_args, kwargs: args.kwargs, }; func.call(func_args, vm) }
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Reviewing files that changed from the base of the PR and between bdd7519 and af2d36c.
📒 Files selected for processing (5)Learnt from: CR PR: RustPython/RustPython#0 File: .github/copilot-instructions.md:0-0 Timestamp: 2025-06-26T12:52:11.109Z Learning: Use the macro system (such as 'pyclass', 'pymodule', 'pyfunction') when implementing Python functionality in Rust for RustPython.
Learnt from: CR PR: RustPython/RustPython#0 File: .github/copilot-instructions.md:0-0 Timestamp: 2025-06-26T12:52:11.109Z Learning: When implementing a Python module in Rust for RustPython, use the #[pymodule] attribute and expose functions and classes using #[pyfunction], #[pyclass], and #[pymethod] as appropriate.
vm/src/stdlib/typing.rs (5)16-17: LGTM! Clean import additions for new functionality.
The added imports (PyObjectRef, PyRef, PyTupleRef, and Iterable) are necessary for the new Iterable trait implementation and properly follow the existing import organization.
Also applies to: 20-20
610-610: Good addition of Iterable trait to TypeVarTuple.
Adding Iterable to the trait list enables iteration support for TypeVarTuple, which aligns with CPython's behavior for handling variadic generics.
670-682: Well-implemented Iterable trait for TypeVarTuple.
The implementation correctly handles the unpacking behavior by:
- Importing the typing module to access Unpack
- Creating Unpack[self] to represent the unpacked tuple
- Returning a list iterator containing the unpacked element
This aligns with CPython's behavior for Generic[*Ts] as mentioned in the comment.
971-978: Excellent refactor to delegate to Python typing module.
The change from directly creating PyGenericAlias to calling _generic_class_getitem via the helper function aligns with CPython's behavior and centralizes the logic in the Python typing module.
980-983: Good delegation to Python typing module for subclass initialization.
The refactor to use call_typing_args_kwargs for _generic_init_subclass is consistent with the class_getitem changes and properly delegates complex logic to the Python side.
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
Summary by CodeRabbit
Bug Fixes
Tests
Chores