hey folks, I'm trying to include <https://github.c...
# general
e
hey folks, I'm trying to include https://github.com/emmett-framework/granian in a PEX, but get the error
Copy code
Traceback (most recent call last):
  File "/Users/dvangeest/.pex/venvs/70bb4cb6cccc66b8cb8e4df6a356c265d7a23b11/779eb2cc0ca9e2fdd204774cbc41848e4e7c5055/pex", line 329, in <module>
    func = namespace = getattr(namespace, attr)
                       ^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: module 'granian' has no attribute 'cli'
any ideas?
more specifically:
Copy code
pex_binary(
        name="bin-local",
        dependencies=[":service", "3rdparty/python:common#granian"],
        execution_mode="venv",
        layout="packed",
        script="granian",
....
b
The first thing I'd try is "eject" from pants: get it working outside of pants and then use that to inform the next steps. In this case, I'd start with https://github.com/pex-tool/pex and building/running a pex that runs
granian
. Something like
pex granian --script=granian
. If you didn't have me here to guess at a starting point, I'd suggest looking into the sandboxes and editing `__run.sh`: https://www.pantsbuild.org/stable/docs/using-pants/troubleshooting-common-issues#debug-tip-inspect-the-sandbox-with---keep-sandboxes For this, there's two sandboxes that are likely relevant: • the sandbox for building the pex (i.e. tells us which flags are being passed) • the sandbox for running I'd inspect the second one, and then copy the invocation into the
__run.sh
of the first one (to have it all in one place), and check it reproduces, and then cut args out to get to the minimal reproducer.
w
While I likely wouldn't build it this way myself (but that might be because I'm skewed from using FastAPI which requires gunicorn, uvicorn, etc...), does something like this work for you?
Copy code
⏺ demo/src % tree
.
|____hellogranian
| |______init__.py
| |____main.py
|____BUILD
Copy code
python_sources(
    name="lib",
    sources=["**/*.py"],
)

pex_binary(
    name="bin",
    dependencies=[":lib", "//:reqs#granian"],
    entry_point="granian",
    args=["--interface", "asgi", "hellogranian.main:app"],
)
Copy code
demo % pants run src:bin
[INFO] Starting granian (main PID: 4163)
[INFO] Listening at: <http://127.0.0.1:8000>
[INFO] Spawning worker-1 with PID: 4169
[WARNING] ASGI Lifespan errored, continuing without Lifespan support (to avoid Lifespan completely use "asginl" interface)
[INFO] Started worker-1
[INFO] Started worker-1 runtime-1
b
A key change there being
entry_point="granian"
instead of
script="granian"
, I suspect
w
Yeah, and passing in the args. As in,
granian
is the “thing” we care about, while the app is just an arg passed in. FastAPI and such also allow running as a script (aka,
if __name__ == “__main__”
can sometimes be handy for dev). Although, I guess FastAPI also has a CLI now, that I’ve just never used - so maybe that would run similarly to this. But yeah, one of probably 10 ways to run this
e
thanks for all of that! I did end up working around it by just starting granian programmatically from
main
. I will try the
entry_point
idea as well.
👍 1