pluggable-namespace 1.0.0__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 (95) hide show
  1. pluggable_namespace-1.0.0/MANIFEST.in +3 -0
  2. pluggable_namespace-1.0.0/PKG-INFO +161 -0
  3. pluggable_namespace-1.0.0/README.rst +117 -0
  4. pluggable_namespace-1.0.0/pyproject.toml +47 -0
  5. pluggable_namespace-1.0.0/setup.cfg +4 -0
  6. pluggable_namespace-1.0.0/setup.py +29 -0
  7. pluggable_namespace-1.0.0/src/_config/config.yaml +41 -0
  8. pluggable_namespace-1.0.0/src/_config/plugin/choices.py +37 -0
  9. pluggable_namespace-1.0.0/src/_config/plugin/contract/init.py +1 -0
  10. pluggable_namespace-1.0.0/src/_config/plugin/default.py +30 -0
  11. pluggable_namespace-1.0.0/src/_config/plugin/display_priority.py +34 -0
  12. pluggable_namespace-1.0.0/src/_config/plugin/group.py +31 -0
  13. pluggable_namespace-1.0.0/src/_config/plugin/init.py +318 -0
  14. pluggable_namespace-1.0.0/src/_config/plugin/options.py +14 -0
  15. pluggable_namespace-1.0.0/src/_config/plugin/os.py +10 -0
  16. pluggable_namespace-1.0.0/src/_config/plugin/positional.py +21 -0
  17. pluggable_namespace-1.0.0/src/_config/plugin/source.py +53 -0
  18. pluggable_namespace-1.0.0/src/_config/plugin/subcommands.py +82 -0
  19. pluggable_namespace-1.0.0/src/_config/plugin/type.py +15 -0
  20. pluggable_namespace-1.0.0/src/_log/config.yaml +82 -0
  21. pluggable_namespace-1.0.0/src/_log/plugin/basic.py +35 -0
  22. pluggable_namespace-1.0.0/src/_log/plugin/contract/init.py +1 -0
  23. pluggable_namespace-1.0.0/src/_log/plugin/init.py +167 -0
  24. pluggable_namespace-1.0.0/src/_log/plugin/timed_rolling.py +20 -0
  25. pluggable_namespace-1.0.0/src/_patt/config.yaml +10 -0
  26. pluggable_namespace-1.0.0/src/_patt/plugin/inst.py +286 -0
  27. pluggable_namespace-1.0.0/src/_pop/file.py +16 -0
  28. pluggable_namespace-1.0.0/src/_pop/sub.py +130 -0
  29. pluggable_namespace-1.0.0/src/_pop/test.py +121 -0
  30. pluggable_namespace-1.0.0/src/_rend/README.rst +17 -0
  31. pluggable_namespace-1.0.0/src/_rend/config.yaml +55 -0
  32. pluggable_namespace-1.0.0/src/_rend/exc/rend.py +16 -0
  33. pluggable_namespace-1.0.0/src/_rend/output/contracts/init.py +1 -0
  34. pluggable_namespace-1.0.0/src/_rend/output/json_out.py +19 -0
  35. pluggable_namespace-1.0.0/src/_rend/output/nested.py +191 -0
  36. pluggable_namespace-1.0.0/src/_rend/output/pretty.py +5 -0
  37. pluggable_namespace-1.0.0/src/_rend/output/raw.py +5 -0
  38. pluggable_namespace-1.0.0/src/_rend/output/yaml_out.py +37 -0
  39. pluggable_namespace-1.0.0/src/_rend/rend/contracts/init.py +1 -0
  40. pluggable_namespace-1.0.0/src/_rend/rend/init.py +136 -0
  41. pluggable_namespace-1.0.0/src/_rend/rend/jinja.py +85 -0
  42. pluggable_namespace-1.0.0/src/_rend/rend/json_file.py +20 -0
  43. pluggable_namespace-1.0.0/src/_rend/rend/toml_file.py +19 -0
  44. pluggable_namespace-1.0.0/src/_rend/rend/yaml_file.py +179 -0
  45. pluggable_namespace-1.0.0/src/_rend/rend/yaml_template.py +25 -0
  46. pluggable_namespace-1.0.0/src/_seed/config.yaml +32 -0
  47. pluggable_namespace-1.0.0/src/_seed/seed/init.py +27 -0
  48. pluggable_namespace-1.0.0/src/_seed/template/plugin/.github/workflows/ci.yaml +70 -0
  49. pluggable_namespace-1.0.0/src/_seed/template/plugin/.gitignore +165 -0
  50. pluggable_namespace-1.0.0/src/_seed/template/plugin/.gitlab-ci.yml +42 -0
  51. pluggable_namespace-1.0.0/src/_seed/template/plugin/.pre-commit-config.yaml +76 -0
  52. pluggable_namespace-1.0.0/src/_seed/template/plugin/README.rst.jinja +4 -0
  53. pluggable_namespace-1.0.0/src/_seed/template/plugin/copier.yml +6 -0
  54. pluggable_namespace-1.0.0/src/_seed/template/plugin/pyproject.toml.jinja +46 -0
  55. pluggable_namespace-1.0.0/src/_seed/template/plugin/src/{{name}}/__init__.py +0 -0
  56. pluggable_namespace-1.0.0/src/_seed/template/plugin/src/{{name}}/config.yaml.jinja +19 -0
  57. pluggable_namespace-1.0.0/src/_seed/template/plugin/src/{{name}}/plugin/init.py.jinja +9 -0
  58. pluggable_namespace-1.0.0/src/_seed/template/plugin/test/conftest.py.jinja +23 -0
  59. pluggable_namespace-1.0.0/src/_seed/template/plugin/test/test_init.py.jinja +25 -0
  60. pluggable_namespace-1.0.0/src/_seed/template/plugin/test/tpath/README.rst +7 -0
  61. pluggable_namespace-1.0.0/src/_seed/template/plugin/test/tpath/mods/config.yaml +9 -0
  62. pluggable_namespace-1.0.0/src/_seed/template/plugin/test/tpath/mods/src/init.py +6 -0
  63. pluggable_namespace-1.0.0/src/hub/README.rst +55 -0
  64. pluggable_namespace-1.0.0/src/hub/__main__.py +57 -0
  65. pluggable_namespace-1.0.0/src/hub/config.yaml +65 -0
  66. pluggable_namespace-1.0.0/src/hub/plugin/cli.py +125 -0
  67. pluggable_namespace-1.0.0/src/hub/plugin/completer.py +143 -0
  68. pluggable_namespace-1.0.0/src/hub/plugin/config.py +35 -0
  69. pluggable_namespace-1.0.0/src/hub/plugin/console.py +78 -0
  70. pluggable_namespace-1.0.0/src/hub/plugin/init.py +88 -0
  71. pluggable_namespace-1.0.0/src/hub/plugin/ref.py +55 -0
  72. pluggable_namespace-1.0.0/src/pluggable_namespace.egg-info/PKG-INFO +161 -0
  73. pluggable_namespace-1.0.0/src/pluggable_namespace.egg-info/SOURCES.txt +96 -0
  74. pluggable_namespace-1.0.0/src/pluggable_namespace.egg-info/dependency_links.txt +1 -0
  75. pluggable_namespace-1.0.0/src/pluggable_namespace.egg-info/entry_points.txt +2 -0
  76. pluggable_namespace-1.0.0/src/pluggable_namespace.egg-info/requires.txt +35 -0
  77. pluggable_namespace-1.0.0/src/pluggable_namespace.egg-info/top_level.txt +8 -0
  78. pluggable_namespace-1.0.0/src/pns/__init__.py +11 -0
  79. pluggable_namespace-1.0.0/src/pns/_contract.c +25301 -0
  80. pluggable_namespace-1.0.0/src/pns/_contract.py +375 -0
  81. pluggable_namespace-1.0.0/src/pns/_data.c +17423 -0
  82. pluggable_namespace-1.0.0/src/pns/_data.py +380 -0
  83. pluggable_namespace-1.0.0/src/pns/_debug.py +24 -0
  84. pluggable_namespace-1.0.0/src/pns/_hub.c +14700 -0
  85. pluggable_namespace-1.0.0/src/pns/_hub.py +178 -0
  86. pluggable_namespace-1.0.0/src/pns/contract.py +159 -0
  87. pluggable_namespace-1.0.0/src/pns/data.py +27 -0
  88. pluggable_namespace-1.0.0/src/pns/dir.py +217 -0
  89. pluggable_namespace-1.0.0/src/pns/hub.py +218 -0
  90. pluggable_namespace-1.0.0/src/pns/loop.py +79 -0
  91. pluggable_namespace-1.0.0/src/pns/mod.py +268 -0
  92. pluggable_namespace-1.0.0/src/pns/ref.py +98 -0
  93. pluggable_namespace-1.0.0/src/pns/shell.py +230 -0
  94. pluggable_namespace-1.0.0/src/pns/shim.py +123 -0
  95. pluggable_namespace-1.0.0/src/pns/verify.py +129 -0
@@ -0,0 +1,3 @@
1
+ graft src
2
+ global-exclude __pycache__ *.py[cod]
3
+ global-exclude *.egg-info
@@ -0,0 +1,161 @@
1
+ Metadata-Version: 2.2
2
+ Name: pluggable-namespace
3
+ Version: 1.0.0
4
+ Summary: Pluggable namespaces
5
+ Author-email: Tyler Levy Conde <yonstib@gmail.com>
6
+ Classifier: Operating System :: OS Independent
7
+ Classifier: Programming Language :: Python :: 3.11
8
+ Classifier: Programming Language :: Python :: 3.12
9
+ Classifier: Development Status :: 5 - Production/Stable
10
+ Classifier: License :: OSI Approved :: Apache Software License
11
+ Requires-Python: >=3.11
12
+ Description-Content-Type: text/x-rst
13
+ Requires-Dist: aioconsole
14
+ Requires-Dist: aiofiles
15
+ Requires-Dist: aiologger
16
+ Requires-Dist: argparse
17
+ Requires-Dist: PyYaml
18
+ Provides-Extra: console
19
+ Requires-Dist: aiomonitor; extra == "console"
20
+ Requires-Dist: prompt-toolkit; extra == "console"
21
+ Provides-Extra: rend
22
+ Requires-Dist: aio-yte; extra == "rend"
23
+ Requires-Dist: jinja2; extra == "rend"
24
+ Requires-Dist: toml; extra == "rend"
25
+ Provides-Extra: seed
26
+ Requires-Dist: copier; extra == "seed"
27
+ Provides-Extra: test
28
+ Requires-Dist: pexpect; extra == "test"
29
+ Requires-Dist: pytest; extra == "test"
30
+ Requires-Dist: pytest-asyncio; extra == "test"
31
+ Requires-Dist: setuptools; extra == "test"
32
+ Provides-Extra: full
33
+ Requires-Dist: aiomonitor; extra == "full"
34
+ Requires-Dist: prompt-toolkit; extra == "full"
35
+ Requires-Dist: aio-yte; extra == "full"
36
+ Requires-Dist: jinja2; extra == "full"
37
+ Requires-Dist: toml; extra == "full"
38
+ Requires-Dist: copier; extra == "full"
39
+ Requires-Dist: pexpect; extra == "full"
40
+ Requires-Dist: pytest; extra == "full"
41
+ Requires-Dist: pytest-asyncio; extra == "full"
42
+ Requires-Dist: setuptools; extra == "full"
43
+ Dynamic: provides-extra
44
+
45
+ ===================
46
+ Pluggable Namespace
47
+ ===================
48
+ This project is designed to facilitate the creation and management of pluggable software architectures using namespaces. The concept of pluggable namespaces enables the development of software that is modular and easy to extend.
49
+
50
+ Pluggable namespaces provide a framework for constructing applications composed entirely of interchangeable modules. This approach allows developers to scale their projects smoothly and integrate complex software components seamlessly.
51
+
52
+ Using pluggable namespaces, developers can build software in smaller, maintainable components. These components can then be combined and deployed as a single entity, simplifying the deployment process.
53
+
54
+ All of this is achieved using Python, one of the world's most popular and powerful programming languages.
55
+
56
+ Installation
57
+ ============
58
+
59
+ You can install ``pluggable-namespace`` from PyPI:
60
+
61
+ .. code-block:: bash
62
+
63
+ pip3 install pluggable-namespace
64
+
65
+ Creating a pluggable application can be accomplished with just a few lines of code. The heart of every pluggable-namespace project is the creation of a hub, adding dynamic subsystems, and interacting with them through the hub's namespace.
66
+
67
+ .. code-block:: python
68
+
69
+ import pns.shim
70
+ import asyncio
71
+
72
+
73
+ async def main():
74
+ hub = await pns.shim.loaded_hub()
75
+ await hub.my_sub.init.cli()
76
+
77
+ if __name__ == "__main__":
78
+ asyncio.run(main())
79
+
80
+ Configuration
81
+ =============
82
+ When building a pluggable-namespace app, all configuration settings are stored in a ``config.yaml`` file.
83
+
84
+ .. code-block:: yaml
85
+
86
+ # Each configuration option for your module
87
+ config:
88
+ my_namespace:
89
+ my_opt:
90
+ default: True
91
+
92
+ # Options exposed on the CLI when your app controls the CLI
93
+ cli_config:
94
+ my_namespace:
95
+ my_opt:
96
+ help: Description of this option
97
+ subcommands:
98
+ - my_subcommand
99
+ group: My arg group
100
+
101
+ # Subcommands to expose for your project
102
+ subcommands:
103
+ my_namespace:
104
+ my_subcommand:
105
+ help: My subcommand
106
+
107
+ # Dynamic namespaces that your app merges onto and which folders extend those namespaces
108
+ dyne:
109
+ my_dyne:
110
+ - src_dir
111
+
112
+ # Python imports that your app uses, to be added to hub.lib for your app
113
+ import:
114
+ - asyncio
115
+ - importlib
116
+ - importlib.resources
117
+ - os
118
+ - toml
119
+
120
+
121
+ From the example above, all arguments are loaded onto the namespace under hub.OPT.my_namespace.
122
+ One ``config.yaml`` can add configuration options to multiple namespaces.
123
+ They are merged in the order found in sys.path.
124
+
125
+ Extending Namespaces
126
+ ====================
127
+
128
+ locally
129
+ -------
130
+
131
+ Extending ``pluggable-namespace`` is straightforward with dynamic namespaces.
132
+ Extend any dynamic namespace on the hub by adding a directory containing a "config.yaml" to PYTHONPATH.
133
+
134
+ .. code-block:: bash
135
+
136
+ export PYTHONPATH=$PYTHONPATH:/path/to/project_root
137
+
138
+ Add a config.yaml to that directory:
139
+
140
+ .. code-block:: yaml
141
+
142
+ # project_root/config.yaml
143
+ dyne:
144
+ namespace:
145
+ # This references the directory project_root/foo
146
+ - foo
147
+
148
+ Now, every Python file in ``project_root/foo`` will be added to the hub under ``hub.namespace``.
149
+
150
+
151
+ With PyPI
152
+ ---------
153
+
154
+ You can use the ``seed`` command to create all the boiler-plate code you need for a pluggable-namespace project.
155
+
156
+ .. code-block:: bash
157
+
158
+ hub seed.init.cli /path/to/project_root name=my_project
159
+
160
+
161
+ Now you can add all your code to /path/to/project_root/src/my_project
@@ -0,0 +1,117 @@
1
+ ===================
2
+ Pluggable Namespace
3
+ ===================
4
+ This project is designed to facilitate the creation and management of pluggable software architectures using namespaces. The concept of pluggable namespaces enables the development of software that is modular and easy to extend.
5
+
6
+ Pluggable namespaces provide a framework for constructing applications composed entirely of interchangeable modules. This approach allows developers to scale their projects smoothly and integrate complex software components seamlessly.
7
+
8
+ Using pluggable namespaces, developers can build software in smaller, maintainable components. These components can then be combined and deployed as a single entity, simplifying the deployment process.
9
+
10
+ All of this is achieved using Python, one of the world's most popular and powerful programming languages.
11
+
12
+ Installation
13
+ ============
14
+
15
+ You can install ``pluggable-namespace`` from PyPI:
16
+
17
+ .. code-block:: bash
18
+
19
+ pip3 install pluggable-namespace
20
+
21
+ Creating a pluggable application can be accomplished with just a few lines of code. The heart of every pluggable-namespace project is the creation of a hub, adding dynamic subsystems, and interacting with them through the hub's namespace.
22
+
23
+ .. code-block:: python
24
+
25
+ import pns.shim
26
+ import asyncio
27
+
28
+
29
+ async def main():
30
+ hub = await pns.shim.loaded_hub()
31
+ await hub.my_sub.init.cli()
32
+
33
+ if __name__ == "__main__":
34
+ asyncio.run(main())
35
+
36
+ Configuration
37
+ =============
38
+ When building a pluggable-namespace app, all configuration settings are stored in a ``config.yaml`` file.
39
+
40
+ .. code-block:: yaml
41
+
42
+ # Each configuration option for your module
43
+ config:
44
+ my_namespace:
45
+ my_opt:
46
+ default: True
47
+
48
+ # Options exposed on the CLI when your app controls the CLI
49
+ cli_config:
50
+ my_namespace:
51
+ my_opt:
52
+ help: Description of this option
53
+ subcommands:
54
+ - my_subcommand
55
+ group: My arg group
56
+
57
+ # Subcommands to expose for your project
58
+ subcommands:
59
+ my_namespace:
60
+ my_subcommand:
61
+ help: My subcommand
62
+
63
+ # Dynamic namespaces that your app merges onto and which folders extend those namespaces
64
+ dyne:
65
+ my_dyne:
66
+ - src_dir
67
+
68
+ # Python imports that your app uses, to be added to hub.lib for your app
69
+ import:
70
+ - asyncio
71
+ - importlib
72
+ - importlib.resources
73
+ - os
74
+ - toml
75
+
76
+
77
+ From the example above, all arguments are loaded onto the namespace under hub.OPT.my_namespace.
78
+ One ``config.yaml`` can add configuration options to multiple namespaces.
79
+ They are merged in the order found in sys.path.
80
+
81
+ Extending Namespaces
82
+ ====================
83
+
84
+ locally
85
+ -------
86
+
87
+ Extending ``pluggable-namespace`` is straightforward with dynamic namespaces.
88
+ Extend any dynamic namespace on the hub by adding a directory containing a "config.yaml" to PYTHONPATH.
89
+
90
+ .. code-block:: bash
91
+
92
+ export PYTHONPATH=$PYTHONPATH:/path/to/project_root
93
+
94
+ Add a config.yaml to that directory:
95
+
96
+ .. code-block:: yaml
97
+
98
+ # project_root/config.yaml
99
+ dyne:
100
+ namespace:
101
+ # This references the directory project_root/foo
102
+ - foo
103
+
104
+ Now, every Python file in ``project_root/foo`` will be added to the hub under ``hub.namespace``.
105
+
106
+
107
+ With PyPI
108
+ ---------
109
+
110
+ You can use the ``seed`` command to create all the boiler-plate code you need for a pluggable-namespace project.
111
+
112
+ .. code-block:: bash
113
+
114
+ hub seed.init.cli /path/to/project_root name=my_project
115
+
116
+
117
+ Now you can add all your code to /path/to/project_root/src/my_project
@@ -0,0 +1,47 @@
1
+ [build-system]
2
+ requires = ["setuptools", "wheel", "Cython"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "pluggable-namespace"
7
+ version = "1.0.0"
8
+ description = "Pluggable namespaces"
9
+ readme = "README.rst"
10
+ authors = [
11
+ {name = "Tyler Levy Conde", email = "yonstib@gmail.com"},
12
+ ]
13
+ classifiers = [
14
+ "Operating System :: OS Independent",
15
+ "Programming Language :: Python :: 3.11",
16
+ "Programming Language :: Python :: 3.12",
17
+ "Development Status :: 5 - Production/Stable",
18
+ "License :: OSI Approved :: Apache Software License",
19
+ ]
20
+ requires-python = ">=3.11"
21
+ dependencies = [
22
+ "aioconsole",
23
+ "aiofiles",
24
+ "aiologger",
25
+ "argparse",
26
+ "PyYaml",
27
+ ]
28
+ dynamic = ["optional-dependencies"]
29
+
30
+ [project.scripts]
31
+ hub = "hub.__main__:main"
32
+
33
+ [tool.setuptools]
34
+ package-dir = {"" = "src"}
35
+
36
+ [tool.setuptools.packages.find]
37
+ where = ["src"]
38
+ namespaces = true
39
+
40
+ [tool.pytest.ini_options]
41
+ testpaths = "test"
42
+ addopts = "--tb native --full-trace --color=yes -vv"
43
+ asyncio_mode = "auto"
44
+ asyncio_default_fixture_loop_scope = "function"
45
+
46
+ [tool.cython-lint]
47
+ max-line-length = 120
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,29 @@
1
+ from pathlib import Path
2
+ from setuptools import setup
3
+ from setuptools.extension import Extension
4
+ from Cython.Build import cythonize
5
+
6
+ extensions = [
7
+ Extension(name="pns._ccontract", sources=["src/pns/_contract.py"]),
8
+ Extension(name="pns._cdata", sources=["src/pns/_data.py"]),
9
+ Extension(name="pns._chub", sources=["src/pns/_hub.py"]),
10
+ ]
11
+
12
+ SETUP_DIRNAME = Path(__file__).parent
13
+
14
+ requirement_extras = {}
15
+ REQUIREMENTS = SETUP_DIRNAME / "requirement"
16
+ assert REQUIREMENTS.exists()
17
+ for req_file in REQUIREMENTS.glob("*.txt"):
18
+ with open(req_file) as f:
19
+ requirement_extras[req_file.stem] = sorted(
20
+ line for line in f.read().splitlines() if line.strip()
21
+ )
22
+
23
+ requirement_extras["full"] = sum(requirement_extras.values(), [])
24
+
25
+ setup(
26
+ extras_require=requirement_extras,
27
+ ext_modules=cythonize(extensions),
28
+ include_package_data=True,
29
+ )
@@ -0,0 +1,41 @@
1
+
2
+ cli_config:
3
+ pns:
4
+ config:
5
+ default: ~/.pns/config.yaml
6
+ os: PNS_CONFIG
7
+ help: The config file used for PNS
8
+ group: Config Options
9
+ options:
10
+ - -c
11
+ subcommands:
12
+ - __global__
13
+
14
+ dyne:
15
+ config:
16
+ - plugin
17
+
18
+ import:
19
+ - aiofiles
20
+ - aiofiles.os
21
+ - aiologger.handlers.files
22
+ - argparse
23
+ - asyncio
24
+ - ast
25
+ - collections
26
+ - pns.contract
27
+ - pns.data
28
+ - pns.exc
29
+ - pns.hub
30
+ - importlib
31
+ - importlib.resources
32
+ - logging
33
+ - msgpack
34
+ - os
35
+ - pathlib
36
+ - pickle
37
+ - signal
38
+ - sys
39
+ - traceback
40
+ - yaml
41
+ - typing
@@ -0,0 +1,37 @@
1
+ async def parse_opt(hub, opts: dict[str, object]) -> dict[str, object]:
2
+ """
3
+ Handle choices that may come from a loaded mod.
4
+
5
+ You specify a sub on the hub for "choices" to dynamically use the loaded mods of that sub as the choices
6
+
7
+ I.e.
8
+
9
+ config:
10
+ my_app:
11
+ my_opt:
12
+ choices:
13
+ my_sub
14
+
15
+ Otherwise, you can specify a list of static choices that may be used
16
+
17
+ I.e.
18
+
19
+ config:
20
+ my_app:
21
+ my_opt:
22
+ choices:
23
+ - choice_1
24
+ - choice_2
25
+ """
26
+ choices = opts.pop("choices", ())
27
+ if isinstance(choices, str):
28
+ finder = hub
29
+ for part in choices.split("."):
30
+ try:
31
+ finder = getattr(finder, part)
32
+ except AttributeError:
33
+ return {}
34
+
35
+ opts["choices"] = sorted(finder)
36
+
37
+ return {}
@@ -0,0 +1 @@
1
+ async def sig_parse_opt(hub, opts: dict[str, object]) -> dict[str, object]: ...
@@ -0,0 +1,30 @@
1
+ async def __init__(hub):
2
+ hub._.REF_PATTERN = hub.lib.re.compile(r"^hub\.(\w+(\.\w+)+)\(\)$")
3
+
4
+
5
+ async def parse_opt(hub, opts: dict[str, object]) -> dict[str, object]:
6
+ """
7
+ Remove 'default' from the argument opts, it will be handled by the config prioritizer, not argparse.
8
+ If the "default" is a function that exists on the hub, then call it to get the default value.
9
+ This allows you to call a function on the hub to do more processing on the default.
10
+ This could be useful for using a different value for the default based on OS.
11
+
12
+ I.e.
13
+
14
+ config:
15
+ my_app:
16
+ my_opt:
17
+ default: hub.my_sub.mod.func()
18
+ """
19
+ default = opts.pop("default", None)
20
+
21
+ if default and isinstance(default, str):
22
+ match = hub._.REF_PATTERN.match(default)
23
+ if match:
24
+ ref = match.group(1)
25
+ func = hub.lib.pns.ref.find(hub, ref)
26
+ default = func()
27
+ if hub.lib.asyncio.iscoroutine(default):
28
+ default = await default
29
+
30
+ return {"default": default}
@@ -0,0 +1,34 @@
1
+ async def parse_opt(hub, opts: dict[str, object]) -> dict[str, object]:
2
+ """
3
+ Handle the display priority of positional arguments.
4
+ This ensures that positional arguments appear in the defined order
5
+
6
+ I.e.
7
+
8
+ config:
9
+ my_app:
10
+ my_opt_1:
11
+ positional: True
12
+ display_priority: 1
13
+ my_opt_2:
14
+ positional: True
15
+ display_priority: 2
16
+ """
17
+ display_priority = opts.pop("display_priority", None)
18
+ return {"display_priority": display_priority}
19
+
20
+
21
+ async def sort(hub, cli_args: list[dict[str, object]]) -> list[dict[str, object]]:
22
+ """
23
+ Sort the CLI arguments by display_priority.
24
+ The negative display_priorities were applied to args with no display priority,
25
+ They will come in the order they were defined
26
+ """
27
+ # Sort arguments by display_priority
28
+ return sorted(
29
+ cli_args,
30
+ key=lambda opt: (
31
+ opt["extra"]["display_priority"] is None,
32
+ opt["extra"]["display_priority"],
33
+ ),
34
+ )
@@ -0,0 +1,31 @@
1
+ async def parse_opt(hub, opts: dict[str, object]) -> dict[str, object]:
2
+ """
3
+ This config value groups arguments together in the --help text.
4
+
5
+ config:
6
+ my_app:
7
+ my_opt:
8
+ group: my_group
9
+ other_opt:
10
+ group: my_group
11
+ """
12
+ group = opts.pop("group", False)
13
+ return {"group": group}
14
+
15
+
16
+ async def merge(hub, name: str, groups: dict[str, object], subcmd: str, subparser):
17
+ """
18
+ Merge the group into the subparser if a group name is provided.
19
+
20
+ Args:
21
+ name (str): The name of the group.
22
+ groups (dict): The existing groups dictionary.
23
+ subcmd (str): The subcommand name.
24
+ subparser (ArgumentParser): The subparser instance.
25
+
26
+ Returns:
27
+ ArgumentParser: The argument group added to the subparser.
28
+ """
29
+ if name:
30
+ return groups[subcmd].setdefault(name, subparser.add_argument_group(name))
31
+ return subparser