-
Notifications
You must be signed in to change notification settings - Fork 0
Migrate CLI from manual argv parsing to tyro with component selection #42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Co-authored-by: hzhangxyz <[email protected]>
Co-authored-by: hzhangxyz <[email protected]>
|
@copilot 1. tyro 最新版本是 1.0.3 . |
- Update tyro dependency from ~=0.9.15 to ~=1.0.3 - Rename async main() to async run() - Move nested run() function to module-level main() - Simplify cli() to just call tyro.cli(main) Co-authored-by: hzhangxyz <[email protected]>
Done in commit 8dbe623:
All tests pass (42/42) and linting is clean. |
ddss/main.py
Outdated
| if len(sys.argv) == 1: | ||
| tmpdir = tempfile.TemporaryDirectory() | ||
| path = pathlib.Path(tmpdir.name) / "ddss.db" | ||
| # Global to keep temporary directory alive during execution |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
不要用global 变量, 不需要考虑这个生存期
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in commit 5c11325. Removed the global _tmpdir variable and its references.
ddss/main.py
Outdated
| break | ||
| else: | ||
| print(f"Unsupported database address: {addr}") | ||
| sys.exit(1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
用SystemExit那个
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in commit 5c11325. Changed from sys.exit(1) to raise SystemExit(1).
ddss/main.py
Outdated
|
|
||
|
|
||
| def cli(): | ||
| """DDSS - Distributed Deductive System Sorts |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
这个函数不需要注释, 这个注释应该给main函数, 那才是 ddss --help 要看到的
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in commit 5c11325. Moved the docstring from cli() to main() function. Now ddss --help displays:
DDSS - Distributed Deductive System Sorts Run DDSS with an interactive deductive reasoning environment.
| "apyds-egg~=0.0.11", | ||
| "prompt-toolkit~=3.0.52", | ||
| "sqlalchemy[aiosqlite,aiomysql,postgresql-asyncpg]~=2.0.45", | ||
| "tyro~=1.0.3", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
记得重新uv lock一下
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in commit 5c11325. Ran uv lock to update the lockfile with tyro 1.0.3 and related dependencies.
| "If not provided, uses a temporary SQLite database." | ||
| ) | ||
| ] = None, | ||
| ) -> None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
缺少一个 component: list[str] 的功能, 默认是 ["input", "output", "ds", "egg"]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in commit 5c11325. Added component: list[str] parameter with default value ["input", "output", "ds", "egg"]. Usage:
# Run all components (default)
ddss
# Run specific components
ddss --component input output
# Run only ds and egg
ddss --component ds egg…mprove docstrings - Remove global _tmpdir variable (no longer needed) - Replace sys.exit(1) with raise SystemExit(1) - Move docstring from cli() to main() for proper --help display - Add component parameter to select which components to run - Default components: ["input", "output", "ds", "egg"] - Run uv lock to update lockfile with tyro 1.0.3 Co-authored-by: hzhangxyz <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR migrates the CLI from manual sys.argv parsing to the tyro library, providing a more structured and type-safe command-line interface. The key addition is selective component execution, allowing users to run specific DDSS subsystems (ds, egg, input, output) rather than always running all of them.
- Added tyro dependency (~=1.0.3) for declarative CLI argument parsing
- Implemented component selection via
--componentparameter with validation - Added
-ashort flag as an alias for--addr
Reviewed changes
Copilot reviewed 2 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| pyproject.toml | Added tyro dependency specification |
| uv.lock | Updated lock file with tyro and its dependencies (docstring-parser, typeguard) |
| ddss/main.py | Restructured CLI with tyro integration: renamed async main to run, created new main function with type-annotated parameters, added component selection logic with validation |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
ddss/main.py
Outdated
|
|
||
| def cli(): | ||
| if len(sys.argv) == 1: | ||
| VALID_COMPONENTS = ["ds", "egg", "input", "output"] |
Copilot
AI
Dec 24, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The VALID_COMPONENTS constant is defined after the run function that uses it. While Python will work at runtime since functions are executed after all module-level code runs, it would be better to define VALID_COMPONENTS before the run function for better code organization and readability.
ddss/main.py
Outdated
| component_map = { | ||
| "ds": ds, | ||
| "egg": egg, | ||
| "input": input, | ||
| "output": output, | ||
| } |
Copilot
AI
Dec 24, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a logical issue here: the component_map is hardcoded in the run function, but the components list is passed as a parameter and validated against VALID_COMPONENTS. If component names don't exist in component_map, the code will raise a KeyError when trying to access component_map[component] on line 34. Since VALID_COMPONENTS is defined separately, there's a risk of inconsistency. Consider either defining component_map at module level along with VALID_COMPONENTS, or deriving VALID_COMPONENTS from component_map.keys() to ensure they stay in sync.
68bf1c8 to
c691d30
Compare
Co-authored-by: Copilot <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 2 out of 3 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| tmpdir = tempfile.TemporaryDirectory() | ||
| path = pathlib.Path(tmpdir.name) / "ddss.db" | ||
| addr = f"sqlite:///{path.as_posix()}" |
Copilot
AI
Dec 24, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The tmpdir variable is created as a TemporaryDirectory object but is never stored or cleaned up properly. This means the temporary directory will be deleted when tmpdir goes out of scope at the end of the main() function, potentially before the async run() function completes and while the database is still in use. This could cause the database file to be deleted while the application is running.
To fix this, the tmpdir should be kept alive for the entire duration of the async operation, or the cleanup should be handled explicitly in the async context.
| list[str], | ||
| tyro.conf.arg( | ||
| aliases=["-c"], | ||
| help="Components to run.", |
Copilot
AI
Dec 24, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The default value for the component parameter is missing "load" and "dump" components that are defined in component_map (lines 11-21). According to the PR description, it claims to preserve existing behavior, but the original code only ran ds, egg, input, and output. However, since load and dump are now added to the component_map, users might expect them to be available. The inconsistency is that these components exist in the map but aren't mentioned in the help text or included as a clear option.
Consider either:
- Documenting which components are available (e.g., in the help text)
- Validating component names against the
component_mapkeys early with a clear error message listing valid options
| help="Components to run.", | |
| help="Components to run. Available: ds, egg, input, output, load, dump.", |
| try: | ||
| coroutines = [component_map[component](addr, engine, session) for component in components] | ||
| except KeyError as e: | ||
| print(f"error: unsupported component {e}") |
Copilot
AI
Dec 24, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error message format is inconsistent. When a component is invalid (line 31), the error prints error: unsupported component {e}, which will display the KeyError exception object (e.g., KeyError('invalid_component')). However, when the database is unsupported (line 83), it prints the actual address value.
For consistency and clarity, line 31 should extract and display the component name from the KeyError, such as: print(f"error: unsupported component: {component}") where component is the invalid component name.
| print(f"error: unsupported component {e}") | |
| component = e.args[0] if e.args else e | |
| print(f"error: unsupported component: {component}") |
| component: Annotated[ | ||
| list[str], | ||
| tyro.conf.arg( | ||
| aliases=["-c"], | ||
| help="Components to run.", | ||
| ), | ||
| ] = ["input", "output", "ds", "egg"], | ||
| ) -> None: | ||
| """DDSS - Distributed Deductive System Sorts | ||
| Run DDSS with an interactive deductive environment. | ||
| """ | ||
| if addr is None: | ||
| tmpdir = tempfile.TemporaryDirectory() | ||
| path = pathlib.Path(tmpdir.name) / "ddss.db" | ||
| addr = f"sqlite:///{path.as_posix()}" | ||
| elif len(sys.argv) == 2 and sys.argv[1] not in ["--help", "-help", "-h", "/help", "/h", "/?"]: | ||
| addr = sys.argv[1] | ||
| else: | ||
| print(f"Usage: {sys.argv[0]} [<database-addr>]") | ||
| sys.exit(1) | ||
|
|
||
| for key, value in sqlalchemy_driver.items(): | ||
| if addr.startswith(f"{key}://"): | ||
| addr = addr.replace(f"{key}://", f"{key}+{value}://") | ||
| if addr.startswith(f"{key}+{value}://"): | ||
| break | ||
| else: | ||
| print(f"Unsupported database address: {addr}") | ||
| sys.exit(1) | ||
| print(f"error: unsupported database: {addr}") | ||
| raise SystemExit(1) | ||
|
|
||
| print(f"addr: {addr}") | ||
| asyncio.run(main(addr)) | ||
| asyncio.run(run(addr, component)) |
Copilot
AI
Dec 24, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The run() function signature in the diff shows parameter types addr: str, components: list[str], but when calling it at line 87, the function is invoked with component (singular) instead of components (plural). However, looking at line 24 in the actual code, the function definition uses components (plural). This appears to be correctly implemented in the actual code, but there's an inconsistency between the parameter name in main() (line 60: component) and in run() (line 24: components).
For consistency and clarity, consider using the same parameter name in both functions, preferably components (plural) since it's a list of multiple components.
|
|
||
|
|
||
| async def main(addr): | ||
| async def run(addr: str, components: list[str]) -> None: |
Copilot
AI
Dec 24, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The run() function is missing type annotations for its parameters. The diff shows type hints in line 24 (async def run(addr: str, components: list[str]) -> None:), but the actual implementation uses async def run(addr, components) without type annotations. Adding type annotations would improve code clarity and enable better static type checking, which is especially important since the rest of the code is being migrated to use type-annotated interfaces with tyro.
The issue requested replacing manual
sys.argvparsing with tyro for a more structured CLI interface supporting--addr|-aflags.Changes
~=1.0.3) topyproject.tomltyro.cli()using type-annotated function-ashort flag as alias for--addrviatyro.conf.arg(aliases=["-a"])--componentparameter to selectively run DDSS components (input, output, ds, egg). Defaults to running all components when not specified.async def main()toasync def run(), moved CLI argument handling function from nested to module-leveldef main(), and simplifiedcli()to calltyro.cli(main)uv lockto update dependenciesBefore/After
New Features
Tyro provides auto-generated help with formatted output and type-safe argument parsing while maintaining backward compatibility. The restructured function names better reflect their purposes:
main()as the CLI entry point,run()as the async implementation, andcli()as a simple wrapper.Original prompt
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.