Skip to content
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

Hygiene: offer a way to set path to pyo3 crate #2022

Merged
merged 6 commits into from
Dec 9, 2021
Merged

Hygiene: offer a way to set path to pyo3 crate #2022

merged 6 commits into from
Dec 9, 2021

Conversation

birkenfeld
Copy link
Member

Unfinished: see // TODO comments. Need to be able to pass #[pyo3(pyo3_path)] attributes to the FromPyObject derive macro, pyclass-enums, and pymethods.

Implementation note: Since I didn't want to add pyo3_path args to dozens of functions, I used scopes whenever possible within which I can do use #pyo3_path as _pyo3; and generate _pyo3:: references everywhere else. This meant adding some const () for impl blocks.

#pyproto is a bit harder to support since it modifies function arguments of the existing impl and I didn't want to move that impl into a const block. We can just deprecate pyproto anyway and document that this feature doesn't work with it.

@davidhewitt
Copy link
Member

davidhewitt commented Nov 23, 2021

Thanks! Will try to give this a full review tomorrow sometime.

We can just deprecate pyproto anyway and document that this feature doesn't work with it.

That's totally reasonable to me; I think that's the plan for 0.16.

@birkenfeld
Copy link
Member Author

When you review, you can exclude the first commit, which is just replacing ::pyo3:: by _pyo3:: everywhere in the proc-macro crate. Should be much easer to see the important stuff then.

@birkenfeld
Copy link
Member Author

birkenfeld commented Nov 24, 2021

This is now feature complete, but I'm unsure how to test it: ::pyo3 is always available in the test suite...

@birkenfeld
Copy link
Member Author

Ah yes, and there is still the issue of generating unique but reproducible const names for pymethods if we're using inventory.

@adamreichold
Copy link
Member

This is now feature complete, but I'm unsure how to test it: ::pyo3 is always available in the test suite...

I think adding a separate crate like those in the examples folder as a kind of build test that is explicitly invoked by the CI could work?

@mejrs
Copy link
Member

mejrs commented Nov 25, 2021

This is now feature complete, but I'm unsure how to test it: ::pyo3 is always available in the test suite...

If we do away with the self-dependency:

# features needed to run the PyO3 test suite
pyo3 = { path = ".", default-features = false, features = ["macros", "auto-initialize"] }

::pyo3 will no longer be available within pyo3 itself, and we can use unit tests for this (I think):

#[test]
fn test(){
    #[crate::pyclass(pyo3_path=crate)]
    struct Foo;
}

@birkenfeld
Copy link
Member Author

#1817 seems to be blocked on some other things though.

@mejrs
Copy link
Member

mejrs commented Nov 26, 2021

It should still raise a compilation error if types/functions/traits from crate and ::pyo3 get mixed up.

For example, this test doesn't compile:

pub struct Foo;

pub fn take_foo(_: Foo){}

#[cfg(test)]
#[test]
fn bar(){
    let foo = crate::Foo;
    ::pyo3::take_foo(foo);
}

@birkenfeld
Copy link
Member Author

@davidhewitt do you have an idea how to generate unique constant names for the inventory pymethods?

@davidhewitt
Copy link
Member

Sorry was hoping to give this a full read soon! Can we get away with just doing const _ instead of giving it a name?

@birkenfeld
Copy link
Member Author

Oh wow, that works? facepalm it does!

@birkenfeld birkenfeld force-pushed the pyo3_path branch 2 times, most recently from caa3d06 to 515eb0d Compare November 28, 2021 17:50
Copy link
Member

@davidhewitt davidhewitt left a comment

Choose a reason for hiding this comment

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

If we do away with the self-dependency:

Seeing this very clever idea convinced me to take another look at that - see #2039


## I want to use the `pyo3` crate re-exported from from dependency but the proc-macros fail!

All PyO3 proc-macros (`#[pyclass]`, `#[pyfunction]`, `#[derive(FromPyObject)]`
Copy link
Member

Choose a reason for hiding this comment

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

It might be nice to document this alongside each macro too. I really like serde's reference on attributes e.g. https://serde.rs/container-attrs.html

I've tried to do this a bit for #[pyfunction] in guide/function.md, but it's sorely lacking. There's also some docs in pyo3-macros/lib.rs.

Copy link
Member Author

Choose a reason for hiding this comment

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

Maybe I'll tackle this (also for the other attributes) in a follow-up? This attribute is certainly the least-needed one :)

Copy link
Member

Choose a reason for hiding this comment

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

Yep I think that's fine!

guide/src/faq.md Outdated

However, when the dependency is renamed, or your crate only indirectly depends
on `pyo3`, you need to let the macro code know where to find the crate. This is
done with the `pyo3_path` attribute:
Copy link
Member

Choose a reason for hiding this comment

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

How do you feel about it being #[pyo3(crate = "...")] rather than pyo3_path, so that we follow exactly the convention set by serde?

Copy link
Member Author

Choose a reason for hiding this comment

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

That was my first try, but unfortunately incompatible with the way we parse keywords (you can't pass crate to syn::custom_keyword, and r#crate is invalid for some reason).

Copy link
Contributor

Choose a reason for hiding this comment

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

I believe it's Token![crate]. FWIW, I personally prefer the crate = "…" syntax as well 🙂

Copy link
Member Author

Choose a reason for hiding this comment

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

Perfect! (Also thanks a lot for your other PR, I've transplanted it onto this branch to see if the tests pass.)

@birkenfeld
Copy link
Member Author

birkenfeld commented Dec 8, 2021

So, rebased against main and moved the test into lib, as suggested.

However, now I get a curious error which I can't make sense of:

error: macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths
   --> src/test_hygiene/pymethods.rs:12:1
    |
12  | #[crate::pymethods]
    | ^^^^^^^^^^^^^^^^^^^
    |
    = note: `#[deny(macro_expanded_macro_exports_accessed_by_absolute_paths)]` on by default
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #52234 <https://github.com/rust-lang/rust/issues/52234>
note: the macro is defined here
   --> src/class/impl_.rs:161:9
    |
161 | /         macro_rules! $generate_macro {
162 | |             ($cls:ty) => {{
163 | |                 unsafe extern "C" fn __wrap(
164 | |                     _slf: *mut $crate::ffi::PyObject,
...   |
184 | |             }};
185 | |         }
    | |_________^
...
189 | / define_pyclass_setattr_slot! {
190 | |     PyClass__setattr__SlotFragment,
191 | |     PyClass__delattr__SlotFragment,
192 | |     __setattr__,
...   |
198 | |     setattrofunc,
199 | | }
    | |_- in this macro invocation
    = note: this error originates in the attribute macro `crate::pymethods` (in Nightly builds, run with -Z macro-backtrace for more info)

Any ideas? Does the generate_macro need to be macro_exported?

@davidhewitt
Copy link
Member

Coverage failure is rust-lang/rust#91689 - I guess we'll just have to live with it broken for a day or two.

Copy link
Member

@davidhewitt davidhewitt left a comment

Choose a reason for hiding this comment

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

Thanks, this looks great to me! I think it's a nice piece of flexibility this adds for users.

Just one thought about variable names, otherwise please merge!

@@ -43,6 +43,19 @@ impl Parse for NameAttribute {
}
}

/// For specifying the path to the pyo3 crate.
#[derive(Clone, Debug, PartialEq)]
pub struct PyO3PathAttribute(pub Path);
Copy link
Member

Choose a reason for hiding this comment

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

Now that we have crate = I'd be nice to call this PyO3CrateAttribute or CrateAttribute, similarly some variables called pyo3_path might want to be called pyo3_crate (or krate?! - then expanding it inside proc-macros as #krate is no longer than macro_rules $crate)

Copy link
Member Author

Choose a reason for hiding this comment

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

Agreed, done!

@davidhewitt
Copy link
Member

Perfect! Thanks @birkenfeld and @danielhenrymantilla!

@davidhewitt davidhewitt merged commit 469d72a into main Dec 9, 2021
@davidhewitt davidhewitt deleted the pyo3_path branch December 9, 2021 20:27
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.

5 participants