dataclass-args 1.4.2__tar.gz → 1.4.3__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.
- {dataclass_args-1.4.2 → dataclass_args-1.4.3}/PKG-INFO +1 -1
- {dataclass_args-1.4.2 → dataclass_args-1.4.3}/dataclass_args/__init__.py +1 -1
- {dataclass_args-1.4.2 → dataclass_args-1.4.3}/dataclass_args/builder.py +12 -4
- {dataclass_args-1.4.2 → dataclass_args-1.4.3}/dataclass_args.egg-info/PKG-INFO +1 -1
- {dataclass_args-1.4.2 → dataclass_args-1.4.3}/dataclass_args.egg-info/SOURCES.txt +1 -0
- {dataclass_args-1.4.2 → dataclass_args-1.4.3}/pyproject.toml +1 -1
- dataclass_args-1.4.3/tests/test_nested_help_text.py +151 -0
- {dataclass_args-1.4.2 → dataclass_args-1.4.3}/LICENSE +0 -0
- {dataclass_args-1.4.2 → dataclass_args-1.4.3}/README.md +0 -0
- {dataclass_args-1.4.2 → dataclass_args-1.4.3}/dataclass_args/annotations.py +0 -0
- {dataclass_args-1.4.2 → dataclass_args-1.4.3}/dataclass_args/append_action.py +0 -0
- {dataclass_args-1.4.2 → dataclass_args-1.4.3}/dataclass_args/config_applicator.py +0 -0
- {dataclass_args-1.4.2 → dataclass_args-1.4.3}/dataclass_args/exceptions.py +0 -0
- {dataclass_args-1.4.2 → dataclass_args-1.4.3}/dataclass_args/file_loading.py +0 -0
- {dataclass_args-1.4.2 → dataclass_args-1.4.3}/dataclass_args/formatter.py +0 -0
- {dataclass_args-1.4.2 → dataclass_args-1.4.3}/dataclass_args/nested_processor.py +0 -0
- {dataclass_args-1.4.2 → dataclass_args-1.4.3}/dataclass_args/type_inspector.py +0 -0
- {dataclass_args-1.4.2 → dataclass_args-1.4.3}/dataclass_args/utils.py +0 -0
- {dataclass_args-1.4.2 → dataclass_args-1.4.3}/dataclass_args.egg-info/dependency_links.txt +0 -0
- {dataclass_args-1.4.2 → dataclass_args-1.4.3}/dataclass_args.egg-info/requires.txt +0 -0
- {dataclass_args-1.4.2 → dataclass_args-1.4.3}/dataclass_args.egg-info/top_level.txt +0 -0
- {dataclass_args-1.4.2 → dataclass_args-1.4.3}/setup.cfg +0 -0
- {dataclass_args-1.4.2 → dataclass_args-1.4.3}/tests/test_annotations.py +0 -0
- {dataclass_args-1.4.2 → dataclass_args-1.4.3}/tests/test_basic.py +0 -0
- {dataclass_args-1.4.2 → dataclass_args-1.4.3}/tests/test_boolean_base_configs.py +0 -0
- {dataclass_args-1.4.2 → dataclass_args-1.4.3}/tests/test_boolean_flags.py +0 -0
- {dataclass_args-1.4.2 → dataclass_args-1.4.3}/tests/test_builder_advanced.py +0 -0
- {dataclass_args-1.4.2 → dataclass_args-1.4.3}/tests/test_cli_append.py +0 -0
- {dataclass_args-1.4.2 → dataclass_args-1.4.3}/tests/test_cli_choices.py +0 -0
- {dataclass_args-1.4.2 → dataclass_args-1.4.3}/tests/test_cli_nested.py +0 -0
- {dataclass_args-1.4.2 → dataclass_args-1.4.3}/tests/test_cli_short.py +0 -0
- {dataclass_args-1.4.2 → dataclass_args-1.4.3}/tests/test_collisions.py +0 -0
- {dataclass_args-1.4.2 → dataclass_args-1.4.3}/tests/test_combine_annotations.py +0 -0
- {dataclass_args-1.4.2 → dataclass_args-1.4.3}/tests/test_config_applicator.py +0 -0
- {dataclass_args-1.4.2 → dataclass_args-1.4.3}/tests/test_config_merging_simple.py +0 -0
- {dataclass_args-1.4.2 → dataclass_args-1.4.3}/tests/test_description.py +0 -0
- {dataclass_args-1.4.2 → dataclass_args-1.4.3}/tests/test_file_loading.py +0 -0
- {dataclass_args-1.4.2 → dataclass_args-1.4.3}/tests/test_nested_dict_override.py +0 -0
- {dataclass_args-1.4.2 → dataclass_args-1.4.3}/tests/test_nested_property_override_bug.py +0 -0
- {dataclass_args-1.4.2 → dataclass_args-1.4.3}/tests/test_positional.py +0 -0
- {dataclass_args-1.4.2 → dataclass_args-1.4.3}/tests/test_type_inspector.py +0 -0
- {dataclass_args-1.4.2 → dataclass_args-1.4.3}/tests/test_utils.py +0 -0
|
@@ -91,7 +91,7 @@ from .exceptions import ConfigBuilderError, ConfigurationError, FileLoadingError
|
|
|
91
91
|
from .file_loading import is_file_loadable_value, load_file_content
|
|
92
92
|
from .utils import load_structured_file
|
|
93
93
|
|
|
94
|
-
__version__ = "1.4.
|
|
94
|
+
__version__ = "1.4.3"
|
|
95
95
|
|
|
96
96
|
__all__ = [
|
|
97
97
|
# Main API
|
|
@@ -450,7 +450,10 @@ class GenericConfigBuilder:
|
|
|
450
450
|
info = mapping["nested_info"]
|
|
451
451
|
prefix = mapping["prefix"]
|
|
452
452
|
if not is_cli_positional(info):
|
|
453
|
-
|
|
453
|
+
parent = mapping["parent_field"]
|
|
454
|
+
self._add_argument(
|
|
455
|
+
parser, nested_field, info, cli_name, prefix, parent
|
|
456
|
+
)
|
|
454
457
|
else:
|
|
455
458
|
# Regular field - skip if nested dataclass
|
|
456
459
|
field_name = mapping["field_name"]
|
|
@@ -520,6 +523,7 @@ class GenericConfigBuilder:
|
|
|
520
523
|
info: Dict[str, Any],
|
|
521
524
|
cli_name: Optional[str] = None,
|
|
522
525
|
prefix: str = "",
|
|
526
|
+
parent_field: Optional[str] = None,
|
|
523
527
|
) -> None:
|
|
524
528
|
"""
|
|
525
529
|
Add CLI argument for a field (unified handler for flat and nested fields).
|
|
@@ -530,6 +534,7 @@ class GenericConfigBuilder:
|
|
|
530
534
|
info: Field info dict
|
|
531
535
|
cli_name: Pre-computed CLI name (for nested fields), uses info["cli_name"] if None
|
|
532
536
|
prefix: Prefix for nested fields (empty string = no prefix)
|
|
537
|
+
parent_field: Parent field name for nested fields (for help text context)
|
|
533
538
|
"""
|
|
534
539
|
# Boolean fields handled separately
|
|
535
540
|
if info["type"] == bool:
|
|
@@ -553,9 +558,12 @@ class GenericConfigBuilder:
|
|
|
553
558
|
|
|
554
559
|
# Build help text
|
|
555
560
|
custom_help = get_cli_help(info)
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
561
|
+
if custom_help:
|
|
562
|
+
help_text = custom_help
|
|
563
|
+
elif parent_field:
|
|
564
|
+
help_text = f"{parent_field}.{field_name}"
|
|
565
|
+
else:
|
|
566
|
+
help_text = field_name
|
|
559
567
|
choices = get_cli_choices(info)
|
|
560
568
|
|
|
561
569
|
# Handle append fields
|
|
@@ -33,6 +33,7 @@ tests/test_config_merging_simple.py
|
|
|
33
33
|
tests/test_description.py
|
|
34
34
|
tests/test_file_loading.py
|
|
35
35
|
tests/test_nested_dict_override.py
|
|
36
|
+
tests/test_nested_help_text.py
|
|
36
37
|
tests/test_nested_property_override_bug.py
|
|
37
38
|
tests/test_positional.py
|
|
38
39
|
tests/test_type_inspector.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "dataclass-args"
|
|
7
|
-
version = "1.4.
|
|
7
|
+
version = "1.4.3"
|
|
8
8
|
description = "Zero-boilerplate CLI generation from Python dataclasses with advanced type support and file loading"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.8"
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"""Test that nested field help text shows parent.field context."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
|
|
6
|
+
from dataclass_args import GenericConfigBuilder, cli_help, cli_nested
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TestNestedHelpText:
|
|
10
|
+
"""Test nested field help text generation."""
|
|
11
|
+
|
|
12
|
+
def test_nested_with_prefix_shows_parent_field_in_help(self):
|
|
13
|
+
"""Nested fields with prefix should show 'parent.field' in help text."""
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class AgentConfig:
|
|
17
|
+
name: str = "default"
|
|
18
|
+
timeout: int = 30
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class Config:
|
|
22
|
+
agent: AgentConfig = cli_nested(prefix="a2a")
|
|
23
|
+
|
|
24
|
+
builder = GenericConfigBuilder(Config)
|
|
25
|
+
parser = argparse.ArgumentParser()
|
|
26
|
+
builder.add_arguments(parser)
|
|
27
|
+
|
|
28
|
+
# Get help text
|
|
29
|
+
help_text = parser.format_help()
|
|
30
|
+
|
|
31
|
+
# Should show parent.field in help text
|
|
32
|
+
assert "agent.name" in help_text
|
|
33
|
+
assert "agent.timeout" in help_text
|
|
34
|
+
|
|
35
|
+
# Should NOT show generic "nested field"
|
|
36
|
+
assert "nested field" not in help_text
|
|
37
|
+
|
|
38
|
+
def test_nested_with_custom_help_uses_custom(self):
|
|
39
|
+
"""Custom help text should override parent.field format."""
|
|
40
|
+
|
|
41
|
+
@dataclass
|
|
42
|
+
class Inner:
|
|
43
|
+
value: str = cli_help("Custom help text", default="test")
|
|
44
|
+
|
|
45
|
+
@dataclass
|
|
46
|
+
class Outer:
|
|
47
|
+
inner: Inner = cli_nested(prefix="i")
|
|
48
|
+
|
|
49
|
+
builder = GenericConfigBuilder(Outer)
|
|
50
|
+
parser = argparse.ArgumentParser()
|
|
51
|
+
builder.add_arguments(parser)
|
|
52
|
+
|
|
53
|
+
help_text = parser.format_help()
|
|
54
|
+
|
|
55
|
+
# Should use custom help text
|
|
56
|
+
assert "Custom help text" in help_text
|
|
57
|
+
|
|
58
|
+
# Should NOT show parent.field format
|
|
59
|
+
assert "inner.value" not in help_text
|
|
60
|
+
|
|
61
|
+
def test_nested_empty_prefix_shows_field_name(self):
|
|
62
|
+
"""Nested fields with empty prefix should show just the field name."""
|
|
63
|
+
|
|
64
|
+
@dataclass
|
|
65
|
+
class Inner:
|
|
66
|
+
count: int = 5
|
|
67
|
+
|
|
68
|
+
@dataclass
|
|
69
|
+
class Outer:
|
|
70
|
+
inner: Inner = cli_nested(prefix="")
|
|
71
|
+
|
|
72
|
+
builder = GenericConfigBuilder(Outer)
|
|
73
|
+
parser = argparse.ArgumentParser()
|
|
74
|
+
builder.add_arguments(parser)
|
|
75
|
+
|
|
76
|
+
help_text = parser.format_help()
|
|
77
|
+
|
|
78
|
+
# With empty prefix, should show field name
|
|
79
|
+
assert "--count" in help_text
|
|
80
|
+
# Should show just "count" not "inner.count" since prefix is empty
|
|
81
|
+
lines = help_text.split("\n")
|
|
82
|
+
count_help = [line for line in lines if "--count" in line]
|
|
83
|
+
assert any("count" in line for line in count_help)
|
|
84
|
+
|
|
85
|
+
def test_multiple_nested_fields_all_show_parent(self):
|
|
86
|
+
"""Multiple nested fields should all show parent.field format."""
|
|
87
|
+
|
|
88
|
+
@dataclass
|
|
89
|
+
class DatabaseConfig:
|
|
90
|
+
host: str = "localhost"
|
|
91
|
+
port: int = 5432
|
|
92
|
+
user: str = "admin"
|
|
93
|
+
|
|
94
|
+
@dataclass
|
|
95
|
+
class CacheConfig:
|
|
96
|
+
host: str = "localhost"
|
|
97
|
+
port: int = 6379
|
|
98
|
+
|
|
99
|
+
@dataclass
|
|
100
|
+
class AppConfig:
|
|
101
|
+
database: DatabaseConfig = cli_nested(prefix="db")
|
|
102
|
+
cache: CacheConfig = cli_nested(prefix="cache")
|
|
103
|
+
|
|
104
|
+
builder = GenericConfigBuilder(AppConfig)
|
|
105
|
+
parser = argparse.ArgumentParser()
|
|
106
|
+
builder.add_arguments(parser)
|
|
107
|
+
|
|
108
|
+
help_text = parser.format_help()
|
|
109
|
+
|
|
110
|
+
# Database fields
|
|
111
|
+
assert "database.host" in help_text
|
|
112
|
+
assert "database.port" in help_text
|
|
113
|
+
assert "database.user" in help_text
|
|
114
|
+
|
|
115
|
+
# Cache fields
|
|
116
|
+
assert "cache.host" in help_text
|
|
117
|
+
assert "cache.port" in help_text
|
|
118
|
+
|
|
119
|
+
# CLI names should have prefixes
|
|
120
|
+
assert "--db-host" in help_text
|
|
121
|
+
assert "--db-port" in help_text
|
|
122
|
+
assert "--cache-host" in help_text
|
|
123
|
+
assert "--cache-port" in help_text
|
|
124
|
+
|
|
125
|
+
def test_nested_dataclass_field_shows_parent_context(self):
|
|
126
|
+
"""Nested dataclass fields (not fully flattened) show parent context."""
|
|
127
|
+
|
|
128
|
+
@dataclass
|
|
129
|
+
class Level3:
|
|
130
|
+
value: str = "deep"
|
|
131
|
+
|
|
132
|
+
@dataclass
|
|
133
|
+
class Level2:
|
|
134
|
+
level3: Level3 = cli_nested(prefix="l3")
|
|
135
|
+
|
|
136
|
+
@dataclass
|
|
137
|
+
class Level1:
|
|
138
|
+
level2: Level2 = cli_nested(prefix="l2")
|
|
139
|
+
|
|
140
|
+
builder = GenericConfigBuilder(Level1)
|
|
141
|
+
parser = argparse.ArgumentParser()
|
|
142
|
+
builder.add_arguments(parser)
|
|
143
|
+
|
|
144
|
+
help_text = parser.format_help()
|
|
145
|
+
|
|
146
|
+
# Level2.level3 is not fully flattened (requires recursive flattening)
|
|
147
|
+
# So it shows as level2.level3
|
|
148
|
+
assert "level2.level3" in help_text
|
|
149
|
+
|
|
150
|
+
# CLI name has the l2 prefix
|
|
151
|
+
assert "--l2-level3" in help_text
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|