dataclass-args 1.1.0__tar.gz → 1.2.2__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.
Files changed (29) hide show
  1. {dataclass_args-1.1.0 → dataclass_args-1.2.2}/PKG-INFO +191 -15
  2. {dataclass_args-1.1.0 → dataclass_args-1.2.2}/README.md +190 -14
  3. {dataclass_args-1.1.0 → dataclass_args-1.2.2}/dataclass_args/__init__.py +1 -1
  4. {dataclass_args-1.1.0 → dataclass_args-1.2.2}/dataclass_args/builder.py +280 -64
  5. {dataclass_args-1.1.0 → dataclass_args-1.2.2}/dataclass_args/utils.py +5 -5
  6. {dataclass_args-1.1.0 → dataclass_args-1.2.2}/dataclass_args.egg-info/PKG-INFO +191 -15
  7. {dataclass_args-1.1.0 → dataclass_args-1.2.2}/dataclass_args.egg-info/SOURCES.txt +3 -0
  8. {dataclass_args-1.1.0 → dataclass_args-1.2.2}/pyproject.toml +1 -2
  9. dataclass_args-1.2.2/tests/test_boolean_base_configs.py +326 -0
  10. {dataclass_args-1.1.0 → dataclass_args-1.2.2}/tests/test_builder_advanced.py +1 -1
  11. dataclass_args-1.2.2/tests/test_config_merging_simple.py +186 -0
  12. dataclass_args-1.2.2/tests/test_description.py +243 -0
  13. {dataclass_args-1.1.0 → dataclass_args-1.2.2}/LICENSE +0 -0
  14. {dataclass_args-1.1.0 → dataclass_args-1.2.2}/dataclass_args/annotations.py +0 -0
  15. {dataclass_args-1.1.0 → dataclass_args-1.2.2}/dataclass_args/exceptions.py +0 -0
  16. {dataclass_args-1.1.0 → dataclass_args-1.2.2}/dataclass_args/file_loading.py +0 -0
  17. {dataclass_args-1.1.0 → dataclass_args-1.2.2}/dataclass_args.egg-info/dependency_links.txt +0 -0
  18. {dataclass_args-1.1.0 → dataclass_args-1.2.2}/dataclass_args.egg-info/requires.txt +0 -0
  19. {dataclass_args-1.1.0 → dataclass_args-1.2.2}/dataclass_args.egg-info/top_level.txt +0 -0
  20. {dataclass_args-1.1.0 → dataclass_args-1.2.2}/setup.cfg +0 -0
  21. {dataclass_args-1.1.0 → dataclass_args-1.2.2}/tests/test_annotations.py +0 -0
  22. {dataclass_args-1.1.0 → dataclass_args-1.2.2}/tests/test_basic.py +0 -0
  23. {dataclass_args-1.1.0 → dataclass_args-1.2.2}/tests/test_boolean_flags.py +0 -0
  24. {dataclass_args-1.1.0 → dataclass_args-1.2.2}/tests/test_cli_choices.py +0 -0
  25. {dataclass_args-1.1.0 → dataclass_args-1.2.2}/tests/test_cli_short.py +0 -0
  26. {dataclass_args-1.1.0 → dataclass_args-1.2.2}/tests/test_combine_annotations.py +0 -0
  27. {dataclass_args-1.1.0 → dataclass_args-1.2.2}/tests/test_file_loading.py +0 -0
  28. {dataclass_args-1.1.0 → dataclass_args-1.2.2}/tests/test_positional.py +0 -0
  29. {dataclass_args-1.1.0 → dataclass_args-1.2.2}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dataclass-args
3
- Version: 1.1.0
3
+ Version: 1.2.2
4
4
  Summary: Zero-boilerplate CLI generation from Python dataclasses with advanced type support and file loading
5
5
  Author-email: Martin Bartlett <martin.j.bartlett@gmail.com>
6
6
  License: MIT
@@ -54,25 +54,23 @@ Generate command-line interfaces from Python dataclasses.
54
54
  [![Tests](https://github.com/bassmanitram/dataclass-args/actions/workflows/test.yml/badge.svg)](https://github.com/bassmanitram/dataclass-args/actions/workflows/test.yml)
55
55
  [![Code Quality](https://github.com/bassmanitram/dataclass-args/actions/workflows/lint.yml/badge.svg)](https://github.com/bassmanitram/dataclass-args/actions/workflows/lint.yml)
56
56
  [![Examples](https://github.com/bassmanitram/dataclass-args/actions/workflows/examples.yml/badge.svg)](https://github.com/bassmanitram/dataclass-args/actions/workflows/examples.yml)
57
- [![codecov](https://codecov.io/gh/bassmanitram/dataclass-args/branch/main/graph/badge.svg)](https://codecov.io/gh/bassmanitram/dataclass-args)
58
57
  [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
59
- [![PyPI version](https://badge.fury.io/py/dataclass-args.svg)](https://badge.fury.io/py/dataclass-args)
58
+ [![PyPI version](https://img.shields.io/pypi/v/dataclass-args.svg)](https://pypi.org/project/dataclass-args/)
60
59
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
61
60
 
62
61
  ## Features
63
62
 
64
- - **Automatic CLI Generation** - Generate CLI from dataclass definitions
65
- - **Type-Safe Parsing** - Type-aware argument parsing for standard Python types
66
- - **Positional Arguments** - Support for positional args with `cli_positional()`
67
- - **Short Options** - Concise `-n` flags in addition to `--name`
68
- - **Boolean Flags** - Proper `--flag` and `--no-flag` boolean handling
69
- - **Value Validation** - Restrict values with `cli_choices()`
70
- - **File Loading** - Load parameters from files using `@filename` syntax
71
- - **Config Merging** - Combine configuration files with CLI overrides
72
- - **Flexible Types** - Support for `List`, `Dict`, `Optional`, and custom types
73
- - **Rich Annotations** - Custom help text, exclusions, and combinations
74
- - **Minimal Dependencies** - Lightweight with optional format support
75
-
63
+ - **[Automatic CLI Generation](#quick-start)** - Generate CLI from dataclass definitions
64
+ - **[Type-Safe Parsing](#type-support)** - Type-aware argument parsing for standard Python types
65
+ - **[Positional Arguments](#positional-arguments)** - Support for positional args with `cli_positional()`
66
+ - **[Short Options](#short-options)** - Concise `-n` flags in addition to `--name`
67
+ - **[Boolean Flags](#boolean-flags)** - Proper `--flag` and `--no-flag` boolean handling
68
+ - **[Value Validation](#value-choices)** - Restrict values with `cli_choices()`
69
+ - **[File Loading](#file-loadable-parameters)** - Load parameters from files using `@filename` syntax
70
+ - **[Config Merging](#configuration-merging)** - Combine configuration sources with hierarchical overrides
71
+ - **[Flexible Types](#type-support)** - Support for `List`, `Dict`, `Optional`, and custom types
72
+ - **[Rich Annotations](#combining-annotations)** - Custom help text, exclusions, and combinations
73
+ - **[Minimal Dependencies](#installation)** - Lightweight with optional format support
76
74
  ## Quick Start
77
75
 
78
76
  ### Installation
@@ -559,6 +557,184 @@ class MLConfig:
559
557
  raise ValueError("Epochs must be positive")
560
558
  ```
561
559
 
560
+
561
+ ### Configuration Merging
562
+
563
+ Dataclass-args supports hierarchical configuration merging from multiple sources with clear precedence rules.
564
+
565
+ #### Merge Order (Highest Priority Last)
566
+
567
+ Configuration sources are merged in this order, with later sources overriding earlier ones:
568
+
569
+ 1. **Programmatic `base_configs`** (if provided) - Lowest priority
570
+ 2. **Config file** from `--config` CLI argument (if provided)
571
+ 3. **CLI arguments** - Highest priority
572
+
573
+ #### Basic Usage
574
+
575
+ Load a base configuration file and override with CLI arguments:
576
+
577
+ ```python
578
+ from dataclasses import dataclass
579
+ from dataclass_args import build_config
580
+
581
+ @dataclass
582
+ class AppConfig:
583
+ name: str
584
+ count: int = 10
585
+ region: str = "us-east-1"
586
+
587
+ # Load from file, override with CLI
588
+ config = build_config(
589
+ AppConfig,
590
+ args=['--config', 'prod.yaml', '--count', '100']
591
+ )
592
+ ```
593
+
594
+ **prod.yaml:**
595
+ ```yaml
596
+ name: "ProductionApp"
597
+ count: 50
598
+ region: "eu-west-1"
599
+ ```
600
+
601
+ **Result:**
602
+ - `name`: "ProductionApp" (from file)
603
+ - `count`: 100 (CLI override)
604
+ - `region`: "eu-west-1" (from file)
605
+
606
+ #### Programmatic Base Configs
607
+
608
+ For advanced use cases, provide base configuration programmatically using the `base_configs` parameter:
609
+
610
+ ```python
611
+ # Single file path
612
+ config = build_config(AppConfig, base_configs='defaults.yaml')
613
+
614
+ # Single configuration dict
615
+ config = build_config(AppConfig, base_configs={'debug': True, 'count': 50})
616
+
617
+ # List mixing files and dicts (applied in order)
618
+ config = build_config(
619
+ AppConfig,
620
+ args=['--config', 'user.yaml', '--name', 'override'],
621
+ base_configs=[
622
+ 'company-defaults.yaml', # Company-wide defaults
623
+ {'environment': 'production'}, # Programmatic override
624
+ 'team-overrides.json', # Team-specific settings
625
+ ]
626
+ )
627
+ ```
628
+
629
+ **Merge order for the list example:**
630
+ 1. `company-defaults.yaml` (loaded and applied first)
631
+ 2. `{'environment': 'production'}` (overrides company defaults)
632
+ 3. `team-overrides.json` (loaded and overrides previous)
633
+ 4. `user.yaml` (from `--config`, overrides all base_configs)
634
+ 5. `--name 'override'` (CLI arg, highest priority)
635
+
636
+ #### Merge Behavior by Type
637
+
638
+ | Type | Behavior | Example |
639
+ |------|----------|---------|
640
+ | **Scalar** (str, int, float) | Replace | Later value replaces earlier value |
641
+ | **List** | Replace | Later list replaces earlier list (not appended) |
642
+ | **Dict** | Shallow merge | Keys are merged; later sources override earlier keys |
643
+
644
+ **Dict merge example:**
645
+ ```python
646
+ # base_configs[0]
647
+ {'name': 'app', 'db': {'host': 'localhost', 'port': 5432}}
648
+
649
+ # base_configs[1]
650
+ {'db': {'port': 3306, 'timeout': 30}}
651
+
652
+ # Result after merging:
653
+ {'name': 'app', 'db': {'host': 'localhost', 'port': 3306, 'timeout': 30}}
654
+ # ^unchanged ^merged: host kept, port updated, timeout added
655
+ ```
656
+
657
+ #### Real-World Example
658
+
659
+ ```python
660
+ import os
661
+ from dataclasses import dataclass
662
+ from dataclass_args import build_config
663
+
664
+ @dataclass
665
+ class DeployConfig:
666
+ app_name: str
667
+ environment: str
668
+ region: str = "us-east-1"
669
+ instance_count: int = 1
670
+
671
+ # Determine environment
672
+ env = os.getenv('ENV', 'dev')
673
+
674
+ # Multi-layer configuration
675
+ config = build_config(
676
+ DeployConfig,
677
+ args=['--config', '~/.myapp/personal.yaml', '--region', 'us-west-2'],
678
+ base_configs=[
679
+ 'config/base.yaml', # Company-wide defaults
680
+ f'config/{env}.yaml', # Environment-specific (dev/staging/prod)
681
+ {'debug': True}, # Quick programmatic toggle
682
+ ]
683
+ )
684
+
685
+ # Configuration is built from all sources with clear precedence
686
+ print(f"Deploying {config.app_name} to {config.environment}")
687
+ ```
688
+
689
+ **Use Cases:**
690
+
691
+ 1. **Multi-environment deployments:**
692
+ ```python
693
+ config = build_config(
694
+ Config,
695
+ args=['--config', f'{env}.yaml'],
696
+ base_configs='base.yaml'
697
+ )
698
+ ```
699
+
700
+ 2. **Testing with fixtures:**
701
+ ```python
702
+ test_config = {'database': 'test_db', 'debug': True}
703
+ config = build_config(
704
+ AppConfig,
705
+ args=['--name', 'test-run'],
706
+ base_configs=test_config
707
+ )
708
+ ```
709
+
710
+ 3. **Team and personal settings:**
711
+ ```python
712
+ config = build_config(
713
+ Config,
714
+ base_configs=[
715
+ 'company.yaml', # Company defaults
716
+ 'team.yaml', # Team overrides
717
+ '~/.myapp/personal.yaml', # Personal settings
718
+ ]
719
+ )
720
+ ```
721
+
722
+ #### Complete Example
723
+
724
+ See [`examples/config_merging_example.py`](examples/config_merging_example.py) for a comprehensive demonstration of configuration merging with multiple sources.
725
+
726
+ ```bash
727
+ # Run the example
728
+ python examples/config_merging_example.py multi-source
729
+ ```
730
+
731
+ #### See Also
732
+
733
+ - [Configuration File Formats](#configuration-file-formats) - Supported formats
734
+ - [Type Support](#type-support) - Type-specific behavior
735
+ - [API Reference](#api-reference) - Full API documentation
736
+
737
+
562
738
  ## API Reference
563
739
 
564
740
  > **📖 Full API Documentation:** See [docs/API.md](docs/API.md) for complete API reference with detailed examples.
@@ -5,25 +5,23 @@ Generate command-line interfaces from Python dataclasses.
5
5
  [![Tests](https://github.com/bassmanitram/dataclass-args/actions/workflows/test.yml/badge.svg)](https://github.com/bassmanitram/dataclass-args/actions/workflows/test.yml)
6
6
  [![Code Quality](https://github.com/bassmanitram/dataclass-args/actions/workflows/lint.yml/badge.svg)](https://github.com/bassmanitram/dataclass-args/actions/workflows/lint.yml)
7
7
  [![Examples](https://github.com/bassmanitram/dataclass-args/actions/workflows/examples.yml/badge.svg)](https://github.com/bassmanitram/dataclass-args/actions/workflows/examples.yml)
8
- [![codecov](https://codecov.io/gh/bassmanitram/dataclass-args/branch/main/graph/badge.svg)](https://codecov.io/gh/bassmanitram/dataclass-args)
9
8
  [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
10
- [![PyPI version](https://badge.fury.io/py/dataclass-args.svg)](https://badge.fury.io/py/dataclass-args)
9
+ [![PyPI version](https://img.shields.io/pypi/v/dataclass-args.svg)](https://pypi.org/project/dataclass-args/)
11
10
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
12
11
 
13
12
  ## Features
14
13
 
15
- - **Automatic CLI Generation** - Generate CLI from dataclass definitions
16
- - **Type-Safe Parsing** - Type-aware argument parsing for standard Python types
17
- - **Positional Arguments** - Support for positional args with `cli_positional()`
18
- - **Short Options** - Concise `-n` flags in addition to `--name`
19
- - **Boolean Flags** - Proper `--flag` and `--no-flag` boolean handling
20
- - **Value Validation** - Restrict values with `cli_choices()`
21
- - **File Loading** - Load parameters from files using `@filename` syntax
22
- - **Config Merging** - Combine configuration files with CLI overrides
23
- - **Flexible Types** - Support for `List`, `Dict`, `Optional`, and custom types
24
- - **Rich Annotations** - Custom help text, exclusions, and combinations
25
- - **Minimal Dependencies** - Lightweight with optional format support
26
-
14
+ - **[Automatic CLI Generation](#quick-start)** - Generate CLI from dataclass definitions
15
+ - **[Type-Safe Parsing](#type-support)** - Type-aware argument parsing for standard Python types
16
+ - **[Positional Arguments](#positional-arguments)** - Support for positional args with `cli_positional()`
17
+ - **[Short Options](#short-options)** - Concise `-n` flags in addition to `--name`
18
+ - **[Boolean Flags](#boolean-flags)** - Proper `--flag` and `--no-flag` boolean handling
19
+ - **[Value Validation](#value-choices)** - Restrict values with `cli_choices()`
20
+ - **[File Loading](#file-loadable-parameters)** - Load parameters from files using `@filename` syntax
21
+ - **[Config Merging](#configuration-merging)** - Combine configuration sources with hierarchical overrides
22
+ - **[Flexible Types](#type-support)** - Support for `List`, `Dict`, `Optional`, and custom types
23
+ - **[Rich Annotations](#combining-annotations)** - Custom help text, exclusions, and combinations
24
+ - **[Minimal Dependencies](#installation)** - Lightweight with optional format support
27
25
  ## Quick Start
28
26
 
29
27
  ### Installation
@@ -510,6 +508,184 @@ class MLConfig:
510
508
  raise ValueError("Epochs must be positive")
511
509
  ```
512
510
 
511
+
512
+ ### Configuration Merging
513
+
514
+ Dataclass-args supports hierarchical configuration merging from multiple sources with clear precedence rules.
515
+
516
+ #### Merge Order (Highest Priority Last)
517
+
518
+ Configuration sources are merged in this order, with later sources overriding earlier ones:
519
+
520
+ 1. **Programmatic `base_configs`** (if provided) - Lowest priority
521
+ 2. **Config file** from `--config` CLI argument (if provided)
522
+ 3. **CLI arguments** - Highest priority
523
+
524
+ #### Basic Usage
525
+
526
+ Load a base configuration file and override with CLI arguments:
527
+
528
+ ```python
529
+ from dataclasses import dataclass
530
+ from dataclass_args import build_config
531
+
532
+ @dataclass
533
+ class AppConfig:
534
+ name: str
535
+ count: int = 10
536
+ region: str = "us-east-1"
537
+
538
+ # Load from file, override with CLI
539
+ config = build_config(
540
+ AppConfig,
541
+ args=['--config', 'prod.yaml', '--count', '100']
542
+ )
543
+ ```
544
+
545
+ **prod.yaml:**
546
+ ```yaml
547
+ name: "ProductionApp"
548
+ count: 50
549
+ region: "eu-west-1"
550
+ ```
551
+
552
+ **Result:**
553
+ - `name`: "ProductionApp" (from file)
554
+ - `count`: 100 (CLI override)
555
+ - `region`: "eu-west-1" (from file)
556
+
557
+ #### Programmatic Base Configs
558
+
559
+ For advanced use cases, provide base configuration programmatically using the `base_configs` parameter:
560
+
561
+ ```python
562
+ # Single file path
563
+ config = build_config(AppConfig, base_configs='defaults.yaml')
564
+
565
+ # Single configuration dict
566
+ config = build_config(AppConfig, base_configs={'debug': True, 'count': 50})
567
+
568
+ # List mixing files and dicts (applied in order)
569
+ config = build_config(
570
+ AppConfig,
571
+ args=['--config', 'user.yaml', '--name', 'override'],
572
+ base_configs=[
573
+ 'company-defaults.yaml', # Company-wide defaults
574
+ {'environment': 'production'}, # Programmatic override
575
+ 'team-overrides.json', # Team-specific settings
576
+ ]
577
+ )
578
+ ```
579
+
580
+ **Merge order for the list example:**
581
+ 1. `company-defaults.yaml` (loaded and applied first)
582
+ 2. `{'environment': 'production'}` (overrides company defaults)
583
+ 3. `team-overrides.json` (loaded and overrides previous)
584
+ 4. `user.yaml` (from `--config`, overrides all base_configs)
585
+ 5. `--name 'override'` (CLI arg, highest priority)
586
+
587
+ #### Merge Behavior by Type
588
+
589
+ | Type | Behavior | Example |
590
+ |------|----------|---------|
591
+ | **Scalar** (str, int, float) | Replace | Later value replaces earlier value |
592
+ | **List** | Replace | Later list replaces earlier list (not appended) |
593
+ | **Dict** | Shallow merge | Keys are merged; later sources override earlier keys |
594
+
595
+ **Dict merge example:**
596
+ ```python
597
+ # base_configs[0]
598
+ {'name': 'app', 'db': {'host': 'localhost', 'port': 5432}}
599
+
600
+ # base_configs[1]
601
+ {'db': {'port': 3306, 'timeout': 30}}
602
+
603
+ # Result after merging:
604
+ {'name': 'app', 'db': {'host': 'localhost', 'port': 3306, 'timeout': 30}}
605
+ # ^unchanged ^merged: host kept, port updated, timeout added
606
+ ```
607
+
608
+ #### Real-World Example
609
+
610
+ ```python
611
+ import os
612
+ from dataclasses import dataclass
613
+ from dataclass_args import build_config
614
+
615
+ @dataclass
616
+ class DeployConfig:
617
+ app_name: str
618
+ environment: str
619
+ region: str = "us-east-1"
620
+ instance_count: int = 1
621
+
622
+ # Determine environment
623
+ env = os.getenv('ENV', 'dev')
624
+
625
+ # Multi-layer configuration
626
+ config = build_config(
627
+ DeployConfig,
628
+ args=['--config', '~/.myapp/personal.yaml', '--region', 'us-west-2'],
629
+ base_configs=[
630
+ 'config/base.yaml', # Company-wide defaults
631
+ f'config/{env}.yaml', # Environment-specific (dev/staging/prod)
632
+ {'debug': True}, # Quick programmatic toggle
633
+ ]
634
+ )
635
+
636
+ # Configuration is built from all sources with clear precedence
637
+ print(f"Deploying {config.app_name} to {config.environment}")
638
+ ```
639
+
640
+ **Use Cases:**
641
+
642
+ 1. **Multi-environment deployments:**
643
+ ```python
644
+ config = build_config(
645
+ Config,
646
+ args=['--config', f'{env}.yaml'],
647
+ base_configs='base.yaml'
648
+ )
649
+ ```
650
+
651
+ 2. **Testing with fixtures:**
652
+ ```python
653
+ test_config = {'database': 'test_db', 'debug': True}
654
+ config = build_config(
655
+ AppConfig,
656
+ args=['--name', 'test-run'],
657
+ base_configs=test_config
658
+ )
659
+ ```
660
+
661
+ 3. **Team and personal settings:**
662
+ ```python
663
+ config = build_config(
664
+ Config,
665
+ base_configs=[
666
+ 'company.yaml', # Company defaults
667
+ 'team.yaml', # Team overrides
668
+ '~/.myapp/personal.yaml', # Personal settings
669
+ ]
670
+ )
671
+ ```
672
+
673
+ #### Complete Example
674
+
675
+ See [`examples/config_merging_example.py`](examples/config_merging_example.py) for a comprehensive demonstration of configuration merging with multiple sources.
676
+
677
+ ```bash
678
+ # Run the example
679
+ python examples/config_merging_example.py multi-source
680
+ ```
681
+
682
+ #### See Also
683
+
684
+ - [Configuration File Formats](#configuration-file-formats) - Supported formats
685
+ - [Type Support](#type-support) - Type-specific behavior
686
+ - [API Reference](#api-reference) - Full API documentation
687
+
688
+
513
689
  ## API Reference
514
690
 
515
691
  > **📖 Full API Documentation:** See [docs/API.md](docs/API.md) for complete API reference with detailed examples.
@@ -65,7 +65,7 @@ from .exceptions import ConfigBuilderError, ConfigurationError, FileLoadingError
65
65
  from .file_loading import is_file_loadable_value, load_file_content
66
66
  from .utils import load_structured_file
67
67
 
68
- __version__ = "1.1.0"
68
+ __version__ = "1.2.2"
69
69
 
70
70
  __all__ = [
71
71
  # Main API