Skip to content

Use new style args for int.__new__ #755

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 6 commits into from
Mar 29, 2019

Conversation

BenLewis-Seequent
Copy link

To achieve this I anhanced FromArgs derive proc macro to support positional arguments. Positional-only arguments have the #[positional] attribute in front of it's field and while keyword-only arguments have #[keyword] attribute in front of it's field.

Copy link
Contributor

@OddCoincidence OddCoincidence left a comment

Choose a reason for hiding this comment

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

Thanks for working on this! I was planning something similar but hadn't had the time yet. I have a few concerns I'd like addressed before we merge this though - it's going to be used a lot so I want us to take the time to make sure the design is right.

end: Option<PyStringRef>,
flush: bool,
#[keyword]
sep: OptionalArg<Option<PyStringRef>>,
Copy link
Contributor

@OddCoincidence OddCoincidence Mar 27, 2019

Choose a reason for hiding this comment

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

I would prefer to avoid using OptionalArg in these:

  1. OptionalArg<Option<...>> feels very weird
  2. We can derive whether an arg is optional or not based on the presence of a future default attribute (or perhaps something like #[pyarg(optional = true)], basically I want to avoid reusing any of the FromArgs extractors inside of these structs)

Copy link
Author

Choose a reason for hiding this comment

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

Agreed, OptionalArg<Option<...>> is awkward, but wouldn't be needed when I implement the default attribute argument. I'm split about having OptionalArg in these structs, I think in some cases we need to differentiate between a missing argument and passing None.

Copy link
Contributor

@OddCoincidence OddCoincidence Mar 27, 2019

Choose a reason for hiding this comment

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

I think we should handle this entirely with attributes rather than reusing OptionalArg. In the following, None refers to a Python None and Option::None refers to Rust's none.

Thing is required, and cannot be None.

#[derive(FromArgs)]
struct MyArgs {
    #[pyarg(positional)]
    thing: ThingRef,
}

Thing is required, but can be None:

#[derive(FromArgs)]
struct MyArgs {
    #[pyarg(positional)]
    thing: Option<ThingRef>,
}

Thing is not required, can be None, and will use the Default impl for Option if not passed, which is Option::None.

#[derive(FromArgs)]
struct MyArgs {
    #[pyarg(positional, default)]
    thing: Option<ThingRef>,
}

Thing is not required, cannot be None, and will use the result of if the argument is not present.

#[derive(FromArgs)]
struct MyArgs {
    #[pyarg(positional, default = "<method that returns ThingRef>")]
    thing: ThingRef,
}

Copy link
Author

Choose a reason for hiding this comment

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

I still think in some situations it's not sufficient to just have a default, some functions need to detect if its argument was provided. For example, if the base argument is provided to int.new then the first argument can then only be a string, otherwise, it can be other types like int or float.

CPython implements int.new by using NULL as the default. https://github.com/python/cpython/blob/master/Objects/longobject.c#L4950

I have added an 'optional 'argument to pyarg, to indicate when we are want to convert into OptionalArg. e.g.

#[derive(FromArgs)]
struct IntOptions {
    #[pyarg(positional, optional = true)]
    val_options: OptionalArg<PyObjectRef>,
    #[pyarg(positional_keyword, optional = true)]
    base: OptionalArg<u32>,
}

Copy link
Contributor

@OddCoincidence OddCoincidence Mar 28, 2019

Choose a reason for hiding this comment

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

For example, if the base argument is provided to int.new then the first argument can then only be a string, otherwise, it can be other types like int or float.

In cases like these, the "function overloading" trick might work, e.g. range does:

fn range_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
    let range = if args.args.len() <= 2 {
        let (cls, stop) = args.bind(vm)?;
        PyRangeRef::new(cls, stop, vm)
    } else {
        let (cls, start, stop, step) = args.bind(vm)?;
        PyRangeRef::new_from(cls, start, stop, step, vm)
    }?;

    Ok(range.into_object())
}

Basically there can be two different int_new methods (that accept different types) decided upon based on arity.

@codecov-io
Copy link

codecov-io commented Mar 28, 2019

Codecov Report

Merging #755 into master will decrease coverage by 0.01%.
The diff coverage is 54.12%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #755      +/-   ##
==========================================
- Coverage   61.86%   61.85%   -0.02%     
==========================================
  Files          80       80              
  Lines       12656    12744      +88     
  Branches     2592     2621      +29     
==========================================
+ Hits         7830     7883      +53     
- Misses       3163     3185      +22     
- Partials     1663     1676      +13
Impacted Files Coverage Δ
vm/src/builtins.rs 63.63% <ø> (ø) ⬆️
vm/src/function.rs 47.36% <40%> (+0.62%) ⬆️
derive/src/lib.rs 52.12% <50%> (-16.63%) ⬇️
vm/src/obj/objint.rs 86.26% <82.35%> (+1.47%) ⬆️
vm/src/pyobject.rs 76.19% <0%> (+0.44%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 6618def...ce9a909. Read the comment docs.

@OddCoincidence
Copy link
Contributor

Thanks, this is looking really great! I'd still like to figure out a way to avoid OptionalArg for that edge case at some point, but I think this is ready to merge with one more small nit addressed (and the conflict resolved).

@OddCoincidence OddCoincidence merged commit c5c9181 into RustPython:master Mar 29, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants