the1conf 1.0.0__py3-none-any.whl

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.
@@ -0,0 +1,516 @@
1
+ Metadata-Version: 2.4
2
+ Name: the1conf
3
+ Version: 1.0.0
4
+ Summary: All in one configuration management tool for your python applications.
5
+ License-Expression: MIT
6
+ License-File: LICENSE
7
+ Keywords: configuration,settings,cli,click,pydantic
8
+ Author: Eric CHASTAN
9
+ Author-email: eric@chastan.consulting
10
+ Requires-Python: >=3.12,<3.13
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Topic :: Software Development :: Libraries
17
+ Requires-Dist: click (>=8.3.1)
18
+ Requires-Dist: pydantic (>=2.12.5)
19
+ Project-URL: Issues, https://gitlab.com/eric-chastan/the1conf/-/issues
20
+ Project-URL: Repository, https://gitlab.com/eric-chastan/the1conf
21
+ Description-Content-Type: text/markdown
22
+
23
+ # ๐Ÿ’ TheOneConf
24
+
25
+ ![Status](https://img.shields.io/badge/status-active-success.svg)
26
+ ![Python](https://img.shields.io/badge/python-3.10%2B-blue.svg)
27
+ ![License](https://img.shields.io/badge/license-MIT-green.svg)
28
+
29
+ **TheOneConf** is a Python library for **robust**, **decentralized**, and **declarative** application configuration management.
30
+
31
+ It allows you to define your configuration schema using standard Python classes, type hints, and inheritance. It supports loading configuration from various sources (environment variables, files, CLI arguments) and provides advanced features like mixins, nested namespaces, and loose coupling between components.
32
+ Additionally, it relies on **Pydantic** to validate configuration values, ensuring they respect type definitions and constraints.
33
+ Finally, it seamlessly integrates with **Click**, enabling you to expose your configuration as command-line options with minimal effort.
34
+
35
+ ## Table of Contents
36
+
37
+ - [โœจ Key Features](#-key-features)
38
+ - [๐Ÿš€ Quick Start](#-quick-start)
39
+ - [๐Ÿงฉ First-Class IDE Support](#-first-class-ide-support)
40
+ - [๐Ÿ”Œ Multiple Configuration Sources](#-multiple-configuration-sources)
41
+ - [๐Ÿง  Dynamic Computed Values (Eval Forms)](#-dynamic-computed-values-eval-forms)
42
+ - [๐Ÿ”„ Data Transformation](#-data-transformation)
43
+ - [๐Ÿ“‚ Path Management](#-path-management)
44
+ - [๐Ÿ“ฆ Nested Configurations (Namespaces)](#-nested-configurations-namespaces)
45
+ - [๐ŸŒ Contexts (Environment Awareness)](#-contexts-environment-awareness)
46
+ - [๐Ÿ—๏ธ Inheritance and Extensibility (Decentralization)](#-inheritance-and-extensibility-decentralization)
47
+ - [๐Ÿ›ก๏ธ Type Casting and Validation](#-type-casting-and-validation)
48
+ - [๐Ÿ–ฑ๏ธ Click Integration](#-click-integration)
49
+
50
+ ## โœจ Key Features
51
+
52
+ - ๐Ÿง  **First-Class IDE Support**: Leverage standard Python type hints for out-of-the-box autocompletion, type checking, and navigation.
53
+ - ๐Ÿ”Œ **Multi-Source Loading**: Seamlessly unify configuration from CLI arguments, environment variables, config files (YAML/JSON), and defaults.
54
+ - ๐Ÿงฎ **Dynamic Computed Values**: Define smart variables that automatically update based on other resolved values using Python callables.
55
+ - ๐Ÿงน **Data Transformation**: Sanitized and format your inputs on the fly (e.g. trimming, case conversion) before they hit your application logic.
56
+ - ๐Ÿ“‚ **Path Management**: Handle file system paths elegantly with auto-creation of directories and relative path resolution.
57
+ - ๐Ÿงฉ **Nested Namespaces**: Organize complex configurations into logical, hierarchical groups (e.g. `db.host`, `server.timeout`) using nested classes.
58
+ - ๐ŸŽญ **Context Awareness**: Activate different sets of variables for different usages / commands (e.g. 'db', 'ui') within a single config class.
59
+ - ๐ŸŒ **Decentralized Configuration**: Modularize your settings by splitting definitions across multiple files or mixins and combining them effortlessly.
60
+ - ๐Ÿ›ก๏ธ **Robust Validation**: Eliminate startup errors with strict type enforcement and powerful constraints (ranges, regex) powered by **Pydantic**.
61
+ - ๐Ÿ–ฑ๏ธ **Seamless Click Integration**: Auto-generate your CLI interface directly from your configuration schema with zero boilerplate.
62
+
63
+ ## ๐Ÿš€ Quick Start
64
+
65
+ A minimal example showing how to define and use configuration variables with default values.
66
+
67
+ ```python
68
+ from the1conf import AppConfig, configvar
69
+
70
+ class MyConfig(AppConfig):
71
+ host: str = configvar(Default="localhost")
72
+ """Server host"""
73
+
74
+ port: int = configvar(Default=8080)
75
+ """Server port"""
76
+
77
+ debug: bool = configvar(Default=False)
78
+
79
+ # Instantiate and resolve
80
+ conf = MyConfig()
81
+ conf.resolve_vars()
82
+
83
+ print(f"Host: {conf.host}, Port: {conf.port}")
84
+ ```
85
+
86
+ **Why it's easier:**
87
+ TheOneConf drastically reduces boilerplate. In a single line, you define the variable name, its type, its default value, and its documentation. No separate schema files, no manual parsingโ€”just standard Python code that works out of the box.
88
+
89
+ ## ๐Ÿงฉ First-Class IDE Support
90
+
91
+ Because TheOneConf uses standard Python type hints, modern IDEs (VS Code, PyCharm) provide excellent support out of the box.
92
+
93
+ - **Autocompletion**: You get instant suggestions for resolved configuration variables as you type `config.`.
94
+ - **Type Checking**: Static analysis tools (mypy, pylance) can catch type errors in your configuration usage.
95
+ - **Go to Definition**: Easily navigate to where a configuration variable is defined.
96
+ - **Documentation**: Hover over a variable to see its help string.
97
+ > **Note**: This requires defining a standard python docstring just below the variable definition (which is also used for the CLI help message when using a click_option as explained below).
98
+
99
+ ![First-Class IDE Support](docs/ide_support.png)
100
+
101
+ ## ๐Ÿ”Œ Multiple Configuration Sources
102
+
103
+ TheOneConf resolves values in this priority order: **CLI > Environment > Config File > Defaults**.
104
+
105
+ Configuration files can be in **YAML** or **JSON** format.
106
+
107
+ Here is a comprehensive example showing resolution from all sources (Click, Env, File, Default), integration with Click will be explain in detail later.
108
+
109
+ ```python
110
+ import os
111
+ import json
112
+ import click
113
+ import the1conf
114
+ from pathlib import Path
115
+
116
+ # 1. Setup Environment Variable
117
+ os.environ["APP_KEY_ENV"] = "value_from_env"
118
+
119
+ # 2. Create a dummy JSON config file
120
+ with open("config.json", "w") as f:
121
+ json.dump({"key_file": "value_from_file"}, f)
122
+
123
+ class AppConfig(the1conf.AppConfig):
124
+ # Variables with different priorities
125
+ key_cli: str = the1conf.configvar(Default="default")
126
+ key_env: str = the1conf.configvar(Default="default", EnvName="APP_KEY_ENV")
127
+ key_file: str = the1conf.configvar(Default="default")
128
+ key_default: str = the1conf.configvar(Default="value_from_default")
129
+
130
+ @click.command()
131
+ @the1conf.click_option(AppConfig.key_cli)
132
+ def main(**kwargs):
133
+ conf = AppConfig()
134
+
135
+ # Resolve vars: CLI (kwargs) > Env > File > Default
136
+ conf.resolve_vars(
137
+ values=kwargs,
138
+ conffile_path=Path("config.json")
139
+ )
140
+
141
+ # Verify sources
142
+ assert conf.key_cli == "value_from_cli" # Source: CLI Argument
143
+ assert conf.key_env == "value_from_env" # Source: Environment Variable
144
+ assert conf.key_file == "value_from_file" # Source: Config File (JSON)
145
+ assert conf.key_default == "value_from_default" # Source: Default Value
146
+ print("All assertions passed!")
147
+
148
+ if __name__ == "__main__":
149
+ from click.testing import CliRunner
150
+
151
+ # Simulate CLI execution: python app.py --key-cli "value_from_cli"
152
+ # We use CliRunner used for testing click applications
153
+ runner = CliRunner()
154
+ result = runner.invoke(main, ["--key-cli", "value_from_cli"])
155
+
156
+ # Cleanup
157
+ if os.path.exists("config.json"):
158
+ os.remove("config.json")
159
+ ```
160
+
161
+ **Focus on Logic, Not Plumbing**
162
+ Notice how clean the `main` function is? You don't verify if a file exists, you don't manually parse environment variables, and you don't write complex `if/else` chains to handle priorities. TheOneConf abstracts all this complexity away, allowing you to focus entirely on your application's business logic.
163
+
164
+ ## ๐Ÿง  Dynamic Computed Values (Eval Forms)
165
+
166
+ TheOneConf allows variables to be computed dynamically based on the values of **already resolved** variables. This is realized using **Eval Forms**: callables that receive the configuration context.
167
+
168
+
169
+ ```python
170
+ from the1conf import AppConfig, configvar
171
+
172
+ class DBConfig(AppConfig):
173
+ host: str = configvar(Default="localhost")
174
+ port: int = configvar(Default=5432)
175
+ name: str = configvar(Default="app_db")
176
+
177
+ # Eval Form signature: (variable_name, context, current_value)
178
+ # We use the 'context' (c) to access previously resolved 'host', 'port', and 'name'
179
+
180
+ dsn: str = configvar(
181
+ Default=lambda _, c, __: f"postgresql://{c.host}:{c.port}/{c.name}",
182
+ NoSearch=True
183
+ )
184
+
185
+ conf = DBConfig()
186
+ # Override host via CLI args style for demonstration
187
+ conf.resolve_vars(values={"host": "db.internal"})
188
+
189
+ assert conf.dsn == "postgresql://db.internal:5432/app_db"
190
+ ```
191
+ **Note**: `NoSearch=True` for `dns` ensures we don't look for 'dsn' in env vars or config file, purely computed.
192
+
193
+ **Decouple Configuration Logic**:
194
+ By moving the logic for computation on variables (like URLs, paths, or connection strings) out of your application code and into the configuration definition, you keep your business logic clean. Your application simply requests the final value (e.g. `conf.dsn`) without needing to know how it was constructed from `host`, `port`, and `name`.
195
+
196
+ This is extremely useful to avoid duplication, for example constructing a Database URL from host and port.
197
+
198
+ ## ๐Ÿ”„ Data Transformation
199
+
200
+ You can also use **Eval Forms** to transform a value after it has been resolved but before it is cast to its final type. This is done using the `Transform` directive.
201
+
202
+ ```python
203
+ from the1conf import AppConfig, configvar
204
+
205
+ class App(AppConfig):
206
+ # Transform: takes the found value (e.g. from env or CLI) and modifies it.
207
+ # Here we ensure the API key is always uppercase and stripped of whitespace.
208
+ api_key: str = configvar(
209
+ Default=" default_key ",
210
+ Transform=lambda _, __, val: val.strip().upper() if val else val
211
+ )
212
+
213
+ conf = App()
214
+ # Pass a value that needs cleaning (whitespace, lowercase)
215
+ conf.resolve_vars(values={"api_key": " my_custom_key "})
216
+
217
+ assert conf.api_key == "MY_CUSTOM_KEY" # Result has been stripped and uppercased
218
+ ```
219
+
220
+ ## ๐Ÿ“‚ Path Management
221
+
222
+ TheOneConf simplifies handling file system paths with built-in directives for resolution and directory creation.
223
+
224
+ - **`CanBeRelativeTo`**: If a path is relative, it is resolved against a base directory (which can be another configuration variable or a fixed path).
225
+ - **`MakeDirs`**: Automatically creates the directory hierarchy if it doesn't exist.
226
+
227
+ ```python
228
+ import os
229
+ import shutil
230
+ from pathlib import Path
231
+ from the1conf import AppConfig, configvar
232
+ from the1conf.app_config import PathType
233
+
234
+ # Set environment variable for the example
235
+ os.environ["APP_BASE_DIR"] = "/tmp/my_app_from_env"
236
+
237
+ class IOConfig(AppConfig):
238
+ # Validates that 'base_dir' is a path
239
+ # NoValueSearch=True means we ignore values passed in resolve_vars() dict
240
+ base_dir: Path = configvar(
241
+ Default="/tmp/default_data",
242
+ EnvName="APP_BASE_DIR",
243
+ NoValueSearch=True
244
+ )
245
+
246
+ # If 'log_dir' is relative (e.g. "logs"), it becomes "{base_dir}/logs"
247
+ # MakeDirs=PathType.Dir ensures the directory is created on resolution
248
+ log_dir: Path = configvar(
249
+ Default="logs",
250
+ CanBeRelativeTo="base_dir",
251
+ MakeDirs=PathType.Dir
252
+ )
253
+
254
+ # Resolves relative to 'base_dir'. Creates parent directory of the file.
255
+ cache_file: Path = configvar(
256
+ Default="cache/db.sqlite",
257
+ CanBeRelativeTo="base_dir",
258
+ MakeDirs=PathType.File
259
+ )
260
+
261
+ conf = IOConfig()
262
+ # Pass a value for base_dir, but it will be IGNORED due to NoValueSearch=True
263
+ conf.resolve_vars(values={"base_dir": "/tmp/ignored_path"})
264
+
265
+ # Verification:
266
+ # 1. base_dir came from ENV: "APP_BASE_DIR" => "/tmp/my_app_from_env"
267
+ # 2. log_dir is resolved relative to base_dir
268
+ assert conf.log_dir == Path("/tmp/my_app_from_env/logs") # Value from Env used, runtime value ignored
269
+ assert conf.log_dir.is_dir() # Directory was automatically created
270
+
271
+ # Clean up for the example
272
+ if conf.base_dir.exists():
273
+ shutil.rmtree(conf.base_dir)
274
+ ```
275
+
276
+ ## ๐Ÿ“ฆ Nested Configurations (Namespaces)
277
+
278
+ For larger applications, flat configuration structures can become unmanageable. TheOneConf supports **Nested Namespaces** using inner classes inheriting from `NameSpace`. This allows you to group related settings logically (e.g., `db`, `logging`, `server`).
279
+
280
+ Important: Namespaces are also used in searching configuration files (e.g. `db.host` looks for `{"db": {"host": ...}}` in YAML/JSON).
281
+
282
+ ```yaml
283
+ # config.yaml
284
+ env: "prod"
285
+ db:
286
+ host: "db.prod"
287
+ port: 5432
288
+ auth:
289
+ username: "admin"
290
+ ```
291
+
292
+ ```python
293
+ from pathlib import Path
294
+ from the1conf import AppConfig, NameSpace, configvar
295
+
296
+ class MyApp(AppConfig):
297
+ env: str = configvar(Default="dev")
298
+
299
+ # Define a 'db' namespace
300
+ class db(NameSpace):
301
+ host: str = configvar(Default="localhost")
302
+ port: int = configvar(Default=5432)
303
+
304
+ # Nested namespaces can be infinitely deep
305
+ class auth(NameSpace):
306
+ username: str = configvar(Default="admin")
307
+ password: str = configvar(EnvName="DB_PASSWORD")
308
+
309
+ conf = MyApp()
310
+ # Resolve variables loading the config.yaml file defined above
311
+ conf.resolve_vars(conffile_path=Path("config.yaml"))
312
+
313
+ # Check that values are loaded from the file
314
+ assert conf.env == "prod"
315
+ assert conf.db.host == "db.prod"
316
+ assert conf.db.auth.username == "admin"
317
+ ```
318
+
319
+ ## ๐ŸŒ Contexts (Environment Awareness)
320
+
321
+ In complex applications, you often need to adapt the configuration schema based on the runtime environment.
322
+ By tagging variables with `Contexts`, you can define which settings are relevant for a specific mode (e.g. "server", "client", "test"). When resolving variables, you specify the active context(s), and TheOneConf will ignore any variable not belonging to it.
323
+
324
+ **Why it matters**:
325
+ This prevents errors and avoids unnecessary computation. Variables specific to one context often depend on data (like CLI arguments or config sections) that are absent in others. By skipping unconnected contexts, you ensure the application doesn't crash trying to resolve or validate settings it doesn't need.
326
+
327
+ ```python
328
+ import the1conf
329
+
330
+ class ToolConfig(the1conf.AppConfig):
331
+ # Common variable (available in all contexts)
332
+ verbose: bool = the1conf.configvar(Default=False)
333
+ """Enable verbose logging"""
334
+
335
+ # Variable specific to 'server' context
336
+ port: int = the1conf.configvar(
337
+ Default=8080,
338
+ Contexts=["server"]
339
+ )
340
+ """Port to listen on"""
341
+
342
+ # Variable specific to 'client' context
343
+ timeout: int = the1conf.configvar(
344
+ Default=30,
345
+ Contexts=["client"]
346
+ )
347
+ """Connection timeout"""
348
+
349
+ conf = ToolConfig()
350
+
351
+ # 1. Resolve for SERVER context
352
+ # Only 'verbose' and 'port' are resolved. 'timeout' is ignored.
353
+ conf.resolve_vars(contexts=["server"])
354
+
355
+ assert conf.port == 8080 # Available in 'server' context
356
+ assert conf.verbose is False # Common variable
357
+ assert conf.timeout is None # Ignored variable
358
+
359
+ # 2. Resolve for CLIENT context (simulating a fresh run for clarity)
360
+ # In a real CLI, you'd likely instantiate a new config or reuse one cleanly.
361
+ conf2 = ToolConfig()
362
+ conf2.resolve_vars(contexts=["client"])
363
+
364
+ assert conf2.timeout == 30 # Available in 'client' context
365
+ assert conf2.verbose is False # Common variable
366
+ assert conf2.port is None # Ignored variable
367
+ print("Context assertions passed!")
368
+ ```
369
+
370
+ ## ๐Ÿ—๏ธ Inheritance and Extensibility (Decentralization)
371
+
372
+ One of the strengths of TheOneConf is its support for standard Python inheritance to achieve **decentralized configuration**.
373
+ You can split your configuration definitions across multiple classes (e.g., one per module or component) or create specialized versions for environments.
374
+
375
+ When you instantiate the final class, TheOneConf **merges** all variables found in the class hierarchy into a single, unified configuration object. This means valid variables are the union of all parent content and the local content.
376
+
377
+ ```python
378
+ import the1conf
379
+
380
+ # Base Component Configuration
381
+ class LogConfig(the1conf.AppConfig):
382
+ verbose: bool = the1conf.configvar(Default=False)
383
+ log_file: str = the1conf.configvar(Default="app.log")
384
+
385
+ # App Configuration extends the Component Config
386
+ class AppConfig(LogConfig):
387
+ # We inherit 'verbose' and 'log_file'
388
+ # And we add new specific variables
389
+ port: int = the1conf.configvar(Default=8080)
390
+
391
+ # We can also override defaults
392
+ log_file: str = the1conf.configvar(Default="server.log")
393
+
394
+ conf = AppConfig()
395
+ conf.resolve_vars()
396
+
397
+ # variable from Base
398
+ assert conf.verbose is False
399
+ # overwritten variable
400
+ assert conf.log_file == "server.log"
401
+ # new variable
402
+ assert conf.port == 8080
403
+ ```
404
+
405
+ **Modular & Independent Configuration**
406
+ By combining **Inheritance**, **Namespaces**, and/or **Contexts**, you can define configurations independently in separate files dedicated to specific parts of the application. Each module can define its own configuration schema (e.g. `db_config.py`, `server_config.py`), and the main application simply composes them. This promotes a clean separation of concerns and makes the codebase easier to maintain.
407
+
408
+ ## ๐Ÿ›ก๏ธ Type Casting and Validation
409
+
410
+ TheOneConf leverages **Pydantic** to ensure that configuration values are not only of the correct type but also adhere to specific constraints.
411
+ This is particularly useful when loading values from typeless sources like environment variables or CLI arguments (which are always strings). TheOneConf automatically **casts** them to your target Python types and **validates** them against any defined constraints.
412
+
413
+ ```python
414
+ from typing import Annotated
415
+ from datetime import date
416
+ from pydantic import PositiveInt, BaseModel, model_validator, Field
417
+ from the1conf import AppConfig, configvar
418
+ from the1conf.app_config import AppConfigException
419
+
420
+ # Identify a Pydantic Model for complex validation
421
+ class DateRange(BaseModel):
422
+ start_date: date
423
+ end_date: date
424
+
425
+ @model_validator(mode='after')
426
+ def check_dates(self):
427
+ if self.end_date <= self.start_date:
428
+ raise ValueError("end_date must be after start_date")
429
+ return self
430
+
431
+ class ServerConfig(AppConfig):
432
+ # Annotated[int, Field(...)] enforces value constraints (1024 < port < 65536)
433
+ port: Annotated[int, Field(gt=1024, lt=65536)] = configvar(Default=8080)
434
+
435
+ # Pydantic types enforce strict constraints
436
+ max_workers: PositiveInt = configvar(Default=4) # Must be > 0
437
+
438
+ # Complex validation using Pydantic Model
439
+ period: DateRange = configvar(
440
+ Default={"start_date": "2024-01-01", "end_date": "2024-12-31"}
441
+ )
442
+
443
+ conf = ServerConfig()
444
+
445
+ # Simulate loading values from environment variables (strings)
446
+ conf.resolve_vars(values={
447
+ "port": "9000", # Cast: "9000" -> 9000 (int)
448
+ "max_workers": "10", # Cast & Validate: "10" -> 10 (int)
449
+ "period": {"start_date": "2024-03-01", "end_date": "2024-03-31"}
450
+ })
451
+
452
+ assert conf.port == 9000
453
+ assert conf.max_workers == 10
454
+ assert conf.period.start_date == date(2024, 3, 1)
455
+
456
+ # Validation ensures integrity
457
+ try:
458
+ # Use a new instance to ensure we don't skip the variable because it's already set
459
+ conf_invalid = ServerConfig()
460
+ # Invalid: end_date before start_date
461
+ conf_invalid.resolve_vars(values={
462
+ "period": {"start_date": "2024-02-01", "end_date": "2024-01-01"}
463
+ })
464
+ except AppConfigException:
465
+ print("Validation correctly rejected invalid date range")
466
+ ```
467
+
468
+ **Powerful & Safe Configuration**
469
+ This combination of strict typing and advanced validation ensures your application starts only with a valid configuration state. By catching errors early (at startup) and providing clear feedback, you prevent subtle runtime bugs and make your application more robust. You can define complex rules (ranges, dependencies between fields, regex patterns) declaratively, keeping your initialization logic clean and simple.
470
+
471
+ ## ๐Ÿ–ฑ๏ธ Click Integration
472
+
473
+ TheOneConf integrates seamlessly with [Click](https://click.palletsprojects.com/) to inject configuration variables into your CLI.
474
+ The `@the1conf.click_option` decorator creates a click option from a `ConfigVarDef`.
475
+
476
+ **Benefits:**
477
+ 1. **Zero Boilerplate**: No need to manually define `click.option('--port', default=8080, help='...')`. TheOneConf infers everything from your class definition.
478
+ 2. **Single Source of Truth**: Change the default value or help text in your Config class, and the CLI updates automatically.
479
+ 3. **Complex Features Support**: It works seamlessly with TheOneConf's features like type casting, validation, and multi-source resolution.
480
+
481
+ ```python
482
+ import click
483
+ import the1conf
484
+
485
+ class MyConfig(the1conf.AppConfig):
486
+ name: str = the1conf.configvar(Default="World")
487
+ """Name to greet. Help text is automatically exposed in --help"""
488
+
489
+ port: int = the1conf.configvar(Default=8080)
490
+ """Server port"""
491
+
492
+ @click.command()
493
+ # Automatically generate --name and --port options
494
+ @the1conf.click_option(MyConfig.name)
495
+ @the1conf.click_option(MyConfig.port)
496
+ def main(**kwargs):
497
+ cfg = MyConfig()
498
+ # Apply CLI args (highest priority) -> Env -> Files -> Defaults
499
+ cfg.resolve_vars(values=kwargs)
500
+
501
+ assert cfg.name == "Alice"
502
+ assert cfg.port == 9000
503
+ print(f"Server starting on {cfg.port} for {cfg.name}...")
504
+
505
+ if __name__ == "__main__":
506
+ from click.testing import CliRunner
507
+
508
+ # Simulate CLI execution: app.py --name Alice --port 9000
509
+ runner = CliRunner()
510
+ result = runner.invoke(main, ["--name", "Alice", "--port", "9000"])
511
+ print(result.output)
512
+ ```
513
+
514
+
515
+
516
+
@@ -0,0 +1,10 @@
1
+ the1conf/__init__.py,sha256=goUG680T43BM9vOPqHXnnhWvzatvH0rBauvA687dGPc,192
2
+ the1conf/app_config.py,sha256=gftWtddsdOBLxs2JfWcBPKT7rMjhKaf2rOyl04bD5wo,27552
3
+ the1conf/attr_dict.py,sha256=kg3b9EylEYewYI9JtERTQWEBel2N-CI4f8Q8nll6x44,5377
4
+ the1conf/click_option.py,sha256=zAPaFciLB2LTr0b7kOseyNIeIKcz-HQoatMCbJZT9Ts,1472
5
+ the1conf/config_var.py,sha256=1iuobXfEmLMbnCy_XjIa0QIThM3IucxRfGzDVYk9OfM,28777
6
+ the1conf/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ the1conf-1.0.0.dist-info/METADATA,sha256=y_TiEJoPHedNdH2EJO4pB9S5mg4IE2gwhdG9r_rtQ_4,21691
8
+ the1conf-1.0.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
9
+ the1conf-1.0.0.dist-info/licenses/LICENSE,sha256=uQf4G5VB4kzrWVzT_gA_vqn9bUQzaRlL4RkeFI17BeI,1069
10
+ the1conf-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 2.2.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Eric CHASTAN
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.