wry 0.2.1__tar.gz → 0.2.2.dev1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {wry-0.2.1 → wry-0.2.2.dev1}/PKG-INFO +1 -1
- {wry-0.2.1 → wry-0.2.2.dev1}/pyproject.toml +1 -1
- {wry-0.2.1 → wry-0.2.2.dev1}/wry/_version.py +2 -2
- {wry-0.2.1 → wry-0.2.2.dev1}/wry/click_integration.py +14 -14
- {wry-0.2.1 → wry-0.2.2.dev1}/wry/core/model.py +2 -1
- {wry-0.2.1 → wry-0.2.2.dev1}/LICENSE +0 -0
- {wry-0.2.1 → wry-0.2.2.dev1}/README.md +0 -0
- {wry-0.2.1 → wry-0.2.2.dev1}/wry/__init__.py +0 -0
- {wry-0.2.1 → wry-0.2.2.dev1}/wry/auto_model.py +0 -0
- {wry-0.2.1 → wry-0.2.2.dev1}/wry/core/__init__.py +0 -0
- {wry-0.2.1 → wry-0.2.2.dev1}/wry/core/accessors.py +0 -0
- {wry-0.2.1 → wry-0.2.2.dev1}/wry/core/env_utils.py +0 -0
- {wry-0.2.1 → wry-0.2.2.dev1}/wry/core/field_utils.py +0 -0
- {wry-0.2.1 → wry-0.2.2.dev1}/wry/core/sources.py +0 -0
- {wry-0.2.1 → wry-0.2.2.dev1}/wry/multi_model.py +0 -0
- {wry-0.2.1 → wry-0.2.2.dev1}/wry/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "wry"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.2.dev1" # Placeholder - actual version comes from git tags via poetry-dynamic-versioning
|
|
4
4
|
description = "Why Repeat Yourself? - Define your CLI once with Pydantic models"
|
|
5
5
|
authors = ["Tyler House <26489166+tahouse@users.noreply.github.com>"]
|
|
6
6
|
readme = "README.md"
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# file generated by poetry-dynamic-versioning
|
|
2
2
|
from typing import TYPE_CHECKING
|
|
3
3
|
|
|
4
|
-
__version__: str = "0.2.
|
|
5
|
-
__version_tuple__: tuple[int, ...] = (0, 2,
|
|
4
|
+
__version__: str = "0.2.2.dev1" # This will be replaced during build
|
|
5
|
+
__version_tuple__: tuple[int, ...] = (0, 2, 2, "dev1") # This will be replaced during build
|
|
6
6
|
__commit_id__: str | None = None # This will be replaced during build
|
|
7
7
|
|
|
8
8
|
# For compatibility with setuptools-scm test expectations
|
|
@@ -380,14 +380,22 @@ def generate_click_parameters(
|
|
|
380
380
|
# Auto-generate Click option from Field info
|
|
381
381
|
option_name = f"--{field_name.replace('_', '-')}"
|
|
382
382
|
|
|
383
|
+
# Check if field is required first (needed to decide on default handling)
|
|
384
|
+
is_required = field_info.is_required() or field_type == AutoClickParameter.REQUIRED_OPTION
|
|
385
|
+
|
|
383
386
|
click_kwargs: dict[str, Any] = {
|
|
384
|
-
"default": (field_info.default if field_info.default is not PydanticUndefined else None),
|
|
385
387
|
"help": field_info.description or f"{field_name.replace('_', ' ').title()}",
|
|
386
388
|
"show_default": True,
|
|
387
389
|
}
|
|
388
390
|
|
|
389
|
-
#
|
|
390
|
-
|
|
391
|
+
# Only set default if field has one, or if field is not required
|
|
392
|
+
# For required fields without a default, we must NOT set default=None
|
|
393
|
+
# or Click will think there IS a default and won't enforce the requirement
|
|
394
|
+
if field_info.default is not PydanticUndefined:
|
|
395
|
+
click_kwargs["default"] = field_info.default
|
|
396
|
+
elif not is_required:
|
|
397
|
+
# Optional field without explicit default gets None
|
|
398
|
+
click_kwargs["default"] = None
|
|
391
399
|
|
|
392
400
|
# Determine Click type from annotation
|
|
393
401
|
base_type = get_args(annotation)[0]
|
|
@@ -740,23 +748,15 @@ def eager_json_config(ctx: click.Context, param: click.Parameter, value: Any) ->
|
|
|
740
748
|
with open(value) as f:
|
|
741
749
|
json_data = json.load(f)
|
|
742
750
|
|
|
743
|
-
#
|
|
744
|
-
# This prevents Click from throwing MissingParameter errors
|
|
745
|
-
for json_key, json_value in json_data.items():
|
|
746
|
-
param_key = json_key.replace("-", "_") # Handle kebab-case
|
|
747
|
-
if param_key not in ctx.params:
|
|
748
|
-
ctx.params[param_key] = json_value
|
|
749
|
-
|
|
750
|
-
# Store for later merging
|
|
751
|
+
# Store JSON data for later merging in from_click_context
|
|
751
752
|
ctx.ensure_object(dict)["json_data"] = json_data
|
|
752
753
|
|
|
753
|
-
#
|
|
754
|
-
# This allows JSON to satisfy required arguments
|
|
754
|
+
# Mark parameters from JSON as not required
|
|
755
|
+
# This allows JSON to satisfy required arguments without modifying defaults
|
|
755
756
|
if hasattr(ctx, "command") and ctx.command:
|
|
756
757
|
for p in ctx.command.params:
|
|
757
758
|
if (isinstance(p, click.Argument) or isinstance(p, click.Option)) and p.name in json_data:
|
|
758
759
|
p.required = False
|
|
759
|
-
p.default = json_data[p.name]
|
|
760
760
|
|
|
761
761
|
return value
|
|
762
762
|
|
|
@@ -66,7 +66,7 @@ class WryModel(BaseModel):
|
|
|
66
66
|
if "_value_sources" not in data:
|
|
67
67
|
# Only initialize sources if not already provided
|
|
68
68
|
self._value_sources = {}
|
|
69
|
-
for field_name in self.model_fields:
|
|
69
|
+
for field_name in self.__class__.model_fields:
|
|
70
70
|
# Mark non-default values as programmatic
|
|
71
71
|
if field_name in data:
|
|
72
72
|
self._value_sources[field_name] = ValueSource.CLI
|
|
@@ -526,6 +526,7 @@ class WryModel(BaseModel):
|
|
|
526
526
|
# Only override if it's actually from CLI
|
|
527
527
|
if "COMMANDLINE" in source_str:
|
|
528
528
|
config_data[field_name] = TrackedValue(value, ValueSource.CLI)
|
|
529
|
+
continue
|
|
529
530
|
# Skip if it's DEFAULT or ENVIRONMENT - already handled above
|
|
530
531
|
continue
|
|
531
532
|
except (AttributeError, RuntimeError):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|