-
Notifications
You must be signed in to change notification settings - Fork 554
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
fix(gazelle): __init__.py in per-file targets #1582
Conversation
As per Python spec, `__init__.py` files are depended upon by every file in the package, so let's make sure that our generated targets also understand this implicit dependency. Note that because Python module dependencies are not a DAG, we can not depend on the Bazel target for `__init__.py` files (to avoid cycles in Bazel), and hence a non-empty `__init__.py` file is added to the `srcs` attribute of every `py_library` target. The language spec also says that each package depends on the parent package, but that is a less commonly used feature, and can make things more complex.
Test is failing because of a timeout in requirements_test. All other tests are passing. |
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 for the PR!
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 for the review.
From a Bazel point of view, would it not be more accurate to say that the |
No. But also Yes. No in that, if an init.py only imports one sub module among many, init.py doesn't depend on the others. In theory, you could have a module that isn't part of the whole package and people opt-into pulling it in. e.g.
With the above, the expensive file only gets included if you depend on the expensive target. Yay, faster builds! But... Yes in that, in practice, maintaining such a distinction is hard once any code or imports go into |
I think people are often confused about the spec of the What people have done in practice is used the package to re-export symbols from the modules in the package, so they can refactor code in between the modules in a package without breaking downstream uses. This is just a level of indirection that people opt into and they have chosen the If a package is supplying a public interface through the PS: I am new to Python and in trying to understand the language spec, and what Gazelle can do for it, I tried to experiment with different things in https://github.com/siddharthab/bazel-gazelle-python including transitive closures of module groups, but I think simply repeating the source file is as good a thing as any (with perhaps the label name used to resolve ambiguities for import resolution when multiple targets have the same file in srcs). |
@siddharthab, what do you think about making this behaviour optional via the gazelle directives? |
Sounds acceptable to me. But note that this is the correct behavior as per the language spec. It will be surprising if it does not happen by default. |
My thinking is that this change may be a breaking change given that this makes the structure of the bazel dep graph more rigid and in certain cases the users may want to have time to migrate - first this feature becomes available, but is not the default, then in the subsequent version it becomes available as a default but users can still switch back and then the version after that we can remove the old behaviour. |
Sounds good. I have now put this feature behind a directive. Let me know if you would like to see more tests. Also happy to address any nits. If you would rather make changes to my branch directly, that is also OK. |
I know I'm piling on to the pile, so feel free to ignore this.
WDYT about amending the Summary with a link to the relevant part of the spec? |
Done. |
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.
LGTM, could you add a CHANGELOG.md
for the fix please?
Done. |
As per Python spec,
__init__.py
files are depended upon by every filein the package, so let's make sure that our generated targets also
understand this implicit dependency. Note that because Python module
dependencies are not a DAG, we can not depend on the Bazel target for
__init__.py
files (to avoid cycles in Bazel), and hence a non-empty__init__.py
file is added to thesrcs
attribute of everypy_library
target.The language spec also says that each package depends on the parent
package, but that is a less commonly used feature, and can make things
more complex.
From importlib docs:
From import language reference: