Uh oh!
There was an error while loading. Please reload this page.
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.
siddharthab
commented
Nov 29, 2023
Test is failing because of a timeout in requirements_test. All other tests are passing. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
siddharthab
left a comment
There was a problem hiding this comment.
Thanks for the review.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
philsc
commented
Dec 7, 2023
From a Bazel point of view, would it not be more accurate to say that the |
rickeylev
commented
Dec 7, 2023
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 |
siddharthab
commented
Dec 8, 2023
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). |
aignas
commented
Dec 8, 2023
@siddharthab, what do you think about making this behaviour optional via the gazelle directives? |
siddharthab
commented
Dec 8, 2023
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. |
aignas
commented
Dec 8, 2023
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. |
siddharthab
commented
Dec 8, 2023
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. |
Uh oh!
There was an error while loading. Please reload this page.
philsc
commented
Dec 12, 2023
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? |
siddharthab
commented
Dec 12, 2023
Done. |
aignas
left a comment
There was a problem hiding this comment.
LGTM, could you add a CHANGELOG.md for the fix please?
siddharthab
commented
Dec 20, 2023
Done. |
Uh oh!
There was an error while loading. Please reload this page.
As per Python spec,
__init__.pyfiles 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__.pyfiles (to avoid cycles in Bazel), and hence a non-empty__init__.pyfile is added to thesrcsattribute of everypy_librarytarget.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: