Hi, so I was finally able to get pants running ins...
# general
a
Hi, so I was finally able to get pants running inside a RHEL based Docker container and after adding bare minimum code, I did
./pants tailor
and got following output:
(pants) [root@9a5752857756 pants-training]# ./pants tailor
Created src/client/BUILD:
- Add python_sources target client
- Add pex_binary target greeter_client
Created src/proto/BUILD:
- Add protobuf_sources target proto
Created src/server/BUILD:
- Add pex_binary target greeter_server
- Add python_requirements target reqs
- Add python_sources target server
(pants) [root@9a5752857756 pants-training]#
After that I did
./pants package src/server/greeter_server.py
and encountered following error:
(pants) [root@9a5752857756 pants-training]# ./pants package src/server/greeter_server.py
05:54:49.45 [ERROR] 1 Exception encountered:
IndexError: string index out of range
(pants) [root@9a5752857756 pants-training]#
Could someone please help? My goal is to generate the stubs from the proto files and publish them as python wheels so that I can pip install them and use them in client and server code. Following is how my repo looks:
Copy code
(pants) [root@9a5752857756 pants-training]# /tree.sh
Tree of: /root/pants-training
.
|__pants
|__src
| |__proto
| | |__helloworld.proto
| | |__BUILD
| |__server
| | |__requirements.txt
| | |__greeter_server.py
| | |__BUILD
| |__client
| | |__greeter_client.py
| | |__BUILD
|__.pants.d
| |__exceptions.log
| |__exceptions.471.log
| |__run-tracker
| | |__pants_run_2022_08_19_05_55_17_757_5184323f0cce42809255925d6987ccbe
| | | |__logs
| | |__pants_run_2022_08_19_05_55_11_114_e3698008d5454c2aa6d584bf5680eaed
| | | |__logs
| | |__pants_run_2022_08_19_05_53_24_325_48f808a97d1c4c8aa6faad9261780e60
| | | |__logs
| | |__pants_run_2022_08_19_05_48_46_160_81dfa93f029b44f0bc191a8560c255c6
| | | |__logs
| | |__pants_run_2022_08_19_05_54_45_113_dee6bff2c02a414c94a754c60781580c
| | | |__logs
| | |__pants_run_2022_08_19_05_54_08_319_a3964cb6faa049c7917933ccfe3ede11
| | | |__logs
| |__pants.log
|__pants.toml
|__.pids
| |__855f83c10083
| | |__pantsd
| | | |__socket
| | | |__pid
| | | |__process_name
| | | |__fingerprint
| |__.lock.pantsd
r
Disclaimer: I have never used protobuf from pants. But if you want to create a python you need to define a
python_distribution
inside the BUILD file and then run `./pants package src/server`: https://www.pantsbuild.org/docs/python-distributions#the-python_distribution-target
a
Thanks but I want to create wheels from the stubs generated from proto file. How do I generate them using pants? Once those stubs are generated which will be .py files, I can define the
python_distribution
target for them and then package them as a wheel.
r
yes that’s what
python_distribution
does. In
dependencies
you need to add the dependency on underlying source code. In this case you can use the
python_sources
target called
server
which I guessed from this
Copy code
Created src/server/BUILD:
 - Add pex_binary target greeter_server
 - Add python_requirements target reqs
 - Add python_sources target server
So
dependencies=[":server"]
a
So I need to make this change in
src/server/BUILD
?
r
yes if you want to package the
server
a
Ok, but I don't want to package the server. I want to package the code generated from proto file
src/proto/helloworld.proto
. The code isn't present here in the repo because it is not generated yet. I am trying to figure out how to generate that using pants and that's where I need help.
To give you the context: I am referring this link and I am kind of lost between Step 3 and Step 4. Step 3 says to use ./pants tailor goal to generate BUILD files and then Step 4 starts talking about importing the generated stubs in the code. How do I generate the stubs in the first place? I added grpc=True in my protobuf_sources target.
r
So it seems as long as you use the correct import statement, pants will auto generate it. As shown in this code https://github.com/pantsbuild/example-codegen/blob/main/src/python/protobuf_examples/simple_example_test.py
By the if you want to run it manually you can use this command as mentioned in the BLUE doc block.
Copy code
./pants export-codegen ::
a
Ok, so does it mean pants will auto generate at run time based on import statement?
I tried
./pants export-codegen ::
but ran into some error.
r
So if you package your server, it will run the auto generation at that time not during run time
Yeah I would debug
export-codegen
and make sure it runs successfully before moving to packaging
So when you run any package goal like
python_distribution
or
pex_binary
, what pants does in background is 1. Run export-codegen (my guess) 2. package the export-codegen generated output with these goals
So it seems what I wrote above isn’t entirely correct. I found this when looking around the doc https://www.pantsbuild.org/docs/plugins-codegen#add-dependency-injection-optional
Pants will not actually write the generated files to disk, except when running
./pants export-codegen
. Instead, any targets that depend on the protocol targets will cause their code to be generated, and those generated files will be copied over into the “chroot” (temporary directory) where Pants executes.
a
Ok, let me shell out some more details of what I want to do: 1. Generate stubs from proto files (save them anywhere within the repo). There's an orange code block just below the blue code block you mentioned which says that the generated code will be in the same directory as proto files. I am not concerned about their location at this point. Trying to figure out the pants command for this purpose. 2. Package those stubs as a wheel so that both client and server can pip install them and use them. 3. Package client as another python wheel so that I can pip install the client to use it anywhere. 4. Package server as a Docker image and deploy it on a remote server. Once the server is deployed, I can pip install the client anywhere and connect to the server. So now if the code is generated at the time of packaging, would it solve my purpose is what I am wondering?
r
Yes it should work. Just to make sure, you will host that built wheel somewhere so that later you can install it anywhere you want.
It’s like publishing any python package. Generally you publish it on public pypi server. But here you might need your own private server. pants even does that for you using
./pants publish
a
Yes, I will host the wheel. But let's talk about client wheel - when I would pip install it, it would try to also install all the dependencies...just like how a normal pip install works. But because the dependency wasn't saved anywhere apart from pants own temp directory and because it wasn't saved anywhere, it would not be published anywhere for the pip to resolve the dependency. Did this make sense?
r
It would package all the underlying dependencies. So it will include the generated python files from protobuf also. As long as pants figure out the underlying dependency (which happens here based on the import statement), it will package it. As I said you will have to add explicit
dependencies=[:server]
in your
python_distribution
you can check what gets packaged by also unzipping the generated wheels. It’s a zip file https://stackoverflow.com/questions/32923952/how-do-i-list-the-files-inside-a-python-wheel
a
Ok, I think I need to try this. One last thing - why do I need to add
dependencies=[:server]
in server's own BUILD file
src/server/BUILD
. Server is dependent on proto.
r
python distribution needs dependency on some source code explicitly. This is bit of gotcha (at least for me).
a
Hi @refined-addition-53644, Good Morning! I am trying to run
./pants package src/server:grpc-server
and get this error. Do you have any idea?
h
Ok, so does it mean pants will auto generate at run time based on import statement?
Correct!
Assuming your source roots are set up correctly, Pants will see an import of the stub, and infer from this that it must generate them.
export-codegen
is useful for debugging, but not needed in normal usage.
Another useful thing for debugging is to add an explicit dependency (in
dependencies=
) from the target using the stubs to the
protobuf_sources
target owning the .proto files
Then you can see if the problem was a failure of implicit dependency inference
That said,
IndexError: string index out of range
is a very bad error message of course
Can you bundle up the code that reproduces this error, and push it to github? Then I can debug easily
Pants usually infers dependencies based on
import
statements. The one case where it cannot is a
python_distribution
must have an explicit dependency at least on its entry point (usually Pants can infer the entry point’s dependencies, and so on)
For building those wheels you were mentioning, for each one, the first question to ask is: do you want to hand-write a setup.py (or some other pep517 backend config) or do you want Pants to generate it for you?
The rest of my answers will depend on the answers to that 🙂
a
Another useful thing for debugging is to add an explicit dependency (in
dependencies=
) from the target using the stubs to the
protobuf_sources
target owning the .proto files
If I understand the above statement correctly, then I must add the dependencies of both client and server in the BUILD file of proto (assuming you are familiar with the repo structure I posted yesterday).
Can you bundle up the code that reproduces this error, and push it to github? Then I can debug easily
On it way!
h
If I understand the above statement correctly, then I must add the dependencies of both client and server in the BUILD file of proto (assuming you are familiar with the repo structure I posted yesterday).
Well, both the client and the server should be inferring the dependency on the proto, we just need to debug why they are not
👍 1
And note that the dependency would go in the BUILD file of the server and the BUILD file of the client, on the proto, not in the BUILD file of the proto
a
Pants usually infers dependencies based on
import
statements. The one case where it cannot is a
python_distribution
must have an explicit dependency at least on its entry point (usually Pants can infer the entry point’s dependencies, and so on)
I have a question from this repo for import statements. The python code
simple_example_test.py
in
src/python/protobuf_examples
imports the stubs from
simple_example.v1.person_pb2
. But according to this BUILD file, the stubs should be generating in
src/python
.
And note that the dependency would go in the BUILD file of the server and the BUILD file of the client, on the proto, not in the BUILD file of the proto
Yeah, so for now I have just added the dependency in server. Let me push the code real quick and come back to the messages later.
h
Yes, src/python is a “source root” (see https://www.pantsbuild.org/docs/source-roots)
If src/python is a source root, then
src/python/foo/bar.py
is imported as
foo.bar
Think of source roots as sys.path entries - they are where the roots of the package hierarchy are
So at least some of the issues you’re facing may be due to misconfigured source roots
You already have source roots in a repo, even if it’s not using Pants! They are just “the directories that are roots of the package hierarchy in my import statements”. You just need to figure out what they are and tell Pants about them
Often they are the repo root itself, or a directory called
src
or
src/python
(pants knows about these automatically). Or sometimes they are just a directory named after the project, in which case you have to tell Pants about them.
a
Ok, so the first thing that came to my mind was also the source roots and then I checked the source roots for the example-codegen repo which are the following:
Copy code
.
src
src/protobuf
src/python
src/thrift
The source roots and everything made sense but then following comment and code line confused me:
Copy code
# This will generate files under `src/python`, rather than `src/protobuf`, which is convenient
    # so that we do not need to add `__init__.py` files to `src/protobuf`. See
    # <https://www.pantsbuild.org/docs/protobuf-python#protobuf-and-source-roots>
    python_source_root="src/python",
If the code is being generated in src/python then why are we importing it from
simple_example.v1
?