nanoconf 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,45 @@
1
+ name: "CodeQL"
2
+
3
+ on:
4
+ push:
5
+ branches: [master]
6
+ pull_request:
7
+ types: [opened, synchronize, reopened]
8
+ paths-ignore:
9
+ - "*.md"
10
+ - ".gitignore"
11
+
12
+ jobs:
13
+ analyze:
14
+ name: CodeQL Analysis
15
+ runs-on: ubuntu-latest
16
+
17
+ strategy:
18
+ fail-fast: false
19
+ matrix:
20
+ python-version: ["3.11", "3.12"]
21
+
22
+ steps:
23
+ - name: Checkout repository
24
+ uses: actions/checkout@v4
25
+
26
+ - name: Initialize CodeQL
27
+ uses: github/codeql-action/init@v2
28
+ with:
29
+ languages: ${{ matrix.language }}
30
+
31
+ - name: Perform CodeQL Analysis
32
+ uses: github/codeql-action/analyze@v2
33
+
34
+ - name: Setup Python
35
+ uses: actions/setup-python@v4
36
+ with:
37
+ python-version: ${{ matrix.python-version }}
38
+
39
+ - name: Pre Commit Checks
40
+ uses: pre-commit/action@v3.0.0
41
+
42
+ - name: Unit Tests
43
+ run: |
44
+ pip install -U pip .[dev]
45
+ pytest -v tests/
@@ -0,0 +1,35 @@
1
+ name: PythonPackage
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "*"
7
+
8
+ jobs:
9
+ publish:
10
+ name: Build and Deploy to PyPi
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ # build/push in lowest support python version
15
+ python-version: [ "3.11" ]
16
+
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - uses: actions/setup-python@v4
21
+ with:
22
+ python-version: ${{ matrix.python-version }}
23
+
24
+ - name: Setup and Build
25
+ run: |
26
+ pip install -U pip .[setup]
27
+ python -m build
28
+ python -m twine check dist/*
29
+
30
+ - name: Build and publish
31
+ uses: pypa/gh-action-pypi-publish@v1.8.10
32
+ with:
33
+ user: __token__
34
+ password: ${{ secrets.PYPI_TOKEN }}
35
+ skip_existing: true
@@ -0,0 +1,5 @@
1
+ venv*
2
+ build/
3
+ *.egg-info/
4
+ __pycache__/
5
+ .vscode/
@@ -0,0 +1,12 @@
1
+ # configuration for pre-commit git hooks
2
+ repos:
3
+ - repo: https://github.com/psf/black
4
+ rev: 23.3.0
5
+ hooks:
6
+ - id: black
7
+ - repo: https://github.com/astral-sh/ruff-pre-commit
8
+ # Ruff version.
9
+ rev: v0.0.277
10
+ hooks:
11
+ - id: ruff
12
+ args: [--fix, --exit-non-zero-on-fix]
nanoconf-1.0.0/LICENSE ADDED
@@ -0,0 +1,3 @@
1
+ Copyright (c) 2023 Jacob Callahan
2
+
3
+ Do what you want with this repo, but I'm not responsible.
@@ -0,0 +1,148 @@
1
+ Metadata-Version: 2.1
2
+ Name: nanoconf
3
+ Version: 1.0.0
4
+ Summary: The tiny opinionated config loader.
5
+ Author-email: Jacob J Callahan <jacob.callahan05@gmail.com>
6
+ Project-URL: Repository, https://github.com/JacobCallahan/nanoconf
7
+ Keywords: nanoconf,config,configuration,settings
8
+ Platform: any
9
+ Classifier: Development Status :: 5 - Production/Stable
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Natural Language :: English
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Requires-Python: >=3.11
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: python-box
19
+ Requires-Dist: pyyaml
20
+ Provides-Extra: dev
21
+ Requires-Dist: black; extra == "dev"
22
+ Requires-Dist: pre-commit; extra == "dev"
23
+ Requires-Dist: pytest; extra == "dev"
24
+ Requires-Dist: ruff; extra == "dev"
25
+ Provides-Extra: setup
26
+ Requires-Dist: build; extra == "setup"
27
+ Requires-Dist: setuptools; extra == "setup"
28
+ Requires-Dist: twine; extra == "setup"
29
+
30
+ NanoConf
31
+ ========
32
+ NanoConf is a tiny, opinionated, and easy to use configuration library for Python. It is designed to be used in small to medium sized projects where a full blown configuration library is overkill.
33
+
34
+ Installation
35
+ ------------
36
+ ```bash
37
+ pip install nanoconf
38
+ ```
39
+
40
+ Usage
41
+ -----
42
+ ```python
43
+ from nanoconf import NanoConf
44
+ # or if NanoConf if too long of a name
45
+ from nanoconf import NC
46
+
47
+ # Create a new configuration object
48
+ config = NanoConf("/path/to/config.nconf")
49
+
50
+ # Use the configuration object
51
+ print(config["key"])
52
+ # Since all values are also loaded as attributes, you can also do
53
+ print(config.key)
54
+ ```
55
+
56
+ Configuration File Format
57
+ -------------------------
58
+ NanoConf uses a simple configuration file format that is easy to read and write.
59
+ Each File is YAML formatted and contains a single top-level dictionary.
60
+ Even though the top-level must be a dictionary, you can nest dictionaries and lists as deep as you want.
61
+ Each config file also must have the .nconf extension. This ensures that NanoConf will only load files that are meant to be configuration files.
62
+ ```yaml
63
+ key: value
64
+ test: 1
65
+ overriden: false
66
+ things:
67
+ - thing1
68
+ - thing2
69
+ - thing3
70
+ top:
71
+ v1: 1
72
+ middle:
73
+ v2: 2
74
+ inner:
75
+ v3: 3
76
+ deep:
77
+ v4: 4
78
+ ```
79
+ If you have multiple config files you want to load into a single config object, you can put them all in the same directory and pass that directory to NanoConf.
80
+ NanoConf will automatically place sub-files by their filename as an attribute of the parent file.
81
+ The contents of that file will be accessible as you'd expect under the corresponding filename attribute.
82
+
83
+ ```
84
+ <project root>
85
+ conf_dir
86
+ |__ cfg1.nconf
87
+ |__ cfg2.nconf
88
+ |__ cfg3.nconf
89
+ ```
90
+
91
+ ```python
92
+ # load an entire directory
93
+ proj_config = NanoConf("/path/to/conf_dir")
94
+ print(proj_config.cfg1.test)
95
+ ```
96
+
97
+ Or you can import additional files or directories from within any config file by using the `_import` keyword.
98
+ ```yaml
99
+ # main.nconf
100
+ _import:
101
+ - /path/to/project/more_config
102
+ key: value
103
+ test: 1
104
+ ```
105
+
106
+ ```
107
+ <project root>
108
+ main.nconf
109
+ more_config
110
+ |__ subcfg1.nconf
111
+ |__ subcfg2.nconf
112
+ |__ subcfg3.nconf
113
+ ```
114
+
115
+ ```python
116
+ # loading the main config file will also load the sub-configs
117
+ proj_config = NanoConf("/path/to/project/main.nconf")
118
+ print(proj_config.more_config.subcfg1.test)
119
+ ```
120
+ Notice how the directory structure was also maintained in the attribute path. This makes it easier to find the file that a value came from.
121
+
122
+ Environment Variables
123
+ ---------------------
124
+ NanoConf supports environment variables either as overrides to existing values or as additions to the loaded config.
125
+ Envars are evaluated on a per-file basis, so you can have different envars for different config files.
126
+ The way we manage this is by having a special `_envar_prefix` key in the config file.
127
+ **Note:** NanoConf will not modify the case of any environment varable name or value. It is up to you to ensure that the case is correct.
128
+ ```yaml
129
+ _envar_prefix: myapp
130
+ key: value
131
+ overrideme: original
132
+ ```
133
+ ```bash
134
+ export myapp_overrideme=changed
135
+ ```
136
+ ```python
137
+ config = NanoConf("/path/to/config.nconf")
138
+ print(config.overrideme)
139
+ ```
140
+
141
+ You can also pass complex data structures as JSON strings in environment variables.
142
+ ```bash
143
+ export myapp_abc='{"a": 1, "b": 2, "c": 3}'
144
+ ```
145
+ ```python
146
+ config = NanoConf("/path/to/config.nconf")
147
+ print(config.abc.b)
148
+ ```
@@ -0,0 +1,119 @@
1
+ NanoConf
2
+ ========
3
+ NanoConf is a tiny, opinionated, and easy to use configuration library for Python. It is designed to be used in small to medium sized projects where a full blown configuration library is overkill.
4
+
5
+ Installation
6
+ ------------
7
+ ```bash
8
+ pip install nanoconf
9
+ ```
10
+
11
+ Usage
12
+ -----
13
+ ```python
14
+ from nanoconf import NanoConf
15
+ # or if NanoConf if too long of a name
16
+ from nanoconf import NC
17
+
18
+ # Create a new configuration object
19
+ config = NanoConf("/path/to/config.nconf")
20
+
21
+ # Use the configuration object
22
+ print(config["key"])
23
+ # Since all values are also loaded as attributes, you can also do
24
+ print(config.key)
25
+ ```
26
+
27
+ Configuration File Format
28
+ -------------------------
29
+ NanoConf uses a simple configuration file format that is easy to read and write.
30
+ Each File is YAML formatted and contains a single top-level dictionary.
31
+ Even though the top-level must be a dictionary, you can nest dictionaries and lists as deep as you want.
32
+ Each config file also must have the .nconf extension. This ensures that NanoConf will only load files that are meant to be configuration files.
33
+ ```yaml
34
+ key: value
35
+ test: 1
36
+ overriden: false
37
+ things:
38
+ - thing1
39
+ - thing2
40
+ - thing3
41
+ top:
42
+ v1: 1
43
+ middle:
44
+ v2: 2
45
+ inner:
46
+ v3: 3
47
+ deep:
48
+ v4: 4
49
+ ```
50
+ If you have multiple config files you want to load into a single config object, you can put them all in the same directory and pass that directory to NanoConf.
51
+ NanoConf will automatically place sub-files by their filename as an attribute of the parent file.
52
+ The contents of that file will be accessible as you'd expect under the corresponding filename attribute.
53
+
54
+ ```
55
+ <project root>
56
+ conf_dir
57
+ |__ cfg1.nconf
58
+ |__ cfg2.nconf
59
+ |__ cfg3.nconf
60
+ ```
61
+
62
+ ```python
63
+ # load an entire directory
64
+ proj_config = NanoConf("/path/to/conf_dir")
65
+ print(proj_config.cfg1.test)
66
+ ```
67
+
68
+ Or you can import additional files or directories from within any config file by using the `_import` keyword.
69
+ ```yaml
70
+ # main.nconf
71
+ _import:
72
+ - /path/to/project/more_config
73
+ key: value
74
+ test: 1
75
+ ```
76
+
77
+ ```
78
+ <project root>
79
+ main.nconf
80
+ more_config
81
+ |__ subcfg1.nconf
82
+ |__ subcfg2.nconf
83
+ |__ subcfg3.nconf
84
+ ```
85
+
86
+ ```python
87
+ # loading the main config file will also load the sub-configs
88
+ proj_config = NanoConf("/path/to/project/main.nconf")
89
+ print(proj_config.more_config.subcfg1.test)
90
+ ```
91
+ Notice how the directory structure was also maintained in the attribute path. This makes it easier to find the file that a value came from.
92
+
93
+ Environment Variables
94
+ ---------------------
95
+ NanoConf supports environment variables either as overrides to existing values or as additions to the loaded config.
96
+ Envars are evaluated on a per-file basis, so you can have different envars for different config files.
97
+ The way we manage this is by having a special `_envar_prefix` key in the config file.
98
+ **Note:** NanoConf will not modify the case of any environment varable name or value. It is up to you to ensure that the case is correct.
99
+ ```yaml
100
+ _envar_prefix: myapp
101
+ key: value
102
+ overrideme: original
103
+ ```
104
+ ```bash
105
+ export myapp_overrideme=changed
106
+ ```
107
+ ```python
108
+ config = NanoConf("/path/to/config.nconf")
109
+ print(config.overrideme)
110
+ ```
111
+
112
+ You can also pass complex data structures as JSON strings in environment variables.
113
+ ```bash
114
+ export myapp_abc='{"a": 1, "b": 2, "c": 3}'
115
+ ```
116
+ ```python
117
+ config = NanoConf("/path/to/config.nconf")
118
+ print(config.abc.b)
119
+ ```
@@ -0,0 +1,3 @@
1
+ from nanoconf.nanoconf import NanoConf
2
+
3
+ NC = NanoConf
@@ -0,0 +1,47 @@
1
+ import contextlib
2
+ import json
3
+ import os
4
+ from pathlib import Path
5
+
6
+ from box import Box
7
+ import yaml
8
+
9
+
10
+ class NanoConf:
11
+ def __init__(self, cfg_path="."):
12
+ self._cfg_path = Path(cfg_path)
13
+ self._name = self._cfg_path.stem
14
+ if self._cfg_path.is_dir():
15
+ for sub in self._cfg_path.glob("*.nconf"):
16
+ new_conf = NanoConf(sub)
17
+ setattr(self, new_conf._name, new_conf)
18
+ elif self._cfg_path.is_file() and self._cfg_path.suffix == ".nconf":
19
+ self._load_config()
20
+ self._pull_envars()
21
+
22
+ def _load_config(self):
23
+ cfg_dict = yaml.load(self._cfg_path.read_text(), Loader=yaml.SafeLoader)
24
+ for sub_import in cfg_dict.pop("_import", []):
25
+ new_conf = NanoConf(
26
+ self._cfg_path / sub_import
27
+ if self._cfg_path.is_dir()
28
+ else self._cfg_path.parent / sub_import
29
+ )
30
+ setattr(self, new_conf._name, new_conf)
31
+ self.__dict__.update(Box(cfg_dict))
32
+
33
+ def _pull_envars(self):
34
+ if getattr(self, "_envar_prefix", ""):
35
+ for key, val in os.environ.items():
36
+ if key.startswith(self._envar_prefix):
37
+ key_name = key.replace(f"{self._envar_prefix}_", "")
38
+ with contextlib.suppress(json.decoder.JSONDecodeError):
39
+ val = Box(json.loads(val)) # noqa: PLW2901
40
+ self.__dict__[key_name] = val
41
+
42
+ def __repr__(self):
43
+ return (
44
+ f"<NanoConf {self._name}.("
45
+ + ", ".join(filter(lambda x: not x.startswith("_"), self.__dict__.keys()))
46
+ + ")>"
47
+ )
@@ -0,0 +1,148 @@
1
+ Metadata-Version: 2.1
2
+ Name: nanoconf
3
+ Version: 1.0.0
4
+ Summary: The tiny opinionated config loader.
5
+ Author-email: Jacob J Callahan <jacob.callahan05@gmail.com>
6
+ Project-URL: Repository, https://github.com/JacobCallahan/nanoconf
7
+ Keywords: nanoconf,config,configuration,settings
8
+ Platform: any
9
+ Classifier: Development Status :: 5 - Production/Stable
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Natural Language :: English
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Requires-Python: >=3.11
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: python-box
19
+ Requires-Dist: pyyaml
20
+ Provides-Extra: dev
21
+ Requires-Dist: black; extra == "dev"
22
+ Requires-Dist: pre-commit; extra == "dev"
23
+ Requires-Dist: pytest; extra == "dev"
24
+ Requires-Dist: ruff; extra == "dev"
25
+ Provides-Extra: setup
26
+ Requires-Dist: build; extra == "setup"
27
+ Requires-Dist: setuptools; extra == "setup"
28
+ Requires-Dist: twine; extra == "setup"
29
+
30
+ NanoConf
31
+ ========
32
+ NanoConf is a tiny, opinionated, and easy to use configuration library for Python. It is designed to be used in small to medium sized projects where a full blown configuration library is overkill.
33
+
34
+ Installation
35
+ ------------
36
+ ```bash
37
+ pip install nanoconf
38
+ ```
39
+
40
+ Usage
41
+ -----
42
+ ```python
43
+ from nanoconf import NanoConf
44
+ # or if NanoConf if too long of a name
45
+ from nanoconf import NC
46
+
47
+ # Create a new configuration object
48
+ config = NanoConf("/path/to/config.nconf")
49
+
50
+ # Use the configuration object
51
+ print(config["key"])
52
+ # Since all values are also loaded as attributes, you can also do
53
+ print(config.key)
54
+ ```
55
+
56
+ Configuration File Format
57
+ -------------------------
58
+ NanoConf uses a simple configuration file format that is easy to read and write.
59
+ Each File is YAML formatted and contains a single top-level dictionary.
60
+ Even though the top-level must be a dictionary, you can nest dictionaries and lists as deep as you want.
61
+ Each config file also must have the .nconf extension. This ensures that NanoConf will only load files that are meant to be configuration files.
62
+ ```yaml
63
+ key: value
64
+ test: 1
65
+ overriden: false
66
+ things:
67
+ - thing1
68
+ - thing2
69
+ - thing3
70
+ top:
71
+ v1: 1
72
+ middle:
73
+ v2: 2
74
+ inner:
75
+ v3: 3
76
+ deep:
77
+ v4: 4
78
+ ```
79
+ If you have multiple config files you want to load into a single config object, you can put them all in the same directory and pass that directory to NanoConf.
80
+ NanoConf will automatically place sub-files by their filename as an attribute of the parent file.
81
+ The contents of that file will be accessible as you'd expect under the corresponding filename attribute.
82
+
83
+ ```
84
+ <project root>
85
+ conf_dir
86
+ |__ cfg1.nconf
87
+ |__ cfg2.nconf
88
+ |__ cfg3.nconf
89
+ ```
90
+
91
+ ```python
92
+ # load an entire directory
93
+ proj_config = NanoConf("/path/to/conf_dir")
94
+ print(proj_config.cfg1.test)
95
+ ```
96
+
97
+ Or you can import additional files or directories from within any config file by using the `_import` keyword.
98
+ ```yaml
99
+ # main.nconf
100
+ _import:
101
+ - /path/to/project/more_config
102
+ key: value
103
+ test: 1
104
+ ```
105
+
106
+ ```
107
+ <project root>
108
+ main.nconf
109
+ more_config
110
+ |__ subcfg1.nconf
111
+ |__ subcfg2.nconf
112
+ |__ subcfg3.nconf
113
+ ```
114
+
115
+ ```python
116
+ # loading the main config file will also load the sub-configs
117
+ proj_config = NanoConf("/path/to/project/main.nconf")
118
+ print(proj_config.more_config.subcfg1.test)
119
+ ```
120
+ Notice how the directory structure was also maintained in the attribute path. This makes it easier to find the file that a value came from.
121
+
122
+ Environment Variables
123
+ ---------------------
124
+ NanoConf supports environment variables either as overrides to existing values or as additions to the loaded config.
125
+ Envars are evaluated on a per-file basis, so you can have different envars for different config files.
126
+ The way we manage this is by having a special `_envar_prefix` key in the config file.
127
+ **Note:** NanoConf will not modify the case of any environment varable name or value. It is up to you to ensure that the case is correct.
128
+ ```yaml
129
+ _envar_prefix: myapp
130
+ key: value
131
+ overrideme: original
132
+ ```
133
+ ```bash
134
+ export myapp_overrideme=changed
135
+ ```
136
+ ```python
137
+ config = NanoConf("/path/to/config.nconf")
138
+ print(config.overrideme)
139
+ ```
140
+
141
+ You can also pass complex data structures as JSON strings in environment variables.
142
+ ```bash
143
+ export myapp_abc='{"a": 1, "b": 2, "c": 3}'
144
+ ```
145
+ ```python
146
+ config = NanoConf("/path/to/config.nconf")
147
+ print(config.abc.b)
148
+ ```
@@ -0,0 +1,23 @@
1
+ .gitignore
2
+ .pre-commit-config.yaml
3
+ LICENSE
4
+ README.md
5
+ pyproject.toml
6
+ .github/workflows/codeql-analysis.yml
7
+ .github/workflows/python-publish.yml
8
+ nanoconf/__init__.py
9
+ nanoconf/nanoconf.py
10
+ nanoconf.egg-info/PKG-INFO
11
+ nanoconf.egg-info/SOURCES.txt
12
+ nanoconf.egg-info/dependency_links.txt
13
+ nanoconf.egg-info/not-zip-safe
14
+ nanoconf.egg-info/requires.txt
15
+ nanoconf.egg-info/top_level.txt
16
+ tests/test_nanoconf.py
17
+ tests/data/nested.nconf
18
+ tests/data/simple.nconf
19
+ tests/data/animals/dog.nconf
20
+ tests/data/animals/elephant.nconf
21
+ tests/data/animals/lion.nconf
22
+ tests/data/animals/panda.nconf
23
+ tests/data/animals/penguin.nconf
@@ -0,0 +1,13 @@
1
+ python-box
2
+ pyyaml
3
+
4
+ [dev]
5
+ black
6
+ pre-commit
7
+ pytest
8
+ ruff
9
+
10
+ [setup]
11
+ build
12
+ setuptools
13
+ twine
@@ -0,0 +1 @@
1
+ nanoconf
@@ -0,0 +1,127 @@
1
+ [project]
2
+ name = "nanoconf"
3
+ description = "The tiny opinionated config loader."
4
+ readme = "README.md"
5
+ requires-python = ">=3.11"
6
+ keywords = ["nanoconf", "config", "configuration", "settings"]
7
+ authors = [
8
+ {name = "Jacob J Callahan", email = "jacob.callahan05@gmail.com"}
9
+ ]
10
+ classifiers = [
11
+ "Development Status :: 5 - Production/Stable",
12
+ "Intended Audience :: Developers",
13
+ "Natural Language :: English",
14
+ "Programming Language :: Python :: 3",
15
+ "Programming Language :: Python :: 3.11",
16
+ "Programming Language :: Python :: 3.12",
17
+ ]
18
+ dependencies = [
19
+ "python-box",
20
+ "pyyaml",
21
+ ]
22
+ dynamic = ["version"] # dynamic fields to update on build - version via setuptools_scm
23
+
24
+ [project.urls]
25
+ Repository = "https://github.com/JacobCallahan/nanoconf"
26
+
27
+ [build-system]
28
+ requires = ["setuptools", "setuptools-scm[toml]", "wheel"]
29
+ build-backend = "setuptools.build_meta"
30
+
31
+ [tool.setuptools]
32
+ platforms = ["any"]
33
+ zip-safe = false
34
+ include-package-data = true
35
+
36
+ [tool.setuptools.packages.find]
37
+ include = ["nanoconf"]
38
+
39
+ [tool.setuptools_scm] # same as use_scm_version=True in setup.py
40
+
41
+ [project.optional-dependencies]
42
+ dev = [
43
+ "black",
44
+ "pre-commit",
45
+ "pytest",
46
+ "ruff",
47
+ ]
48
+ setup = [
49
+ "build",
50
+ "setuptools",
51
+ "twine",
52
+ ]
53
+
54
+ [tool.pytest.ini_options]
55
+ testpaths = ["tests"]
56
+ addopts = ["-v", "-l", "--color=yes", "--code-highlight=yes"]
57
+
58
+ [tool.black]
59
+ line-length = 100
60
+ target-version = ["py311"]
61
+ include = '\.pyi?$'
62
+ exclude = '''
63
+ /(
64
+ \.git
65
+ | \.venv
66
+ | build
67
+ | dist
68
+ | tests/data
69
+ )/
70
+ '''
71
+
72
+ [tool.ruff]
73
+ target-version = "py311"
74
+ fixable = ["ALL"]
75
+
76
+ select = [
77
+ "B", # bugbear
78
+ "C", # complexity
79
+ "C4", # flake8-comprehensions
80
+ "COM818", # Trailing comma on bare tuple prohibited
81
+ "E", # pycodestyle
82
+ "F", # pyflakes/autoflake
83
+ "G", # flake8-logging-format
84
+ "I", # isort
85
+ "ISC001", # Implicitly concatenated string literals on one License
86
+ "N",
87
+ "PERF", # Perflint rules
88
+ "PGH004", # Use specific rule codes when using noqa
89
+ "PLC", # pylint
90
+ "PLE", # pylint
91
+ "PLR", # pylint
92
+ "PLW", # pylint
93
+ "PTH", # Use pathlib
94
+ "RUF", # Ruff-specific rules
95
+ "S", # flake8-bandit
96
+ "SIM", #flake8-simplify
97
+ "T100", # leftover breakpoint()
98
+ "T20", # leftover print()
99
+ "TRY004", # Prefer TypeError exception for invalid type
100
+ "TRY200", # Use raise from to specify exception cause
101
+ "TRY302", # Remove exception handler; error is immediately re-raised
102
+ "PL", # pylint
103
+ "UP", # pyupgrade
104
+ "W", # pycodestyle
105
+ ]
106
+
107
+ ignore = [
108
+ "ANN", # flake8-annotations
109
+ "E501", # line too long
110
+ "RUF012", # Mutable class attributes should be annotated with typing.ClassVar
111
+ ]
112
+
113
+ [tool.ruff.per-file-ignores]
114
+ "tests/test_nanoconf.py" = ["S101", "PLR2004"]
115
+
116
+ [tool.ruff.flake8-pytest-style]
117
+ fixture-parentheses = false
118
+
119
+ [tool.ruff.isort]
120
+ force-sort-within-sections = true
121
+ known-first-party = [
122
+ "nanoconf",
123
+ ]
124
+ combine-as-imports = true
125
+
126
+ [tool.ruff.mccabe]
127
+ max-complexity = 25
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,6 @@
1
+ _envar_prefix: testdog
2
+ name: Dog
3
+ species: Canis lupus familiaris
4
+ habitat: Domesticated
5
+ diet: Omnivore
6
+ weight: 5-70 kg
@@ -0,0 +1,5 @@
1
+ name: Elephant
2
+ species: Loxodonta africana
3
+ habitat: Savannas, forests, and deserts
4
+ diet: Herbivore
5
+ weight: 2,268-5,443 kg
@@ -0,0 +1,5 @@
1
+ name: Lion
2
+ species: Panthera leo
3
+ habitat: Grasslands, savannas, and forests
4
+ diet: Carnivore
5
+ weight: 190-260 kg
@@ -0,0 +1,5 @@
1
+ name: Panda
2
+ species: Ailuropoda melanoleuca
3
+ habitat: Temperate forests of China
4
+ diet: Herbivore
5
+ weight: 70-125 kg
@@ -0,0 +1,5 @@
1
+ name: Penguin
2
+ species: Spheniscidae
3
+ habitat: Antarctica, southern Africa, South America, and New Zealand
4
+ diet: Carnivore
5
+ weight: 1-5 kg
@@ -0,0 +1,9 @@
1
+ _import:
2
+ - animals
3
+ test: value
4
+ top:
5
+ v1: 1
6
+ middle:
7
+ v2: 2
8
+ lowest:
9
+ v3: 3
@@ -0,0 +1,8 @@
1
+ _envar_prefix: simple
2
+ key: value
3
+ test: 1
4
+ overriden: False
5
+ things:
6
+ - thing1
7
+ - thing2
8
+ - thing3
@@ -0,0 +1,54 @@
1
+ import os
2
+
3
+ from box import Box, BoxList
4
+ import pytest
5
+
6
+ from nanoconf import NC
7
+
8
+
9
+ @pytest.fixture
10
+ def set_envars(request):
11
+ if isinstance(request.param, list):
12
+ for pair in request.param:
13
+ os.environ[pair[0]] = pair[1]
14
+ yield
15
+ for pair in request.param:
16
+ del os.environ[pair[0]]
17
+ else:
18
+ os.environ[request.param[0]] = request.param[1]
19
+ yield
20
+ del os.environ[request.param[0]]
21
+
22
+
23
+ @pytest.mark.parametrize("set_envars", [("simple_overriden", "True")], indirect=True)
24
+ def test_simple(set_envars):
25
+ nconf = NC("tests/data/simple.nconf")
26
+ assert nconf._name == "simple"
27
+ assert nconf._envar_prefix == "simple"
28
+ assert isinstance(nconf.things, BoxList)
29
+ assert len(nconf.things) == 3
30
+ assert nconf.overriden == "True"
31
+
32
+
33
+ @pytest.mark.parametrize("set_envars", [("simple_overriden", '{"a": 1, "b": 2}')], indirect=True)
34
+ def test_json_envar(set_envars):
35
+ nconf = NC("tests/data/simple.nconf")
36
+ assert isinstance(nconf.overriden, Box)
37
+ assert nconf.overriden.a == 1
38
+ assert nconf.overriden.b == 2
39
+
40
+
41
+ def test_nested():
42
+ nconf = NC("tests/data/nested.nconf")
43
+ assert nconf._name == "nested"
44
+ assert nconf.top.v1 == 1
45
+ assert nconf.top.middle.v2 == 2
46
+ assert nconf.top.middle.lowest.v3 == 3
47
+ assert nconf.animals.panda.diet == "Herbivore"
48
+ assert all([nconf.animals.dog, nconf.animals.lion, nconf.animals.penguin])
49
+
50
+
51
+ @pytest.mark.parametrize("set_envars", [("testdog_diet", "Treats!")], indirect=True)
52
+ def test_nested_envar(set_envars):
53
+ nconf = NC("tests/data/nested.nconf")
54
+ assert nconf.animals.dog.diet == "Treats!"