-
Notifications
You must be signed in to change notification settings - Fork 52
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
Replace "import *"; define __all__; no implicit relative imports #17
Conversation
tobych
commented
May 24, 2020
•
edited
edited
The 2
|
Out of curiosity is this an idiomatic thing to do? It seems like it's a bit of a bummer to have to redundantly state what's imported for each module? |
Some Python programmers insist that it's the only reasonable approach, but that is by no means an universally agreed upon truth. There are downsides for it as you mention and I would suggest that you choose based on personal preference and experience rather than perceived legitimacy as an idiom. |
Ah ok, thanks for the clarification! I'd personally like to at least ensure that we're not accidentally exporting private internals (like |
Unfortunately you can't really do that in Python idiomatically, it's one of the things I consider a significant language flaw. The unidiomatic fix to this is to wrap your entire module into a function that hides the imports into its lexical scope but literally no one actually does that. |
@whitequark, I like your style. "... but literally no one actually does that." Made me laugh. :-) This is indeed a big ol' PR from someone you'd never heard of until last week isn't it: I'm not surprised it wasn't merged straight away. It's been fun to see my other smaller commits get merged in, especially the README one! Over the weekend I gradually came to terms with the fact that I would probably need to make a case for this one, and that this would be a good thing to have to do. So... I'm very sure that what I'm suggesting has big benefits and few costs. But I clearly need to make a case, and I might be wrong. This is my first open source project for a while and at work I'm used to being able to pick up the phone or get on call: doing this by writing messages is hard to do without failing to get anywhere in a reasonable time, or failing to convince anyone, or very occasionally, failing to be thought a decent person. @whitequark I can see you've a lot of experience (probably more than @alexcrichton) with Python (I've done a bit of work trying to figure out who's who on this project and in WebAssembly world). Would you be up for a phone call or something? Or is that just not a thing in open source world? Meanwhile I'll write a little more here. Because maybe I can convince, or learn, in less time than I was thinking. And it'll be good practice anyway and no doubt useful for me to try writing about something technical I feel strongly about.
That's all for now! I hope this makes sense, regardless of its ability to convince. |
For personal reasons I am not able to do phone calls. But I've heard that some people working on OSS do them, so don't take this as an universal.
To be clear, I am not rejecting this PR (or proposing that it be). I understand that a lot of people have very strong opinions on how code should be structured in Python and ultimately a lot of this is a personal call. That's fine though and you don't need to convince me otherwise as I'm not blocking anything! In the projects I maintain I would not accept it, but I am not the maintainer of wasmtime-py and this is not my call at all. All I'm doing here is providing @alexcrichton with the context: namely that the approach here is not quite as overwhelmingly prevalent as some other things we discussed in previous PRs, and that I believe it should be considered on its own technical merits rather than simply because it is the idiom to use. I'm sure he'll consider your points fairly and do something that's right for wasmtime-py. |
Hi @whitequark. Thanks, nice! Okay, message received about phone calls. I did realize you're not "rejecting" the PR, and I know you're a contributor and not a committer. But I've assumed that your opinion is likely to be trusted by @alexcrichton more than mine, so figured I'd try to convince you, and if that fails, Alex. Your comment has shown me I should be explicit about my intent here, just as I try to be in code. :-) Also I really liked "There are downsides for it as you mention and I would suggest that you choose based on personal preference and experience rather than perceived legitimacy as an idiom." and wanted to address some of the upsides and share my experience with @alexcrichton (and you too) to help him decide. If you don't mind, and have a moment, what are the downsides, from your point of view? One is no doubt having to figure out where each name is. If you've an IDE, it'll help you do that in milliseconds, of course. And out of interest, for context, what IDE do you use? I'm beginning to think my reliance on PyCharm might make me sounds like a bit of a zealot, or like I'm all about enabling PyCharm's squiggling habit. |
I don't. I use Sublime Text 3. That makes maintaining import lists manual work, which essentially causes issues during testing (because a name might not be available), and during PR submission (because I have to remove unused names). In my experience figuring out where a name comes from is not something that is a significant obstacle, even in projects I'm unfamiliar with, provided that the project does not have the same name in many different namespaces. In general I think that it is a good idea to avoid having identical names in multiple namespaces (with some exceptions that don't apply to wasmtime, e.g. sometimes it makes sense to have identical names that are always qualified when used), because you are not always able to rely on an IDE. You can't use an IDE when you're reviewing a PR on GitHub! On the other hand ST3, GitHub, and many other editors (maybe even ctags?) provide a "jump to definition" feature, which makes it a lot less important to be able to see at a glance where an (unique) imported name comes from just by looking at the source. Read-only indexing is a lot easier to add to an editor than a feature that rewrites source code so I think it is reasonable to expect such a feature to be widely available. |
Interesting. And a good data point. I'm usually kinda "What! No IDE!" so its good to see another data point. I guess if I was reviewing a big PR, which I've never done, I might pull the commit into my working copy and review it using the IDE. Perhaps by "not always able to rely on an IDE" you're thinking of situations where you're just looking at GitHub and don't even have your development environment to hand. |
I don't normally check out the source code of a PR I am reviewing, I'm using just the GitHub interface for that. In my experience this is a farily common approach since it'd be laborous to track down the right line to comment on when you want to raise some concern. |
Thanks for the background provided here @tobych! I am indeed trying to boot up on all the context here because I'm no Python wizard myself. To clarify something I'm wondering about, do the red squigglies in your IDE come from the usage of |
You're welcome. Glad it helped. The (yellow) squigglies show if you're importing a name from a module with an I want to see those squigglies if I'm importing something I shouldn't be. I don't see them in Adding the |
Oh it's definitely a bug we should fix if any names are imported from |
My motivation for this work is not because there are bugs in the package as it stands. It's about being able to change the code, perhaps refactoring it, with confidence. I see potential for improving the architecture of the package, but wouldn't want to move forward without sorting out the imports problems. What I'll do next is separate out the work into three commits, ordered as follows:
This is the order I'd typically do this work on a codebase. The commits will also, it seems, be ordered by controversiality. |
Defining `__all__` in each module makes it explicit what names are intended to be used by client modules. An "import *" from a module will only import names that included in `__all__`, if it's defined. IDEs can warn you if you try to explicitly import something not included in a module's `__all__`.
Using explicit, individual imports instead of "from X import *" makes it clear to contributors (and others reading the code) which module each name is being imported from, without relying on an IDE to tell you. None of the imports in __init__.py are now being relied on, in the source. The unit tests still rely on them. Ideally they wouldn't, as this would make the unit tests similarly clearer, but I don't want to touch the unit tests in this commit, or really in this PR at all.
@alexcrichton Okay, I've split this PR into two commits, as planned. I'm envisaging you merging the |
Sorry I'm still a bit confused. To be clear I would prefer to not list everything in I've been trying to figure out why this change was motivated in the discussion here. It sounded related to your IDE but I still don't feel like I've pinned down exactly why? I'm sort of confused... In any case for any |
It's been quite some time since this was opened so I'm going to close this, but if it's rebased we can continue the discussion! |