{ "info": { "author": "Yclept Nemo", "author_email": "orbisvicis@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)", "Operating System :: OS Independent", "Programming Language :: Python :: 3 :: Only", "Topic :: Software Development :: Libraries" ], "description": "===========\nEnvArgParse\n===========\n\nArgparse with environment variables. This module is both a derived `argparse`\nclass supporting environment variables and a framework or set of proposed\nenhancements for extending `argparse`.\n\nThe Example\n===========\n\nThe following is taken from the `__main__` conditional at the end of this module::\n\n parser = EnvArgParser\\\n ( prog=\"Test Program\"\n , formatter_class=EnvArgDefaultsHelpFormatter\n )\n\n parser.add_argument\\\n ( '--bar'\n , required=True\n , env_var=\"BAR\"\n , type=int\n , nargs=\"+\"\n , default=22\n , help=\"Help message for bar.\"\n )\n\n parser.add_argument\\\n ( 'baz'\n , type=int\n )\n\n args = parser.parse_args()\n print(args)\n\nExample output::\n\n $ BAR=\"1 2 3 '45 ' 6 7\" ./envargparse.py 123\n Namespace(bar=[1, 2, 3, 45, 6, 7], baz=123)\n\n::\n\n $ ./envargparse.py -h\n usage: Test Program [-h] --bar BAR [BAR ...] baz\n\n positional arguments:\n baz\n\n optional arguments:\n -h, --help show this help message and exit\n --bar BAR [BAR ...] Help message for bar. (default: 22) (env_var: BAR)\n\n\nHow it Works\n============\n\nThis module tries to be very simple: it may look longer because it is well-commented. There is no code duplication. In fact version 0.1 (tagged) was even simpler: actions were tracked via the namespace argument of `parse_args()`, maintaining the precedence:\n\n cmd args > env var > preexisting namespace > defaults\n\nBasically it stores within the namespace a unique instance for each action with an environment variable key, if that variable exists. After `argparse` finishes parsing it executes the action associated with each remaining unique instance. The idea is this: if the action was provided via the command-line, it'll have been overwritten from the namespace.\n\nYou can still use that version if you want, but it doesn't handle actions which fail to set the `dest` attribute on the namespace. To be fair, such actions are exceedingly unlikely. For a while I considered sandboxing an empty namespace for each action and copying only the `dest` attribute back to the main namespace, but that would be artificially limiting. Instead...\n\nVersion 0.2 (tagged) is built around tracking actions. Whether an action has been seen as a command-line argument, and how often it has been called. Believe it or not, there are edge cases in which an action can be seen but not called. See the comments for more information.\n\nThe `EnvArgParser` class maintains the set of seen actions while every action, environment variable key or not, is embedded within a tracking object. Originally a function-local variable within the parsing function, the set of seen actions is lifted into the instance namespace and reset before each new parsing pass. Luckily there is a 1:1 call correspondence between seeing an action and calling `_get_values`, which now updates the set of seen actions. Each tracking object increments a counter whenever called and otherwise behaves exactly like the contained action, forwarding (almost) all attribute access.\n\nAn environment variable record, `EnvArgRecord`, is attached to each valid environment variable action whether or not that environment variable actually exists - for help-formatting reasons. Such actions will only be executed if the environment variable is available and the action hasn't already been seen or been called.\n\nAs for actually parsing the environment variable, that hasn't changed much between 0.1 and 0.2. Reflecting that the parsing function can be overridden per action, the default `env_var_parse` is a static method; the first argument is always the parser. The default mimics the parsing of `argparse` (counting, converting and checking values) without code duplication by relying on internal `ArgumentParser` methods. It's actually quite simple - just two method calls. The arguments are split using `shlex` but your custom parser can use `yaml`, `json`, or whatever else you prefer. Whatever the case, the parsing function must return a tuple: the resulting value(s) and a list of extra values. As extra values usually raise an exception you might consider leaving this empty.\n\nAnd that's basically it, aside the from the value-checking boilerplate. As of python 3.7 the newly introduced intermixed parsing methods perform two-pass parsing, forcing `EnvArgParser` to track the parsing \"depth\". The set of seen actions is only cleared before this two-pass process and the environment variables only parsed afterwards - both at the uppermost depth.\n\nThere is a help formatter which adds environment variable keys to the argument help, and an example mix-in class with `argparse.ArgumentDefaultsHelpFormatter` to add both environment variable keys and argument defaults. Custom help formatters can be created simply by inheriting from the appropriate base classes.\n\nWhat comes next is just icing on top of the cake. While it isn't actually used by this module nor does it change the default behavior it's a nice showcase for what's possible and perhaps useful for user-defined derived classes.\n\nThe original `ArgumentParser` is relatively monolithic while `EnvArgParser` uses cooperative OOP between action and parser to allow per-action overrides of parser methods. Whereas the parser would normally call its own methods it now calls into the action. By default the action calls the matching method from the parser's base class, so out-of-the-box there is no behavioral change. As a proof-of-concept this is only implemented for `_get_values` and `get_value` but if useful can be extended to all methods via a custom `__getattribute__`. In the meantime feel free to add additional methods or override the default behavior by deriving from `EnvArgParser` and `EnvArgAction`.\n\nThe Proposal\n============\n\nLet's step a bit back. Why can't you just use argument defaults::\n\n import argparse\n import os\n\n parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)\n parser.add_argument(\"--foo\", default=os.environ.get(\"FOO\",\"bar\"), help=\"FOO!\")\n parser.parse_args()\n\nThat creates an artificial dependency between the default and the environment. In other words, it's not what the user expects. Notice how the default doesn't stay constant::\n\n $ ./test.py -h\n usage: test.py [-h] [--foo FOO]\n\n optional arguments:\n -h, --help show this help message and exit\n --foo FOO FOO! (default: bar)\n\n::\n\n $ FOO=456 ./test.py -h\n usage: test.py [-h] [--foo FOO]\n\n optional arguments:\n -h, --help show this help message and exit\n --foo FOO FOO! (default: 456)\n\n\nUnfortunately `argparse` is too much of a black box to cleanly modify and therefore this module serves as a working roadmap for proposed improvements. The important points:\n\n* Be more transparent about parsing.\n\n Maintain parsing state, such as the set of seen actions, within the instance namespace rather than as inaccessible method variables. Track parsing depth during multistage parsing. Have the base `Action` class track how often it has been called. Mark core parsing methods as a part of the public API.\n\n* Make parsing more modular.\n\n Create re-usable entry-points (methods) for:\n\n * parsing individual optional arguments.\n * parsing individual positional arguments.\n * checking for conflicts based on argument values.\n * handling exceptions (as a decorator).\n\n Right now the code is too monolithic.\n\n* Don't force each argument to use the same parsing chain.\n\n Allow actions to overload important parser methods such as `_get_values`, `_get_value` or `_match_argument`.\n\nThe more trivial points:\n\n* Switch to new-style (`{}`) string formatting.\n\n Old-style (`%`) string formatting cannot access object attributes. The `_get_help_string` method is expected to return a format string which would be unable to access attributes of `EnvArgRecord`.\n\nThe Module\n==========\n\nThe code is well-commented, so here is a brief list of the provided classes:\n\n* `EnvArgRecord`\n* `EnvArgParser`\n* `EnvArgAction`\n* `EnvArgHelpFormatter`\n* `EnvArgDefaultsHelpFormatter`\n* `Container`\n\nRequirements\n============\n\n * Python 3.7+\n * module: `decorator` (`@PyPI`__)\n\n__ decoratorPyPI_\n\nLicense\n=======\n\n GPLv3+; see `LICENSE.txt`\n\nAuthor\n======\n\n Yclept Nemo \n\nLinks\n=====\n\n * `EnvArgParse@GitHub`__\n * `EnvArgParse@PyPI`__\n\n__ envargparseGitHub_\n__ envargparsePyPI_\n\n\n.. _decoratorPyPI: https://pypi.org/project/decorator/\n.. _decoratorGitHub: https://github.com/micheles/decorator\n\n.. _envargparsePyPI: https://pypi.python.org/pypi/envargparse/\n.. _envargparseGitHub: https://github.com/orbisvicis/envargparse\n\n\n", "description_content_type": "text/x-rst", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/orbisvicis/envargparse", "keywords": "", "license": "GPLv3+", "maintainer": "", "maintainer_email": "", "name": "envargparse", "package_url": "https://pypi.org/project/envargparse/", "platform": "", "project_url": "https://pypi.org/project/envargparse/", "project_urls": { "Homepage": "https://github.com/orbisvicis/envargparse" }, "release_url": "https://pypi.org/project/envargparse/1.0.0/", "requires_dist": [ "decorator" ], "requires_python": ">= 3.7", "summary": "Argparse with environment variables", "version": "1.0.0" }, "last_serial": 5432818, "releases": { "1.0.0": [ { "comment_text": "", "digests": { "md5": "6d0ebfb1768f304941beb9d9f51d2be3", "sha256": "3aa527897cd5bf03706420e9e3646dac70aa0b2f9997c67c49285615af5f21c6" }, "downloads": -1, "filename": "envargparse-1.0.0-py3-none-any.whl", "has_sig": false, "md5_digest": "6d0ebfb1768f304941beb9d9f51d2be3", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">= 3.7", "size": 21865, "upload_time": "2019-06-21T21:01:51", "url": "https://files.pythonhosted.org/packages/8d/38/b0ff0bb106fdbfa046cbb94ebf6099e7f873d864c47dffab65e351d47cca/envargparse-1.0.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "afb88afa461b4b828c1de079b49e07f6", "sha256": "f81ffc49c20f561dcd9ebe4c14f054da8b2f760a160569b5afc5743de5d9f26d" }, "downloads": -1, "filename": "envargparse-1.0.0.tar.gz", "has_sig": false, "md5_digest": "afb88afa461b4b828c1de079b49e07f6", "packagetype": "sdist", "python_version": "source", "requires_python": ">= 3.7", "size": 9756, "upload_time": "2019-06-21T21:01:53", "url": "https://files.pythonhosted.org/packages/12/cb/a4d43870fdf1356e934d953a0f9ae7f9868026d5bd0e48951bcb63e31709/envargparse-1.0.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "6d0ebfb1768f304941beb9d9f51d2be3", "sha256": "3aa527897cd5bf03706420e9e3646dac70aa0b2f9997c67c49285615af5f21c6" }, "downloads": -1, "filename": "envargparse-1.0.0-py3-none-any.whl", "has_sig": false, "md5_digest": "6d0ebfb1768f304941beb9d9f51d2be3", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">= 3.7", "size": 21865, "upload_time": "2019-06-21T21:01:51", "url": "https://files.pythonhosted.org/packages/8d/38/b0ff0bb106fdbfa046cbb94ebf6099e7f873d864c47dffab65e351d47cca/envargparse-1.0.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "afb88afa461b4b828c1de079b49e07f6", "sha256": "f81ffc49c20f561dcd9ebe4c14f054da8b2f760a160569b5afc5743de5d9f26d" }, "downloads": -1, "filename": "envargparse-1.0.0.tar.gz", "has_sig": false, "md5_digest": "afb88afa461b4b828c1de079b49e07f6", "packagetype": "sdist", "python_version": "source", "requires_python": ">= 3.7", "size": 9756, "upload_time": "2019-06-21T21:01:53", "url": "https://files.pythonhosted.org/packages/12/cb/a4d43870fdf1356e934d953a0f9ae7f9868026d5bd0e48951bcb63e31709/envargparse-1.0.0.tar.gz" } ] }