Skip to content

Fix lazy commands losing function signature defaults#35

Open
lbliii wants to merge 1 commit intomainfrom
lbliii/lazy-cmd-defaults
Open

Fix lazy commands losing function signature defaults#35
lbliii wants to merge 1 commit intomainfrom
lbliii/lazy-cmd-defaults

Conversation

@lbliii
Copy link
Copy Markdown
Owner

@lbliii lbliii commented Apr 10, 2026

Summary

  • Lazy commands weren't propagating function parameter defaults to argparse, causing optional args (e.g. host, port) to be None instead of their declared defaults
  • function_to_schema() now stores "default" values in JSON schema properties
  • _add_arguments_from_schema() falls back to schema defaults when the function signature is unavailable (lazy commands)
  • Added 4 tests covering lazy default propagation for int, bool, and pre-computed schemas

Test plan

  • All 26 lazy command tests pass (including 4 new ones)
  • Full suite: 1118 passed, 0 new failures

🤖 Generated with Claude Code

…arse

function_to_schema() tracked which params had defaults but never stored the
actual values. _add_arguments_from_schema() only read defaults via
inspect.signature() for eager commands. This caused lazy command parameters
to resolve to None instead of their declared defaults.

Store default values in the JSON schema and fall back to schema defaults
when the function signature is unavailable.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 10, 2026 21:12
@github-actions
Copy link
Copy Markdown

Coverage Report

87.6% overall coverage

File Coverage
src/milo/\_\_init\_\_.py 100.0%
src/milo/\_child.py 92.7%
src/milo/\_cli\_help.py 90.5%
src/milo/\_command\_defs.py 97.1%
src/milo/\_compat.py 63.3%
src/milo/\_errors.py 95.4%
src/milo/\_jsonrpc.py 100.0%
src/milo/\_mcp\_router.py 100.0%
src/milo/\_protocols.py 100.0%
src/milo/\_types.py 100.0%
src/milo/app.py 57.8%
src/milo/cli.py 98.6%
src/milo/commands.py 88.5%
src/milo/completions.py 96.0%
src/milo/config.py 86.8%
src/milo/context.py 87.1%
src/milo/dev.py 91.9%
src/milo/doctor.py 89.9%
src/milo/flow.py 96.2%
src/milo/form.py 87.0%
src/milo/gateway.py 74.0%
src/milo/groups.py 96.6%
src/milo/help.py 100.0%
src/milo/input/\_\_init\_\_.py 100.0%
src/milo/input/\_platform.py 77.8%
src/milo/input/\_reader.py 96.2%
src/milo/input/\_sequences.py 100.0%
src/milo/llms.py 78.7%
src/milo/mcp.py 81.9%
src/milo/middleware.py 100.0%
src/milo/observability.py 100.0%
src/milo/output.py 70.2%
src/milo/pipeline.py 85.6%
src/milo/plugins.py 100.0%
src/milo/reducers.py 100.0%
src/milo/registry.py 78.6%
src/milo/schema.py 95.0%
src/milo/state.py 91.9%
src/milo/streaming.py 100.0%
src/milo/templates/\_\_init\_\_.py 93.3%
src/milo/testing/\_\_init\_\_.py 100.0%
src/milo/testing/\_mcp.py 100.0%
src/milo/testing/\_record.py 85.5%
src/milo/testing/\_replay.py 87.1%
src/milo/testing/\_snapshot.py 100.0%
src/milo/theme.py 100.0%
src/milo/version\_check.py 62.7%

Copy link
Copy Markdown

Copilot AI left a 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 fixes lazy-command argument parsing so omitted optional parameters use the handler’s declared defaults (instead of becoming None) by propagating defaults through the JSON schema and using schema defaults when the function signature isn’t available.

Changes:

  • Store function parameter defaults in function_to_schema() as JSON Schema "default" values.
  • Teach _add_arguments_from_schema() to fall back to schema defaults when signature inspection is unavailable (lazy/schema-only mode).
  • Add tests validating lazy default propagation for ints and booleans, including precomputed schemas.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
src/milo/schema.py Adds "default" to generated JSON schema properties based on function signature defaults.
src/milo/commands.py Applies schema-provided defaults when signature defaults can’t be read (lazy/schema-only mode).
tests/test_lazy.py Adds test coverage for default propagation in lazy commands (signature + precomputed schema).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 163 to 166
has_default = param.default is not inspect.Parameter.empty
if has_default:
prop["default"] = param.default
if not has_default and not is_optional:
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

function_to_schema() now copies param.default into the JSON schema. Function defaults can be arbitrary Python objects (e.g., Enum members, Path objects, dataclass instances), which can make cmd.schema non-JSON-serializable and cause --mcp (e.g. tools/list) to crash when responses are serialized with json.dumps (no default= handler). Consider normalizing defaults to JSON-compatible values (Enum -> .value, Path -> str, dataclass -> asdict), or omitting/encoding non-serializable defaults in the schema while still keeping Python defaults for argparse/runtime dispatch.

Copilot uses AI. Check for mistakes.
Comment on lines +728 to +732
# Set default from signature or schema
if param and param.default is not inspect.Parameter.empty and json_type != "boolean":
kwargs["default"] = param.default
elif "default" not in kwargs and "default" in param_schema:
kwargs["default"] = param_schema["default"]
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Schema defaults are now applied for non-boolean params, but boolean params still compute default as False when the signature is unavailable (lazy / schema-only mode) because the boolean branch sets kwargs["default"] before the new schema-default fallback. This means a precomputed schema like {type: "boolean", default: true} (or a handler defaulting to True captured into schema) will still parse as False when omitted. Consider using param_schema.get("default", False) for booleans when param is missing, and ensure the chosen argparse action is compatible with the intended default.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants