-
Notifications
You must be signed in to change notification settings - Fork 782
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
Trait ergonomics str implementation #4233
Conversation
This update brings custom string formatting for PyClass with a #[pyclass(str = "format string")] attribute. It allows users to specify how their PyClass objects are converted to string in Python. The implementation includes additional tests and parsing logic.
This is a draft. The following still needs to be implemented:
The format expansion and optional value passed to the str are both completed. I wanted to get this created before going too far so I could get feedback. 😃 |
…d str is not allowed when automated str is requested
…a future implementation since it will use the same shorthand formatting logic
@davidhewitt I have an implementation question for you. Currently we have the following implementation for
Given that the user can rename variants on an enum, it seems to me that it makes sense for us to respect this only when the user provides a shorthand formatting string to Are there any other renaming gotchas I should consider? Does this implementation seem reasonable? |
Implemented the capacity to handle renamed variants in enum string representation. Now, custom Python names for enum variants will be correctly reflected when calling the __str__() method on an enum instance. Additionally, the related test has been updated to reflect this change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I really like this. Nicely done :)
Co-authored-by: Bruno Kolenbrander <59372212+mejrs@users.noreply.github.com>
Co-authored-by: Bruno Kolenbrander <59372212+mejrs@users.noreply.github.com>
…e targeted span for the format string.
…e targeted span for the format string.
… reduce span of invalid member)
Thanks, this looks looks really great! I have to confess when I proposed the feature I hadn't realised the number of interactions around renaming and really thought too hard about the differences between Rust and Python formatting. I'd prefer move slowly and err on the side of not making this too powerful too quickly, even if that means we only allow simpler cases we're sure about. The cases I think risk causing users pain:
Moving to the design, this makes me wonder a few things:
Maybe a lot of these design problems are more related to I suppose, what are the forms we're sure about? I think it's the following:
Does this imply that for this first version it would be wiser to support this just for structs, and allow ourselves a bit more time to work out the enum design? |
I agree here. On structs it seems pretty clear and intuitive what this does. One thing we should probably test here: Does the formatting handle raw identifiers (correctly)? #[pyclass(str = "{r#type}")]
struct Foo {
r#type: Bar
}
It's true that it easier to leak syntax here, but we also don't have any guarantee about the syntax in any |
@Icxolu Thank you for pointing out the raw identifiers case! It now properly handles that in the latest commit. I agree that it is reasonable to expect that the users know what will come from a debug format in Rust for a given type. For me, the biggest concern in this feature is ensuring the user understands the cause and effect of this feature without surprises. I mentioned above that if we have strange cases, we would need to document them thoroughly, but such a thing is brittle (it relies on the user finding and reading those caveats). As for the leaking of rust syntax, I don't see that as a problem. As mentioned, the user should generally know what to expect with debug output in Rust. Also, I actually like some of the debug output in rust and would personally retain it in my own implementations of repr or str. In my Python code I mostly use repr implementations (since they are picked up by str unless you implement str separately) and use them for logging and debugging. As my primary use case is debugging quickly, I prefer output structures that "map the issue" quickly and efficiently for me. While there is guidance for repr and str implementation in Python, there is a "practicality beats purity" implementation within dataclasses themselves (for example enabling/disabling per field - such that repr could not be used to fully recreate the instance). I think that the renaming of variants adds surprises to how the user implements the shorthand format strings and I think requiring every variant to have a str format might be onerous. I am leaning toward the following caveats:
One that I'm not sure about:
If we do this automatically (I really wish Rust would do this by default for Debug derivations :-) ), it would lead to surprising behavior if they have implemented Display directly, as it wouldn't be used. I haven't played around with this, but could we detect the Display implementation in the macro and use that knowledge to switch accordingly? Then I think this would be great. |
# Conflicts: # pyo3-macros-backend/src/pyclass.rs
…ss, field, or variant args when mixed with a str shorthand formatter.
@davidhewitt @Icxolu Sorry for the delay in returning to this PR. I have made the string shorthand format incompatible with any renaming, forcing the user to either implement
For the first case, I don't want to make the assumption that they need the name prepended if Display has been implemented explicitly. I don't believe proc-macros have access to the what traits have been implemented. I looked into how I could bound the problem, but I'm not entirely sure how to do so just yet. There is also the other case where they explicitly prepended the class name using a shorthand string formatter and we don't want to double prepend it. If we make the assumption for the user, it would be surprising. For the second case, it was mentioned
This again runs into the potential double pre-pending mentioned above, or maybe the user simply doesn't want it prepended at all. Which brings us to the following:
It isn't clear to me how the shorthand logic should function for the enum cases without making potentially bad assumptions for the user. For this initial release, should we release just the following?
Thoughts? |
Sorry for the lack of response here, I was away from my keyboard for a few days.
Doing anything other than simply delegating to
I agree, structs seem pretty clear while enums definitely need more design first.
This is probably a good idea. We might want to disallow the self shorthand as well for the time beeing. For structs I don't see a real use case, so I would defer that to future work on enums if it then turns out to be needed/useful. Even though me could lift the restriction about renaming in this case, having it in place for now seems not too problematic and gives us more room in the future to adapt and improve. |
… shorthand format to structs only, updated docs with changes, refactored renaming incompatibility check with str shorthand.
…ified back to inline check for structs
@Icxolu I have implemented everything as discussed above. I am having some trouble though with the codecov/patch test. It is claiming that this piece of code is not covered, but I do have 2 tests fully covering it here, throwing the compile time error from this code block as seen here. Do the tests in tests/ui not count as part of the patch coverage check? |
…g Ok issue in eq implementation.
…d, fixing Ok issue in eq implementation." This reverts commit a37c24b.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, implementation wise this looks good to me now. I left some thoughts about the error messages, but these are just what came to my mind while reading them and I'm also completely fine if we decide that they are good as is.
Regarding codecov, I'm not sure, maybe @davidhewitt knows more, but I don't think we need to worry too much here.
…R for additional details regarding the implementation of `str`
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks again for all the effort! Looking forward to see this landing.
# Conflicts: # src/tests/hygiene/pyclass.rs
@Icxolu This merge conflicted with the fix in my other PR. I just fixed the conflict. |
Implementation of str as identified in #4207
This introduces the str argument to pyclass which optionally takes a formatting string using the shorthand notation inspired by thiserror. It expands similarly to this library with the one addition that empty unnamed or indexed format brackets will place
self
in the call to format. (this is particularly useful for enum formatting. Any formatting arguments after:
within the brackets will be respected.If no formatting string is provided an implementation of the
Display
trait is expected.