├── .github └── workflows │ ├── coverage.yml │ └── tests.yml ├── LICENSE ├── README.rst ├── code_coverage.py ├── dev_requirements.txt ├── duckargs ├── __init__.py └── __main__.py ├── setup.py └── tests ├── test_data ├── choices │ ├── args.txt │ ├── expected_c.txt │ └── expected_python.txt ├── env_all │ ├── args.txt │ ├── expected_c.txt │ └── expected_python.txt ├── env_comment │ ├── args.txt │ ├── expected_c.txt │ └── expected_python.txt ├── env_print │ ├── args.txt │ ├── expected_c.txt │ └── expected_python.txt ├── flags_only │ ├── args.txt │ ├── expected_c.txt │ └── expected_python.txt ├── hex │ ├── args.txt │ ├── expected_c.txt │ └── expected_python.txt ├── many_opts │ ├── args.txt │ ├── expected_c.txt │ └── expected_python.txt ├── nargs │ └── expected_python.txt ├── negative_hex │ ├── args.txt │ ├── expected_c.txt │ └── expected_python.txt ├── negative_int │ ├── args.txt │ ├── expected_c.txt │ └── expected_python.txt ├── normalize_names │ ├── args.txt │ ├── expected_c.txt │ └── expected_python.txt ├── options_only │ ├── args.txt │ ├── expected_c.txt │ └── expected_python.txt ├── positional_only │ ├── args.txt │ ├── expected_c.txt │ └── expected_python.txt ├── positional_values │ ├── args.txt │ ├── expected_c.txt │ └── expected_python.txt ├── readme_example │ ├── args.txt │ ├── expected_c.txt │ └── expected_python.txt ├── reserved_words_c │ ├── args.txt │ ├── expected_c.txt │ └── expected_python.txt └── reserved_words_python │ ├── args.txt │ ├── expected_c.txt │ └── expected_python.txt └── test_duckargs.py /.github/workflows/coverage.yml: -------------------------------------------------------------------------------- 1 | name: Test coverage 2 | on: [push, workflow_dispatch] 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | 7 | strategy: 8 | matrix: 9 | python-version: ['3.9'] 10 | 11 | steps: 12 | # Checkout the latest code from the repo 13 | - name: Checkout repo 14 | uses: actions/checkout@v2 15 | # Setup which version of Python to use 16 | - name: Set Up Python ${{ matrix.python-version }} 17 | uses: actions/setup-python@v2 18 | with: 19 | python-version: ${{ matrix.python-version }} 20 | # Display the Python version being used 21 | - name: Display Python version 22 | run: python -c "import sys; print(sys.version)" 23 | # Install the package using the setup.py 24 | - name: Install package 25 | run: python setup.py install 26 | - name: Install coverage 27 | run: python -m pip install coverage 28 | # Run the tests 29 | - name: Run coverage script 30 | run: python code_coverage.py 31 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | on: [push, workflow_dispatch] 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | 7 | strategy: 8 | matrix: 9 | # Run in all these versions of Python 10 | python-version: ['3.8', '3.9', '3.10'] 11 | 12 | steps: 13 | # Checkout the latest code from the repo 14 | - name: Checkout repo 15 | uses: actions/checkout@v2 16 | # Setup which version of Python to use 17 | - name: Set Up Python ${{ matrix.python-version }} 18 | uses: actions/setup-python@v2 19 | with: 20 | python-version: ${{ matrix.python-version }} 21 | # Display the Python version being used 22 | - name: Display Python version 23 | run: python -c "import sys; print(sys.version)" 24 | # Install the package using the setup.py 25 | - name: Install package 26 | run: python setup.py install 27 | # Run the tests 28 | - name: Run tests 29 | run: python setup.py test 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | Copyright 2023 Erik K. Nyquist 180 | 181 | Licensed under the Apache License, Version 2.0 (the "License"); 182 | you may not use this file except in compliance with the License. 183 | You may obtain a copy of the License at 184 | 185 | http://www.apache.org/licenses/LICENSE-2.0 186 | 187 | Unless required by applicable law or agreed to in writing, software 188 | distributed under the License is distributed on an "AS IS" BASIS, 189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 190 | See the License for the specific language governing permissions and 191 | limitations under the License. 192 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | .. contents:: **Table of Contents** 2 | 3 | .. |duck| unicode:: 0x1F986 4 | 5 | duckargs |duck| 6 | --------------- 7 | 8 | .. |tests_badge| image:: https://github.com/eriknyquist/duckargs/actions/workflows/tests.yml/badge.svg 9 | .. |cov_badge| image:: https://github.com/eriknyquist/duckargs/actions/workflows/coverage.yml/badge.svg 10 | .. |version_badge| image:: https://badgen.net/pypi/v/duckargs 11 | .. |license_badge| image:: https://badgen.net/pypi/license/duckargs 12 | .. |downloads_badge| image:: https://static.pepy.tech/badge/duckargs 13 | .. |conda_badge| image:: https://img.shields.io/conda/dn/conda-forge/duckargs.svg?label=conda-forge 14 | 15 | |tests_badge| |cov_badge| |version_badge| |license_badge| |downloads_badge| |conda_badge| 16 | 17 | Brief description 18 | ================= 19 | 20 | The purpose of ``duckargs`` is to save some typing whenever you want to quickly 21 | create a python program or C program that accepts command line arguments. Just run 22 | ``duckargs`` (generates python), ``duckargs-python`` (also generates python) or 23 | ``duckargs-c`` (generates C) with all the options/arguments that you want your program 24 | to accept, and ``duckargs`` will print the code for a program that handles those 25 | options/arguments. 26 | 27 | Longer description 28 | ================== 29 | 30 | If you're like me, then you often need to create little throwaway command-line tools, 31 | but *not* often enough to remember the exact syntax/details of ``argparse`` or ``getopt.h``, 32 | so you often start these efforts by looking up the relevant docs and refreshing your memory. 33 | 34 | Next, you spend some time typing out the boilerplate arg-parsing code, with one eye 35 | on the docs, and eventually (depending on how much arg-parsing boilerplate code you need) 36 | you may forget some interesting detail that was part of your original idea, or you may 37 | just get sick of it and decide that you don't event *need* a command-line tool, and 38 | you'll do the thing manually instead. 39 | 40 | ``duckargs`` makes this process a little bit simpler, and shortens the time between your 41 | idea and having a working C or Python program. 42 | 43 | Let's imagine that you want to create a little command-line tool that accepts the 44 | following command line options/arguments: 45 | 46 | * A positional argument, string 47 | * An optional integer value (``-i`` or ``--intval``) 48 | * An optional float value (``-f`` or ``--floatval``) 49 | * A flag (``-q``) 50 | 51 | You can run ``duckargs`` and pass all those options/arguments/flags, and ``duckargs`` will 52 | generate a working program with all the boilerplate taken care of: 53 | 54 | **Generating Python** 55 | 56 | .. code:: 57 | 58 | $ duckargs somestring -i --intval 99 -f --floatval 7.7 -q 59 | 60 | **Output** 61 | 62 | .. code:: 63 | 64 | # Generated by duckargs, invoked with the following arguments: 65 | # somestring -i --intval 99 -f --floatval 7.7 -q 66 | 67 | import argparse 68 | 69 | def main(): 70 | parser = argparse.ArgumentParser(description='A command-line program generated by duckargs', 71 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 72 | 73 | parser.add_argument('somestring', help='a string') 74 | parser.add_argument('-i', '--intval', default=99, type=int, help='an int value') 75 | parser.add_argument('-f', '--floatval', default=7.7, type=float, help='a float value') 76 | parser.add_argument('-q', action='store_true', help='q flag') 77 | args = parser.parse_args() 78 | 79 | print(args.somestring) 80 | print(args.intval) 81 | print(args.floatval) 82 | print(args.q) 83 | 84 | if __name__ == "__main__": 85 | main() 86 | 87 | **Generating C** 88 | 89 | .. code:: 90 | 91 | $ duckargs-c somestring -i --intval 99 -f --floatval 7.7 -q 92 | 93 | **Output** 94 | 95 | .. code:: 96 | 97 | // Generated by duckargs, invoked with the following arguments: 98 | // somestring -i --intval 99 -f --floatval 7.7 -q 99 | 100 | #include 101 | #include 102 | #include 103 | #include 104 | 105 | static char *somestring = "somestring"; 106 | static long int intval = 99; 107 | static float floatval = 7.7; 108 | static bool q = false; 109 | 110 | static struct option long_options[] = 111 | { 112 | {"intval", required_argument, NULL, 'i'}, 113 | {"floatval", required_argument, NULL, 'f'}, 114 | {NULL, 0, NULL, 0} 115 | }; 116 | 117 | void print_usage(void) 118 | { 119 | printf("\n"); 120 | printf("USAGE:\n\n"); 121 | printf("program_name [OPTIONS] somestring\n"); 122 | printf("\nOPTIONS:\n\n"); 123 | printf("-i --intval [int] An int value (default: %ld)\n", int); 124 | printf("-f --floatval [float] A float value (default: %.2f)\n", float); 125 | printf("-q A flag\n"); 126 | printf("\n"); 127 | } 128 | 129 | int parse_args(int argc, char *argv[]) 130 | { 131 | char *endptr = NULL; 132 | int ch; 133 | 134 | while ((ch = getopt_long(argc, argv, "i:f:q", long_options, NULL)) != -1) 135 | { 136 | switch (ch) 137 | { 138 | case 'i': 139 | { 140 | intval = strtol(optarg, &endptr, 0); 141 | if (endptr && (*endptr != '\0')) 142 | { 143 | printf("Option '-i' requires an integer argument\n"); 144 | return -1; 145 | } 146 | break; 147 | } 148 | case 'f': 149 | { 150 | floatval = strtof(optarg, &endptr); 151 | if (endptr == optarg) 152 | { 153 | printf("Option '-f' requires a floating-point argument\n"); 154 | return -1; 155 | } 156 | break; 157 | } 158 | case 'q': 159 | { 160 | q = true; 161 | break; 162 | } 163 | } 164 | } 165 | 166 | if (argc < (optind + 1)) 167 | { 168 | printf("Missing positional arguments\n"); 169 | return -1; 170 | } 171 | 172 | somestring = argv[optind]; 173 | 174 | return 0; 175 | } 176 | 177 | int main(int argc, char *argv[]) 178 | { 179 | if (argc < 2) 180 | { 181 | print_usage(); 182 | return -1; 183 | } 184 | 185 | int ret = parse_args(argc, argv); 186 | if (0 != ret) 187 | { 188 | return ret; 189 | } 190 | 191 | printf("somestring: %s\n", somestring ? somestring : "null"); 192 | printf("intval: %ld\n", intval); 193 | printf("floatval: %.4f\n", floatval); 194 | printf("q: %s\n", q ? "true" : "false"); 195 | 196 | return 0; 197 | } 198 | 199 | Install 200 | ======= 201 | 202 | Install with pip (python 3x required): 203 | 204 | :: 205 | 206 | pip install duckargs 207 | 208 | Comma-separated choices for option argument 209 | =========================================== 210 | 211 | If you have an option which accepts an argument, and you write an argument string with 212 | multiple values separated by commas (e.g. ``-m --mode active,idle,sim``), then generated 213 | python code will use the comma-separated values as a ``choices`` list for argparse: 214 | 215 | .. code:: python 216 | 217 | parser.add_argument('-m', '--mode', choices=['active', 'idle', 'sim'], default='active', help='a string') 218 | 219 | And generated C code will use the comma-separated values to restrict values in a similar manner: 220 | 221 | .. code:: c 222 | 223 | static char *mode_choices[] = {"active", "idle", "stop"}; 224 | static char *mode = "active"; 225 | 226 | static struct option long_options[] = 227 | { 228 | {"mode", required_argument, NULL, 'm'}, 229 | {NULL, 0, NULL, 0} 230 | }; 231 | 232 | void print_usage(void) 233 | { 234 | printf("\n"); 235 | printf("USAGE:\n\n"); 236 | printf("program_name [OPTIONS]\n"); 237 | printf("\nOPTIONS:\n\n"); 238 | printf("-m --mode [active|idle|stop] A string value (default: %s)\n", mode ? mode : "null"); 239 | printf("\n"); 240 | } 241 | 242 | int parse_args(int argc, char *argv[]) 243 | { 244 | int ch; 245 | 246 | while ((ch = getopt_long(argc, argv, "m:", long_options, NULL)) != -1) 247 | { 248 | switch (ch) 249 | { 250 | case 'm': 251 | { 252 | mode = optarg; 253 | for (int i = 0; i < 3; i++) 254 | { 255 | if (0 == strcmp(mode_choices[i], mode)) 256 | { 257 | break; 258 | } 259 | if (i == 2) 260 | { 261 | printf("Option '-m' must be one of ['active', 'idle', 'stop']\n"); 262 | return -1; 263 | } 264 | } 265 | break; 266 | } 267 | } 268 | } 269 | 270 | return 0; 271 | } 272 | 273 | Filenames for option arguments 274 | ============================== 275 | 276 | If you have an option that you want to accept a filename, you have two ways to tell 277 | ``duckargs`` that the option argument should be treated as a file: 278 | 279 | * Pass the path to a file that actually exists (e.g. ``-f --filename file.txt``) 280 | as the option argument 281 | 282 | * Pass ``FILE`` as the option argument (e.g. ``-f --filename FILE``) 283 | 284 | Either of which will generate python code like this: 285 | 286 | .. code:: python 287 | 288 | parser.add_argument('-f', '--filename', default='file', type=argparse.FileType(), help='a filename') 289 | 290 | And will generate C code like this: 291 | 292 | .. code:: c 293 | 294 | static char *filename = NULL; 295 | 296 | static struct option long_options[] = 297 | { 298 | {"filename", required_argument, NULL, 'f'}, 299 | {NULL, 0, NULL, 0} 300 | }; 301 | 302 | void print_usage(void) 303 | { 304 | printf("\n"); 305 | printf("USAGE:\n\n"); 306 | printf("program_name [OPTIONS]\n"); 307 | printf("\nOPTIONS:\n\n"); 308 | printf("-f --filename FILE A filename (default: %s)\n", filename ? filename : "null"); 309 | printf("\n"); 310 | } 311 | 312 | int parse_args(int argc, char *argv[]) 313 | { 314 | int ch; 315 | 316 | while ((ch = getopt_long(argc, argv, "f:", long_options, NULL)) != -1) 317 | { 318 | switch (ch) 319 | { 320 | case 'f': 321 | { 322 | filename = optarg; 323 | break; 324 | } 325 | } 326 | } 327 | 328 | return 0; 329 | } 330 | 331 | Environment variables 332 | ===================== 333 | 334 | Some things can be configured by setting environment variables. 335 | 336 | ``DUCKARGS_PRINT`` 337 | ################## 338 | 339 | By default, ``duckargs`` generates a program that prints all provided arguments/options 340 | to stdout after argument parsing is complete. 341 | If you want to disable this and generate programs without the print statements, set 342 | ``DUCKARGS_PRINT=0`` in your environment variables. This environment variable affects 343 | generated C code and generated python code. 344 | 345 | ``DUCKARGS_COMMENT`` 346 | #################### 347 | 348 | By default, ``duckargs`` generates a program that prints a comment header at the top, 349 | showing the arguments that ``duckargs`` was invoked with. If you want to disable this and 350 | generate programs without the comment header, set ``DUCKARGS_COMMENT=0`` in your environment 351 | variables. This environment variable affects generated C code and generated python code. 352 | 353 | Use duckargs in python code 354 | =========================== 355 | 356 | If you want to use duckargs in your own script, you can use the ``duckargs.generate_python_code`` and 357 | ``duckargs.generate_c_code`` functions, both of which accept a list of command line arguments: 358 | 359 | .. code:: python 360 | 361 | import sys 362 | from duckargs import generate_python_code, generate_c_code 363 | 364 | python_code = generate_python_code(sys.argv) 365 | 366 | c_code = generate_c_code(sys.argv) 367 | 368 | Pitfalls 369 | ======== 370 | 371 | If you have a combination of flags and positional arguments, and you happen to have a flag 372 | followed by a positional argument (as in: ``python -m duckargs -q --quiet positional_arg``), 373 | ``duckargs`` has no way to tell that you wanted a positional arg, so it will assume you want 374 | an option ``-q --quiet`` with a required argument. 375 | 376 | To avoid this, it is recommended to declare your positional arguments first (as in: ``python -m duckargs positional_arg -q --quiet``) 377 | 378 | Contributions 379 | ============= 380 | 381 | Contributions are welcome, please open a pull request at ``_. 382 | You will need to install packages required for development by doing ``pip install -r dev_requirements.txt``. 383 | 384 | Please ensure that all existing tests pass, new test(s) are added if required, and the code coverage 385 | check passes. 386 | 387 | * Run tests with ``python setup.py test``. 388 | * Run tests and and generate code coverage report with ``python code_coverage.py`` 389 | (this script will report an error if coverage is below 95%) 390 | 391 | If you have any questions about / need help with contributions or tests, please 392 | contact Erik at eknyquist@gmail.com. 393 | -------------------------------------------------------------------------------- /code_coverage.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import sys 3 | 4 | from coverage import Coverage 5 | 6 | 7 | MIN_COVERAGE_PERCENT = 95 8 | 9 | 10 | def main(): 11 | cov = Coverage(omit="tests/*") 12 | cov.start() 13 | 14 | suite = unittest.TestLoader().discover("tests") 15 | t = unittest.TextTestRunner(verbosity = 2) 16 | t.run(suite) 17 | 18 | cov.stop() 19 | pc = cov.report() 20 | 21 | exit_code = 0 22 | msg = "" 23 | 24 | if pc < float(MIN_COVERAGE_PERCENT): 25 | msg = f"ERROR!! Test coverage is {pc:.4f}% (must be at least {MIN_COVERAGE_PERCENT}%)." 26 | exit_code = 1 27 | else: 28 | msg = f"All good. Test coverage is sufficient (must be at least {MIN_COVERAGE_PERCENT}%)." 29 | 30 | print(f"\n{msg}\n") 31 | sys.exit(exit_code) 32 | 33 | if __name__ == "__main__": 34 | main() 35 | -------------------------------------------------------------------------------- /dev_requirements.txt: -------------------------------------------------------------------------------- 1 | setuptools 2 | wheel 3 | coverage 4 | -------------------------------------------------------------------------------- /duckargs/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = "1.5.0" 2 | 3 | import sys 4 | import os 5 | import re 6 | from keyword import iskeyword 7 | 8 | PYTHON_TEMPLATE = """{0}import argparse 9 | 10 | def main(): 11 | parser = argparse.ArgumentParser(description='A command-line program generated by duckargs', 12 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 13 | 14 | {1} 15 | args = parser.parse_args(){2} 16 | 17 | if __name__ == "__main__": 18 | main() 19 | """ 20 | 21 | C_TEMPLATE = """{0}#include 22 | #include 23 | 24 | {1} 25 | void print_usage(void) 26 | {{ 27 | {2} 28 | }} 29 | 30 | int parse_args(int argc, char *argv[]) 31 | {{ 32 | {3} 33 | }} 34 | 35 | int main(int argc, char *argv[]) 36 | {{ 37 | if (argc < 2) 38 | {{ 39 | print_usage(); 40 | return -1; 41 | }} 42 | 43 | int ret = parse_args(argc, argv); 44 | if (0 != ret) 45 | {{ 46 | return ret; 47 | }} 48 | 49 | {4} return 0; 50 | }} 51 | """ 52 | 53 | class ArgType(object): 54 | """ 55 | Enumerates all argument types that will be recognized 56 | """ 57 | INT = "int" 58 | FLOAT = "float" 59 | FILE = "argparse.FileType()" 60 | STRING = "str" 61 | 62 | 63 | def _is_int(arg): 64 | ret = True 65 | 66 | try: 67 | intval = int(arg) 68 | except ValueError: 69 | if ((len(arg) >= 3) and (arg[:2].lower() == "0x")) or \ 70 | ((len(arg) >= 4) and (arg[:3].lower() == "-0x")): 71 | try: 72 | intval = int(arg, 16) 73 | except ValueError: 74 | ret = False 75 | else: 76 | ret = False 77 | 78 | return ret 79 | 80 | class CmdlineOpt(object): 81 | """ 82 | Represents a single option / flag / positional argument parsed from command line arguments 83 | """ 84 | 85 | positional_count = 0 86 | 87 | # Return values for add_arg 88 | SUCCESS = 0 89 | SUCCESS_AND_FULL = 1 90 | FAILURE = 2 91 | 92 | nonalpha_rgx = re.compile("[^0-9a-zA-Z\-\_]") 93 | 94 | def __init__(self): 95 | self.value = None 96 | self.opt = None 97 | self.longopt = None 98 | self.type = None 99 | self.var_name = None 100 | self.desc = None 101 | 102 | def finalize(self): 103 | """ 104 | Called for final post-processing after all required data for a single 105 | CmdlineOpt instance has been collected 106 | """ 107 | if self.is_positional(): 108 | if self.value.isidentifier(): 109 | self.var_name = self.value 110 | else: 111 | varname = f"positional_arg{CmdlineOpt.positional_count}" 112 | self.var_name = varname 113 | CmdlineOpt.positional_count += 1 114 | else: 115 | if self.longopt is not None: 116 | varname = self.longopt 117 | elif self.opt is not None: 118 | varname = self.opt 119 | else: 120 | raise RuntimeError(f"Invalid attribute values for {self.__class__.__name__}") 121 | 122 | self.var_name = varname.lstrip('-').replace('-', '_') 123 | 124 | self.desc = self.var_name 125 | 126 | if self.value is None: 127 | return 128 | 129 | if _is_int(self.value): 130 | self.type = ArgType.INT 131 | 132 | if self.type is None: 133 | try: 134 | fltval = float(self.value) 135 | except ValueError: 136 | pass 137 | else: 138 | self.type = ArgType.FLOAT 139 | 140 | if self.type is None: 141 | if ('FILE' == self.value) or os.path.isfile(self.value): 142 | self.type = ArgType.FILE 143 | else: 144 | self.type = ArgType.STRING 145 | 146 | def add_arg(self, arg): 147 | """ 148 | Process a single argument command-line argument 149 | 150 | :param str arg: the command-line argument to process 151 | :return: 0 if success and room for more, 1 if success but no more room, 2 if no room 152 | """ 153 | cleaned_arg = self.nonalpha_rgx.sub('-', arg) 154 | 155 | is_int = _is_int(arg) 156 | 157 | if arg.startswith('--'): 158 | if self.opt is None: 159 | raise ValueError(f"long option ({arg}) is not allowed without short option") 160 | 161 | if self.longopt is None: 162 | self.longopt = cleaned_arg 163 | else: 164 | return self.FAILURE 165 | 166 | elif arg.startswith('-') and not is_int: 167 | if len(arg) > 2: 168 | raise ValueError(f"short option ({arg}) must have exactly one character after the dash (-)") 169 | 170 | if self.opt is None: 171 | self.opt = cleaned_arg 172 | else: 173 | return self.FAILURE 174 | else: 175 | if self.value is None: 176 | if (self.opt) or (self.longopt) or is_int: 177 | self.value = arg 178 | else: 179 | self.value = arg.replace('-', '_') 180 | 181 | return self.SUCCESS_AND_FULL 182 | 183 | return self.SUCCESS 184 | 185 | def opttext(self): 186 | ret = [] 187 | if self.opt is not None: 188 | ret.append(f"'{self.opt}'") 189 | if self.longopt is not None: 190 | ret.append(f"'{self.longopt}'") 191 | 192 | return ', '.join(ret) 193 | 194 | def is_empty(self): 195 | """ 196 | Returns true if nothing has been set yet 197 | """ 198 | return (self.value is None) and (self.opt is None) and (self.longopt is None) 199 | 200 | def is_flag(self): 201 | """ 202 | Returns true if this is an option with no argument (a flag) 203 | """ 204 | return ((self.opt is not None) or (self.longopt is not None)) and self.value is None 205 | 206 | def is_option(self): 207 | """ 208 | Returns true if this is an option with an argument 209 | """ 210 | return ((self.opt is not None) or (self.longopt is not None)) and self.value is not None 211 | 212 | def is_positional(self): 213 | """ 214 | Returns true if this is a positional argument 215 | """ 216 | return (self.opt is None) and (self.longopt is None) and (self.value is not None) 217 | 218 | def __str__(self): 219 | return f"{self.__class__.__name__}({self.opt}, {self.longopt}, {self.value})" 220 | 221 | def __repr__(self): 222 | return self.__str__() 223 | 224 | 225 | def process_args(reserved_str_check, argv=sys.argv): 226 | """ 227 | Process all command line arguments and return a list of CmdlineOpt instances 228 | 229 | :return: List of CmdlineOpt instances 230 | """ 231 | ret = [] 232 | curr = CmdlineOpt() 233 | 234 | for arg in argv[1:]: 235 | status = curr.add_arg(arg) 236 | if status != CmdlineOpt.SUCCESS: 237 | curr.finalize() 238 | ret.append(curr) 239 | curr = CmdlineOpt() 240 | 241 | if status == CmdlineOpt.FAILURE: 242 | curr.add_arg(arg) 243 | 244 | if not curr.is_empty(): 245 | curr.finalize() 246 | ret.append(curr) 247 | 248 | # Check for duplicate attr names 249 | seen_attr_names = {} 250 | seen_opt_names = {} 251 | seen_longopt_names = {} 252 | 253 | for o in ret: 254 | if o.var_name in seen_attr_names: 255 | raise ValueError(f"Option '{o.var_name}' was defined more than once") 256 | else: 257 | seen_attr_names[o.var_name] = None 258 | 259 | if o.opt is not None: 260 | if o.opt in seen_opt_names: 261 | raise ValueError(f"Short option '{o.opt}' was defined more than once") 262 | else: 263 | seen_opt_names[o.opt] = None 264 | 265 | if o.longopt is not None: 266 | if o.longopt in seen_longopt_names: 267 | raise ValueError(f"Long option '{o.longopt}' was defined more than once") 268 | else: 269 | seen_longopt_names[o.opt] = None 270 | 271 | if reserved_str_check(o.var_name): 272 | # If var_name is a reserved word for generated language, append 'val'. 273 | # So if you pass '-i --int', for example, the var name will be 'intval' 274 | o.var_name += "val" 275 | 276 | return ret 277 | 278 | def _is_python_reserved_str(var_name): 279 | if iskeyword(var_name): 280 | return True 281 | 282 | return var_name in ['int', 'float', 'bool', 'dict', 'list', 'tuple'] 283 | 284 | def _is_c_reserved_str(var_name): 285 | return var_name in [ 286 | 'bool', '_Bool', 'char', 'unsigned', 'short', 'int', 'long', 'size_t', 'ssize_t', 287 | 'time_t', 'float', 'double', 'long', 'wchar_t', 'void', 'int8_t', 'uint8_t', 288 | 'int16_t', 'uint16_t', 'int32_t', 'uint32_t', 'int64_t', 'uint64_t', 'int128_t', 289 | 'uint128_t', 'static', 'void', 'auto', 'restrict', 'register', 'return', 'switch', 290 | 'union', 'extern', 'enum', 'if', 'else', 'for', 'while', 'do', 'break', 'signed', 291 | 'sizeof', 'typedef', 'struct', 'case', 'default', 'volatile', 'goto', 'continue', 292 | 'const' 293 | ] 294 | 295 | def _generate_python_code_line(opt): 296 | """ 297 | Generate the 'parser.add_argument(...)' line for an option 298 | 299 | :return: Line of python code to add this option to the arg parser 300 | :rtype: str 301 | """ 302 | if opt.is_flag(): 303 | funcargs = opt.opttext() + ", action='store_true'" 304 | 305 | elif opt.is_option(): 306 | funcargs = opt.opttext() 307 | if opt.type == ArgType.STRING: 308 | # Check if string argument has comma-separated choices 309 | choices = opt.value.split(',') 310 | if len(choices) > 1: 311 | funcargs += f", choices={choices}" 312 | value = f"'{choices[0]}'" 313 | else: 314 | value = f"'{opt.value}'" 315 | 316 | elif opt.type == ArgType.FILE: 317 | value = f"'{opt.value}'" 318 | else: 319 | value = opt.value 320 | 321 | default_str = "None" if opt.type is ArgType.FILE else str(value) 322 | funcargs += f", default={default_str}" 323 | 324 | if opt.type is not ArgType.STRING: 325 | funcargs += f", type={opt.type}" 326 | 327 | elif opt.is_positional(): 328 | if opt.value.isidentifier(): 329 | funcargs = f"'{opt.value}'" 330 | else: 331 | funcargs = f"'{opt.var_name}'" 332 | 333 | if opt.type is not ArgType.STRING: 334 | funcargs += f", type={opt.type}" 335 | else: 336 | raise RuntimeError('Invalid options provided') 337 | 338 | if opt.type is not None: 339 | if opt.type == ArgType.INT: 340 | helptext = "an int value" 341 | elif opt.type == ArgType.FLOAT: 342 | helptext = "a float value" 343 | elif opt.type == ArgType.FILE: 344 | helptext = "a filename" 345 | elif opt.type == ArgType.STRING: 346 | helptext = "a string" 347 | else: 348 | raise RuntimeError('Invalid type setting') 349 | 350 | elif opt.is_flag(): 351 | helptext = f"{opt.desc} flag" 352 | 353 | funcargs += f", help='{helptext}'" 354 | 355 | return f"parser.add_argument({funcargs})" 356 | 357 | 358 | def generate_python_code(argv=sys.argv): 359 | """ 360 | Process all command line arguments and return the text of a python program 361 | which handles the described command-line options 362 | 363 | :param list processed_args: List of CmdlineOpt instances 364 | 365 | :return: text of the corresponding python program 366 | :rtype: str 367 | """ 368 | processed_args = process_args(_is_python_reserved_str, argv) 369 | optlines = " " + "\n ".join([_generate_python_code_line(o) for o in processed_args]) 370 | 371 | printlines = "" 372 | env_print = os.environ.get('DUCKARGS_PRINT', 1) 373 | try: 374 | env_print_int = int(env_print) 375 | except ValueError: 376 | raise RuntimeError("DUCKARGS_PRINT must be an integer") 377 | 378 | if env_print_int > 0: 379 | printlines += "\n\n " + "\n ".join([f"print(args.{o.var_name})" for o in processed_args]) 380 | 381 | comment = "" 382 | env_comment = os.environ.get("DUCKARGS_COMMENT", 1) 383 | try: 384 | env_comment_int = int(env_comment) 385 | except ValueError: 386 | raise RuntimeError("DUCKARGS_COMMENT must be an integer") 387 | 388 | if env_comment_int > 0: 389 | comment = (f"# Generated by duckargs, invoked with the following arguments:\n# " + 390 | ' '.join(argv[1:]) + "\n\n") 391 | 392 | CmdlineOpt.positional_count = 0 393 | 394 | return PYTHON_TEMPLATE.format(comment, optlines, printlines) 395 | 396 | def _generate_c_opt_lines(arg, desc=None, optarg='optarg'): 397 | ret = [] 398 | 399 | if desc is None: 400 | desc = f"Option '{arg.opt}'" 401 | 402 | if arg.is_flag(): 403 | ret.append(f"{arg.var_name} = true;") 404 | 405 | elif ArgType.FLOAT == arg.type: 406 | ret.append(f"{arg.var_name} = strtof({optarg}, &endptr);") 407 | ret.append(f"if (endptr == {optarg})") 408 | ret.append(f"{{") 409 | ret.append(f" printf(\"{desc} requires a floating-point argument\\n\");") 410 | ret.append(f" return -1;") 411 | ret.append(f"}}") 412 | elif ArgType.INT == arg.type: 413 | ret.append(f"{arg.var_name} = strtol({optarg}, &endptr, 0);") 414 | ret.append(f"if (endptr && (*endptr != '\\0'))") 415 | ret.append(f"{{") 416 | ret.append(f" printf(\"{desc} requires an integer argument\\n\");") 417 | ret.append(f" return -1;") 418 | ret.append(f"}}") 419 | elif ArgType.FILE == arg.type: 420 | ret.append(f"{arg.var_name} = {optarg};") 421 | elif ArgType.STRING == arg.type: 422 | ret.append(f"{arg.var_name} = {optarg};") 423 | 424 | if type(arg.value) == list: 425 | ret.append(f"for (int i = 0; i < {len(arg.value)}; i++)") 426 | ret.append(f"{{") 427 | ret.append(f" if (0 == strcmp({arg.var_name}_choices[i], {arg.var_name}))") 428 | ret.append(f" {{") 429 | ret.append(f" break;") 430 | ret.append(f" }}") 431 | ret.append(f" if (i == {len(arg.value) - 1})") 432 | ret.append(f" {{") 433 | ret.append(f" printf(\"{desc} must be one of {arg.value}\\n\");") 434 | ret.append(f" return -1;") 435 | ret.append(f" }}") 436 | ret.append(f"}}") 437 | 438 | return ret 439 | 440 | def _generate_c_getopt_code(processed_args, getopt_string, opts, positionals, has_longopts): 441 | ret = "" 442 | needs_endptr = False 443 | 444 | for arg in processed_args: 445 | if arg.type in [ArgType.INT, ArgType.FLOAT]: 446 | needs_endptr = True 447 | break 448 | 449 | if needs_endptr: 450 | ret += " char *endptr = NULL;\n" 451 | 452 | if opts: 453 | ret += " int ch;\n\n" 454 | 455 | if opts: 456 | if has_longopts: 457 | ret += f" while ((ch = getopt_long(argc, argv, \"{getopt_string}\", long_options, NULL)) != -1)\n" 458 | else: 459 | ret += f" while ((ch = getopt(argc, argv, \"{getopt_string}\")) != -1)\n" 460 | 461 | ret += f" {{\n" 462 | ret += f" switch (ch)\n" 463 | ret += f" {{\n" 464 | 465 | for arg in opts: 466 | ret += f" case '{arg.opt[1]}':\n" 467 | ret += f" {{\n" 468 | 469 | ret += '\n'.join([" " + x for x in _generate_c_opt_lines(arg)]) 470 | ret += '\n' 471 | 472 | ret += f" break;\n" 473 | ret += f" }}\n" 474 | 475 | ret += f" }}\n" 476 | ret += f" }}\n\n" 477 | 478 | if positionals: 479 | # Has both positionals and opts 480 | ret += f" if (argc < (optind + {len(positionals)}))\n" 481 | ret += f" {{\n" 482 | ret += f" printf(\"Missing positional arguments\\n\");\n" 483 | ret += f" return -1;\n" 484 | ret += f" }}\n\n" 485 | 486 | for i in range(len(positionals)): 487 | arg = positionals[i] 488 | desc = f"Positional argument #{i + 1} ({arg.var_name})" 489 | optarg = f"argv[optind]" 490 | ret += '\n'.join([" " + x for x in _generate_c_opt_lines(arg, desc, optarg)]) 491 | 492 | if i < (len(positionals) - 1): 493 | ret += "\n optind++;" 494 | 495 | ret += "\n\n" 496 | 497 | pass 498 | 499 | elif positionals: 500 | # Has only positionals and no opts 501 | ret += f" if (argc < {len(positionals) + 1})\n" 502 | ret += f" {{\n" 503 | ret += f" printf(\"Missing positional arguments\\n\");\n" 504 | ret += f" return -1;\n" 505 | ret += f" }}\n\n" 506 | 507 | for i in range(len(positionals)): 508 | arg = positionals[i] 509 | desc = f"Positional argument #{i + 1} ({arg.var_name})" 510 | optarg = f"argv[{i + 1}]" 511 | ret += '\n'.join([" " + x for x in _generate_c_opt_lines(arg, desc, optarg)]) 512 | ret += "\n\n" 513 | 514 | ret += f" return 0;" 515 | 516 | return ret 517 | 518 | def _generate_c_print_code(processed_args): 519 | ret = "" 520 | 521 | for arg in processed_args: 522 | format_arg = "" 523 | var_name = "" 524 | 525 | if arg.is_flag(): 526 | format_arg = "%s" 527 | var_name = f"{arg.var_name} ? \"true\" : \"false\"" 528 | elif arg.type == ArgType.INT: 529 | format_arg = "%ld" 530 | var_name = f"{arg.var_name}" 531 | elif arg.type == ArgType.FLOAT: 532 | format_arg = "%.4f" 533 | var_name = f"{arg.var_name}" 534 | elif arg.type in [ArgType.FILE, ArgType.STRING]: 535 | format_arg = "%s" 536 | var_name = f"{arg.var_name} ? {arg.var_name} : \"null\"" 537 | 538 | ret += f" printf(\"{arg.desc}: {format_arg}\\n\", {var_name});\n" 539 | 540 | return ret + "\n" 541 | 542 | def _generate_c_usage_code(processed_args): 543 | lines = [] 544 | positionals = [] 545 | opts = [] 546 | 547 | for arg in processed_args: 548 | if arg.is_positional(): 549 | positionals.append(arg) 550 | else: 551 | opts.append(arg) 552 | 553 | lines.append("\"\\n\"") 554 | lines.append("\"USAGE:\\n\\n\"") 555 | 556 | line = "program_name" 557 | if opts: 558 | line += " [OPTIONS]" 559 | 560 | if positionals: 561 | positional_names = ' '.join([x.var_name for x in positionals]) 562 | line += f" {positional_names}" 563 | 564 | lines.append("\"" + line + "\\n\"") 565 | 566 | if opts: 567 | lines.append("\"\\nOPTIONS:\\n\\n\"") 568 | longest_left_col = 0 569 | usage_lines = [] 570 | 571 | for opt in opts: 572 | left_col = "\"" + opt.opt 573 | if opt.longopt is not None: 574 | left_col += " " + opt.longopt 575 | 576 | arg = None 577 | right_col = "" 578 | if opt.is_flag(): 579 | right_col = f"{opt.desc} flag\\n\"" 580 | else: 581 | if type(opt.value) == list: 582 | right_col = f"A string value (default: %s)\\n\", {opt.var_name} ? {opt.var_name} : \"null\"" 583 | choices = '|'.join(opt.value) 584 | arg = f" [{choices}]" 585 | elif ArgType.INT == opt.type: 586 | right_col = f"An int value (default: %ld)\\n\", {opt.var_name}" 587 | arg = " [int]" 588 | elif ArgType.FLOAT == opt.type: 589 | right_col = f"A float value (default: %.2f)\\n\", {opt.var_name}" 590 | arg = " [float]" 591 | elif ArgType.STRING == opt.type: 592 | right_col = f"A string value (default: %s)\\n\", {opt.var_name} ? {opt.var_name} : \"null\"" 593 | arg = " [string]" 594 | elif ArgType.FILE == opt.type: 595 | right_col = f"A filename (default: %s)\\n\", {opt.var_name} ? {opt.var_name} : \"null\"" 596 | arg = " FILE" 597 | 598 | if arg is not None: 599 | left_col += arg 600 | 601 | if len(left_col) > longest_left_col: 602 | longest_left_col = len(left_col) 603 | 604 | usage_lines.append((left_col, right_col)) 605 | 606 | for leftcol, rightcol in usage_lines: 607 | num_spaces = (longest_left_col + 2) - len(leftcol) 608 | lines.append(leftcol + (" " * num_spaces) + rightcol) 609 | 610 | lines.append("\"\\n\"") 611 | 612 | return '\n'.join([f" printf({line});" for line in lines]) 613 | 614 | def generate_c_code(argv=sys.argv): 615 | """ 616 | Process all command line arguments and return the text of a C program 617 | which handles the described command-line options 618 | 619 | :param list processed_args: List of CmdlineOpt instances 620 | 621 | :return: text of the corresponding C program 622 | :rtype: str 623 | """ 624 | processed_args = process_args(_is_c_reserved_str, argv) 625 | 626 | long_opts = [] 627 | has_flags = False 628 | has_choices = False 629 | opts = [] 630 | positionals = [] 631 | decls = "" 632 | getopt_string = "" 633 | 634 | for arg in processed_args: 635 | typename = arg.type 636 | varname = arg.var_name 637 | value = arg.value 638 | 639 | if arg.is_positional(): 640 | positionals.append(arg) 641 | else: 642 | opts.append(arg) 643 | 644 | if arg.is_flag(): 645 | typename = "bool" 646 | value = "false" 647 | has_flags = True 648 | 649 | elif arg.type == ArgType.INT: 650 | typename = "long int" 651 | 652 | elif arg.type in [ArgType.STRING, ArgType.FILE]: 653 | typename = "char" 654 | varname = "*" + varname 655 | value = f"\"{value}\"" 656 | 657 | if arg.type == ArgType.FILE: 658 | if arg.value == "FILE": 659 | value = "NULL" 660 | else: 661 | # ArgType.STRING 662 | choices = arg.value.split(',') 663 | if len(choices) > 1: 664 | arg.value = choices 665 | value = f"\"{choices[0]}\"" 666 | choicestrings = ", ".join([f"\"{c}\"" for c in choices]) 667 | decls += f"static char *{arg.var_name}_choices[] = {{{choicestrings}}};\n" 668 | has_choices = True 669 | 670 | decls += f"static {typename} {varname} = {value};\n" 671 | 672 | if arg.opt: 673 | getopt_string += arg.opt[1] 674 | if not arg.is_flag(): 675 | getopt_string += ":" 676 | 677 | if arg.longopt is not None: 678 | longopt = arg.longopt.lstrip('-') 679 | opt = arg.opt.lstrip('-') 680 | argtype = "no_argument" if arg.is_flag() else "required_argument" 681 | long_opts.append(f"{{\"{longopt}\", {argtype}, NULL, '{opt}'}},") 682 | 683 | if long_opts: 684 | decls += "\nstatic struct option long_options[] =\n{\n" 685 | decls += "\n".join([" " + opt for opt in long_opts]) 686 | decls += "\n {NULL, 0, NULL, 0}\n};\n" 687 | 688 | comment_header = "" 689 | env_comment = os.environ.get("DUCKARGS_COMMENT", 1) 690 | try: 691 | env_comment_int = int(env_comment) 692 | except ValueError: 693 | raise RuntimeError("DUCKARGS_COMMENT must be an integer") 694 | 695 | if env_comment_int > 0: 696 | comment_header = (f"// Generated by duckargs, invoked with the following arguments:\n// " + 697 | ' '.join(argv[1:]) + "\n\n") 698 | 699 | CmdlineOpt.positional_count = 0 700 | 701 | if has_flags: 702 | comment_header += "#include \n" 703 | 704 | if opts: 705 | comment_header += "#include \n" 706 | 707 | if has_choices: 708 | comment_header += "#include \n" 709 | 710 | parsing_code = _generate_c_getopt_code(processed_args, getopt_string, opts, 711 | positionals, len(long_opts) > 0) 712 | 713 | print_code = "" 714 | env_print = os.environ.get('DUCKARGS_PRINT', 1) 715 | try: 716 | env_print_int = int(env_print) 717 | except ValueError: 718 | raise RuntimeError("DUCKARGS_PRINT must be an integer") 719 | 720 | if env_print_int > 0: 721 | print_code = _generate_c_print_code(processed_args) 722 | 723 | usage_code = _generate_c_usage_code(processed_args) 724 | 725 | return C_TEMPLATE.format(comment_header, decls, usage_code, parsing_code, print_code) 726 | -------------------------------------------------------------------------------- /duckargs/__main__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from duckargs import generate_python_code, generate_c_code, __version__ 3 | 4 | PYTHON_USAGE = """ 5 | duckargs-python %s 6 | 7 | Erik K. Nyquist 2023 8 | 9 | The purpose of duckargs-python is to save some typing whenever you want to quickly create 10 | a python program that accepts command line arguments. Just run duckargs-python with all the 11 | options & arguments that you want your program to accept, and duckargs-python will print 12 | the python code for a program that uses argparse to handle those options & arguments. 13 | 14 | For example, running duckargs-python like this: 15 | 16 | 17 | duckargs-python positional_arg -i --intval 12 -d --floatval 99.9 -f --somefile FILE 18 | 19 | 20 | Prints the following python code: 21 | 22 | 23 | import argparse 24 | 25 | def main(): 26 | parser = argparse.ArgumentParser(description='', 27 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 28 | 29 | parser.add_argument('positional_arg', help='a string') 30 | parser.add_argument('-i', '--intval', default=12, type=int, help='an int value') 31 | parser.add_argument('-d', '--floatval', default=99.9, type=float, help='a float value') 32 | parser.add_argument('-f', '--somefile', default=None, type=argparse.FileType(), help='a filename') 33 | args = parser.parse_args() 34 | 35 | print(args.positional_arg) 36 | print(args.intval) 37 | print(args.floatval) 38 | print(args.somefile) 39 | 40 | if __name__ == "__main__": 41 | main() 42 | 43 | """ % __version__ 44 | 45 | C_USAGE = """ 46 | duckargs-c %s 47 | 48 | Erik K. Nyquist 2023 49 | 50 | The purpose of duckargs-c is to save some typing whenever you want to quickly create 51 | a C program that accepts command line arguments. Just run duckargs-c with the options & 52 | arguments that you want your program to accept, and duckargs-c will generate the C code 53 | for a program that uses getopt.h to handle those options & arguments. 54 | 55 | For example, running duckargs-c like this: 56 | 57 | 58 | duckargs-c positional_arg -i --intval 12 -d --floatval 99.9 -f --somefile FILE 59 | 60 | 61 | Prints the C code for a command-line program which accepts arguments in the following 62 | manner: 63 | 64 | program_name [OPTIONS] positional_arg 65 | 66 | -i --intval [int] An int value (default: 12) 67 | -d --floatval [float] A float value (default: 99.9) 68 | -f --somefile FILE A filename (default: null) 69 | 70 | """ % __version__ 71 | 72 | def duckargs_python(): 73 | """ 74 | CLI entry point for 'duckargs-python' 75 | """ 76 | if len(sys.argv) == 1: 77 | print(PYTHON_USAGE) 78 | return 79 | 80 | try: 81 | print(generate_python_code()) 82 | except (ValueError, RuntimeError) as e: 83 | print(f"Error: {e}") 84 | 85 | def duckargs_c(): 86 | """ 87 | CLI entry point for 'duckargs-c' 88 | """ 89 | if len(sys.argv) == 1: 90 | print(C_USAGE) 91 | return 92 | 93 | try: 94 | print(generate_c_code()) 95 | except (ValueError, RuntimeError) as e: 96 | print(f"Error: {e}") 97 | 98 | if __name__ == "__main__": 99 | duckargs_python() 100 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import os 3 | from setuptools import setup 4 | from distutils.core import Command 5 | 6 | from duckargs import __version__ 7 | 8 | HERE = os.path.abspath(os.path.dirname(__file__)) 9 | README = os.path.join(HERE, "README.rst") 10 | 11 | class RunDuckargsTests(Command): 12 | user_options = [] 13 | 14 | def initialize_options(self): 15 | pass 16 | 17 | def finalize_options(self): 18 | pass 19 | 20 | def run(self): 21 | suite = unittest.TestLoader().discover("tests") 22 | t = unittest.TextTestRunner(verbosity = 2) 23 | t.run(suite) 24 | 25 | with open(README, 'r') as f: 26 | long_description = f.read() 27 | 28 | setup( 29 | name='duckargs', 30 | version=__version__, 31 | description=('Code generation tool, creates python / C programs that parse command line arguments. ' 32 | 'Say goodbye to starting each project by reading "argparse" / "geptopt.h" docs.'), 33 | long_description=long_description, 34 | url='http://github.com/eriknyquist/duckargs', 35 | author='Erik Nyquist', 36 | author_email='eknyquist@gmail.com', 37 | license='Apache 2.0', 38 | packages=['duckargs'], 39 | cmdclass={'test': RunDuckargsTests}, 40 | include_package_data=True, 41 | zip_safe=False, 42 | entry_points = { 43 | 'console_scripts': [ 44 | 'duckargs=duckargs.__main__:duckargs_python', 45 | 'duckargs-c=duckargs.__main__:duckargs_c', 46 | 'duckargs-python=duckargs.__main__:duckargs_python' 47 | ] 48 | }, 49 | python_requires=">=3.7", 50 | classifiers=[ 51 | "Development Status :: 5 - Production/Stable", 52 | "Intended Audience :: Developers", 53 | "Intended Audience :: Science/Research", 54 | "Intended Audience :: Education", 55 | "Intended Audience :: Information Technology", 56 | "License :: OSI Approved :: Apache Software License", 57 | "Programming Language :: Python :: 3", 58 | "Programming Language :: Python :: 3.7", 59 | "Programming Language :: Python :: 3.8", 60 | "Programming Language :: Python :: 3.9", 61 | "Programming Language :: Python :: 3.10", 62 | ], 63 | project_urls={ 64 | "Documentation": "https://github.com/eriknyquist/duckargs", 65 | "Issues": "https://github.com/eriknyquist/duckargs/issues", 66 | "Contributions": "https://github.com/eriknyquist/duckargs/pulls" 67 | } 68 | ) 69 | -------------------------------------------------------------------------------- /tests/test_data/choices/args.txt: -------------------------------------------------------------------------------- 1 | duckargs pos1 pos2 -f -g -q --qefqaf op,ep,orp 2 | -------------------------------------------------------------------------------- /tests/test_data/choices/expected_c.txt: -------------------------------------------------------------------------------- 1 | // Generated by duckargs, invoked with the following arguments: 2 | // pos1 pos2 -f -g -q --qefqaf op,ep,orp 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | static char *pos1 = "pos1"; 11 | static char *pos2 = "pos2"; 12 | static bool f = false; 13 | static bool g = false; 14 | static char *qefqaf_choices[] = {"op", "ep", "orp"}; 15 | static char *qefqaf = "op"; 16 | 17 | static struct option long_options[] = 18 | { 19 | {"qefqaf", required_argument, NULL, 'q'}, 20 | {NULL, 0, NULL, 0} 21 | }; 22 | 23 | void print_usage(void) 24 | { 25 | printf("\n"); 26 | printf("USAGE:\n\n"); 27 | printf("program_name [OPTIONS] pos1 pos2\n"); 28 | printf("\nOPTIONS:\n\n"); 29 | printf("-f f flag\n"); 30 | printf("-g g flag\n"); 31 | printf("-q --qefqaf [op|ep|orp] A string value (default: %s)\n", qefqaf ? qefqaf : "null"); 32 | printf("\n"); 33 | } 34 | 35 | int parse_args(int argc, char *argv[]) 36 | { 37 | int ch; 38 | 39 | while ((ch = getopt_long(argc, argv, "fgq:", long_options, NULL)) != -1) 40 | { 41 | switch (ch) 42 | { 43 | case 'f': 44 | { 45 | f = true; 46 | break; 47 | } 48 | case 'g': 49 | { 50 | g = true; 51 | break; 52 | } 53 | case 'q': 54 | { 55 | qefqaf = optarg; 56 | for (int i = 0; i < 3; i++) 57 | { 58 | if (0 == strcmp(qefqaf_choices[i], qefqaf)) 59 | { 60 | break; 61 | } 62 | if (i == 2) 63 | { 64 | printf("Option '-q' must be one of ['op', 'ep', 'orp']\n"); 65 | return -1; 66 | } 67 | } 68 | break; 69 | } 70 | } 71 | } 72 | 73 | if (argc < (optind + 2)) 74 | { 75 | printf("Missing positional arguments\n"); 76 | return -1; 77 | } 78 | 79 | pos1 = argv[optind]; 80 | optind++; 81 | 82 | pos2 = argv[optind]; 83 | 84 | return 0; 85 | } 86 | 87 | int main(int argc, char *argv[]) 88 | { 89 | if (argc < 2) 90 | { 91 | print_usage(); 92 | return -1; 93 | } 94 | 95 | int ret = parse_args(argc, argv); 96 | if (0 != ret) 97 | { 98 | return ret; 99 | } 100 | 101 | printf("pos1: %s\n", pos1 ? pos1 : "null"); 102 | printf("pos2: %s\n", pos2 ? pos2 : "null"); 103 | printf("f: %s\n", f ? "true" : "false"); 104 | printf("g: %s\n", g ? "true" : "false"); 105 | printf("qefqaf: %s\n", qefqaf ? qefqaf : "null"); 106 | 107 | return 0; 108 | } 109 | 110 | -------------------------------------------------------------------------------- /tests/test_data/choices/expected_python.txt: -------------------------------------------------------------------------------- 1 | # Generated by duckargs, invoked with the following arguments: 2 | # pos1 pos2 -f -g -q --qefqaf op,ep,orp 3 | 4 | import argparse 5 | 6 | def main(): 7 | parser = argparse.ArgumentParser(description='A command-line program generated by duckargs', 8 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 9 | 10 | parser.add_argument('pos1', help='a string') 11 | parser.add_argument('pos2', help='a string') 12 | parser.add_argument('-f', action='store_true', help='f flag') 13 | parser.add_argument('-g', action='store_true', help='g flag') 14 | parser.add_argument('-q', '--qefqaf', choices=['op', 'ep', 'orp'], default='op', help='a string') 15 | args = parser.parse_args() 16 | 17 | print(args.pos1) 18 | print(args.pos2) 19 | print(args.f) 20 | print(args.g) 21 | print(args.qefqaf) 22 | 23 | if __name__ == "__main__": 24 | main() 25 | 26 | -------------------------------------------------------------------------------- /tests/test_data/env_all/args.txt: -------------------------------------------------------------------------------- 1 | duckargs positional_arg1 positional_arg2 -i --int-val 4 -e 3.3 -f --file FILE -F --otherfile FILE -a -b -c 2 | -------------------------------------------------------------------------------- /tests/test_data/env_all/expected_c.txt: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | static char *positional_arg1 = "positional_arg1"; 7 | static char *positional_arg2 = "positional_arg2"; 8 | static long int int_val = 4; 9 | static float e = 3.3; 10 | static char *file = NULL; 11 | static char *otherfile = NULL; 12 | static bool a = false; 13 | static bool b = false; 14 | static bool c = false; 15 | 16 | static struct option long_options[] = 17 | { 18 | {"int-val", required_argument, NULL, 'i'}, 19 | {"file", required_argument, NULL, 'f'}, 20 | {"otherfile", required_argument, NULL, 'F'}, 21 | {NULL, 0, NULL, 0} 22 | }; 23 | 24 | void print_usage(void) 25 | { 26 | printf("\n"); 27 | printf("USAGE:\n\n"); 28 | printf("program_name [OPTIONS] positional_arg1 positional_arg2\n"); 29 | printf("\nOPTIONS:\n\n"); 30 | printf("-i --int-val [int] An int value (default: %ld)\n", int_val); 31 | printf("-e [float] A float value (default: %.2f)\n", e); 32 | printf("-f --file FILE A filename (default: %s)\n", file ? file : "null"); 33 | printf("-F --otherfile FILE A filename (default: %s)\n", otherfile ? otherfile : "null"); 34 | printf("-a a flag\n"); 35 | printf("-b b flag\n"); 36 | printf("-c c flag\n"); 37 | printf("\n"); 38 | } 39 | 40 | int parse_args(int argc, char *argv[]) 41 | { 42 | char *endptr = NULL; 43 | int ch; 44 | 45 | while ((ch = getopt_long(argc, argv, "i:e:f:F:abc", long_options, NULL)) != -1) 46 | { 47 | switch (ch) 48 | { 49 | case 'i': 50 | { 51 | int_val = strtol(optarg, &endptr, 0); 52 | if (endptr && (*endptr != '\0')) 53 | { 54 | printf("Option '-i' requires an integer argument\n"); 55 | return -1; 56 | } 57 | break; 58 | } 59 | case 'e': 60 | { 61 | e = strtof(optarg, &endptr); 62 | if (endptr == optarg) 63 | { 64 | printf("Option '-e' requires a floating-point argument\n"); 65 | return -1; 66 | } 67 | break; 68 | } 69 | case 'f': 70 | { 71 | file = optarg; 72 | break; 73 | } 74 | case 'F': 75 | { 76 | otherfile = optarg; 77 | break; 78 | } 79 | case 'a': 80 | { 81 | a = true; 82 | break; 83 | } 84 | case 'b': 85 | { 86 | b = true; 87 | break; 88 | } 89 | case 'c': 90 | { 91 | c = true; 92 | break; 93 | } 94 | } 95 | } 96 | 97 | if (argc < (optind + 2)) 98 | { 99 | printf("Missing positional arguments\n"); 100 | return -1; 101 | } 102 | 103 | positional_arg1 = argv[optind]; 104 | optind++; 105 | 106 | positional_arg2 = argv[optind]; 107 | 108 | return 0; 109 | } 110 | 111 | int main(int argc, char *argv[]) 112 | { 113 | if (argc < 2) 114 | { 115 | print_usage(); 116 | return -1; 117 | } 118 | 119 | int ret = parse_args(argc, argv); 120 | if (0 != ret) 121 | { 122 | return ret; 123 | } 124 | 125 | return 0; 126 | } 127 | 128 | -------------------------------------------------------------------------------- /tests/test_data/env_all/expected_python.txt: -------------------------------------------------------------------------------- 1 | import argparse 2 | 3 | def main(): 4 | parser = argparse.ArgumentParser(description='A command-line program generated by duckargs', 5 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 6 | 7 | parser.add_argument('positional_arg1', help='a string') 8 | parser.add_argument('positional_arg2', help='a string') 9 | parser.add_argument('-i', '--int-val', default=4, type=int, help='an int value') 10 | parser.add_argument('-e', default=3.3, type=float, help='a float value') 11 | parser.add_argument('-f', '--file', default=None, type=argparse.FileType(), help='a filename') 12 | parser.add_argument('-F', '--otherfile', default=None, type=argparse.FileType(), help='a filename') 13 | parser.add_argument('-a', action='store_true', help='a flag') 14 | parser.add_argument('-b', action='store_true', help='b flag') 15 | parser.add_argument('-c', action='store_true', help='c flag') 16 | args = parser.parse_args() 17 | 18 | if __name__ == "__main__": 19 | main() 20 | 21 | -------------------------------------------------------------------------------- /tests/test_data/env_comment/args.txt: -------------------------------------------------------------------------------- 1 | duckargs positional_arg1 positional_arg2 -i --int-val 4 -e 3.3 -f --file FILE -F --otherfile FILE -a -b -c 2 | -------------------------------------------------------------------------------- /tests/test_data/env_comment/expected_c.txt: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | static char *positional_arg1 = "positional_arg1"; 7 | static char *positional_arg2 = "positional_arg2"; 8 | static long int int_val = 4; 9 | static float e = 3.3; 10 | static char *file = NULL; 11 | static char *otherfile = NULL; 12 | static bool a = false; 13 | static bool b = false; 14 | static bool c = false; 15 | 16 | static struct option long_options[] = 17 | { 18 | {"int-val", required_argument, NULL, 'i'}, 19 | {"file", required_argument, NULL, 'f'}, 20 | {"otherfile", required_argument, NULL, 'F'}, 21 | {NULL, 0, NULL, 0} 22 | }; 23 | 24 | void print_usage(void) 25 | { 26 | printf("\n"); 27 | printf("USAGE:\n\n"); 28 | printf("program_name [OPTIONS] positional_arg1 positional_arg2\n"); 29 | printf("\nOPTIONS:\n\n"); 30 | printf("-i --int-val [int] An int value (default: %ld)\n", int_val); 31 | printf("-e [float] A float value (default: %.2f)\n", e); 32 | printf("-f --file FILE A filename (default: %s)\n", file ? file : "null"); 33 | printf("-F --otherfile FILE A filename (default: %s)\n", otherfile ? otherfile : "null"); 34 | printf("-a a flag\n"); 35 | printf("-b b flag\n"); 36 | printf("-c c flag\n"); 37 | printf("\n"); 38 | } 39 | 40 | int parse_args(int argc, char *argv[]) 41 | { 42 | char *endptr = NULL; 43 | int ch; 44 | 45 | while ((ch = getopt_long(argc, argv, "i:e:f:F:abc", long_options, NULL)) != -1) 46 | { 47 | switch (ch) 48 | { 49 | case 'i': 50 | { 51 | int_val = strtol(optarg, &endptr, 0); 52 | if (endptr && (*endptr != '\0')) 53 | { 54 | printf("Option '-i' requires an integer argument\n"); 55 | return -1; 56 | } 57 | break; 58 | } 59 | case 'e': 60 | { 61 | e = strtof(optarg, &endptr); 62 | if (endptr == optarg) 63 | { 64 | printf("Option '-e' requires a floating-point argument\n"); 65 | return -1; 66 | } 67 | break; 68 | } 69 | case 'f': 70 | { 71 | file = optarg; 72 | break; 73 | } 74 | case 'F': 75 | { 76 | otherfile = optarg; 77 | break; 78 | } 79 | case 'a': 80 | { 81 | a = true; 82 | break; 83 | } 84 | case 'b': 85 | { 86 | b = true; 87 | break; 88 | } 89 | case 'c': 90 | { 91 | c = true; 92 | break; 93 | } 94 | } 95 | } 96 | 97 | if (argc < (optind + 2)) 98 | { 99 | printf("Missing positional arguments\n"); 100 | return -1; 101 | } 102 | 103 | positional_arg1 = argv[optind]; 104 | optind++; 105 | 106 | positional_arg2 = argv[optind]; 107 | 108 | return 0; 109 | } 110 | 111 | int main(int argc, char *argv[]) 112 | { 113 | if (argc < 2) 114 | { 115 | print_usage(); 116 | return -1; 117 | } 118 | 119 | int ret = parse_args(argc, argv); 120 | if (0 != ret) 121 | { 122 | return ret; 123 | } 124 | 125 | printf("positional_arg1: %s\n", positional_arg1 ? positional_arg1 : "null"); 126 | printf("positional_arg2: %s\n", positional_arg2 ? positional_arg2 : "null"); 127 | printf("int_val: %ld\n", int_val); 128 | printf("e: %.4f\n", e); 129 | printf("file: %s\n", file ? file : "null"); 130 | printf("otherfile: %s\n", otherfile ? otherfile : "null"); 131 | printf("a: %s\n", a ? "true" : "false"); 132 | printf("b: %s\n", b ? "true" : "false"); 133 | printf("c: %s\n", c ? "true" : "false"); 134 | 135 | return 0; 136 | } 137 | 138 | -------------------------------------------------------------------------------- /tests/test_data/env_comment/expected_python.txt: -------------------------------------------------------------------------------- 1 | import argparse 2 | 3 | def main(): 4 | parser = argparse.ArgumentParser(description='A command-line program generated by duckargs', 5 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 6 | 7 | parser.add_argument('positional_arg1', help='a string') 8 | parser.add_argument('positional_arg2', help='a string') 9 | parser.add_argument('-i', '--int-val', default=4, type=int, help='an int value') 10 | parser.add_argument('-e', default=3.3, type=float, help='a float value') 11 | parser.add_argument('-f', '--file', default=None, type=argparse.FileType(), help='a filename') 12 | parser.add_argument('-F', '--otherfile', default=None, type=argparse.FileType(), help='a filename') 13 | parser.add_argument('-a', action='store_true', help='a flag') 14 | parser.add_argument('-b', action='store_true', help='b flag') 15 | parser.add_argument('-c', action='store_true', help='c flag') 16 | args = parser.parse_args() 17 | 18 | print(args.positional_arg1) 19 | print(args.positional_arg2) 20 | print(args.int_val) 21 | print(args.e) 22 | print(args.file) 23 | print(args.otherfile) 24 | print(args.a) 25 | print(args.b) 26 | print(args.c) 27 | 28 | if __name__ == "__main__": 29 | main() 30 | 31 | -------------------------------------------------------------------------------- /tests/test_data/env_print/args.txt: -------------------------------------------------------------------------------- 1 | duckargs positional_arg1 positional_arg2 -i --int-val 4 -e 3.3 -f --file FILE -F --otherfile FILE -a -b -c 2 | -------------------------------------------------------------------------------- /tests/test_data/env_print/expected_c.txt: -------------------------------------------------------------------------------- 1 | // Generated by duckargs, invoked with the following arguments: 2 | // positional_arg1 positional_arg2 -i --int-val 4 -e 3.3 -f --file FILE -F --otherfile FILE -a -b -c 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | static char *positional_arg1 = "positional_arg1"; 10 | static char *positional_arg2 = "positional_arg2"; 11 | static long int int_val = 4; 12 | static float e = 3.3; 13 | static char *file = NULL; 14 | static char *otherfile = NULL; 15 | static bool a = false; 16 | static bool b = false; 17 | static bool c = false; 18 | 19 | static struct option long_options[] = 20 | { 21 | {"int-val", required_argument, NULL, 'i'}, 22 | {"file", required_argument, NULL, 'f'}, 23 | {"otherfile", required_argument, NULL, 'F'}, 24 | {NULL, 0, NULL, 0} 25 | }; 26 | 27 | void print_usage(void) 28 | { 29 | printf("\n"); 30 | printf("USAGE:\n\n"); 31 | printf("program_name [OPTIONS] positional_arg1 positional_arg2\n"); 32 | printf("\nOPTIONS:\n\n"); 33 | printf("-i --int-val [int] An int value (default: %ld)\n", int_val); 34 | printf("-e [float] A float value (default: %.2f)\n", e); 35 | printf("-f --file FILE A filename (default: %s)\n", file ? file : "null"); 36 | printf("-F --otherfile FILE A filename (default: %s)\n", otherfile ? otherfile : "null"); 37 | printf("-a a flag\n"); 38 | printf("-b b flag\n"); 39 | printf("-c c flag\n"); 40 | printf("\n"); 41 | } 42 | 43 | int parse_args(int argc, char *argv[]) 44 | { 45 | char *endptr = NULL; 46 | int ch; 47 | 48 | while ((ch = getopt_long(argc, argv, "i:e:f:F:abc", long_options, NULL)) != -1) 49 | { 50 | switch (ch) 51 | { 52 | case 'i': 53 | { 54 | int_val = strtol(optarg, &endptr, 0); 55 | if (endptr && (*endptr != '\0')) 56 | { 57 | printf("Option '-i' requires an integer argument\n"); 58 | return -1; 59 | } 60 | break; 61 | } 62 | case 'e': 63 | { 64 | e = strtof(optarg, &endptr); 65 | if (endptr == optarg) 66 | { 67 | printf("Option '-e' requires a floating-point argument\n"); 68 | return -1; 69 | } 70 | break; 71 | } 72 | case 'f': 73 | { 74 | file = optarg; 75 | break; 76 | } 77 | case 'F': 78 | { 79 | otherfile = optarg; 80 | break; 81 | } 82 | case 'a': 83 | { 84 | a = true; 85 | break; 86 | } 87 | case 'b': 88 | { 89 | b = true; 90 | break; 91 | } 92 | case 'c': 93 | { 94 | c = true; 95 | break; 96 | } 97 | } 98 | } 99 | 100 | if (argc < (optind + 2)) 101 | { 102 | printf("Missing positional arguments\n"); 103 | return -1; 104 | } 105 | 106 | positional_arg1 = argv[optind]; 107 | optind++; 108 | 109 | positional_arg2 = argv[optind]; 110 | 111 | return 0; 112 | } 113 | 114 | int main(int argc, char *argv[]) 115 | { 116 | if (argc < 2) 117 | { 118 | print_usage(); 119 | return -1; 120 | } 121 | 122 | int ret = parse_args(argc, argv); 123 | if (0 != ret) 124 | { 125 | return ret; 126 | } 127 | 128 | return 0; 129 | } 130 | 131 | -------------------------------------------------------------------------------- /tests/test_data/env_print/expected_python.txt: -------------------------------------------------------------------------------- 1 | # Generated by duckargs, invoked with the following arguments: 2 | # positional_arg1 positional_arg2 -i --int-val 4 -e 3.3 -f --file FILE -F --otherfile FILE -a -b -c 3 | 4 | import argparse 5 | 6 | def main(): 7 | parser = argparse.ArgumentParser(description='A command-line program generated by duckargs', 8 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 9 | 10 | parser.add_argument('positional_arg1', help='a string') 11 | parser.add_argument('positional_arg2', help='a string') 12 | parser.add_argument('-i', '--int-val', default=4, type=int, help='an int value') 13 | parser.add_argument('-e', default=3.3, type=float, help='a float value') 14 | parser.add_argument('-f', '--file', default=None, type=argparse.FileType(), help='a filename') 15 | parser.add_argument('-F', '--otherfile', default=None, type=argparse.FileType(), help='a filename') 16 | parser.add_argument('-a', action='store_true', help='a flag') 17 | parser.add_argument('-b', action='store_true', help='b flag') 18 | parser.add_argument('-c', action='store_true', help='c flag') 19 | args = parser.parse_args() 20 | 21 | if __name__ == "__main__": 22 | main() 23 | 24 | -------------------------------------------------------------------------------- /tests/test_data/flags_only/args.txt: -------------------------------------------------------------------------------- 1 | duckargs -a -b -c -d -e -f -g -h 2 | -------------------------------------------------------------------------------- /tests/test_data/flags_only/expected_c.txt: -------------------------------------------------------------------------------- 1 | // Generated by duckargs, invoked with the following arguments: 2 | // -a -b -c -d -e -f -g -h 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | static bool a = false; 10 | static bool b = false; 11 | static bool c = false; 12 | static bool d = false; 13 | static bool e = false; 14 | static bool f = false; 15 | static bool g = false; 16 | static bool h = false; 17 | 18 | void print_usage(void) 19 | { 20 | printf("\n"); 21 | printf("USAGE:\n\n"); 22 | printf("program_name [OPTIONS]\n"); 23 | printf("\nOPTIONS:\n\n"); 24 | printf("-a a flag\n"); 25 | printf("-b b flag\n"); 26 | printf("-c c flag\n"); 27 | printf("-d d flag\n"); 28 | printf("-e e flag\n"); 29 | printf("-f f flag\n"); 30 | printf("-g g flag\n"); 31 | printf("-h h flag\n"); 32 | printf("\n"); 33 | } 34 | 35 | int parse_args(int argc, char *argv[]) 36 | { 37 | int ch; 38 | 39 | while ((ch = getopt(argc, argv, "abcdefgh")) != -1) 40 | { 41 | switch (ch) 42 | { 43 | case 'a': 44 | { 45 | a = true; 46 | break; 47 | } 48 | case 'b': 49 | { 50 | b = true; 51 | break; 52 | } 53 | case 'c': 54 | { 55 | c = true; 56 | break; 57 | } 58 | case 'd': 59 | { 60 | d = true; 61 | break; 62 | } 63 | case 'e': 64 | { 65 | e = true; 66 | break; 67 | } 68 | case 'f': 69 | { 70 | f = true; 71 | break; 72 | } 73 | case 'g': 74 | { 75 | g = true; 76 | break; 77 | } 78 | case 'h': 79 | { 80 | h = true; 81 | break; 82 | } 83 | } 84 | } 85 | 86 | return 0; 87 | } 88 | 89 | int main(int argc, char *argv[]) 90 | { 91 | if (argc < 2) 92 | { 93 | print_usage(); 94 | return -1; 95 | } 96 | 97 | int ret = parse_args(argc, argv); 98 | if (0 != ret) 99 | { 100 | return ret; 101 | } 102 | 103 | printf("a: %s\n", a ? "true" : "false"); 104 | printf("b: %s\n", b ? "true" : "false"); 105 | printf("c: %s\n", c ? "true" : "false"); 106 | printf("d: %s\n", d ? "true" : "false"); 107 | printf("e: %s\n", e ? "true" : "false"); 108 | printf("f: %s\n", f ? "true" : "false"); 109 | printf("g: %s\n", g ? "true" : "false"); 110 | printf("h: %s\n", h ? "true" : "false"); 111 | 112 | return 0; 113 | } 114 | 115 | -------------------------------------------------------------------------------- /tests/test_data/flags_only/expected_python.txt: -------------------------------------------------------------------------------- 1 | # Generated by duckargs, invoked with the following arguments: 2 | # -a -b -c -d -e -f -g -h 3 | 4 | import argparse 5 | 6 | def main(): 7 | parser = argparse.ArgumentParser(description='A command-line program generated by duckargs', 8 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 9 | 10 | parser.add_argument('-a', action='store_true', help='a flag') 11 | parser.add_argument('-b', action='store_true', help='b flag') 12 | parser.add_argument('-c', action='store_true', help='c flag') 13 | parser.add_argument('-d', action='store_true', help='d flag') 14 | parser.add_argument('-e', action='store_true', help='e flag') 15 | parser.add_argument('-f', action='store_true', help='f flag') 16 | parser.add_argument('-g', action='store_true', help='g flag') 17 | parser.add_argument('-h', action='store_true', help='h flag') 18 | args = parser.parse_args() 19 | 20 | print(args.a) 21 | print(args.b) 22 | print(args.c) 23 | print(args.d) 24 | print(args.e) 25 | print(args.f) 26 | print(args.g) 27 | print(args.h) 28 | 29 | if __name__ == "__main__": 30 | main() 31 | 32 | -------------------------------------------------------------------------------- /tests/test_data/hex/args.txt: -------------------------------------------------------------------------------- 1 | duckargs -f --fff 0xabc -q 0x -t --test ox2 2 | -------------------------------------------------------------------------------- /tests/test_data/hex/expected_c.txt: -------------------------------------------------------------------------------- 1 | // Generated by duckargs, invoked with the following arguments: 2 | // -f --fff 0xabc -q 0x -t --test ox2 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | static long int fff = 0xabc; 9 | static char *q = "0x"; 10 | static char *test = "ox2"; 11 | 12 | static struct option long_options[] = 13 | { 14 | {"fff", required_argument, NULL, 'f'}, 15 | {"test", required_argument, NULL, 't'}, 16 | {NULL, 0, NULL, 0} 17 | }; 18 | 19 | void print_usage(void) 20 | { 21 | printf("\n"); 22 | printf("USAGE:\n\n"); 23 | printf("program_name [OPTIONS]\n"); 24 | printf("\nOPTIONS:\n\n"); 25 | printf("-f --fff [int] An int value (default: %ld)\n", fff); 26 | printf("-q [string] A string value (default: %s)\n", q ? q : "null"); 27 | printf("-t --test [string] A string value (default: %s)\n", test ? test : "null"); 28 | printf("\n"); 29 | } 30 | 31 | int parse_args(int argc, char *argv[]) 32 | { 33 | char *endptr = NULL; 34 | int ch; 35 | 36 | while ((ch = getopt_long(argc, argv, "f:q:t:", long_options, NULL)) != -1) 37 | { 38 | switch (ch) 39 | { 40 | case 'f': 41 | { 42 | fff = strtol(optarg, &endptr, 0); 43 | if (endptr && (*endptr != '\0')) 44 | { 45 | printf("Option '-f' requires an integer argument\n"); 46 | return -1; 47 | } 48 | break; 49 | } 50 | case 'q': 51 | { 52 | q = optarg; 53 | break; 54 | } 55 | case 't': 56 | { 57 | test = optarg; 58 | break; 59 | } 60 | } 61 | } 62 | 63 | return 0; 64 | } 65 | 66 | int main(int argc, char *argv[]) 67 | { 68 | if (argc < 2) 69 | { 70 | print_usage(); 71 | return -1; 72 | } 73 | 74 | int ret = parse_args(argc, argv); 75 | if (0 != ret) 76 | { 77 | return ret; 78 | } 79 | 80 | printf("fff: %ld\n", fff); 81 | printf("q: %s\n", q ? q : "null"); 82 | printf("test: %s\n", test ? test : "null"); 83 | 84 | return 0; 85 | } 86 | 87 | -------------------------------------------------------------------------------- /tests/test_data/hex/expected_python.txt: -------------------------------------------------------------------------------- 1 | # Generated by duckargs, invoked with the following arguments: 2 | # -f --fff 0xabc -q 0x -t --test ox2 3 | 4 | import argparse 5 | 6 | def main(): 7 | parser = argparse.ArgumentParser(description='A command-line program generated by duckargs', 8 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 9 | 10 | parser.add_argument('-f', '--fff', default=0xabc, type=int, help='an int value') 11 | parser.add_argument('-q', default='0x', help='a string') 12 | parser.add_argument('-t', '--test', default='ox2', help='a string') 13 | args = parser.parse_args() 14 | 15 | print(args.fff) 16 | print(args.q) 17 | print(args.test) 18 | 19 | if __name__ == "__main__": 20 | main() 21 | -------------------------------------------------------------------------------- /tests/test_data/many_opts/args.txt: -------------------------------------------------------------------------------- 1 | duckargs pos1 pos2 pos3 pos4 pos5 pos6 pos7 pos8 pos9 pos10 -a --aye 5 -w --yyy -k 0.0 -l jkjkj -o --out -d --ede -v --vvv jfijfdsifj -s --ssss -z --zzzz -c --ccc jiji -b --bbb 8 -i --ii d -q --qqqqsdgvs -y -r 2 | -------------------------------------------------------------------------------- /tests/test_data/many_opts/expected_c.txt: -------------------------------------------------------------------------------- 1 | // Generated by duckargs, invoked with the following arguments: 2 | // pos1 pos2 pos3 pos4 pos5 pos6 pos7 pos8 pos9 pos10 -a --aye 5 -w --yyy -k 0.0 -l jkjkj -o --out -d --ede -v --vvv jfijfdsifj -s --ssss -z --zzzz -c --ccc jiji -b --bbb 8 -i --ii d -q --qqqqsdgvs -y -r 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | static char *pos1 = "pos1"; 10 | static char *pos2 = "pos2"; 11 | static char *pos3 = "pos3"; 12 | static char *pos4 = "pos4"; 13 | static char *pos5 = "pos5"; 14 | static char *pos6 = "pos6"; 15 | static char *pos7 = "pos7"; 16 | static char *pos8 = "pos8"; 17 | static char *pos9 = "pos9"; 18 | static char *pos10 = "pos10"; 19 | static long int aye = 5; 20 | static bool yyy = false; 21 | static float k = 0.0; 22 | static char *l = "jkjkj"; 23 | static bool out = false; 24 | static bool ede = false; 25 | static char *vvv = "jfijfdsifj"; 26 | static bool ssss = false; 27 | static bool zzzz = false; 28 | static char *ccc = "jiji"; 29 | static long int bbb = 8; 30 | static char *ii = "d"; 31 | static bool qqqqsdgvs = false; 32 | static bool y = false; 33 | static bool r = false; 34 | 35 | static struct option long_options[] = 36 | { 37 | {"aye", required_argument, NULL, 'a'}, 38 | {"yyy", no_argument, NULL, 'w'}, 39 | {"out", no_argument, NULL, 'o'}, 40 | {"ede", no_argument, NULL, 'd'}, 41 | {"vvv", required_argument, NULL, 'v'}, 42 | {"ssss", no_argument, NULL, 's'}, 43 | {"zzzz", no_argument, NULL, 'z'}, 44 | {"ccc", required_argument, NULL, 'c'}, 45 | {"bbb", required_argument, NULL, 'b'}, 46 | {"ii", required_argument, NULL, 'i'}, 47 | {"qqqqsdgvs", no_argument, NULL, 'q'}, 48 | {NULL, 0, NULL, 0} 49 | }; 50 | 51 | void print_usage(void) 52 | { 53 | printf("\n"); 54 | printf("USAGE:\n\n"); 55 | printf("program_name [OPTIONS] pos1 pos2 pos3 pos4 pos5 pos6 pos7 pos8 pos9 pos10\n"); 56 | printf("\nOPTIONS:\n\n"); 57 | printf("-a --aye [int] An int value (default: %ld)\n", aye); 58 | printf("-w --yyy yyy flag\n"); 59 | printf("-k [float] A float value (default: %.2f)\n", k); 60 | printf("-l [string] A string value (default: %s)\n", l ? l : "null"); 61 | printf("-o --out out flag\n"); 62 | printf("-d --ede ede flag\n"); 63 | printf("-v --vvv [string] A string value (default: %s)\n", vvv ? vvv : "null"); 64 | printf("-s --ssss ssss flag\n"); 65 | printf("-z --zzzz zzzz flag\n"); 66 | printf("-c --ccc [string] A string value (default: %s)\n", ccc ? ccc : "null"); 67 | printf("-b --bbb [int] An int value (default: %ld)\n", bbb); 68 | printf("-i --ii [string] A string value (default: %s)\n", ii ? ii : "null"); 69 | printf("-q --qqqqsdgvs qqqqsdgvs flag\n"); 70 | printf("-y y flag\n"); 71 | printf("-r r flag\n"); 72 | printf("\n"); 73 | } 74 | 75 | int parse_args(int argc, char *argv[]) 76 | { 77 | char *endptr = NULL; 78 | int ch; 79 | 80 | while ((ch = getopt_long(argc, argv, "a:wk:l:odv:szc:b:i:qyr", long_options, NULL)) != -1) 81 | { 82 | switch (ch) 83 | { 84 | case 'a': 85 | { 86 | aye = strtol(optarg, &endptr, 0); 87 | if (endptr && (*endptr != '\0')) 88 | { 89 | printf("Option '-a' requires an integer argument\n"); 90 | return -1; 91 | } 92 | break; 93 | } 94 | case 'w': 95 | { 96 | yyy = true; 97 | break; 98 | } 99 | case 'k': 100 | { 101 | k = strtof(optarg, &endptr); 102 | if (endptr == optarg) 103 | { 104 | printf("Option '-k' requires a floating-point argument\n"); 105 | return -1; 106 | } 107 | break; 108 | } 109 | case 'l': 110 | { 111 | l = optarg; 112 | break; 113 | } 114 | case 'o': 115 | { 116 | out = true; 117 | break; 118 | } 119 | case 'd': 120 | { 121 | ede = true; 122 | break; 123 | } 124 | case 'v': 125 | { 126 | vvv = optarg; 127 | break; 128 | } 129 | case 's': 130 | { 131 | ssss = true; 132 | break; 133 | } 134 | case 'z': 135 | { 136 | zzzz = true; 137 | break; 138 | } 139 | case 'c': 140 | { 141 | ccc = optarg; 142 | break; 143 | } 144 | case 'b': 145 | { 146 | bbb = strtol(optarg, &endptr, 0); 147 | if (endptr && (*endptr != '\0')) 148 | { 149 | printf("Option '-b' requires an integer argument\n"); 150 | return -1; 151 | } 152 | break; 153 | } 154 | case 'i': 155 | { 156 | ii = optarg; 157 | break; 158 | } 159 | case 'q': 160 | { 161 | qqqqsdgvs = true; 162 | break; 163 | } 164 | case 'y': 165 | { 166 | y = true; 167 | break; 168 | } 169 | case 'r': 170 | { 171 | r = true; 172 | break; 173 | } 174 | } 175 | } 176 | 177 | if (argc < (optind + 10)) 178 | { 179 | printf("Missing positional arguments\n"); 180 | return -1; 181 | } 182 | 183 | pos1 = argv[optind]; 184 | optind++; 185 | 186 | pos2 = argv[optind]; 187 | optind++; 188 | 189 | pos3 = argv[optind]; 190 | optind++; 191 | 192 | pos4 = argv[optind]; 193 | optind++; 194 | 195 | pos5 = argv[optind]; 196 | optind++; 197 | 198 | pos6 = argv[optind]; 199 | optind++; 200 | 201 | pos7 = argv[optind]; 202 | optind++; 203 | 204 | pos8 = argv[optind]; 205 | optind++; 206 | 207 | pos9 = argv[optind]; 208 | optind++; 209 | 210 | pos10 = argv[optind]; 211 | 212 | return 0; 213 | } 214 | 215 | int main(int argc, char *argv[]) 216 | { 217 | if (argc < 2) 218 | { 219 | print_usage(); 220 | return -1; 221 | } 222 | 223 | int ret = parse_args(argc, argv); 224 | if (0 != ret) 225 | { 226 | return ret; 227 | } 228 | 229 | printf("pos1: %s\n", pos1 ? pos1 : "null"); 230 | printf("pos2: %s\n", pos2 ? pos2 : "null"); 231 | printf("pos3: %s\n", pos3 ? pos3 : "null"); 232 | printf("pos4: %s\n", pos4 ? pos4 : "null"); 233 | printf("pos5: %s\n", pos5 ? pos5 : "null"); 234 | printf("pos6: %s\n", pos6 ? pos6 : "null"); 235 | printf("pos7: %s\n", pos7 ? pos7 : "null"); 236 | printf("pos8: %s\n", pos8 ? pos8 : "null"); 237 | printf("pos9: %s\n", pos9 ? pos9 : "null"); 238 | printf("pos10: %s\n", pos10 ? pos10 : "null"); 239 | printf("aye: %ld\n", aye); 240 | printf("yyy: %s\n", yyy ? "true" : "false"); 241 | printf("k: %.4f\n", k); 242 | printf("l: %s\n", l ? l : "null"); 243 | printf("out: %s\n", out ? "true" : "false"); 244 | printf("ede: %s\n", ede ? "true" : "false"); 245 | printf("vvv: %s\n", vvv ? vvv : "null"); 246 | printf("ssss: %s\n", ssss ? "true" : "false"); 247 | printf("zzzz: %s\n", zzzz ? "true" : "false"); 248 | printf("ccc: %s\n", ccc ? ccc : "null"); 249 | printf("bbb: %ld\n", bbb); 250 | printf("ii: %s\n", ii ? ii : "null"); 251 | printf("qqqqsdgvs: %s\n", qqqqsdgvs ? "true" : "false"); 252 | printf("y: %s\n", y ? "true" : "false"); 253 | printf("r: %s\n", r ? "true" : "false"); 254 | 255 | return 0; 256 | } 257 | 258 | -------------------------------------------------------------------------------- /tests/test_data/many_opts/expected_python.txt: -------------------------------------------------------------------------------- 1 | # Generated by duckargs, invoked with the following arguments: 2 | # pos1 pos2 pos3 pos4 pos5 pos6 pos7 pos8 pos9 pos10 -a --aye 5 -w --yyy -k 0.0 -l jkjkj -o --out -d --ede -v --vvv jfijfdsifj -s --ssss -z --zzzz -c --ccc jiji -b --bbb 8 -i --ii d -q --qqqqsdgvs -y -r 3 | 4 | import argparse 5 | 6 | def main(): 7 | parser = argparse.ArgumentParser(description='A command-line program generated by duckargs', 8 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 9 | 10 | parser.add_argument('pos1', help='a string') 11 | parser.add_argument('pos2', help='a string') 12 | parser.add_argument('pos3', help='a string') 13 | parser.add_argument('pos4', help='a string') 14 | parser.add_argument('pos5', help='a string') 15 | parser.add_argument('pos6', help='a string') 16 | parser.add_argument('pos7', help='a string') 17 | parser.add_argument('pos8', help='a string') 18 | parser.add_argument('pos9', help='a string') 19 | parser.add_argument('pos10', help='a string') 20 | parser.add_argument('-a', '--aye', default=5, type=int, help='an int value') 21 | parser.add_argument('-w', '--yyy', action='store_true', help='yyy flag') 22 | parser.add_argument('-k', default=0.0, type=float, help='a float value') 23 | parser.add_argument('-l', default='jkjkj', help='a string') 24 | parser.add_argument('-o', '--out', action='store_true', help='out flag') 25 | parser.add_argument('-d', '--ede', action='store_true', help='ede flag') 26 | parser.add_argument('-v', '--vvv', default='jfijfdsifj', help='a string') 27 | parser.add_argument('-s', '--ssss', action='store_true', help='ssss flag') 28 | parser.add_argument('-z', '--zzzz', action='store_true', help='zzzz flag') 29 | parser.add_argument('-c', '--ccc', default='jiji', help='a string') 30 | parser.add_argument('-b', '--bbb', default=8, type=int, help='an int value') 31 | parser.add_argument('-i', '--ii', default='d', help='a string') 32 | parser.add_argument('-q', '--qqqqsdgvs', action='store_true', help='qqqqsdgvs flag') 33 | parser.add_argument('-y', action='store_true', help='y flag') 34 | parser.add_argument('-r', action='store_true', help='r flag') 35 | args = parser.parse_args() 36 | 37 | print(args.pos1) 38 | print(args.pos2) 39 | print(args.pos3) 40 | print(args.pos4) 41 | print(args.pos5) 42 | print(args.pos6) 43 | print(args.pos7) 44 | print(args.pos8) 45 | print(args.pos9) 46 | print(args.pos10) 47 | print(args.aye) 48 | print(args.yyy) 49 | print(args.k) 50 | print(args.l) 51 | print(args.out) 52 | print(args.ede) 53 | print(args.vvv) 54 | print(args.ssss) 55 | print(args.zzzz) 56 | print(args.ccc) 57 | print(args.bbb) 58 | print(args.ii) 59 | print(args.qqqqsdgvs) 60 | print(args.y) 61 | print(args.r) 62 | 63 | if __name__ == "__main__": 64 | main() 65 | 66 | -------------------------------------------------------------------------------- /tests/test_data/nargs/expected_python.txt: -------------------------------------------------------------------------------- 1 | # pos1 pos2:* -f:? --file:? FILE:+ -q:11 hello:12 2 | 3 | import argparse 4 | 5 | def main(): 6 | parser = argparse.ArgumentParser(description='', 7 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 8 | 9 | parser.add_argument('pos1', help='a string') 10 | parser.add_argument('pos2', nargs='*', help='a string') 11 | parser.add_argument('-f', '--file', default=None, type=argparse.FileType(), nargs='+', help='a filename') 12 | parser.add_argument('-q', default='hello', nargs=12, help='a string') 13 | args = parser.parse_args() 14 | 15 | print(args.pos1) 16 | print(args.pos2) 17 | print(args.file) 18 | print(args.q) 19 | 20 | if __name__ == "__main__": 21 | main() 22 | -------------------------------------------------------------------------------- /tests/test_data/negative_hex/args.txt: -------------------------------------------------------------------------------- 1 | duckargs -0x44 -f --fell -0x235 -x -0x8 2 | -------------------------------------------------------------------------------- /tests/test_data/negative_hex/expected_c.txt: -------------------------------------------------------------------------------- 1 | // Generated by duckargs, invoked with the following arguments: 2 | // -0x44 -f --fell -0x235 -x -0x8 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | static long int positional_arg0 = -0x44; 9 | static long int fell = -0x235; 10 | static long int x = -0x8; 11 | 12 | static struct option long_options[] = 13 | { 14 | {"fell", required_argument, NULL, 'f'}, 15 | {NULL, 0, NULL, 0} 16 | }; 17 | 18 | void print_usage(void) 19 | { 20 | printf("\n"); 21 | printf("USAGE:\n\n"); 22 | printf("program_name [OPTIONS] positional_arg0\n"); 23 | printf("\nOPTIONS:\n\n"); 24 | printf("-f --fell [int] An int value (default: %ld)\n", fell); 25 | printf("-x [int] An int value (default: %ld)\n", x); 26 | printf("\n"); 27 | } 28 | 29 | int parse_args(int argc, char *argv[]) 30 | { 31 | char *endptr = NULL; 32 | int ch; 33 | 34 | while ((ch = getopt_long(argc, argv, "f:x:", long_options, NULL)) != -1) 35 | { 36 | switch (ch) 37 | { 38 | case 'f': 39 | { 40 | fell = strtol(optarg, &endptr, 0); 41 | if (endptr && (*endptr != '\0')) 42 | { 43 | printf("Option '-f' requires an integer argument\n"); 44 | return -1; 45 | } 46 | break; 47 | } 48 | case 'x': 49 | { 50 | x = strtol(optarg, &endptr, 0); 51 | if (endptr && (*endptr != '\0')) 52 | { 53 | printf("Option '-x' requires an integer argument\n"); 54 | return -1; 55 | } 56 | break; 57 | } 58 | } 59 | } 60 | 61 | if (argc < (optind + 1)) 62 | { 63 | printf("Missing positional arguments\n"); 64 | return -1; 65 | } 66 | 67 | positional_arg0 = strtol(argv[optind], &endptr, 0); 68 | if (endptr && (*endptr != '\0')) 69 | { 70 | printf("Positional argument #1 (positional_arg0) requires an integer argument\n"); 71 | return -1; 72 | } 73 | 74 | return 0; 75 | } 76 | 77 | int main(int argc, char *argv[]) 78 | { 79 | if (argc < 2) 80 | { 81 | print_usage(); 82 | return -1; 83 | } 84 | 85 | int ret = parse_args(argc, argv); 86 | if (0 != ret) 87 | { 88 | return ret; 89 | } 90 | 91 | printf("positional_arg0: %ld\n", positional_arg0); 92 | printf("fell: %ld\n", fell); 93 | printf("x: %ld\n", x); 94 | 95 | return 0; 96 | } 97 | 98 | -------------------------------------------------------------------------------- /tests/test_data/negative_hex/expected_python.txt: -------------------------------------------------------------------------------- 1 | # Generated by duckargs, invoked with the following arguments: 2 | # -0x44 -f --fell -0x235 -x -0x8 3 | 4 | import argparse 5 | 6 | def main(): 7 | parser = argparse.ArgumentParser(description='A command-line program generated by duckargs', 8 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 9 | 10 | parser.add_argument('positional_arg0', type=int, help='an int value') 11 | parser.add_argument('-f', '--fell', default=-0x235, type=int, help='an int value') 12 | parser.add_argument('-x', default=-0x8, type=int, help='an int value') 13 | args = parser.parse_args() 14 | 15 | print(args.positional_arg0) 16 | print(args.fell) 17 | print(args.x) 18 | 19 | if __name__ == "__main__": 20 | main() 21 | -------------------------------------------------------------------------------- /tests/test_data/negative_int/args.txt: -------------------------------------------------------------------------------- 1 | duckargs -4 -f --ff -2 -b --blah -2343 -6 2 | -------------------------------------------------------------------------------- /tests/test_data/negative_int/expected_c.txt: -------------------------------------------------------------------------------- 1 | // Generated by duckargs, invoked with the following arguments: 2 | // -4 -f --ff -2 -b --blah -2343 -6 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | static long int positional_arg0 = -4; 9 | static long int ff = -2; 10 | static long int blah = -2343; 11 | static long int positional_arg1 = -6; 12 | 13 | static struct option long_options[] = 14 | { 15 | {"ff", required_argument, NULL, 'f'}, 16 | {"blah", required_argument, NULL, 'b'}, 17 | {NULL, 0, NULL, 0} 18 | }; 19 | 20 | void print_usage(void) 21 | { 22 | printf("\n"); 23 | printf("USAGE:\n\n"); 24 | printf("program_name [OPTIONS] positional_arg0 positional_arg1\n"); 25 | printf("\nOPTIONS:\n\n"); 26 | printf("-f --ff [int] An int value (default: %ld)\n", ff); 27 | printf("-b --blah [int] An int value (default: %ld)\n", blah); 28 | printf("\n"); 29 | } 30 | 31 | int parse_args(int argc, char *argv[]) 32 | { 33 | char *endptr = NULL; 34 | int ch; 35 | 36 | while ((ch = getopt_long(argc, argv, "f:b:", long_options, NULL)) != -1) 37 | { 38 | switch (ch) 39 | { 40 | case 'f': 41 | { 42 | ff = strtol(optarg, &endptr, 0); 43 | if (endptr && (*endptr != '\0')) 44 | { 45 | printf("Option '-f' requires an integer argument\n"); 46 | return -1; 47 | } 48 | break; 49 | } 50 | case 'b': 51 | { 52 | blah = strtol(optarg, &endptr, 0); 53 | if (endptr && (*endptr != '\0')) 54 | { 55 | printf("Option '-b' requires an integer argument\n"); 56 | return -1; 57 | } 58 | break; 59 | } 60 | } 61 | } 62 | 63 | if (argc < (optind + 2)) 64 | { 65 | printf("Missing positional arguments\n"); 66 | return -1; 67 | } 68 | 69 | positional_arg0 = strtol(argv[optind], &endptr, 0); 70 | if (endptr && (*endptr != '\0')) 71 | { 72 | printf("Positional argument #1 (positional_arg0) requires an integer argument\n"); 73 | return -1; 74 | } 75 | optind++; 76 | 77 | positional_arg1 = strtol(argv[optind], &endptr, 0); 78 | if (endptr && (*endptr != '\0')) 79 | { 80 | printf("Positional argument #2 (positional_arg1) requires an integer argument\n"); 81 | return -1; 82 | } 83 | 84 | return 0; 85 | } 86 | 87 | int main(int argc, char *argv[]) 88 | { 89 | if (argc < 2) 90 | { 91 | print_usage(); 92 | return -1; 93 | } 94 | 95 | int ret = parse_args(argc, argv); 96 | if (0 != ret) 97 | { 98 | return ret; 99 | } 100 | 101 | printf("positional_arg0: %ld\n", positional_arg0); 102 | printf("ff: %ld\n", ff); 103 | printf("blah: %ld\n", blah); 104 | printf("positional_arg1: %ld\n", positional_arg1); 105 | 106 | return 0; 107 | } 108 | 109 | -------------------------------------------------------------------------------- /tests/test_data/negative_int/expected_python.txt: -------------------------------------------------------------------------------- 1 | # Generated by duckargs, invoked with the following arguments: 2 | # -4 -f --ff -2 -b --blah -2343 -6 3 | 4 | import argparse 5 | 6 | def main(): 7 | parser = argparse.ArgumentParser(description='A command-line program generated by duckargs', 8 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 9 | 10 | parser.add_argument('positional_arg0', type=int, help='an int value') 11 | parser.add_argument('-f', '--ff', default=-2, type=int, help='an int value') 12 | parser.add_argument('-b', '--blah', default=-2343, type=int, help='an int value') 13 | parser.add_argument('positional_arg1', type=int, help='an int value') 14 | args = parser.parse_args() 15 | 16 | print(args.positional_arg0) 17 | print(args.ff) 18 | print(args.blah) 19 | print(args.positional_arg1) 20 | 21 | if __name__ == "__main__": 22 | main() 23 | -------------------------------------------------------------------------------- /tests/test_data/normalize_names/args.txt: -------------------------------------------------------------------------------- 1 | duckargs -a --test_1 5464 -b --test__-2 8 -j --j==j -g --lp++l pos-1 pos_2 pos_-_3 2 | -------------------------------------------------------------------------------- /tests/test_data/normalize_names/expected_c.txt: -------------------------------------------------------------------------------- 1 | // Generated by duckargs, invoked with the following arguments: 2 | // -a --test_1 5464 -b --test__-2 8 -j --j==j -g --lp++l pos-1 pos_2 pos_-_3 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | static long int test_1 = 5464; 10 | static long int test___2 = 8; 11 | static bool j__j = false; 12 | static char *lp__l = "pos-1"; 13 | static char *pos_2 = "pos_2"; 14 | static char *pos___3 = "pos___3"; 15 | 16 | static struct option long_options[] = 17 | { 18 | {"test_1", required_argument, NULL, 'a'}, 19 | {"test__-2", required_argument, NULL, 'b'}, 20 | {"j--j", no_argument, NULL, 'j'}, 21 | {"lp--l", required_argument, NULL, 'g'}, 22 | {NULL, 0, NULL, 0} 23 | }; 24 | 25 | void print_usage(void) 26 | { 27 | printf("\n"); 28 | printf("USAGE:\n\n"); 29 | printf("program_name [OPTIONS] pos_2 pos___3\n"); 30 | printf("\nOPTIONS:\n\n"); 31 | printf("-a --test_1 [int] An int value (default: %ld)\n", test_1); 32 | printf("-b --test__-2 [int] An int value (default: %ld)\n", test___2); 33 | printf("-j --j--j j__j flag\n"); 34 | printf("-g --lp--l [string] A string value (default: %s)\n", lp__l ? lp__l : "null"); 35 | printf("\n"); 36 | } 37 | 38 | int parse_args(int argc, char *argv[]) 39 | { 40 | char *endptr = NULL; 41 | int ch; 42 | 43 | while ((ch = getopt_long(argc, argv, "a:b:jg:", long_options, NULL)) != -1) 44 | { 45 | switch (ch) 46 | { 47 | case 'a': 48 | { 49 | test_1 = strtol(optarg, &endptr, 0); 50 | if (endptr && (*endptr != '\0')) 51 | { 52 | printf("Option '-a' requires an integer argument\n"); 53 | return -1; 54 | } 55 | break; 56 | } 57 | case 'b': 58 | { 59 | test___2 = strtol(optarg, &endptr, 0); 60 | if (endptr && (*endptr != '\0')) 61 | { 62 | printf("Option '-b' requires an integer argument\n"); 63 | return -1; 64 | } 65 | break; 66 | } 67 | case 'j': 68 | { 69 | j__j = true; 70 | break; 71 | } 72 | case 'g': 73 | { 74 | lp__l = optarg; 75 | break; 76 | } 77 | } 78 | } 79 | 80 | if (argc < (optind + 2)) 81 | { 82 | printf("Missing positional arguments\n"); 83 | return -1; 84 | } 85 | 86 | pos_2 = argv[optind]; 87 | optind++; 88 | 89 | pos___3 = argv[optind]; 90 | 91 | return 0; 92 | } 93 | 94 | int main(int argc, char *argv[]) 95 | { 96 | if (argc < 2) 97 | { 98 | print_usage(); 99 | return -1; 100 | } 101 | 102 | int ret = parse_args(argc, argv); 103 | if (0 != ret) 104 | { 105 | return ret; 106 | } 107 | 108 | printf("test_1: %ld\n", test_1); 109 | printf("test___2: %ld\n", test___2); 110 | printf("j__j: %s\n", j__j ? "true" : "false"); 111 | printf("lp__l: %s\n", lp__l ? lp__l : "null"); 112 | printf("pos_2: %s\n", pos_2 ? pos_2 : "null"); 113 | printf("pos___3: %s\n", pos___3 ? pos___3 : "null"); 114 | 115 | return 0; 116 | } 117 | 118 | -------------------------------------------------------------------------------- /tests/test_data/normalize_names/expected_python.txt: -------------------------------------------------------------------------------- 1 | # Generated by duckargs, invoked with the following arguments: 2 | # -a --test_1 5464 -b --test__-2 8 -j --j==j -g --lp++l pos-1 pos_2 pos_-_3 3 | 4 | import argparse 5 | 6 | def main(): 7 | parser = argparse.ArgumentParser(description='A command-line program generated by duckargs', 8 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 9 | 10 | parser.add_argument('-a', '--test_1', default=5464, type=int, help='an int value') 11 | parser.add_argument('-b', '--test__-2', default=8, type=int, help='an int value') 12 | parser.add_argument('-j', '--j--j', action='store_true', help='j__j flag') 13 | parser.add_argument('-g', '--lp--l', default='pos-1', help='a string') 14 | parser.add_argument('pos_2', help='a string') 15 | parser.add_argument('pos___3', help='a string') 16 | args = parser.parse_args() 17 | 18 | print(args.test_1) 19 | print(args.test___2) 20 | print(args.j__j) 21 | print(args.lp__l) 22 | print(args.pos_2) 23 | print(args.pos___3) 24 | 25 | if __name__ == "__main__": 26 | main() 27 | 28 | -------------------------------------------------------------------------------- /tests/test_data/options_only/args.txt: -------------------------------------------------------------------------------- 1 | duckargs -a 4 -b --bbb 5.5 -t --tttt test -j srgsrh -k wergwshg 2 | -------------------------------------------------------------------------------- /tests/test_data/options_only/expected_c.txt: -------------------------------------------------------------------------------- 1 | // Generated by duckargs, invoked with the following arguments: 2 | // -a 4 -b --bbb 5.5 -t --tttt test -j srgsrh -k wergwshg 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | static long int a = 4; 9 | static float bbb = 5.5; 10 | static char *tttt = "test"; 11 | static char *j = "srgsrh"; 12 | static char *k = "wergwshg"; 13 | 14 | static struct option long_options[] = 15 | { 16 | {"bbb", required_argument, NULL, 'b'}, 17 | {"tttt", required_argument, NULL, 't'}, 18 | {NULL, 0, NULL, 0} 19 | }; 20 | 21 | void print_usage(void) 22 | { 23 | printf("\n"); 24 | printf("USAGE:\n\n"); 25 | printf("program_name [OPTIONS]\n"); 26 | printf("\nOPTIONS:\n\n"); 27 | printf("-a [int] An int value (default: %ld)\n", a); 28 | printf("-b --bbb [float] A float value (default: %.2f)\n", bbb); 29 | printf("-t --tttt [string] A string value (default: %s)\n", tttt ? tttt : "null"); 30 | printf("-j [string] A string value (default: %s)\n", j ? j : "null"); 31 | printf("-k [string] A string value (default: %s)\n", k ? k : "null"); 32 | printf("\n"); 33 | } 34 | 35 | int parse_args(int argc, char *argv[]) 36 | { 37 | char *endptr = NULL; 38 | int ch; 39 | 40 | while ((ch = getopt_long(argc, argv, "a:b:t:j:k:", long_options, NULL)) != -1) 41 | { 42 | switch (ch) 43 | { 44 | case 'a': 45 | { 46 | a = strtol(optarg, &endptr, 0); 47 | if (endptr && (*endptr != '\0')) 48 | { 49 | printf("Option '-a' requires an integer argument\n"); 50 | return -1; 51 | } 52 | break; 53 | } 54 | case 'b': 55 | { 56 | bbb = strtof(optarg, &endptr); 57 | if (endptr == optarg) 58 | { 59 | printf("Option '-b' requires a floating-point argument\n"); 60 | return -1; 61 | } 62 | break; 63 | } 64 | case 't': 65 | { 66 | tttt = optarg; 67 | break; 68 | } 69 | case 'j': 70 | { 71 | j = optarg; 72 | break; 73 | } 74 | case 'k': 75 | { 76 | k = optarg; 77 | break; 78 | } 79 | } 80 | } 81 | 82 | return 0; 83 | } 84 | 85 | int main(int argc, char *argv[]) 86 | { 87 | if (argc < 2) 88 | { 89 | print_usage(); 90 | return -1; 91 | } 92 | 93 | int ret = parse_args(argc, argv); 94 | if (0 != ret) 95 | { 96 | return ret; 97 | } 98 | 99 | printf("a: %ld\n", a); 100 | printf("bbb: %.4f\n", bbb); 101 | printf("tttt: %s\n", tttt ? tttt : "null"); 102 | printf("j: %s\n", j ? j : "null"); 103 | printf("k: %s\n", k ? k : "null"); 104 | 105 | return 0; 106 | } 107 | 108 | -------------------------------------------------------------------------------- /tests/test_data/options_only/expected_python.txt: -------------------------------------------------------------------------------- 1 | # Generated by duckargs, invoked with the following arguments: 2 | # -a 4 -b --bbb 5.5 -t --tttt test -j srgsrh -k wergwshg 3 | 4 | import argparse 5 | 6 | def main(): 7 | parser = argparse.ArgumentParser(description='A command-line program generated by duckargs', 8 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 9 | 10 | parser.add_argument('-a', default=4, type=int, help='an int value') 11 | parser.add_argument('-b', '--bbb', default=5.5, type=float, help='a float value') 12 | parser.add_argument('-t', '--tttt', default='test', help='a string') 13 | parser.add_argument('-j', default='srgsrh', help='a string') 14 | parser.add_argument('-k', default='wergwshg', help='a string') 15 | args = parser.parse_args() 16 | 17 | print(args.a) 18 | print(args.bbb) 19 | print(args.tttt) 20 | print(args.j) 21 | print(args.k) 22 | 23 | if __name__ == "__main__": 24 | main() 25 | 26 | -------------------------------------------------------------------------------- /tests/test_data/positional_only/args.txt: -------------------------------------------------------------------------------- 1 | duckargs pos1 pos2 pos3 pos4 pos5 pos6 2 | -------------------------------------------------------------------------------- /tests/test_data/positional_only/expected_c.txt: -------------------------------------------------------------------------------- 1 | // Generated by duckargs, invoked with the following arguments: 2 | // pos1 pos2 pos3 pos4 pos5 pos6 3 | 4 | #include 5 | #include 6 | 7 | static char *pos1 = "pos1"; 8 | static char *pos2 = "pos2"; 9 | static char *pos3 = "pos3"; 10 | static char *pos4 = "pos4"; 11 | static char *pos5 = "pos5"; 12 | static char *pos6 = "pos6"; 13 | 14 | void print_usage(void) 15 | { 16 | printf("\n"); 17 | printf("USAGE:\n\n"); 18 | printf("program_name pos1 pos2 pos3 pos4 pos5 pos6\n"); 19 | printf("\n"); 20 | } 21 | 22 | int parse_args(int argc, char *argv[]) 23 | { 24 | if (argc < 7) 25 | { 26 | printf("Missing positional arguments\n"); 27 | return -1; 28 | } 29 | 30 | pos1 = argv[1]; 31 | 32 | pos2 = argv[2]; 33 | 34 | pos3 = argv[3]; 35 | 36 | pos4 = argv[4]; 37 | 38 | pos5 = argv[5]; 39 | 40 | pos6 = argv[6]; 41 | 42 | return 0; 43 | } 44 | 45 | int main(int argc, char *argv[]) 46 | { 47 | if (argc < 2) 48 | { 49 | print_usage(); 50 | return -1; 51 | } 52 | 53 | int ret = parse_args(argc, argv); 54 | if (0 != ret) 55 | { 56 | return ret; 57 | } 58 | 59 | printf("pos1: %s\n", pos1 ? pos1 : "null"); 60 | printf("pos2: %s\n", pos2 ? pos2 : "null"); 61 | printf("pos3: %s\n", pos3 ? pos3 : "null"); 62 | printf("pos4: %s\n", pos4 ? pos4 : "null"); 63 | printf("pos5: %s\n", pos5 ? pos5 : "null"); 64 | printf("pos6: %s\n", pos6 ? pos6 : "null"); 65 | 66 | return 0; 67 | } 68 | 69 | -------------------------------------------------------------------------------- /tests/test_data/positional_only/expected_python.txt: -------------------------------------------------------------------------------- 1 | # Generated by duckargs, invoked with the following arguments: 2 | # pos1 pos2 pos3 pos4 pos5 pos6 3 | 4 | import argparse 5 | 6 | def main(): 7 | parser = argparse.ArgumentParser(description='A command-line program generated by duckargs', 8 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 9 | 10 | parser.add_argument('pos1', help='a string') 11 | parser.add_argument('pos2', help='a string') 12 | parser.add_argument('pos3', help='a string') 13 | parser.add_argument('pos4', help='a string') 14 | parser.add_argument('pos5', help='a string') 15 | parser.add_argument('pos6', help='a string') 16 | args = parser.parse_args() 17 | 18 | print(args.pos1) 19 | print(args.pos2) 20 | print(args.pos3) 21 | print(args.pos4) 22 | print(args.pos5) 23 | print(args.pos6) 24 | 25 | if __name__ == "__main__": 26 | main() 27 | 28 | -------------------------------------------------------------------------------- /tests/test_data/positional_values/args.txt: -------------------------------------------------------------------------------- 1 | duckargs 0x 0x123 2.3 hello -r --ra FILE 2 | -------------------------------------------------------------------------------- /tests/test_data/positional_values/expected_c.txt: -------------------------------------------------------------------------------- 1 | // Generated by duckargs, invoked with the following arguments: 2 | // 0x 0x123 2.3 hello -r --ra FILE 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | static char *positional_arg0 = "0x"; 9 | static long int positional_arg1 = 0x123; 10 | static float positional_arg2 = 2.3; 11 | static char *hello = "hello"; 12 | static char *ra = NULL; 13 | 14 | static struct option long_options[] = 15 | { 16 | {"ra", required_argument, NULL, 'r'}, 17 | {NULL, 0, NULL, 0} 18 | }; 19 | 20 | void print_usage(void) 21 | { 22 | printf("\n"); 23 | printf("USAGE:\n\n"); 24 | printf("program_name [OPTIONS] positional_arg0 positional_arg1 positional_arg2 hello\n"); 25 | printf("\nOPTIONS:\n\n"); 26 | printf("-r --ra FILE A filename (default: %s)\n", ra ? ra : "null"); 27 | printf("\n"); 28 | } 29 | 30 | int parse_args(int argc, char *argv[]) 31 | { 32 | char *endptr = NULL; 33 | int ch; 34 | 35 | while ((ch = getopt_long(argc, argv, "r:", long_options, NULL)) != -1) 36 | { 37 | switch (ch) 38 | { 39 | case 'r': 40 | { 41 | ra = optarg; 42 | break; 43 | } 44 | } 45 | } 46 | 47 | if (argc < (optind + 4)) 48 | { 49 | printf("Missing positional arguments\n"); 50 | return -1; 51 | } 52 | 53 | positional_arg0 = argv[optind]; 54 | optind++; 55 | 56 | positional_arg1 = strtol(argv[optind], &endptr, 0); 57 | if (endptr && (*endptr != '\0')) 58 | { 59 | printf("Positional argument #2 (positional_arg1) requires an integer argument\n"); 60 | return -1; 61 | } 62 | optind++; 63 | 64 | positional_arg2 = strtof(argv[optind], &endptr); 65 | if (endptr == argv[optind]) 66 | { 67 | printf("Positional argument #3 (positional_arg2) requires a floating-point argument\n"); 68 | return -1; 69 | } 70 | optind++; 71 | 72 | hello = argv[optind]; 73 | 74 | return 0; 75 | } 76 | 77 | int main(int argc, char *argv[]) 78 | { 79 | if (argc < 2) 80 | { 81 | print_usage(); 82 | return -1; 83 | } 84 | 85 | int ret = parse_args(argc, argv); 86 | if (0 != ret) 87 | { 88 | return ret; 89 | } 90 | 91 | printf("positional_arg0: %s\n", positional_arg0 ? positional_arg0 : "null"); 92 | printf("positional_arg1: %ld\n", positional_arg1); 93 | printf("positional_arg2: %.4f\n", positional_arg2); 94 | printf("hello: %s\n", hello ? hello : "null"); 95 | printf("ra: %s\n", ra ? ra : "null"); 96 | 97 | return 0; 98 | } 99 | 100 | -------------------------------------------------------------------------------- /tests/test_data/positional_values/expected_python.txt: -------------------------------------------------------------------------------- 1 | # Generated by duckargs, invoked with the following arguments: 2 | # 0x 0x123 2.3 hello -r --ra FILE 3 | 4 | import argparse 5 | 6 | def main(): 7 | parser = argparse.ArgumentParser(description='A command-line program generated by duckargs', 8 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 9 | 10 | parser.add_argument('positional_arg0', help='a string') 11 | parser.add_argument('positional_arg1', type=int, help='an int value') 12 | parser.add_argument('positional_arg2', type=float, help='a float value') 13 | parser.add_argument('hello', help='a string') 14 | parser.add_argument('-r', '--ra', default=None, type=argparse.FileType(), help='a filename') 15 | args = parser.parse_args() 16 | 17 | print(args.positional_arg0) 18 | print(args.positional_arg1) 19 | print(args.positional_arg2) 20 | print(args.hello) 21 | print(args.ra) 22 | 23 | if __name__ == "__main__": 24 | main() 25 | -------------------------------------------------------------------------------- /tests/test_data/readme_example/args.txt: -------------------------------------------------------------------------------- 1 | duckargs positional_arg1 positional_arg2 -i --int-val 4 -e 3.3 -f --file FILE -F --otherfile FILE -a -b -c 2 | -------------------------------------------------------------------------------- /tests/test_data/readme_example/expected_c.txt: -------------------------------------------------------------------------------- 1 | // Generated by duckargs, invoked with the following arguments: 2 | // positional_arg1 positional_arg2 -i --int-val 4 -e 3.3 -f --file FILE -F --otherfile FILE -a -b -c 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | static char *positional_arg1 = "positional_arg1"; 10 | static char *positional_arg2 = "positional_arg2"; 11 | static long int int_val = 4; 12 | static float e = 3.3; 13 | static char *file = NULL; 14 | static char *otherfile = NULL; 15 | static bool a = false; 16 | static bool b = false; 17 | static bool c = false; 18 | 19 | static struct option long_options[] = 20 | { 21 | {"int-val", required_argument, NULL, 'i'}, 22 | {"file", required_argument, NULL, 'f'}, 23 | {"otherfile", required_argument, NULL, 'F'}, 24 | {NULL, 0, NULL, 0} 25 | }; 26 | 27 | void print_usage(void) 28 | { 29 | printf("\n"); 30 | printf("USAGE:\n\n"); 31 | printf("program_name [OPTIONS] positional_arg1 positional_arg2\n"); 32 | printf("\nOPTIONS:\n\n"); 33 | printf("-i --int-val [int] An int value (default: %ld)\n", int_val); 34 | printf("-e [float] A float value (default: %.2f)\n", e); 35 | printf("-f --file FILE A filename (default: %s)\n", file ? file : "null"); 36 | printf("-F --otherfile FILE A filename (default: %s)\n", otherfile ? otherfile : "null"); 37 | printf("-a a flag\n"); 38 | printf("-b b flag\n"); 39 | printf("-c c flag\n"); 40 | printf("\n"); 41 | } 42 | 43 | int parse_args(int argc, char *argv[]) 44 | { 45 | char *endptr = NULL; 46 | int ch; 47 | 48 | while ((ch = getopt_long(argc, argv, "i:e:f:F:abc", long_options, NULL)) != -1) 49 | { 50 | switch (ch) 51 | { 52 | case 'i': 53 | { 54 | int_val = strtol(optarg, &endptr, 0); 55 | if (endptr && (*endptr != '\0')) 56 | { 57 | printf("Option '-i' requires an integer argument\n"); 58 | return -1; 59 | } 60 | break; 61 | } 62 | case 'e': 63 | { 64 | e = strtof(optarg, &endptr); 65 | if (endptr == optarg) 66 | { 67 | printf("Option '-e' requires a floating-point argument\n"); 68 | return -1; 69 | } 70 | break; 71 | } 72 | case 'f': 73 | { 74 | file = optarg; 75 | break; 76 | } 77 | case 'F': 78 | { 79 | otherfile = optarg; 80 | break; 81 | } 82 | case 'a': 83 | { 84 | a = true; 85 | break; 86 | } 87 | case 'b': 88 | { 89 | b = true; 90 | break; 91 | } 92 | case 'c': 93 | { 94 | c = true; 95 | break; 96 | } 97 | } 98 | } 99 | 100 | if (argc < (optind + 2)) 101 | { 102 | printf("Missing positional arguments\n"); 103 | return -1; 104 | } 105 | 106 | positional_arg1 = argv[optind]; 107 | optind++; 108 | 109 | positional_arg2 = argv[optind]; 110 | 111 | return 0; 112 | } 113 | 114 | int main(int argc, char *argv[]) 115 | { 116 | if (argc < 2) 117 | { 118 | print_usage(); 119 | return -1; 120 | } 121 | 122 | int ret = parse_args(argc, argv); 123 | if (0 != ret) 124 | { 125 | return ret; 126 | } 127 | 128 | printf("positional_arg1: %s\n", positional_arg1 ? positional_arg1 : "null"); 129 | printf("positional_arg2: %s\n", positional_arg2 ? positional_arg2 : "null"); 130 | printf("int_val: %ld\n", int_val); 131 | printf("e: %.4f\n", e); 132 | printf("file: %s\n", file ? file : "null"); 133 | printf("otherfile: %s\n", otherfile ? otherfile : "null"); 134 | printf("a: %s\n", a ? "true" : "false"); 135 | printf("b: %s\n", b ? "true" : "false"); 136 | printf("c: %s\n", c ? "true" : "false"); 137 | 138 | return 0; 139 | } 140 | 141 | -------------------------------------------------------------------------------- /tests/test_data/readme_example/expected_python.txt: -------------------------------------------------------------------------------- 1 | # Generated by duckargs, invoked with the following arguments: 2 | # positional_arg1 positional_arg2 -i --int-val 4 -e 3.3 -f --file FILE -F --otherfile FILE -a -b -c 3 | 4 | import argparse 5 | 6 | def main(): 7 | parser = argparse.ArgumentParser(description='A command-line program generated by duckargs', 8 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 9 | 10 | parser.add_argument('positional_arg1', help='a string') 11 | parser.add_argument('positional_arg2', help='a string') 12 | parser.add_argument('-i', '--int-val', default=4, type=int, help='an int value') 13 | parser.add_argument('-e', default=3.3, type=float, help='a float value') 14 | parser.add_argument('-f', '--file', default=None, type=argparse.FileType(), help='a filename') 15 | parser.add_argument('-F', '--otherfile', default=None, type=argparse.FileType(), help='a filename') 16 | parser.add_argument('-a', action='store_true', help='a flag') 17 | parser.add_argument('-b', action='store_true', help='b flag') 18 | parser.add_argument('-c', action='store_true', help='c flag') 19 | args = parser.parse_args() 20 | 21 | print(args.positional_arg1) 22 | print(args.positional_arg2) 23 | print(args.int_val) 24 | print(args.e) 25 | print(args.file) 26 | print(args.otherfile) 27 | print(args.a) 28 | print(args.b) 29 | print(args.c) 30 | 31 | if __name__ == "__main__": 32 | main() 33 | 34 | -------------------------------------------------------------------------------- /tests/test_data/reserved_words_c/args.txt: -------------------------------------------------------------------------------- 1 | duckargs-c -i --int 55 -f --float 4.4 -s --struct -v --void -q --for -b --break -r --return -a --auto -p --static -w --while -o --switch -t --typedef -k --double -x --signed -e --const 2 | -------------------------------------------------------------------------------- /tests/test_data/reserved_words_c/expected_c.txt: -------------------------------------------------------------------------------- 1 | // Generated by duckargs, invoked with the following arguments: 2 | // -i --int 55 -f --float 4.4 -s --struct -v --void -q --for -b --break -r --return -a --auto -p --static -w --while -o --switch -t --typedef -k --double -x --signed -e --const 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | static long int intval = 55; 10 | static float floatval = 4.4; 11 | static bool structval = false; 12 | static bool voidval = false; 13 | static bool forval = false; 14 | static bool breakval = false; 15 | static bool returnval = false; 16 | static bool autoval = false; 17 | static bool staticval = false; 18 | static bool whileval = false; 19 | static bool switchval = false; 20 | static bool typedefval = false; 21 | static bool doubleval = false; 22 | static bool signedval = false; 23 | static bool constval = false; 24 | 25 | static struct option long_options[] = 26 | { 27 | {"int", required_argument, NULL, 'i'}, 28 | {"float", required_argument, NULL, 'f'}, 29 | {"struct", no_argument, NULL, 's'}, 30 | {"void", no_argument, NULL, 'v'}, 31 | {"for", no_argument, NULL, 'q'}, 32 | {"break", no_argument, NULL, 'b'}, 33 | {"return", no_argument, NULL, 'r'}, 34 | {"auto", no_argument, NULL, 'a'}, 35 | {"static", no_argument, NULL, 'p'}, 36 | {"while", no_argument, NULL, 'w'}, 37 | {"switch", no_argument, NULL, 'o'}, 38 | {"typedef", no_argument, NULL, 't'}, 39 | {"double", no_argument, NULL, 'k'}, 40 | {"signed", no_argument, NULL, 'x'}, 41 | {"const", no_argument, NULL, 'e'}, 42 | {NULL, 0, NULL, 0} 43 | }; 44 | 45 | void print_usage(void) 46 | { 47 | printf("\n"); 48 | printf("USAGE:\n\n"); 49 | printf("program_name [OPTIONS]\n"); 50 | printf("\nOPTIONS:\n\n"); 51 | printf("-i --int [int] An int value (default: %ld)\n", intval); 52 | printf("-f --float [float] A float value (default: %.2f)\n", floatval); 53 | printf("-s --struct struct flag\n"); 54 | printf("-v --void void flag\n"); 55 | printf("-q --for for flag\n"); 56 | printf("-b --break break flag\n"); 57 | printf("-r --return return flag\n"); 58 | printf("-a --auto auto flag\n"); 59 | printf("-p --static static flag\n"); 60 | printf("-w --while while flag\n"); 61 | printf("-o --switch switch flag\n"); 62 | printf("-t --typedef typedef flag\n"); 63 | printf("-k --double double flag\n"); 64 | printf("-x --signed signed flag\n"); 65 | printf("-e --const const flag\n"); 66 | printf("\n"); 67 | } 68 | 69 | int parse_args(int argc, char *argv[]) 70 | { 71 | char *endptr = NULL; 72 | int ch; 73 | 74 | while ((ch = getopt_long(argc, argv, "i:f:svqbrapwotkxe", long_options, NULL)) != -1) 75 | { 76 | switch (ch) 77 | { 78 | case 'i': 79 | { 80 | intval = strtol(optarg, &endptr, 0); 81 | if (endptr && (*endptr != '\0')) 82 | { 83 | printf("Option '-i' requires an integer argument\n"); 84 | return -1; 85 | } 86 | break; 87 | } 88 | case 'f': 89 | { 90 | floatval = strtof(optarg, &endptr); 91 | if (endptr == optarg) 92 | { 93 | printf("Option '-f' requires a floating-point argument\n"); 94 | return -1; 95 | } 96 | break; 97 | } 98 | case 's': 99 | { 100 | structval = true; 101 | break; 102 | } 103 | case 'v': 104 | { 105 | voidval = true; 106 | break; 107 | } 108 | case 'q': 109 | { 110 | forval = true; 111 | break; 112 | } 113 | case 'b': 114 | { 115 | breakval = true; 116 | break; 117 | } 118 | case 'r': 119 | { 120 | returnval = true; 121 | break; 122 | } 123 | case 'a': 124 | { 125 | autoval = true; 126 | break; 127 | } 128 | case 'p': 129 | { 130 | staticval = true; 131 | break; 132 | } 133 | case 'w': 134 | { 135 | whileval = true; 136 | break; 137 | } 138 | case 'o': 139 | { 140 | switchval = true; 141 | break; 142 | } 143 | case 't': 144 | { 145 | typedefval = true; 146 | break; 147 | } 148 | case 'k': 149 | { 150 | doubleval = true; 151 | break; 152 | } 153 | case 'x': 154 | { 155 | signedval = true; 156 | break; 157 | } 158 | case 'e': 159 | { 160 | constval = true; 161 | break; 162 | } 163 | } 164 | } 165 | 166 | return 0; 167 | } 168 | 169 | int main(int argc, char *argv[]) 170 | { 171 | if (argc < 2) 172 | { 173 | print_usage(); 174 | return -1; 175 | } 176 | 177 | int ret = parse_args(argc, argv); 178 | if (0 != ret) 179 | { 180 | return ret; 181 | } 182 | 183 | printf("int: %ld\n", intval); 184 | printf("float: %.4f\n", floatval); 185 | printf("struct: %s\n", structval ? "true" : "false"); 186 | printf("void: %s\n", voidval ? "true" : "false"); 187 | printf("for: %s\n", forval ? "true" : "false"); 188 | printf("break: %s\n", breakval ? "true" : "false"); 189 | printf("return: %s\n", returnval ? "true" : "false"); 190 | printf("auto: %s\n", autoval ? "true" : "false"); 191 | printf("static: %s\n", staticval ? "true" : "false"); 192 | printf("while: %s\n", whileval ? "true" : "false"); 193 | printf("switch: %s\n", switchval ? "true" : "false"); 194 | printf("typedef: %s\n", typedefval ? "true" : "false"); 195 | printf("double: %s\n", doubleval ? "true" : "false"); 196 | printf("signed: %s\n", signedval ? "true" : "false"); 197 | printf("const: %s\n", constval ? "true" : "false"); 198 | 199 | return 0; 200 | } 201 | 202 | -------------------------------------------------------------------------------- /tests/test_data/reserved_words_c/expected_python.txt: -------------------------------------------------------------------------------- 1 | # Generated by duckargs, invoked with the following arguments: 2 | # -i --int 55 -f --float 4.4 -s --struct -v --void -q --for -b --break -r --return -a --auto -p --static -w --while -o --switch -t --typedef -k --double -x --signed -e --const 3 | 4 | import argparse 5 | 6 | def main(): 7 | parser = argparse.ArgumentParser(description='A command-line program generated by duckargs', 8 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 9 | 10 | parser.add_argument('-i', '--int', default=55, type=int, help='an int value') 11 | parser.add_argument('-f', '--float', default=4.4, type=float, help='a float value') 12 | parser.add_argument('-s', '--struct', action='store_true', help='struct flag') 13 | parser.add_argument('-v', '--void', action='store_true', help='void flag') 14 | parser.add_argument('-q', '--for', action='store_true', help='for flag') 15 | parser.add_argument('-b', '--break', action='store_true', help='break flag') 16 | parser.add_argument('-r', '--return', action='store_true', help='return flag') 17 | parser.add_argument('-a', '--auto', action='store_true', help='auto flag') 18 | parser.add_argument('-p', '--static', action='store_true', help='static flag') 19 | parser.add_argument('-w', '--while', action='store_true', help='while flag') 20 | parser.add_argument('-o', '--switch', action='store_true', help='switch flag') 21 | parser.add_argument('-t', '--typedef', action='store_true', help='typedef flag') 22 | parser.add_argument('-k', '--double', action='store_true', help='double flag') 23 | parser.add_argument('-x', '--signed', action='store_true', help='signed flag') 24 | parser.add_argument('-e', '--const', action='store_true', help='const flag') 25 | args = parser.parse_args() 26 | 27 | print(args.intval) 28 | print(args.floatval) 29 | print(args.struct) 30 | print(args.void) 31 | print(args.forval) 32 | print(args.breakval) 33 | print(args.returnval) 34 | print(args.auto) 35 | print(args.static) 36 | print(args.whileval) 37 | print(args.switch) 38 | print(args.typedef) 39 | print(args.double) 40 | print(args.signed) 41 | print(args.const) 42 | 43 | if __name__ == "__main__": 44 | main() 45 | 46 | -------------------------------------------------------------------------------- /tests/test_data/reserved_words_python/args.txt: -------------------------------------------------------------------------------- 1 | duckargs -i --int 5 -f --float 6.6 -d --dict -t --tuple -r --return -l --lambda -n --nonlocal -q --from -c --continue -y --yield -g --global -u --del -b --class 2 | -------------------------------------------------------------------------------- /tests/test_data/reserved_words_python/expected_c.txt: -------------------------------------------------------------------------------- 1 | // Generated by duckargs, invoked with the following arguments: 2 | // -i --int 5 -f --float 6.6 -d --dict -t --tuple -r --return -l --lambda -n --nonlocal -q --from -c --continue -y --yield -g --global -u --del -b --class 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | static long int intval = 5; 10 | static float floatval = 6.6; 11 | static bool dict = false; 12 | static bool tuple = false; 13 | static bool returnval = false; 14 | static bool lambda = false; 15 | static bool nonlocal = false; 16 | static bool from = false; 17 | static bool continueval = false; 18 | static bool yield = false; 19 | static bool global = false; 20 | static bool del = false; 21 | static bool class = false; 22 | 23 | static struct option long_options[] = 24 | { 25 | {"int", required_argument, NULL, 'i'}, 26 | {"float", required_argument, NULL, 'f'}, 27 | {"dict", no_argument, NULL, 'd'}, 28 | {"tuple", no_argument, NULL, 't'}, 29 | {"return", no_argument, NULL, 'r'}, 30 | {"lambda", no_argument, NULL, 'l'}, 31 | {"nonlocal", no_argument, NULL, 'n'}, 32 | {"from", no_argument, NULL, 'q'}, 33 | {"continue", no_argument, NULL, 'c'}, 34 | {"yield", no_argument, NULL, 'y'}, 35 | {"global", no_argument, NULL, 'g'}, 36 | {"del", no_argument, NULL, 'u'}, 37 | {"class", no_argument, NULL, 'b'}, 38 | {NULL, 0, NULL, 0} 39 | }; 40 | 41 | void print_usage(void) 42 | { 43 | printf("\n"); 44 | printf("USAGE:\n\n"); 45 | printf("program_name [OPTIONS]\n"); 46 | printf("\nOPTIONS:\n\n"); 47 | printf("-i --int [int] An int value (default: %ld)\n", intval); 48 | printf("-f --float [float] A float value (default: %.2f)\n", floatval); 49 | printf("-d --dict dict flag\n"); 50 | printf("-t --tuple tuple flag\n"); 51 | printf("-r --return return flag\n"); 52 | printf("-l --lambda lambda flag\n"); 53 | printf("-n --nonlocal nonlocal flag\n"); 54 | printf("-q --from from flag\n"); 55 | printf("-c --continue continue flag\n"); 56 | printf("-y --yield yield flag\n"); 57 | printf("-g --global global flag\n"); 58 | printf("-u --del del flag\n"); 59 | printf("-b --class class flag\n"); 60 | printf("\n"); 61 | } 62 | 63 | int parse_args(int argc, char *argv[]) 64 | { 65 | char *endptr = NULL; 66 | int ch; 67 | 68 | while ((ch = getopt_long(argc, argv, "i:f:dtrlnqcygub", long_options, NULL)) != -1) 69 | { 70 | switch (ch) 71 | { 72 | case 'i': 73 | { 74 | intval = strtol(optarg, &endptr, 0); 75 | if (endptr && (*endptr != '\0')) 76 | { 77 | printf("Option '-i' requires an integer argument\n"); 78 | return -1; 79 | } 80 | break; 81 | } 82 | case 'f': 83 | { 84 | floatval = strtof(optarg, &endptr); 85 | if (endptr == optarg) 86 | { 87 | printf("Option '-f' requires a floating-point argument\n"); 88 | return -1; 89 | } 90 | break; 91 | } 92 | case 'd': 93 | { 94 | dict = true; 95 | break; 96 | } 97 | case 't': 98 | { 99 | tuple = true; 100 | break; 101 | } 102 | case 'r': 103 | { 104 | returnval = true; 105 | break; 106 | } 107 | case 'l': 108 | { 109 | lambda = true; 110 | break; 111 | } 112 | case 'n': 113 | { 114 | nonlocal = true; 115 | break; 116 | } 117 | case 'q': 118 | { 119 | from = true; 120 | break; 121 | } 122 | case 'c': 123 | { 124 | continueval = true; 125 | break; 126 | } 127 | case 'y': 128 | { 129 | yield = true; 130 | break; 131 | } 132 | case 'g': 133 | { 134 | global = true; 135 | break; 136 | } 137 | case 'u': 138 | { 139 | del = true; 140 | break; 141 | } 142 | case 'b': 143 | { 144 | class = true; 145 | break; 146 | } 147 | } 148 | } 149 | 150 | return 0; 151 | } 152 | 153 | int main(int argc, char *argv[]) 154 | { 155 | if (argc < 2) 156 | { 157 | print_usage(); 158 | return -1; 159 | } 160 | 161 | int ret = parse_args(argc, argv); 162 | if (0 != ret) 163 | { 164 | return ret; 165 | } 166 | 167 | printf("int: %ld\n", intval); 168 | printf("float: %.4f\n", floatval); 169 | printf("dict: %s\n", dict ? "true" : "false"); 170 | printf("tuple: %s\n", tuple ? "true" : "false"); 171 | printf("return: %s\n", returnval ? "true" : "false"); 172 | printf("lambda: %s\n", lambda ? "true" : "false"); 173 | printf("nonlocal: %s\n", nonlocal ? "true" : "false"); 174 | printf("from: %s\n", from ? "true" : "false"); 175 | printf("continue: %s\n", continueval ? "true" : "false"); 176 | printf("yield: %s\n", yield ? "true" : "false"); 177 | printf("global: %s\n", global ? "true" : "false"); 178 | printf("del: %s\n", del ? "true" : "false"); 179 | printf("class: %s\n", class ? "true" : "false"); 180 | 181 | return 0; 182 | } 183 | 184 | -------------------------------------------------------------------------------- /tests/test_data/reserved_words_python/expected_python.txt: -------------------------------------------------------------------------------- 1 | # Generated by duckargs, invoked with the following arguments: 2 | # -i --int 5 -f --float 6.6 -d --dict -t --tuple -r --return -l --lambda -n --nonlocal -q --from -c --continue -y --yield -g --global -u --del -b --class 3 | 4 | import argparse 5 | 6 | def main(): 7 | parser = argparse.ArgumentParser(description='A command-line program generated by duckargs', 8 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 9 | 10 | parser.add_argument('-i', '--int', default=5, type=int, help='an int value') 11 | parser.add_argument('-f', '--float', default=6.6, type=float, help='a float value') 12 | parser.add_argument('-d', '--dict', action='store_true', help='dict flag') 13 | parser.add_argument('-t', '--tuple', action='store_true', help='tuple flag') 14 | parser.add_argument('-r', '--return', action='store_true', help='return flag') 15 | parser.add_argument('-l', '--lambda', action='store_true', help='lambda flag') 16 | parser.add_argument('-n', '--nonlocal', action='store_true', help='nonlocal flag') 17 | parser.add_argument('-q', '--from', action='store_true', help='from flag') 18 | parser.add_argument('-c', '--continue', action='store_true', help='continue flag') 19 | parser.add_argument('-y', '--yield', action='store_true', help='yield flag') 20 | parser.add_argument('-g', '--global', action='store_true', help='global flag') 21 | parser.add_argument('-u', '--del', action='store_true', help='del flag') 22 | parser.add_argument('-b', '--class', action='store_true', help='class flag') 23 | args = parser.parse_args() 24 | 25 | print(args.intval) 26 | print(args.floatval) 27 | print(args.dictval) 28 | print(args.tupleval) 29 | print(args.returnval) 30 | print(args.lambdaval) 31 | print(args.nonlocalval) 32 | print(args.fromval) 33 | print(args.continueval) 34 | print(args.yieldval) 35 | print(args.globalval) 36 | print(args.delval) 37 | print(args.classval) 38 | 39 | if __name__ == "__main__": 40 | main() 41 | 42 | -------------------------------------------------------------------------------- /tests/test_duckargs.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import unittest 4 | 5 | from duckargs import generate_python_code, generate_c_code 6 | 7 | 8 | TEST_DATA_DIR = os.path.join(os.path.dirname(__file__), "test_data") 9 | 10 | class TestDuckargs(unittest.TestCase): 11 | def setUp(self): 12 | # Set default env. var values 13 | os.environ["DUCKARGS_PRINT"] = "1" 14 | os.environ["DUCKARGS_COMMENT"] = "1" 15 | 16 | def _run_python_test(self, test_dir_name): 17 | test_dir_path = os.path.join(TEST_DATA_DIR, test_dir_name) 18 | expected_python_path = os.path.join(test_dir_path, "expected_python.txt") 19 | args_path = os.path.join(test_dir_path, "args.txt") 20 | 21 | with open(args_path, 'r') as fh: 22 | args = fh.read().strip().split() 23 | 24 | with open(expected_python_path, 'r') as fh: 25 | expected_python = fh.read().strip() 26 | 27 | generated_python = generate_python_code(args).strip() 28 | self.assertEqual(generated_python, expected_python) 29 | 30 | def _run_c_test(self, test_dir_name): 31 | test_dir_path = os.path.join(TEST_DATA_DIR, test_dir_name) 32 | expected_c_path = os.path.join(test_dir_path, "expected_c.txt") 33 | args_path = os.path.join(test_dir_path, "args.txt") 34 | 35 | with open(args_path, 'r') as fh: 36 | args = fh.read().strip().split() 37 | 38 | with open(expected_c_path, 'r') as fh: 39 | expected_c = fh.read().strip() 40 | 41 | generated_c = generate_c_code(args).strip() 42 | self.assertEqual(generated_c, expected_c) 43 | 44 | def test_readme_example_c(self): 45 | self._run_c_test("readme_example") 46 | 47 | def test_readme_example_python(self): 48 | self._run_python_test("readme_example") 49 | 50 | def test_flags_only_c(self): 51 | self._run_c_test("flags_only") 52 | 53 | def test_flags_only_python(self): 54 | self._run_python_test("flags_only") 55 | 56 | def test_positional_only_c(self): 57 | self._run_c_test("positional_only") 58 | 59 | def test_positional_only_python(self): 60 | self._run_python_test("positional_only") 61 | 62 | def test_options_only_c(self): 63 | self._run_c_test("options_only") 64 | 65 | def test_options_only_python(self): 66 | self._run_python_test("options_only") 67 | 68 | def test_normalize_names_c(self): 69 | self._run_c_test("normalize_names") 70 | 71 | def test_normalize_names_python(self): 72 | self._run_python_test("normalize_names") 73 | 74 | def test_many_opts_c(self): 75 | self._run_c_test("many_opts") 76 | 77 | def test_many_opts_python(self): 78 | self._run_python_test("many_opts") 79 | 80 | def test_choices_c(self): 81 | self._run_c_test("choices") 82 | 83 | def test_choices_python(self): 84 | self._run_python_test("choices") 85 | 86 | def test_hex_c(self): 87 | self._run_c_test("hex") 88 | 89 | def test_hex_python(self): 90 | self._run_python_test("hex") 91 | 92 | def test_positional_values_c(self): 93 | self._run_c_test("positional_values") 94 | 95 | def test_positional_values_python(self): 96 | self._run_python_test("positional_values") 97 | 98 | def test_negative_int_c(self): 99 | self._run_c_test("negative_int") 100 | 101 | def test_negative_int_python(self): 102 | self._run_python_test("negative_int") 103 | 104 | def test_negative_hex_c(self): 105 | self._run_c_test("negative_hex") 106 | 107 | def test_negative_hex_python(self): 108 | self._run_python_test("negative_hex") 109 | 110 | def test_reserved_words_python_python(self): 111 | self._run_python_test("reserved_words_python") 112 | 113 | def test_reserved_words_python_c(self): 114 | self._run_c_test("reserved_words_python") 115 | 116 | def test_reserved_words_c_python(self): 117 | self._run_python_test("reserved_words_c") 118 | 119 | def test_reserved_words_c_c(self): 120 | self._run_c_test("reserved_words_c") 121 | 122 | def test_env_print_c(self): 123 | os.environ["DUCKARGS_PRINT"] = "0" 124 | self._run_c_test("env_print") 125 | 126 | def test_env_print_python(self): 127 | os.environ["DUCKARGS_PRINT"] = "0" 128 | self._run_python_test("env_print") 129 | 130 | def test_env_comment_c(self): 131 | os.environ["DUCKARGS_COMMENT"] = "0" 132 | self._run_c_test("env_comment") 133 | 134 | def test_env_comment_python(self): 135 | os.environ["DUCKARGS_COMMENT"] = "0" 136 | self._run_python_test("env_comment") 137 | 138 | def test_env_all_c(self): 139 | os.environ["DUCKARGS_COMMENT"] = "0" 140 | os.environ["DUCKARGS_PRINT"] = "0" 141 | self._run_c_test("env_all") 142 | 143 | def test_env_all_python(self): 144 | os.environ["DUCKARGS_COMMENT"] = "0" 145 | os.environ["DUCKARGS_PRINT"] = "0" 146 | self._run_python_test("env_all") 147 | 148 | def test_invalid_env_print(self): 149 | os.environ["DUCKARGS_PRINT"] = "ksfensik" 150 | self.assertRaises(RuntimeError, generate_python_code, ['duckargs', '-a']) 151 | 152 | def test_invalid_env_comment(self): 153 | os.environ["DUCKARGS_COMMENT"] = "ksfensik" 154 | self.assertRaises(RuntimeError, generate_python_code, ['duckargs', '-a']) 155 | 156 | def test_duplicate_names(self): 157 | self.assertRaises(ValueError, generate_python_code, ['duckargs', '-a', '-a']) 158 | self.assertRaises(ValueError, generate_python_code, ['duckargs', '-a', '-b', '-a']) 159 | self.assertRaises(ValueError, generate_python_code, ['duckargs', '-a-a', '-a_a']) 160 | self.assertRaises(ValueError, generate_python_code, ['duckargs', '-a-a', '-a+a']) 161 | self.assertRaises(ValueError, generate_python_code, ['duckargs', '-a', '--aye', '-b', '-a', '--aye']) 162 | self.assertRaises(ValueError, generate_python_code, ['duckargs', 'pos1', 'pos1']) 163 | self.assertRaises(ValueError, generate_python_code, ['duckargs', '-a', '--aye', '6', '-b', '--aye']) 164 | 165 | def test_longopt_without_shortopt(self): 166 | self.assertRaises(ValueError, generate_python_code, ['duckargs', '--a']) 167 | self.assertRaises(ValueError, generate_python_code, ['duckargs', '-a', '--apple', '3', '--ya']) 168 | self.assertRaises(ValueError, generate_python_code, ['duckargs', 'a', 'b', 'c', '--apple']) 169 | 170 | def test_shortopt_too_long(self): 171 | self.assertRaises(ValueError, generate_python_code, ['duckargs', '-rr']) 172 | self.assertRaises(ValueError, generate_python_code, ['duckargs', '-a', '--apple', '3', '-ya']) 173 | self.assertRaises(ValueError, generate_python_code, ['duckargs', 'a', 'b', 'c', '-apple']) 174 | --------------------------------------------------------------------------------