pyRPC
← Back to Blog

v0.3.2 - Cleaner terminal, smarter prompts, no more :app confusion

·6 min read

v0.3.2 is a UX-focused release. No new features, no breaking changes — just the kind of polish that makes a tool feel like it was built for people. Three areas got attention: the first-run setup flow, the terminal output, and the import path handling.

The setup flow: from text input to arrow keys

The old framework prompt used rich.prompt.Prompt.ask(), which showed the choices but required you to type the name. A small friction, but friction at the very first impression sets the tone.

Before:
  Which web framework are you using? (fastapi/flask/asgi): _

After:
  ? Which web framework are you using?
    > fastapi
      flask
      asgi

Now powered by questionary, the framework picker is an arrow-key select menu — the same pattern you see in Better Auth, Prisma, and every modern CLI. One keypress instead of typing.

Entry point: what does :app even mean?

The old prompt read Entry point (e.g. app.main:app) with a default of app.main:app. This looks like Uvicorn’smodule:variable convention — but pyrpc’sdev command never used the :app part. It just imports the module to trigger @rpc decorator registration. The app variable is created internally.

The prompt now says Python module to scan for @rpc procedures (e.g. main, app.main)with a default of main. No more colon syntax. No more confusion about what variable name to use. Just the module path.

Why main gave “No module named ‘main’”

This was the bug that triggered the whole release. A user ran pyrpc dev, entered main at the prompt, and got:

Error regenerating types: No module named 'main'

The problem: reload_module() calls importlib.import_module("main")but never ensured the current working directory was on sys.path. The serve command’s _import_module() helperdid add it (sys.path.append(os.getcwd())), but the dev command’s regenerate() went straight toreload_module() without it.

The fix follows Uvicorn’s standard: sys.path.insert(0, os.getcwd())before any user module import. We also changed _import_module()from append to insert(0, ...) for consistency. Now main finds main.py in the current directory, just like uvicorn main:app does.

Terminal output: less noise, more signal

The old pyrpc dev terminal was loud:

Generating initial TypeScript types...
  (big Panel box)
  Watching 1 directories for Python changes...
  pyrpc> type help for commands
  INFO:     Will watch for changes in these directories: [...]
  INFO:     Started reloader process [21332] using WatchFiles
  INFO:     Started server process [14932]
  INFO:     Waiting for application startup.
  INFO:     Application startup complete.

The new output:

  ✓ Types regenerated (3 procs)

  pyRPC dev server  http://127.0.0.1:8000/rpc
  Types: node_modules/@pyrpc/types/src/index.ts

type help for commands
pyrpc>

Specific changes:

  • Uvicorn logs suppressed--log-level error on the subprocess kills 6 lines of reloader/server spam.
  • Panel box gone — replaced with a clean 2-line status block. No borders, no padding, just what you need.
  • pyrpc> prompt actually renders — was using raw input() which showed [cyan]pyrpc>[/cyan] as literal text. Now uses console.input().
  • No “Generating initial types” — types generate silently on startup.
  • No “Watching X directories” — the user doesn’t need to know.
  • Consistent symbol prefixes✓ for success, âœ- for error, âš  for warning, â-‹ for in-progress. Borrowed from the pattern Next.js, tRPC, and Better Auth use — scannable at a glance.

Help text updated everywhere

Every command argument that referenced the old app.main convention was updated to match the new wording: Python module to scan for @rpc procedures (e.g. main, app.main). Consistent across dev, serve, and pull.

Full changelog

  • Dependencies: Added questionary>=2.0.0 for interactive CLI prompts.
  • Setup flow: Framework picker changed from rich.prompt.Prompt.ask (text input) to questionary.select (arrow-key menu). Entry point prompt simplified to Python module to scan for @rpc procedures (e.g. main, app.main) with default main.
  • Import path: sys.path.insert(0, os.getcwd()) added in dev() before module import. _import_module() changed from sys.path.append to sys.path.insert(0, ...). Now matches Uvicorn’s standard --app-dir behavior.
  • Terminal DX: Uvicorn subprocess uses --log-level error. Panel box replaced with minimal status lines. input() replaced with console.input() so Rich markup renders. Removed “Generating initial types”, “Watching X directories”, and “Types regenerated” verbose lines. Error/success messages use consistent ✓, âœ-, âš , â-‹ symbol prefixes.
  • Help text: Updated dev, serve, and pull argument descriptions to match new wording.

See the full changelog for details.