pyhabitat v1.0.44 notes for CLI arguments
Date: 2025 11-November 28
That's a great goal. Maintaining zero dependencies while improving type safety and argument handling is a common challenge in pure Python projects.
The current if args.command section works, but as you pointed out, it's not type-safe and can dump "ill-fitting args" like path and debug into every called function, leading to potential TypeError exceptions.
You can achieve type-safe, dynamic argument passing in vanilla Python using the built-in inspect module.
🛠️ Inspecting Function Signatures for Better CLI
The inspect module is the standard Python tool for examining live objects, including function signatures. Here is how you can use it to check if a dynamically retrieved function accepts the path or debug arguments before passing them.
Revised if args.command Section
You should replace the argument preparation part of your existing code with logic that uses inspect.signature.
Python
if args.command:
func = getattr(pyhabitat, args.command, None)
if callable(func):
# 1. Get the function's signature
import inspect
sig = inspect.signature(func)
# 2. Prepare arguments to pass, checking against the signature
kwargs = {}
# Check for 'path' argument
if 'path' in sig.parameters and args.path:
kwargs['path'] = Path(args.path)
# Check for 'debug' argument
if 'debug' in sig.parameters and args.debug:
kwargs['debug'] = args.debug
# Check for 'kwargs' or positional arguments to allow general passing
# (Optional, for future-proofing, but not needed if only checking path/debug)
# if any(p.kind in (p.VAR_KEYWORD, p.VAR_POSITIONAL) for p in sig.parameters.values()):
# # Can pass anything, so you could optionally pass path/debug even if not explicitly named
# pass
# 3. Execute the function with only the pertinent arguments
print(func(**kwargs))
return
else:
print(f"Function not callable. Check spelling: {args.command}")
return
How this achieves Type Safety:
-
sig = inspect.signature(func): This gets an object representing the function's official signature (def my_func(a, b, *, c=1):). -
'path' in sig.parameters: This checks if a parameter namedpathis explicitly defined in the function's signature. -
The
kwargsdictionary only gets thepathvalue if the user supplied it AND the target function is defined to accept it. -
This prevents the
TypeError: <function> got an unexpected keyword argument 'path'error that you were correctly concerned about.
🎯 Vanilla Python CLI Design
To make this approach fully effective, you need a clear convention for your pyhabitat functions:
1. Standardizing CLI Arguments
All functions in pyhabitat should either:
-
Only accept arguments they use (e.g.,
def get_version():). -
Explicitly accept the common CLI arguments if they are intended to be configurable via the command line (e.g.,
def get_info(path=None, debug=False):).
2. Using argparse for CLI Definition
You already have a good foundation with argparse. The current parser correctly defines the universal options (--path, --debug, --clear-cache, --list, command).
To summarize the vanilla Python approach:
| Feature | Tool | Implementation |
|---|---|---|
| CLI Definition | argparse (Built-in) |
Define all available flags and the positional command. |
| Argument Validation | inspect (Built-in) |
Use inspect.signature() to check if the invoked function accepts a given flag (--path, --debug). |
| Dynamic Execution | getattr() and **kwargs (Built-in) |
Use getattr() to find the function object and **kwargs to safely pass only the relevant arguments. |
This pattern gives you a powerful, dependency-free command-line dispatcher with argument safety.