Bazel is amazing in providing reproducible builds, but the rules_python is still under heavy development. Pytest usage from within bazel can be a bit tricky, as can be seen from:
- A stack overflow answer where we make each test file a script and need to remember to add an
if __name__ == "__main__"block. - Answer within the same thread where we create a wrapper macro, which allows us not need the solution from above.
All was well until I started to use rules_proto_grpc. I could not get the example code to work if I was using pytest as my testing framework.
Some important things to note:
rules_proto_grpcis defining a python library with usage ofimportsflag. This is needed because the path to the python files in thebazel-bindirectory arebazel-bin/idl/path/to/build/file/name_pb/idl/for/importwhereidl/path/to/build/filesegment mirrors the path of the BUILD file relative to yourWORKSPACEandidl/for/importsegment mirrors the import path found in the.protofile. Benefits of such approach as far as I can see are manyfold:- The import path and the bazel path are decoupled.
- No two rules are going to overwrite the same files.
- Probably something else which I am not yet aware.
- This
importsusage in turn modifies the PYTHONPATH such that we could import the generated python package viafrom idl.for.import import foo_pb2 as foo_pb. - Everything works until
pytestcomes along, which is doing some test discovery and is also modifying the paths which python checks when importingidl.for.import. The way it is doing this is:- We are running inside
bazel-bin/lib/foo/foo_test.runfiles/<my-workspace-name>directory (assuming the test rule we are running from is calledfoo_test). - Pytest crawls the directories it can see and notices that we have python
files within
idl/path/to/build/file/name_pb/idl/for/importand happily prepends the list of paths to search python modules from so our previously workingidl/for/importstops working because both of the paths haveidlin the prefix and Python checks theidldirectory and does not go intoidl/path/to/build/file/name_pb/idldirectory because it is failing fast in this case. - Everything works if pytest is doing the crawl after we load the
idl.for.importmodule because of how python module caching works as alluded here.
- We are running inside
So this repo shows a solution:
pass an extra
--import-mode=importlib, which will become later the default.
If you know a better solution, let me know via an issue or a PR to this example!