arg-init


Namearg-init JSON
Version 0.0.5 PyPI version JSON
download
home_page
SummaryResolve argument values from arg, env or default values
upload_time2023-11-03 22:17:38
maintainer
docs_urlNone
author
requires_python>=3.8
licenseMIT
keywords argparse init environment
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Overview

[![tests][tests_badge]][tests_url]
[![codecov][codecov_badge]][codecov_url]
[![Docs][docs_badge]][docs_url]
[![PyPI][pypi_badge]][pypi_url]
[![PyPI - License][license_badge]][license_url]

When running code there is often a need to initialise arguments either directly from a passed in value, indirectly via an environment variable or a via default value. Argparse provides this functionality (or can be easily augmented to) already but has one major drawback; It does not work when the code is invoked as a library.

arg_init provides functionality to resolve argument values for a given function/method from either an environment variable, an argument value or a default value. Introspection is used to determine the arguments of the calling function, and a dictionary is created of resolved values for each argument. Resolved values are determined using either Environment Priority (default) or Argument Priority.

When resolving from an environment variable, the environment variable name is assumed to be the same as the argument name, in uppercase e.g. An argument, arg1 would resolve from an environment variable, "ARG1". This behaviour can be modified by providing a custom env_name via argument defaults or by setting an env_prefix.

If the calling function is a class method, arguments may also be made available as class attributes. See reference for more details.

Because it is implemented in the application, it will work if called via a CLI script or as a library by another python program.

**arg_init** provides two classes; ClassArgInit and FunctionArgInit for initialising arguments of bound class functions and unbound functions respectively. These classes iterate over all arguments of the calling function, exposing a dictionary containing key/value pairs of argument name, with values assigned according to the priority system selected.

## Notes

ArgInit uses introspection (via the [inspect](https://docs.python.org/3/library/inspect.html) module) to determine function arguments and values. Its use is minimal and is only executed once at startup so performance should not be an issue.

It is not practical to dynamically determine if the function to be processed is a bound function (a class method, with a class reference (self) as the first parameter) or an unbound function (a simple function), so selection is determined by the use of the called class: ClassArgInit of FunctionArgInit.

Fucntionality is identical for both implementations, with the following exception:

ClassArgInit:

- Class attributes may be set that represent the resolved argument values

If ArgumentParser is used to create a CLI for an application then default values should **not** be assigned in add_argument(). This is to prevent different behaviours between launching as a CLI and an imported library. What happens is that ArgumentParser will provide values for all arguments that have a default assigned. This effectively renders default values in the called function redundant as a value is always provided, even if the value is None.

## Priority

The argument value is set when a non **None** value is found, or all options are exhausted. At this point the argument is set to None.

What priority should be used to set an argument?

### Argument Priority Order

If passed in arguments have priorty over environment variables.

1. Arg
2. Env
3. Default

And if the function has a non **None** default argument e.g. f(a=1), then the argument value will always be used to set the value, never allowing an env value to take effect.

There are two obvious solutions to this:

1. Change the priority order.
2. Provide an alternate means to specify a default value. If a default value is required in the function signature, to allow ommission of an argument when calling, ensure it is set to None.

### Env Priority Order

Environment variables have prioirty over passed in arguments.

1. Env
2. Arg
3. Default

This allows use of the standard default argument values for a python function if no env is defined.

**ArgInit** supports both priority models.
This becomes a personal choice, and behaviour can be chosen at implementation/run time. Default priority order is: **Env Priority**.

## Usage

### Simple Useage

The following examples show how to use arg_init to initialise a class or function, with a default value assigned to the argument.

```python
from arg_init import ClassArgInit

class MyApp:
    def __init__(self, arg1=10):
        ClassArgInit()
        ...
```

```python
from arg_init import FunctionArgInit

def func(arg1=10):
    FunctionArgInit()
    ...
```

In the examples above, arg1 will be initialised with the value from the environment variable "ARG1" if set, else it will take the passed in value. Finally it will have a default value of None assigned.

As these examples use the default priority sytem: ENV_PRIORITY, standard python function defaults can be used in the function signature.

### Other use cases

The example below shows how to change the environment variable name used to initialise the argument using ArgDefaults.

```python
from arg_init import FunctionArgInit, ArgDefaults

def func(arg1=None):
    arg1_defaults = ArgDefaults(env_name="TEST")
    args = FunctionArgInit(defaults={"arg1": arg1_defaults}).args
    ...
```

The example below shows how to use argument priority when resolving the values of arguments.

```python
from arg_init import FunctionArgInit, ARG_PRIOIRITY, ArgDefaults

def func(arg1=None):
    arg1_defaults = ArgDefaults(default_value=1)
    args = FunctionArgInit(priority=ARG_PRIORITY, defaults={"arg1": arg1_defaults}).args
    ...
```

Note:
As this example uses argument priority, a default **must** be provided via ArgDefaults.

### Recommendation

To avoid namespace clashes with environment variables, it is recommneded to always supply an env_prefix argument when initialising ClassArgInit/FunctionArgInit. All environment variables are expected to have this prefix e.g. with an env_prefix of "myapp", arg1 would map to the environment variable "MYAPP_ARG1".

```python
from arg_init import ClassArgInit

class MyApp:
    def __init__(self, arg1=None):
        args = ClassArgInit(env_prefix="myapp").args
        ...
```

Note:
In this example, arg1 would resolve against the environment variable "MYAPP_ARG1".

Please see the [documentation](https://srfoster65.github.io/arg_init/) for further details on usage.

[tests_badge]: https://github.com/srfoster65/arg_init/actions/workflows/build.yml/badge.svg
[tests_url]: https://github.com/srfoster65/arg_init/actions/workflows/build.yml
[codecov_badge]: https://codecov.io/gh/srfoster65/arg_init/graph/badge.svg?token=FFNWSCS4BB
[codecov_url]: https://codecov.io/gh/srfoster65/arg_init
[docs_badge]: https://github.com/srfoster65/arg_init/actions/workflows/docs.yml/badge.svg
[docs_url]: https://srfoster65.github.io/arg_init/
[pypi_badge]: https://img.shields.io/pypi/v/arg-init?logo=python&logoColor=%23cccccc
[pypi_url]: https://pypi.org/project/arg-init
[license_badge]: https://img.shields.io/pypi/l/arg-init
[license_url]: https://srfoster65.github.io/arg-init/license/

            

Raw data

            {
    "_id": null,
    "home_page": "",
    "name": "arg-init",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": "",
    "keywords": "argparse init environment",
    "author": "",
    "author_email": "srfoster65 <135555068+srfoster65@users.noreply.github.com>",
    "download_url": "https://files.pythonhosted.org/packages/b0/f6/48c8a85d03c0c4186e653e4bca29073681abf00b2aef23f831ca029c2169/arg_init-0.0.5.tar.gz",
    "platform": null,
    "description": "# Overview\n\n[![tests][tests_badge]][tests_url]\n[![codecov][codecov_badge]][codecov_url]\n[![Docs][docs_badge]][docs_url]\n[![PyPI][pypi_badge]][pypi_url]\n[![PyPI - License][license_badge]][license_url]\n\nWhen running code there is often a need to initialise arguments either directly from a passed in value, indirectly via an environment variable or a via default value. Argparse provides this functionality (or can be easily augmented to) already but has one major drawback; It does not work when the code is invoked as a library.\n\narg_init provides functionality to resolve argument values for a given function/method from either an environment variable, an argument value or a default value. Introspection is used to determine the arguments of the calling function, and a dictionary is created of resolved values for each argument. Resolved values are determined using either Environment Priority (default) or Argument Priority.\n\nWhen resolving from an environment variable, the environment variable name is assumed to be the same as the argument name, in uppercase e.g. An argument, arg1 would resolve from an environment variable, \"ARG1\". This behaviour can be modified by providing a custom env_name via argument defaults or by setting an env_prefix.\n\nIf the calling function is a class method, arguments may also be made available as class attributes. See reference for more details.\n\nBecause it is implemented in the application, it will work if called via a CLI script or as a library by another python program.\n\n**arg_init** provides two classes; ClassArgInit and FunctionArgInit for initialising arguments of bound class functions and unbound functions respectively. These classes iterate over all arguments of the calling function, exposing a dictionary containing key/value pairs of argument name, with values assigned according to the priority system selected.\n\n## Notes\n\nArgInit uses introspection (via the [inspect](https://docs.python.org/3/library/inspect.html) module) to determine function arguments and values. Its use is minimal and is only executed once at startup so performance should not be an issue.\n\nIt is not practical to dynamically determine if the function to be processed is a bound function (a class method, with a class reference (self) as the first parameter) or an unbound function (a simple function), so selection is determined by the use of the called class: ClassArgInit of FunctionArgInit.\n\nFucntionality is identical for both implementations, with the following exception:\n\nClassArgInit:\n\n- Class attributes may be set that represent the resolved argument values\n\nIf ArgumentParser is used to create a CLI for an application then default values should **not** be assigned in add_argument(). This is to prevent different behaviours between launching as a CLI and an imported library. What happens is that ArgumentParser will provide values for all arguments that have a default assigned. This effectively renders default values in the called function redundant as a value is always provided, even if the value is None.\n\n## Priority\n\nThe argument value is set when a non **None** value is found, or all options are exhausted. At this point the argument is set to None.\n\nWhat priority should be used to set an argument?\n\n### Argument Priority Order\n\nIf passed in arguments have priorty over environment variables.\n\n1. Arg\n2. Env\n3. Default\n\nAnd if the function has a non **None** default argument e.g. f(a=1), then the argument value will always be used to set the value, never allowing an env value to take effect.\n\nThere are two obvious solutions to this:\n\n1. Change the priority order.\n2. Provide an alternate means to specify a default value. If a default value is required in the function signature, to allow ommission of an argument when calling, ensure it is set to None.\n\n### Env Priority Order\n\nEnvironment variables have prioirty over passed in arguments.\n\n1. Env\n2. Arg\n3. Default\n\nThis allows use of the standard default argument values for a python function if no env is defined.\n\n**ArgInit** supports both priority models.\nThis becomes a personal choice, and behaviour can be chosen at implementation/run time. Default priority order is: **Env Priority**.\n\n## Usage\n\n### Simple Useage\n\nThe following examples show how to use arg_init to initialise a class or function, with a default value assigned to the argument.\n\n```python\nfrom arg_init import ClassArgInit\n\nclass MyApp:\n    def __init__(self, arg1=10):\n        ClassArgInit()\n        ...\n```\n\n```python\nfrom arg_init import FunctionArgInit\n\ndef func(arg1=10):\n    FunctionArgInit()\n    ...\n```\n\nIn the examples above, arg1 will be initialised with the value from the environment variable \"ARG1\" if set, else it will take the passed in value. Finally it will have a default value of None assigned.\n\nAs these examples use the default priority sytem: ENV_PRIORITY, standard python function defaults can be used in the function signature.\n\n### Other use cases\n\nThe example below shows how to change the environment variable name used to initialise the argument using ArgDefaults.\n\n```python\nfrom arg_init import FunctionArgInit, ArgDefaults\n\ndef func(arg1=None):\n    arg1_defaults = ArgDefaults(env_name=\"TEST\")\n    args = FunctionArgInit(defaults={\"arg1\": arg1_defaults}).args\n    ...\n```\n\nThe example below shows how to use argument priority when resolving the values of arguments.\n\n```python\nfrom arg_init import FunctionArgInit, ARG_PRIOIRITY, ArgDefaults\n\ndef func(arg1=None):\n    arg1_defaults = ArgDefaults(default_value=1)\n    args = FunctionArgInit(priority=ARG_PRIORITY, defaults={\"arg1\": arg1_defaults}).args\n    ...\n```\n\nNote:\nAs this example uses argument priority, a default **must** be provided via ArgDefaults.\n\n### Recommendation\n\nTo avoid namespace clashes with environment variables, it is recommneded to always supply an env_prefix argument when initialising ClassArgInit/FunctionArgInit. All environment variables are expected to have this prefix e.g. with an env_prefix of \"myapp\", arg1 would map to the environment variable \"MYAPP_ARG1\".\n\n```python\nfrom arg_init import ClassArgInit\n\nclass MyApp:\n    def __init__(self, arg1=None):\n        args = ClassArgInit(env_prefix=\"myapp\").args\n        ...\n```\n\nNote:\nIn this example, arg1 would resolve against the environment variable \"MYAPP_ARG1\".\n\nPlease see the [documentation](https://srfoster65.github.io/arg_init/) for further details on usage.\n\n[tests_badge]: https://github.com/srfoster65/arg_init/actions/workflows/build.yml/badge.svg\n[tests_url]: https://github.com/srfoster65/arg_init/actions/workflows/build.yml\n[codecov_badge]: https://codecov.io/gh/srfoster65/arg_init/graph/badge.svg?token=FFNWSCS4BB\n[codecov_url]: https://codecov.io/gh/srfoster65/arg_init\n[docs_badge]: https://github.com/srfoster65/arg_init/actions/workflows/docs.yml/badge.svg\n[docs_url]: https://srfoster65.github.io/arg_init/\n[pypi_badge]: https://img.shields.io/pypi/v/arg-init?logo=python&logoColor=%23cccccc\n[pypi_url]: https://pypi.org/project/arg-init\n[license_badge]: https://img.shields.io/pypi/l/arg-init\n[license_url]: https://srfoster65.github.io/arg-init/license/\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Resolve argument values from arg, env or default values",
    "version": "0.0.5",
    "project_urls": {
        "Documentation": "https://srfoster65.github.io/arg_init/",
        "Source": "https://github.com/srfoster65/arg_init"
    },
    "split_keywords": [
        "argparse",
        "init",
        "environment"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "7dafbf062606b569fa50a0825dfddeb5597779132e48d1acd03db019dc551336",
                "md5": "37da4ef04c2e0c3ea830a413aa6331d4",
                "sha256": "868dcd6e87219809a9657009847257bfae217959753af7daedf1a6cb1e4fef86"
            },
            "downloads": -1,
            "filename": "arg_init-0.0.5-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "37da4ef04c2e0c3ea830a413aa6331d4",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 9539,
            "upload_time": "2023-11-03T22:17:36",
            "upload_time_iso_8601": "2023-11-03T22:17:36.959691Z",
            "url": "https://files.pythonhosted.org/packages/7d/af/bf062606b569fa50a0825dfddeb5597779132e48d1acd03db019dc551336/arg_init-0.0.5-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "b0f648c8a85d03c0c4186e653e4bca29073681abf00b2aef23f831ca029c2169",
                "md5": "bf0d513ffc299ec00adf8b2c9a406f38",
                "sha256": "7de2a19e1aad3262ae0dbd5bc14f095c8f3c1a274e3488eedeb008eda46c00b8"
            },
            "downloads": -1,
            "filename": "arg_init-0.0.5.tar.gz",
            "has_sig": false,
            "md5_digest": "bf0d513ffc299ec00adf8b2c9a406f38",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 12304,
            "upload_time": "2023-11-03T22:17:38",
            "upload_time_iso_8601": "2023-11-03T22:17:38.395236Z",
            "url": "https://files.pythonhosted.org/packages/b0/f6/48c8a85d03c0c4186e653e4bca29073681abf00b2aef23f831ca029c2169/arg_init-0.0.5.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-11-03 22:17:38",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "srfoster65",
    "github_project": "arg_init",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "arg-init"
}
        
Elapsed time: 0.19487s