config-file


Nameconfig-file JSON
Version 0.13.1 PyPI version JSON
download
home_pagehttps://github.com/eugenetriguba/config_file
SummaryModify configuration files of various formats with the same simple API.
upload_time2023-11-05 20:56:14
maintainer
docs_urlNone
authorEugene Triguba
requires_python>=3.6,<4.0
license
keywords configuration ini json toml yaml
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage
            # Config File

> Simple manipulation of ini, json, yaml, or toml files

![Python Version](https://img.shields.io/pypi/pyversions/config-file.svg)
[![Version](https://img.shields.io/pypi/v/config-file)](https://pypi.org/project/config-file/)
[![Style](https://img.shields.io/badge/code%20style-black-000000.svg)](https://pypi.org/project/black/)
[![Build Status](https://github.com/eugenetriguba/config-file/actions/workflows/python-package-ci.yml/badge.svg)](https://github.com/eugenetriguba/config-file/actions/workflows/python-package-ci.yml)
[![Codecov](https://codecov.io/gh/eugenetriguba/config-file/graph/badge.svg)](https://codecov.io/gh/eugenetriguba/config-file)

## About Config File

The Config File project is designed to allow you to easily manipulate your
configuration files with the same simple API whether they are in INI, JSON,
YAML, or TOML.

## Installation

Config File is available to download through PyPI.

```bash
$ pip install config-file
```

### Installing Extras

If you want to manipulate YAML and TOML, you'll want to download the extras as well.

```bash
$ pip install config-file[yaml, toml]
```

You can also use [Poetry](https://python-poetry.org).

```bash
$ poetry install config-file -E yaml -E toml
```

## Usage

For this overview, let's say you have the following `ini` file
you want to manipulate.

Do note, however, that the `ini` format is the oddest format that
`ConfigFile` supports in that it has no formal specification and is
not type aware. When retrieving items from the file, it will return
them as strings by default. Others are more type aware and do not
require as much type coercion.

```ini
[section]
num_key = 5
str_key = blah
bool_key = true
list_key = [1, 2]

[second_section]
dict_key = { "another_num": 5 }
```

It must have a `.ini` extension in order
for the package to recognize it and use the correct parser for it.

### Setting up ConfigFile

To use the package, we import in the `ConfigFile` object. This object
is the only thing considered to be part of the public API.

We can set it up by giving it a string or `pathlib.Path` as the argument.
Any home tildes `~` in the string or `Path` are recognized and converted
to the full path for us.

```python
from config_file import ConfigFile

config = ConfigFile("~/some-project/config.ini")
```

#### Handling ConfigFile Initialization Errors

```python
from config_file import ConfigFile, ParsingError

try:
    config = ConfigFile("~/some-file.ini")
except ParsingError:
    print("could not parse the file")
except ValueError:
    print("extension that isn't supported was used or is a directory")
except FileNotFoundError:
    print("file does not exist")
```

### Using `get()`

A recurring pattern you'll see here is that all methods that
need to specify something inside your configuration file will
do so using a dot syntax.

#### Retrieving keys and sections

So to retrieve our `num_key`, we'd specify the heading and the
key separated by a dot. All values will then be retrieved as
strings.

```python
config.get('section.num_key')
>>> '5'
```

While we can retrieves keys, we can also retrieve the entire
section, which will be returned back to us as a dictionary.

```python
config.get('section')
>>> {'num_key': '5', 'str_key': 'blah', 'bool_key': 'true', 'list_key': '[1, 2]'}
```

Furthermore, you can also index into the `ConfigFile` object
to retrieve keys if that is preferred.

```python
config['section']['num_key']
>>> '5'
```

#### Coercing the return types

However, some of these keys are obviously not strings natively.
If we are retrieving a particular value of a key, we may want to
coerce it right away without doing clunky type conversions after
each time we retrieve a value. To do this, we can utilize the
`return_type` keyword argument.

```python
config.get('section.num_key', return_type=int)
>>> 5
```

Sometimes we don't have structures quite that simple though. What
if we wanted all the values in `section` coerced? For that, we can
utilize a `parse_types` keyword argument.

```python
config.get('section', parse_types=True)
>>> {'num_key': 5, 'str_key': 'blah', 'bool_key': True, 'list_key': [1, 2]}
```

It also works for regular keys.

```python
config.get('section.num_key', parse_types=True)
>>> 5
```

#### Handling non-existent keys

Sometimes we want to retrieve a key but are unsure of if it will exist.
There are two ways we could handle that.

The first is the one we're used to seeing: catch the error.

```python
try:
    important_value = config.get('section.i_do_not_exist')
except KeyError:
    important_value = 42
```

However, the `get` method comes with a `default` keyword argument that we
can utilze for this purpose.

```python
config.get('section.i_do_not_exist', default=42)
>>> 42
```

This can be handy if you have a default for a particular configuration value.

### Using `set()`

We can use `set()` to set a existing key's value.

```python
config.set('section.num_key', 6)
```

The method does not return anything, since there is nothing
useful to return. If something goes wrong where it is unable to set
the value, an exception will be raised instead. This is the case
for most methods on `ConfigFile`, such as `delete()` or `save()`,
where there would be no useful return value to utilize.

With `set()`, we can also create and set keys that don't exist yet.

```python
config.set('new_section.new_key', 'New key value!')
```

Would then result in the following section being added to our original file:

```ini
[new_section]
new_key = New key value!
```

The exact behavior of how these new keys or sections are added are a bit
dependent on the file format we're using, since every format is a little
different in it's structure and in what it supports. Mostly though, `ini`
is just the odd one.

If we try the following in `ini`, which does not support subsections or
nested keys, we simply get a single section.

```python
config.set("section.sub_section.sub_sub_section.key", 5)
```

```ini
[section.sub_section.sub_sub_section]
key = 5
```

Lastly, we can set values using an array notation as well. The underlying
content is all manipulated as a dictionary for every file type. If we wanted
to create a new section, we'd simply set it to be an empty dictionary.

```python
config['new_section'] = {}
```

Which would result to be an empty section:

```ini
[new_section]
```

### Using `delete()`

`delete()` allows us to delete entire sections or specific keys.

```python
config.delete('section')
```

Would result in the entire section being removed from our configuration file.
However, we can also just delete a single key.

```python
config.delete('section.num_key')
```

We can also use the array notation here as well.

```python
del config['section']['num_key']
```

### Using `has()`

`has()` allows us to check whether a given key exists in our file. There
are two ways to use `has()`.

The first is using the dot syntax.

```python
config.has('section.str_key')
>>> True
config.has('does_not_exist')
>>> False
```

This will check if our specific key or section exists. However, we can
also check in general if a given key or sections exists anywhere in our
file with the `wild` keyword argument.

```python
config.has('str_key', wild=True)
>>> True
```

### Using `save()`

For any changes we make to our configuration file, they are not written out
to the filesystem until we call `save()`. This is to avoid unnecessary write
calls after each operation until we actually need to save.

```python
config.delete('section.list_key')
config.save()
```

### Stringifying our ConfigFile

To retrieve the file as a string, with any changes we've made, we can use the
built-in `str()` method on the ConfigFile. This will always show us our latest changes since it is stringify-ing our internal representation of the configuration file, not just the file we've read in.

```python
str(config)
>>> '[section]\nnum_key = 5\nstr_key = blah\nbool_key = true\nlist_key = [1, 2]\n\n[second_section]\ndict_key = { "another_num": 5 }\n\n'
```

### Using `restore_original()`

If we have a initial configuration file state, we could keep a copy of that
initial file and restore back to it whenever needed using `restore_original()`.

By default, if we created our `ConfigFile` object with the path of `~/some-project/config.ini`,
`restore_original()` will look for our original file at `~/some-project/config.original.ini`.

```python
config.restore_original()
```

However, if we have a specific path elsewhere that this original configuration file is or it
is named differently than what the default expects, we can utilize the `original_path`
keyword argument.

```python
config.restore_original(original_path="~/some-project/original-configs/config.ini")
```


## Format Versions Supported

| Format  | Specification version supported |
| ------------- | ------------- |
| INI  | No official specification. |
| JSON  | [RFC 7159](https://tools.ietf.org/html/rfc7159.html)  |
| YAML  | [v1.2](https://yaml.org/spec/1.2/spec.html)  |
| TOML  | [v1.0.0-rc.1](https://github.com/toml-lang/toml/releases/tag/v1.0.0-rc.1)  |

For `ini` and `json`, Python's standard library modules are used.
Regarding `ini`, there is no formal specification so the syntax that configparser
supports is what is supported here.

## License

The [MIT](https://github.com/eugenetriguba/config-file/blob/main/LICENSE) License.


            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/eugenetriguba/config_file",
    "name": "config-file",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.6,<4.0",
    "maintainer_email": "",
    "keywords": "configuration,ini,json,toml,yaml",
    "author": "Eugene Triguba",
    "author_email": "eugenetriguba@gmail.com",
    "download_url": "https://files.pythonhosted.org/packages/30/5c/4046f33205e7751475b72d4eb0db95bfe1e6a8ed3ff9fc35af296bbe3da2/config_file-0.13.1.tar.gz",
    "platform": null,
    "description": "# Config File\n\n> Simple manipulation of ini, json, yaml, or toml files\n\n![Python Version](https://img.shields.io/pypi/pyversions/config-file.svg)\n[![Version](https://img.shields.io/pypi/v/config-file)](https://pypi.org/project/config-file/)\n[![Style](https://img.shields.io/badge/code%20style-black-000000.svg)](https://pypi.org/project/black/)\n[![Build Status](https://github.com/eugenetriguba/config-file/actions/workflows/python-package-ci.yml/badge.svg)](https://github.com/eugenetriguba/config-file/actions/workflows/python-package-ci.yml)\n[![Codecov](https://codecov.io/gh/eugenetriguba/config-file/graph/badge.svg)](https://codecov.io/gh/eugenetriguba/config-file)\n\n## About Config File\n\nThe Config File project is designed to allow you to easily manipulate your\nconfiguration files with the same simple API whether they are in INI, JSON,\nYAML, or TOML.\n\n## Installation\n\nConfig File is available to download through PyPI.\n\n```bash\n$ pip install config-file\n```\n\n### Installing Extras\n\nIf you want to manipulate YAML and TOML, you'll want to download the extras as well.\n\n```bash\n$ pip install config-file[yaml, toml]\n```\n\nYou can also use [Poetry](https://python-poetry.org).\n\n```bash\n$ poetry install config-file -E yaml -E toml\n```\n\n## Usage\n\nFor this overview, let's say you have the following `ini` file\nyou want to manipulate.\n\nDo note, however, that the `ini` format is the oddest format that\n`ConfigFile` supports in that it has no formal specification and is\nnot type aware. When retrieving items from the file, it will return\nthem as strings by default. Others are more type aware and do not\nrequire as much type coercion.\n\n```ini\n[section]\nnum_key = 5\nstr_key = blah\nbool_key = true\nlist_key = [1, 2]\n\n[second_section]\ndict_key = { \"another_num\": 5 }\n```\n\nIt must have a `.ini` extension in order\nfor the package to recognize it and use the correct parser for it.\n\n### Setting up ConfigFile\n\nTo use the package, we import in the `ConfigFile` object. This object\nis the only thing considered to be part of the public API.\n\nWe can set it up by giving it a string or `pathlib.Path` as the argument.\nAny home tildes `~` in the string or `Path` are recognized and converted\nto the full path for us.\n\n```python\nfrom config_file import ConfigFile\n\nconfig = ConfigFile(\"~/some-project/config.ini\")\n```\n\n#### Handling ConfigFile Initialization Errors\n\n```python\nfrom config_file import ConfigFile, ParsingError\n\ntry:\n    config = ConfigFile(\"~/some-file.ini\")\nexcept ParsingError:\n    print(\"could not parse the file\")\nexcept ValueError:\n    print(\"extension that isn't supported was used or is a directory\")\nexcept FileNotFoundError:\n    print(\"file does not exist\")\n```\n\n### Using `get()`\n\nA recurring pattern you'll see here is that all methods that\nneed to specify something inside your configuration file will\ndo so using a dot syntax.\n\n#### Retrieving keys and sections\n\nSo to retrieve our `num_key`, we'd specify the heading and the\nkey separated by a dot. All values will then be retrieved as\nstrings.\n\n```python\nconfig.get('section.num_key')\n>>> '5'\n```\n\nWhile we can retrieves keys, we can also retrieve the entire\nsection, which will be returned back to us as a dictionary.\n\n```python\nconfig.get('section')\n>>> {'num_key': '5', 'str_key': 'blah', 'bool_key': 'true', 'list_key': '[1, 2]'}\n```\n\nFurthermore, you can also index into the `ConfigFile` object\nto retrieve keys if that is preferred.\n\n```python\nconfig['section']['num_key']\n>>> '5'\n```\n\n#### Coercing the return types\n\nHowever, some of these keys are obviously not strings natively.\nIf we are retrieving a particular value of a key, we may want to\ncoerce it right away without doing clunky type conversions after\neach time we retrieve a value. To do this, we can utilize the\n`return_type` keyword argument.\n\n```python\nconfig.get('section.num_key', return_type=int)\n>>> 5\n```\n\nSometimes we don't have structures quite that simple though. What\nif we wanted all the values in `section` coerced? For that, we can\nutilize a `parse_types` keyword argument.\n\n```python\nconfig.get('section', parse_types=True)\n>>> {'num_key': 5, 'str_key': 'blah', 'bool_key': True, 'list_key': [1, 2]}\n```\n\nIt also works for regular keys.\n\n```python\nconfig.get('section.num_key', parse_types=True)\n>>> 5\n```\n\n#### Handling non-existent keys\n\nSometimes we want to retrieve a key but are unsure of if it will exist.\nThere are two ways we could handle that.\n\nThe first is the one we're used to seeing: catch the error.\n\n```python\ntry:\n    important_value = config.get('section.i_do_not_exist')\nexcept KeyError:\n    important_value = 42\n```\n\nHowever, the `get` method comes with a `default` keyword argument that we\ncan utilze for this purpose.\n\n```python\nconfig.get('section.i_do_not_exist', default=42)\n>>> 42\n```\n\nThis can be handy if you have a default for a particular configuration value.\n\n### Using `set()`\n\nWe can use `set()` to set a existing key's value.\n\n```python\nconfig.set('section.num_key', 6)\n```\n\nThe method does not return anything, since there is nothing\nuseful to return. If something goes wrong where it is unable to set\nthe value, an exception will be raised instead. This is the case\nfor most methods on `ConfigFile`, such as `delete()` or `save()`,\nwhere there would be no useful return value to utilize.\n\nWith `set()`, we can also create and set keys that don't exist yet.\n\n```python\nconfig.set('new_section.new_key', 'New key value!')\n```\n\nWould then result in the following section being added to our original file:\n\n```ini\n[new_section]\nnew_key = New key value!\n```\n\nThe exact behavior of how these new keys or sections are added are a bit\ndependent on the file format we're using, since every format is a little\ndifferent in it's structure and in what it supports. Mostly though, `ini`\nis just the odd one.\n\nIf we try the following in `ini`, which does not support subsections or\nnested keys, we simply get a single section.\n\n```python\nconfig.set(\"section.sub_section.sub_sub_section.key\", 5)\n```\n\n```ini\n[section.sub_section.sub_sub_section]\nkey = 5\n```\n\nLastly, we can set values using an array notation as well. The underlying\ncontent is all manipulated as a dictionary for every file type. If we wanted\nto create a new section, we'd simply set it to be an empty dictionary.\n\n```python\nconfig['new_section'] = {}\n```\n\nWhich would result to be an empty section:\n\n```ini\n[new_section]\n```\n\n### Using `delete()`\n\n`delete()` allows us to delete entire sections or specific keys.\n\n```python\nconfig.delete('section')\n```\n\nWould result in the entire section being removed from our configuration file.\nHowever, we can also just delete a single key.\n\n```python\nconfig.delete('section.num_key')\n```\n\nWe can also use the array notation here as well.\n\n```python\ndel config['section']['num_key']\n```\n\n### Using `has()`\n\n`has()` allows us to check whether a given key exists in our file. There\nare two ways to use `has()`.\n\nThe first is using the dot syntax.\n\n```python\nconfig.has('section.str_key')\n>>> True\nconfig.has('does_not_exist')\n>>> False\n```\n\nThis will check if our specific key or section exists. However, we can\nalso check in general if a given key or sections exists anywhere in our\nfile with the `wild` keyword argument.\n\n```python\nconfig.has('str_key', wild=True)\n>>> True\n```\n\n### Using `save()`\n\nFor any changes we make to our configuration file, they are not written out\nto the filesystem until we call `save()`. This is to avoid unnecessary write\ncalls after each operation until we actually need to save.\n\n```python\nconfig.delete('section.list_key')\nconfig.save()\n```\n\n### Stringifying our ConfigFile\n\nTo retrieve the file as a string, with any changes we've made, we can use the\nbuilt-in `str()` method on the ConfigFile. This will always show us our latest changes since it is stringify-ing our internal representation of the configuration file, not just the file we've read in.\n\n```python\nstr(config)\n>>> '[section]\\nnum_key = 5\\nstr_key = blah\\nbool_key = true\\nlist_key = [1, 2]\\n\\n[second_section]\\ndict_key = { \"another_num\": 5 }\\n\\n'\n```\n\n### Using `restore_original()`\n\nIf we have a initial configuration file state, we could keep a copy of that\ninitial file and restore back to it whenever needed using `restore_original()`.\n\nBy default, if we created our `ConfigFile` object with the path of `~/some-project/config.ini`,\n`restore_original()` will look for our original file at `~/some-project/config.original.ini`.\n\n```python\nconfig.restore_original()\n```\n\nHowever, if we have a specific path elsewhere that this original configuration file is or it\nis named differently than what the default expects, we can utilize the `original_path`\nkeyword argument.\n\n```python\nconfig.restore_original(original_path=\"~/some-project/original-configs/config.ini\")\n```\n\n\n## Format Versions Supported\n\n| Format  | Specification version supported |\n| ------------- | ------------- |\n| INI  | No official specification. |\n| JSON  | [RFC 7159](https://tools.ietf.org/html/rfc7159.html)  |\n| YAML  | [v1.2](https://yaml.org/spec/1.2/spec.html)  |\n| TOML  | [v1.0.0-rc.1](https://github.com/toml-lang/toml/releases/tag/v1.0.0-rc.1)  |\n\nFor `ini` and `json`, Python's standard library modules are used.\nRegarding `ini`, there is no formal specification so the syntax that configparser\nsupports is what is supported here.\n\n## License\n\nThe [MIT](https://github.com/eugenetriguba/config-file/blob/main/LICENSE) License.\n\n",
    "bugtrack_url": null,
    "license": "",
    "summary": "Modify configuration files of various formats with the same simple API.",
    "version": "0.13.1",
    "project_urls": {
        "Documentation": "https://config-file.readthedocs.io/",
        "Homepage": "https://github.com/eugenetriguba/config_file",
        "Repository": "https://github.com/eugenetriguba/config_file"
    },
    "split_keywords": [
        "configuration",
        "ini",
        "json",
        "toml",
        "yaml"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "c28dfc9d115061b41f8f4cd9fafe6ed5dd05a62d185739321d65e615a9b951fb",
                "md5": "2a80054568c7f643e8ad8650812a9f03",
                "sha256": "aef074f8b91a413c6fc4ce51e7206baf6963b3662bcb9ae48c0eaba464fda813"
            },
            "downloads": -1,
            "filename": "config_file-0.13.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "2a80054568c7f643e8ad8650812a9f03",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.6,<4.0",
            "size": 20705,
            "upload_time": "2023-11-05T20:56:13",
            "upload_time_iso_8601": "2023-11-05T20:56:13.340366Z",
            "url": "https://files.pythonhosted.org/packages/c2/8d/fc9d115061b41f8f4cd9fafe6ed5dd05a62d185739321d65e615a9b951fb/config_file-0.13.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "305c4046f33205e7751475b72d4eb0db95bfe1e6a8ed3ff9fc35af296bbe3da2",
                "md5": "dc4effacded4428eb97e5b4c7eeefcce",
                "sha256": "dd0b00c0f38ef5754d2ce7dc242db90fba21e8f1e9bbc8fa939d8e9bf22c8133"
            },
            "downloads": -1,
            "filename": "config_file-0.13.1.tar.gz",
            "has_sig": false,
            "md5_digest": "dc4effacded4428eb97e5b4c7eeefcce",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.6,<4.0",
            "size": 31969,
            "upload_time": "2023-11-05T20:56:14",
            "upload_time_iso_8601": "2023-11-05T20:56:14.665197Z",
            "url": "https://files.pythonhosted.org/packages/30/5c/4046f33205e7751475b72d4eb0db95bfe1e6a8ed3ff9fc35af296bbe3da2/config_file-0.13.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-11-05 20:56:14",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "eugenetriguba",
    "github_project": "config_file",
    "travis_ci": false,
    "coveralls": true,
    "github_actions": true,
    "lcname": "config-file"
}
        
Elapsed time: 0.13702s