argbind-dbraun 0.5.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.
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020, Prem Seetharaman
4
+ Copyright (c) 2024-2026, David Braun (fork maintainer)
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
@@ -0,0 +1,341 @@
1
+ Metadata-Version: 2.4
2
+ Name: argbind-dbraun
3
+ Version: 0.5.2
4
+ Summary: Simple way to bind function arguments to the command line. An extended fork of pseeth/argbind with new features (imported as `argbind`).
5
+ Author-email: Prem Seetharaman <prem@descript.com>
6
+ Maintainer-email: David Braun <braun@ccrma.stanford.edu>
7
+ License: MIT
8
+ Project-URL: Homepage, https://github.com/DBraun/argbind/
9
+ Project-URL: Repository, https://github.com/DBraun/argbind/
10
+ Project-URL: Original project, https://github.com/pseeth/argbind/
11
+ Keywords: command-line,configuration,yaml,argument,parsing
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Programming Language :: Python :: 3.14
18
+ Classifier: Operating System :: POSIX :: Linux
19
+ Classifier: Operating System :: MacOS
20
+ Classifier: Operating System :: Microsoft :: Windows
21
+ Requires-Python: >=3.11
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE.md
24
+ Requires-Dist: pyyaml
25
+ Requires-Dist: docstring-parser
26
+ Provides-Extra: tests
27
+ Requires-Dist: pytest; extra == "tests"
28
+ Requires-Dist: pytest-cov; extra == "tests"
29
+ Provides-Extra: lint
30
+ Requires-Dist: ruff; extra == "lint"
31
+ Dynamic: license-file
32
+
33
+ # ArgBind
34
+
35
+ **Build CLIs via docstrings and type annotations, with YAML support.**
36
+
37
+ [![Tests](https://github.com/DBraun/argbind/actions/workflows/tests.yml/badge.svg)](https://github.com/DBraun/argbind/actions/workflows/tests.yml)
38
+ [![PyPI version](https://badge.fury.io/py/argbind-dbraun.svg)](https://pypi.org/project/argbind-dbraun/)
39
+ [![Python versions](https://img.shields.io/pypi/pyversions/argbind-dbraun)](https://pypi.org/project/argbind-dbraun/)
40
+ [![Downloads](https://static.pepy.tech/badge/argbind-dbraun)](https://pepy.tech/project/argbind-dbraun)
41
+
42
+ > **Note:** This is an extended fork of [pseeth/argbind](https://github.com/pseeth/argbind) that
43
+ > adds new features — modern type annotations (PEP 585/604), `Literal` and flexible boolean
44
+ > handling, dataclass `default_factory`, and YAML `$include` — published on PyPI as
45
+ > [`argbind-dbraun`](https://pypi.org/project/argbind-dbraun/). The import name is unchanged
46
+ > (`import argbind`), so it remains a drop-in replacement.
47
+
48
+ *ArgBind is a simple way to bind function or class arguments to the command line or to .yml files!*
49
+ It supports scoping of arguments, similar to other frameworks like
50
+ [Hydra](https://github.com/facebookresearch/hydra) and
51
+ [gin-config](https://github.com/google/gin-config).
52
+ ArgBind is *very* small (only ~800 lines of code, in one file), can be used to make complex and well-documented command line programs, and allows
53
+ you to configure program execution from .yml files.
54
+
55
+ If you're migrating from an ArgParse script to an ArgBind script, check out the
56
+ [migration guide](./examples/migration). Scroll down to see some [examples](#examples). Please also look at the
57
+ current known [limitations](#limitations-and-known-issues) of ArgBind.
58
+
59
+ ## Why ArgBind?
60
+
61
+ ArgBind was written by [Prem Seetharaman](https://github.com/pseeth) to help configure machine
62
+ learning experiments. ML experiment configuration is often highly nested, and can get out of hand
63
+ quickly. Rather than switching workflows around too much to accommodate a new framework, the goal
64
+ was to make already-written scripts easily adaptable, to achieve a few things:
65
+
66
+ 1. Configure scripts using `.yml` files. Be able to save `.yml` files that can be used to rerun scripts the exact same way twice.
67
+ 2. Spend time writing actual functions needed to run experiments, not argument parsers.
68
+ 3. Be able to run experiment code from other Python scripts, notebooks, or the command line.
69
+ 4. Be able to specify arguments from the command line directly to various functions.
70
+ 5. Be able to use scoping patterns, so a function can run inside a `train` scope and `test` scope, with different results (e.g., for getting a train dataset and a test dataset).
71
+
72
+ Nothing out there really fit the bill, so Prem wrote ArgBind. If you have
73
+ an `argparse` based script, converting it to ArgBind should be very quick! ArgBind is simple,
74
+ small, and easy to use. To get a feel for how it works, check out [usage](#usage), [design](#design), and [examples](#examples)!
75
+
76
+ ## Installation
77
+
78
+ Install via `pip`:
79
+
80
+ ```
81
+ python -m pip install argbind-dbraun
82
+ ```
83
+
84
+ Or from source:
85
+
86
+ ```
87
+ git clone https://github.com/DBraun/argbind.git
88
+ cd argbind
89
+ python -m pip install -e .
90
+ ```
91
+
92
+ This project uses [uv](https://docs.astral.sh/uv/). To create a dev environment with the
93
+ test dependencies and run the suite:
94
+
95
+ ```
96
+ uv sync --extra tests
97
+ uv run pytest
98
+ ```
99
+
100
+ Install the [pre-commit](https://pre-commit.com/) hooks (ruff format + lint) so they run
101
+ on every commit:
102
+
103
+ ```
104
+ uvx pre-commit install
105
+ ```
106
+
107
+ ## Examples
108
+
109
+ - [Example 1: Hello World](./examples/hello_world/)
110
+ - [Example 2: Scope patterns](./examples/scoping/)
111
+ - [Example 3: Typing](./examples/typing/)
112
+ - [Example 4: Modern type annotations (PEP 585/604)](./examples/modern_typing)
113
+ - [Example 5: Literal arguments](./examples/literal)
114
+ - [Example 6: Flexible boolean syntax](./examples/booleans)
115
+ - [Example 7: Using default_factory with dataclasses](./examples/default_factory)
116
+ - [Example 8: Loading, saving, and using .yml files](./examples/yaml)
117
+ - [Example 9: Nested .yml files with `$include`](./examples/nested_yaml)
118
+ - [Example 10: Multi-stage programs](./examples/multistage)
119
+ - [Example 11: Mimic more traditional CLI, without `func.arg` notation](./examples/without_prefix)
120
+ - [Example 12: Debug mode](./examples/debug)
121
+ - [Example 13: Migrating from ArgParse](./examples/migration)
122
+ - [Example 14: Binding existing functions and classes](./examples/bind_existing)
123
+ - [Example 15: Binding functions to specific groups](./examples/groups)
124
+
125
+ ## Usage
126
+
127
+ There are six main functions.
128
+
129
+ - `bind`: Binds keyword arguments (and positional arguments if `positional=True`) of a function or class to ArgBind.
130
+ - `parse_args`: Actually parses command line arguments into a dictionary.
131
+ - `scope`: Context manager that scopes a dictionary containing function arguments to be used by the functions.
132
+ - `dump_args`: Dumps the args dictionary to a `.yml` file. Used internally when program is called with `--args.save path/to/save.yml`.
133
+ - `load_args`: Loads args from a `.yml` file. Used internally when program is called with `--args.load path/to/load.yml`.
134
+ - `get_used_args`: Gets arguments that have actually been used by call functions up to this point.
135
+
136
+ Your code with ArgBind generally follows this pattern:
137
+
138
+ 1. Write a function with a good docstring, and typed arguments. If arguments are not typed, their type will be inferred from the type of the default.
139
+ 2. Bind it via `bind`.
140
+ 3. When program is called, parse the arguments via `parse_args`.
141
+ 4. Scope the arguments, and call the bound function within the context block.
142
+ 5. Optionally call program with `--args.save` to save the current execution configuration to a `.yml` file or `--args.load` to load arguments from a prior saved execution configuration to run it the same way twice.
143
+ 6. Optionally, run your script with `--args.debug=1` to see exactly how every bound function is called.
144
+
145
+ In your program, you can call `get_used_args` to see which arguments were actually used. Here's a minimal example:
146
+
147
+ ```python
148
+ import argbind
149
+
150
+ @argbind.bind()
151
+ def hello(
152
+ name : str = 'world'
153
+ ):
154
+ """Say hello to someone.
155
+
156
+ Parameters
157
+ ----------
158
+ name : str, optional
159
+ Who you're saying hello to, by default 'world'
160
+ """
161
+ print("Hello " + name)
162
+
163
+ if __name__ == "__main__":
164
+ # Arguments for CLI automatically generated from bound functions under the pattern
165
+ # function_name.function_arg.
166
+ args = argbind.parse_args()
167
+ # When called within a scope, the keyword arguments map to those from CLI or
168
+ # from defaults.
169
+ with argbind.scope(args):
170
+ hello()
171
+ # get_used_args() returns the arguments that were actually used by the bound
172
+ # functions that ran -- here, {'hello.name': 'world'}.
173
+ print(argbind.get_used_args())
174
+ ```
175
+
176
+ Help text is automatically generated from the docstring:
177
+
178
+ ```
179
+ ❯ python examples/hello_world/with_argbind.py -h
180
+ usage: with_argbind.py [-h] [--args.save ARGS.SAVE] [--args.load ARGS.LOAD] [--args.debug ARGS.DEBUG] [--hello.name HELLO.NAME]
181
+
182
+ optional arguments:
183
+ -h, --help show this help message and exit
184
+ --args.save ARGS.SAVE
185
+ Path to save all arguments used to run script to.
186
+ --args.load ARGS.LOAD
187
+ Path to load arguments from, stored as a .yml file.
188
+ --args.debug ARGS.DEBUG
189
+ Print arguments as they are passed to each function.
190
+
191
+ Generated arguments for function hello:
192
+ Say hello to someone.
193
+
194
+ --hello.name HELLO.NAME
195
+ Who you're saying hello to, by default 'world'
196
+ ```
197
+
198
+ Execution of this could look like:
199
+
200
+ ```
201
+ # Default arguments
202
+ ❯ python examples/hello_world/with_argbind.py
203
+ Hello world
204
+ # Binding name from the command line and saving the args.
205
+ ❯ python examples/hello_world/with_argbind.py --hello.name=you --args.save=/tmp/args.yml
206
+ Hello you
207
+ # Loading saved arguments.
208
+ ❯ python examples/hello_world/with_argbind.py --args.load=/tmp/args.yml
209
+ Hello you
210
+ # Loading saved arguments, and overriding via command line.
211
+ ❯ python examples/hello_world/with_argbind.py --args.load=/tmp/args.yml --hello.name=me
212
+ Hello me
213
+ # See how each function is called with args.debug=1.
214
+ ❯ python examples/hello_world/with_argbind.py --args.load=/tmp/args.yml --args.debug=1
215
+ hello(
216
+ name : str = you
217
+ )
218
+ Hello you
219
+ ```
220
+
221
+ You can also run the `hello` function from another Python script or a Jupyter notebook:
222
+
223
+ ```python
224
+ import argbind
225
+ # Import the bound function
226
+ from .hello_world import hello
227
+ # Load the args
228
+ args = argbind.load_args('/tmp/args.yml')
229
+ # Scope the args
230
+ with argbind.scope(args):
231
+ # Run the bound function
232
+ hello() # Prints 'Hello you'.
233
+ hello() # Prints 'Hello world', as it's outside scope.
234
+ # Can edit the args before scoping again.
235
+ args['hello.name'] = 'me'
236
+ with argbind.scope(args):
237
+ hello() # Prints 'Hello me'.
238
+ ```
239
+
240
+ You'll notice that ArgBind forces you to document and type your
241
+ function arguments, which is always a good idea!
242
+ Please check out the [examples](#examples) for more details!
243
+
244
+
245
+ ## Design
246
+
247
+ ArgBind is designed around a decorator that can be used on
248
+ functions the user wants to expose to command line or to a .yml file.
249
+ The arguments to that function are
250
+ then bound to a dictionary. When the function is called,
251
+ each argument is looked up in the dictionary and its
252
+ value is replaced with the corresponding value in the dictionary. The
253
+ dictionary that the function looks for values in is controlled by
254
+ `scope`:
255
+
256
+ ```python
257
+ import argbind
258
+
259
+ @argbind.bind()
260
+ def func(arg : str = 'default'):
261
+ print(arg)
262
+
263
+ dict1 = {
264
+ 'func.arg': 1,
265
+ }
266
+ dict2 = {
267
+ 'func.arg': 2
268
+ }
269
+
270
+ with argbind.scope(dict1):
271
+ func() # prints 1
272
+ with argbind.scope(dict2):
273
+ func() # prints 2
274
+ func(arg=3) # prints 3.
275
+ ```
276
+
277
+ The function arguments are bound to the command line. Continuing the
278
+ simple program from above:
279
+
280
+ ```python
281
+ if __name__ == "__main__":
282
+ args = argbind.parse_args()
283
+ with argbind.scope(args):
284
+ func()
285
+ with argbind.scope(args):
286
+ func(arg=3)
287
+ ```
288
+
289
+ You can call this function like so:
290
+
291
+ ```bash
292
+ ❯ python examples/readme_example.py --func.arg 5
293
+ 1 # Looks up `arg` in dict1
294
+ 2 # Looks up `arg` in dict2
295
+ 3 # arg is passed in on python call `func(arg=3)`
296
+ 5 # Looks up `arg` from command line call `--func.arg 5`
297
+ 3 # arg is passed in from two places: `func(arg=3)` and `--func.arg 5`. Former overrides the latter.
298
+ ```
299
+
300
+ The logic here is that arguments that are bound that are closer to the actual function call get priority. From highest priority, to lowest, it goes:
301
+
302
+ 1. Bound explicitly in Python code
303
+ 2. Bound via command line
304
+ 3. Bound via .yml file
305
+ 4. Bound via default for kwarg
306
+
307
+ You can also use `bind` directly on classes - see [here](./examples/bind_existing).
308
+
309
+ # Limitations and known issues
310
+
311
+ There are some limitations to ArgBind, some due to how Python function decorators work,
312
+ and others out of a desire to keep ArgBind's code simple and straightforward.
313
+
314
+ ## Bound function names should be unique
315
+
316
+ Functions that are bound must be unique, even if they are in different files. The
317
+ function name is resolved in the argument parser only using the immediate name, not
318
+ a path to the function etc.
319
+
320
+ ## Supported docstring formats
321
+
322
+ ArgBind uses [docstring-parser](https://github.com/rr-/docstring_parser), and so
323
+ the only supported styles are: ReST, Google, and Numpydoc-style docstrings.
324
+
325
+ ## Not all types are supported
326
+
327
+ ArgBind supports most types that might pop up in your script, but not all. The
328
+ supported types can be seen in the [typing](./examples/typing/) and
329
+ [modern annotations](./examples/modern_typing/) examples.
330
+
331
+ ## Positional arguments should not be saved into .yml files
332
+
333
+ If a positional argument is saved into a .yml file and loaded via `--args.load`,
334
+ then any positional argument passed in the command line will be overridden. Take
335
+ care not to pass positional arguments via `.yml` files.
336
+
337
+ # Issues? Questions?
338
+
339
+ If you've run into some issues with ArgBind, or have some questions, please ask
340
+ via GitHub Issues. Projects like ArgBind are pretty tricky to get right, so there
341
+ may be some edge cases that have been missed.
@@ -0,0 +1,309 @@
1
+ # ArgBind
2
+
3
+ **Build CLIs via docstrings and type annotations, with YAML support.**
4
+
5
+ [![Tests](https://github.com/DBraun/argbind/actions/workflows/tests.yml/badge.svg)](https://github.com/DBraun/argbind/actions/workflows/tests.yml)
6
+ [![PyPI version](https://badge.fury.io/py/argbind-dbraun.svg)](https://pypi.org/project/argbind-dbraun/)
7
+ [![Python versions](https://img.shields.io/pypi/pyversions/argbind-dbraun)](https://pypi.org/project/argbind-dbraun/)
8
+ [![Downloads](https://static.pepy.tech/badge/argbind-dbraun)](https://pepy.tech/project/argbind-dbraun)
9
+
10
+ > **Note:** This is an extended fork of [pseeth/argbind](https://github.com/pseeth/argbind) that
11
+ > adds new features — modern type annotations (PEP 585/604), `Literal` and flexible boolean
12
+ > handling, dataclass `default_factory`, and YAML `$include` — published on PyPI as
13
+ > [`argbind-dbraun`](https://pypi.org/project/argbind-dbraun/). The import name is unchanged
14
+ > (`import argbind`), so it remains a drop-in replacement.
15
+
16
+ *ArgBind is a simple way to bind function or class arguments to the command line or to .yml files!*
17
+ It supports scoping of arguments, similar to other frameworks like
18
+ [Hydra](https://github.com/facebookresearch/hydra) and
19
+ [gin-config](https://github.com/google/gin-config).
20
+ ArgBind is *very* small (only ~800 lines of code, in one file), can be used to make complex and well-documented command line programs, and allows
21
+ you to configure program execution from .yml files.
22
+
23
+ If you're migrating from an ArgParse script to an ArgBind script, check out the
24
+ [migration guide](./examples/migration). Scroll down to see some [examples](#examples). Please also look at the
25
+ current known [limitations](#limitations-and-known-issues) of ArgBind.
26
+
27
+ ## Why ArgBind?
28
+
29
+ ArgBind was written by [Prem Seetharaman](https://github.com/pseeth) to help configure machine
30
+ learning experiments. ML experiment configuration is often highly nested, and can get out of hand
31
+ quickly. Rather than switching workflows around too much to accommodate a new framework, the goal
32
+ was to make already-written scripts easily adaptable, to achieve a few things:
33
+
34
+ 1. Configure scripts using `.yml` files. Be able to save `.yml` files that can be used to rerun scripts the exact same way twice.
35
+ 2. Spend time writing actual functions needed to run experiments, not argument parsers.
36
+ 3. Be able to run experiment code from other Python scripts, notebooks, or the command line.
37
+ 4. Be able to specify arguments from the command line directly to various functions.
38
+ 5. Be able to use scoping patterns, so a function can run inside a `train` scope and `test` scope, with different results (e.g., for getting a train dataset and a test dataset).
39
+
40
+ Nothing out there really fit the bill, so Prem wrote ArgBind. If you have
41
+ an `argparse` based script, converting it to ArgBind should be very quick! ArgBind is simple,
42
+ small, and easy to use. To get a feel for how it works, check out [usage](#usage), [design](#design), and [examples](#examples)!
43
+
44
+ ## Installation
45
+
46
+ Install via `pip`:
47
+
48
+ ```
49
+ python -m pip install argbind-dbraun
50
+ ```
51
+
52
+ Or from source:
53
+
54
+ ```
55
+ git clone https://github.com/DBraun/argbind.git
56
+ cd argbind
57
+ python -m pip install -e .
58
+ ```
59
+
60
+ This project uses [uv](https://docs.astral.sh/uv/). To create a dev environment with the
61
+ test dependencies and run the suite:
62
+
63
+ ```
64
+ uv sync --extra tests
65
+ uv run pytest
66
+ ```
67
+
68
+ Install the [pre-commit](https://pre-commit.com/) hooks (ruff format + lint) so they run
69
+ on every commit:
70
+
71
+ ```
72
+ uvx pre-commit install
73
+ ```
74
+
75
+ ## Examples
76
+
77
+ - [Example 1: Hello World](./examples/hello_world/)
78
+ - [Example 2: Scope patterns](./examples/scoping/)
79
+ - [Example 3: Typing](./examples/typing/)
80
+ - [Example 4: Modern type annotations (PEP 585/604)](./examples/modern_typing)
81
+ - [Example 5: Literal arguments](./examples/literal)
82
+ - [Example 6: Flexible boolean syntax](./examples/booleans)
83
+ - [Example 7: Using default_factory with dataclasses](./examples/default_factory)
84
+ - [Example 8: Loading, saving, and using .yml files](./examples/yaml)
85
+ - [Example 9: Nested .yml files with `$include`](./examples/nested_yaml)
86
+ - [Example 10: Multi-stage programs](./examples/multistage)
87
+ - [Example 11: Mimic more traditional CLI, without `func.arg` notation](./examples/without_prefix)
88
+ - [Example 12: Debug mode](./examples/debug)
89
+ - [Example 13: Migrating from ArgParse](./examples/migration)
90
+ - [Example 14: Binding existing functions and classes](./examples/bind_existing)
91
+ - [Example 15: Binding functions to specific groups](./examples/groups)
92
+
93
+ ## Usage
94
+
95
+ There are six main functions.
96
+
97
+ - `bind`: Binds keyword arguments (and positional arguments if `positional=True`) of a function or class to ArgBind.
98
+ - `parse_args`: Actually parses command line arguments into a dictionary.
99
+ - `scope`: Context manager that scopes a dictionary containing function arguments to be used by the functions.
100
+ - `dump_args`: Dumps the args dictionary to a `.yml` file. Used internally when program is called with `--args.save path/to/save.yml`.
101
+ - `load_args`: Loads args from a `.yml` file. Used internally when program is called with `--args.load path/to/load.yml`.
102
+ - `get_used_args`: Gets arguments that have actually been used by call functions up to this point.
103
+
104
+ Your code with ArgBind generally follows this pattern:
105
+
106
+ 1. Write a function with a good docstring, and typed arguments. If arguments are not typed, their type will be inferred from the type of the default.
107
+ 2. Bind it via `bind`.
108
+ 3. When program is called, parse the arguments via `parse_args`.
109
+ 4. Scope the arguments, and call the bound function within the context block.
110
+ 5. Optionally call program with `--args.save` to save the current execution configuration to a `.yml` file or `--args.load` to load arguments from a prior saved execution configuration to run it the same way twice.
111
+ 6. Optionally, run your script with `--args.debug=1` to see exactly how every bound function is called.
112
+
113
+ In your program, you can call `get_used_args` to see which arguments were actually used. Here's a minimal example:
114
+
115
+ ```python
116
+ import argbind
117
+
118
+ @argbind.bind()
119
+ def hello(
120
+ name : str = 'world'
121
+ ):
122
+ """Say hello to someone.
123
+
124
+ Parameters
125
+ ----------
126
+ name : str, optional
127
+ Who you're saying hello to, by default 'world'
128
+ """
129
+ print("Hello " + name)
130
+
131
+ if __name__ == "__main__":
132
+ # Arguments for CLI automatically generated from bound functions under the pattern
133
+ # function_name.function_arg.
134
+ args = argbind.parse_args()
135
+ # When called within a scope, the keyword arguments map to those from CLI or
136
+ # from defaults.
137
+ with argbind.scope(args):
138
+ hello()
139
+ # get_used_args() returns the arguments that were actually used by the bound
140
+ # functions that ran -- here, {'hello.name': 'world'}.
141
+ print(argbind.get_used_args())
142
+ ```
143
+
144
+ Help text is automatically generated from the docstring:
145
+
146
+ ```
147
+ ❯ python examples/hello_world/with_argbind.py -h
148
+ usage: with_argbind.py [-h] [--args.save ARGS.SAVE] [--args.load ARGS.LOAD] [--args.debug ARGS.DEBUG] [--hello.name HELLO.NAME]
149
+
150
+ optional arguments:
151
+ -h, --help show this help message and exit
152
+ --args.save ARGS.SAVE
153
+ Path to save all arguments used to run script to.
154
+ --args.load ARGS.LOAD
155
+ Path to load arguments from, stored as a .yml file.
156
+ --args.debug ARGS.DEBUG
157
+ Print arguments as they are passed to each function.
158
+
159
+ Generated arguments for function hello:
160
+ Say hello to someone.
161
+
162
+ --hello.name HELLO.NAME
163
+ Who you're saying hello to, by default 'world'
164
+ ```
165
+
166
+ Execution of this could look like:
167
+
168
+ ```
169
+ # Default arguments
170
+ ❯ python examples/hello_world/with_argbind.py
171
+ Hello world
172
+ # Binding name from the command line and saving the args.
173
+ ❯ python examples/hello_world/with_argbind.py --hello.name=you --args.save=/tmp/args.yml
174
+ Hello you
175
+ # Loading saved arguments.
176
+ ❯ python examples/hello_world/with_argbind.py --args.load=/tmp/args.yml
177
+ Hello you
178
+ # Loading saved arguments, and overriding via command line.
179
+ ❯ python examples/hello_world/with_argbind.py --args.load=/tmp/args.yml --hello.name=me
180
+ Hello me
181
+ # See how each function is called with args.debug=1.
182
+ ❯ python examples/hello_world/with_argbind.py --args.load=/tmp/args.yml --args.debug=1
183
+ hello(
184
+ name : str = you
185
+ )
186
+ Hello you
187
+ ```
188
+
189
+ You can also run the `hello` function from another Python script or a Jupyter notebook:
190
+
191
+ ```python
192
+ import argbind
193
+ # Import the bound function
194
+ from .hello_world import hello
195
+ # Load the args
196
+ args = argbind.load_args('/tmp/args.yml')
197
+ # Scope the args
198
+ with argbind.scope(args):
199
+ # Run the bound function
200
+ hello() # Prints 'Hello you'.
201
+ hello() # Prints 'Hello world', as it's outside scope.
202
+ # Can edit the args before scoping again.
203
+ args['hello.name'] = 'me'
204
+ with argbind.scope(args):
205
+ hello() # Prints 'Hello me'.
206
+ ```
207
+
208
+ You'll notice that ArgBind forces you to document and type your
209
+ function arguments, which is always a good idea!
210
+ Please check out the [examples](#examples) for more details!
211
+
212
+
213
+ ## Design
214
+
215
+ ArgBind is designed around a decorator that can be used on
216
+ functions the user wants to expose to command line or to a .yml file.
217
+ The arguments to that function are
218
+ then bound to a dictionary. When the function is called,
219
+ each argument is looked up in the dictionary and its
220
+ value is replaced with the corresponding value in the dictionary. The
221
+ dictionary that the function looks for values in is controlled by
222
+ `scope`:
223
+
224
+ ```python
225
+ import argbind
226
+
227
+ @argbind.bind()
228
+ def func(arg : str = 'default'):
229
+ print(arg)
230
+
231
+ dict1 = {
232
+ 'func.arg': 1,
233
+ }
234
+ dict2 = {
235
+ 'func.arg': 2
236
+ }
237
+
238
+ with argbind.scope(dict1):
239
+ func() # prints 1
240
+ with argbind.scope(dict2):
241
+ func() # prints 2
242
+ func(arg=3) # prints 3.
243
+ ```
244
+
245
+ The function arguments are bound to the command line. Continuing the
246
+ simple program from above:
247
+
248
+ ```python
249
+ if __name__ == "__main__":
250
+ args = argbind.parse_args()
251
+ with argbind.scope(args):
252
+ func()
253
+ with argbind.scope(args):
254
+ func(arg=3)
255
+ ```
256
+
257
+ You can call this function like so:
258
+
259
+ ```bash
260
+ ❯ python examples/readme_example.py --func.arg 5
261
+ 1 # Looks up `arg` in dict1
262
+ 2 # Looks up `arg` in dict2
263
+ 3 # arg is passed in on python call `func(arg=3)`
264
+ 5 # Looks up `arg` from command line call `--func.arg 5`
265
+ 3 # arg is passed in from two places: `func(arg=3)` and `--func.arg 5`. Former overrides the latter.
266
+ ```
267
+
268
+ The logic here is that arguments that are bound that are closer to the actual function call get priority. From highest priority, to lowest, it goes:
269
+
270
+ 1. Bound explicitly in Python code
271
+ 2. Bound via command line
272
+ 3. Bound via .yml file
273
+ 4. Bound via default for kwarg
274
+
275
+ You can also use `bind` directly on classes - see [here](./examples/bind_existing).
276
+
277
+ # Limitations and known issues
278
+
279
+ There are some limitations to ArgBind, some due to how Python function decorators work,
280
+ and others out of a desire to keep ArgBind's code simple and straightforward.
281
+
282
+ ## Bound function names should be unique
283
+
284
+ Functions that are bound must be unique, even if they are in different files. The
285
+ function name is resolved in the argument parser only using the immediate name, not
286
+ a path to the function etc.
287
+
288
+ ## Supported docstring formats
289
+
290
+ ArgBind uses [docstring-parser](https://github.com/rr-/docstring_parser), and so
291
+ the only supported styles are: ReST, Google, and Numpydoc-style docstrings.
292
+
293
+ ## Not all types are supported
294
+
295
+ ArgBind supports most types that might pop up in your script, but not all. The
296
+ supported types can be seen in the [typing](./examples/typing/) and
297
+ [modern annotations](./examples/modern_typing/) examples.
298
+
299
+ ## Positional arguments should not be saved into .yml files
300
+
301
+ If a positional argument is saved into a .yml file and loaded via `--args.load`,
302
+ then any positional argument passed in the command line will be overridden. Take
303
+ care not to pass positional arguments via `.yml` files.
304
+
305
+ # Issues? Questions?
306
+
307
+ If you've run into some issues with ArgBind, or have some questions, please ask
308
+ via GitHub Issues. Projects like ArgBind are pretty tricky to get right, so there
309
+ may be some edge cases that have been missed.
@@ -0,0 +1,21 @@
1
+ from .argbind import (
2
+ bind,
3
+ bind_module,
4
+ build_parser,
5
+ dump_args,
6
+ get_used_args,
7
+ load_args,
8
+ parse_args,
9
+ scope,
10
+ )
11
+
12
+ __all__ = [
13
+ "bind",
14
+ "bind_module",
15
+ "build_parser",
16
+ "parse_args",
17
+ "dump_args",
18
+ "load_args",
19
+ "get_used_args",
20
+ "scope",
21
+ ]