heracless 0.2.2__tar.gz → 0.3.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.
Files changed (44) hide show
  1. heracless-0.3.0/PKG-INFO +185 -0
  2. heracless-0.3.0/README.md +165 -0
  3. heracless-0.3.0/heracless/__init__.py +4 -0
  4. {heracless-0.2.2 → heracless-0.3.0}/heracless/cli_tool.py +5 -4
  5. heracless-0.3.0/heracless/fight.py +133 -0
  6. heracless-0.3.0/heracless/utils/__init__.py +1 -0
  7. {heracless-0.2.2 → heracless-0.3.0}/heracless/utils/cfg_tree.py +222 -32
  8. {heracless-0.2.2 → heracless-0.3.0}/heracless/utils/exceptions.py +8 -0
  9. {heracless-0.2.2 → heracless-0.3.0}/heracless/utils/helper.py +5 -5
  10. heracless-0.3.0/heracless/utils/load_func_template.py +33 -0
  11. heracless-0.3.0/heracless/utils/utils.py +35 -0
  12. heracless-0.3.0/heracless/wapp/About.py +56 -0
  13. heracless-0.3.0/heracless/wapp/pages/ConfigGenerator.py +40 -0
  14. heracless-0.3.0/heracless/wapp/pages/Documentation.py +207 -0
  15. heracless-0.3.0/heracless/wapp/utils.py +27 -0
  16. heracless-0.3.0/heracless.egg-info/PKG-INFO +185 -0
  17. heracless-0.3.0/heracless.egg-info/SOURCES.txt +48 -0
  18. heracless-0.3.0/heracless.egg-info/requires.txt +6 -0
  19. heracless-0.3.0/heracless.egg-info/top_level.txt +4 -0
  20. heracless-0.3.0/pyproject.toml +36 -0
  21. heracless-0.2.2/heracless/tests/conftest.py → heracless-0.3.0/tests/config/types.py +14 -16
  22. heracless-0.2.2/heracless/tests/config/types.py → heracless-0.3.0/tests/config/types.pyi +13 -9
  23. heracless-0.3.0/tests/conftest.py +40 -0
  24. heracless-0.3.0/tests/conftest.pyi +49 -0
  25. {heracless-0.2.2/heracless → heracless-0.3.0}/tests/quick_test.py +2 -1
  26. heracless-0.3.0/tests/test_end_to_end.py +30 -0
  27. heracless-0.3.0/tests/test_units.py +85 -0
  28. heracless-0.2.2/PKG-INFO +0 -150
  29. heracless-0.2.2/README.md +0 -130
  30. heracless-0.2.2/heracless/__init__.py +0 -43
  31. heracless-0.2.2/heracless/decorator.py +0 -57
  32. heracless-0.2.2/heracless/main.py +0 -81
  33. heracless-0.2.2/heracless/tests/load.py +0 -35
  34. heracless-0.2.2/heracless/tests/test_end_to_end.py +0 -37
  35. heracless-0.2.2/heracless/tests/test_units.py +0 -49
  36. heracless-0.2.2/heracless/utils/c_types.py +0 -6
  37. heracless-0.2.2/heracless.egg-info/PKG-INFO +0 -150
  38. heracless-0.2.2/heracless.egg-info/SOURCES.txt +0 -22
  39. heracless-0.2.2/heracless.egg-info/requires.txt +0 -4
  40. heracless-0.2.2/heracless.egg-info/top_level.txt +0 -1
  41. heracless-0.2.2/pyproject.toml +0 -31
  42. {heracless-0.2.2 → heracless-0.3.0}/LICENSE +0 -0
  43. {heracless-0.2.2 → heracless-0.3.0}/heracless.egg-info/dependency_links.txt +0 -0
  44. {heracless-0.2.2 → heracless-0.3.0}/setup.cfg +0 -0
@@ -0,0 +1,185 @@
1
+ Metadata-Version: 2.1
2
+ Name: heracless
3
+ Version: 0.3.0
4
+ Summary: Yaml to Dataclass parser for Config files
5
+ Author-email: Felix Schelling <felix.schelling@protonmail.com>
6
+ Project-URL: Repository, https://github.com/felixscode/heracless.git
7
+ Project-URL: Homepage, https://heracless.io
8
+ Keywords: YAML,Dataclass,Config,config,config management
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Programming Language :: Python :: 3.12
11
+ Requires-Python: >=3.10
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: black
15
+ Requires-Dist: pytest
16
+ Requires-Dist: PyYAML
17
+ Requires-Dist: art
18
+ Requires-Dist: streamlit
19
+ Requires-Dist: streamlit_ace
20
+
21
+ # ⚔️ Heracless ⚔️
22
+
23
+ A simple and minimalistic Config Manager using YAML.
24
+ [![GitHub](https://img.shields.io/badge/GitHub-Repository-blue?logo=github)](https://github.com/felixscode/heracless)
25
+ [![PyPi](https://img.shields.io/badge/PyPi-Package-blue?logo=pypi)](https://pypi.org/project/heracless/)
26
+ [![Documentation](https://img.shields.io/badge/Documentation-Read-green?logo=readthedocs)](https://heracless.io/docs)
27
+
28
+
29
+ ## Table of Contents
30
+ - [Description](#description)
31
+ - [Installation](#installation)
32
+ - [Setup](#setup)
33
+ - [CLI Tool](#cli-tool)
34
+ - [Helper Functions in Heracless](#helper-functions-in-heracless)
35
+ - [mutate_config](#mutate_config)
36
+ - [as_dict](#as_dict)
37
+ - [from_dict](#from_dict)
38
+ - [Version](#version)
39
+ - [Future](#future)
40
+ - [Author](#author)
41
+
42
+ ## Description
43
+
44
+ Heracless aims to make working with config files in Python easy. It parses a config file into a dataclass and creates types as a Python stub file (.pyi) which can be used for type hints. Generated types also make autocompletion dreamy!
45
+
46
+ ## Installation
47
+
48
+ Heracless is available as a pip package:
49
+
50
+ ```bash
51
+ pip install heracless
52
+ ```
53
+
54
+ If you want to build from source, run:
55
+
56
+ ```bash
57
+ git clone https://github.com/felixscode/heracless.git
58
+ cd heracless
59
+ pip install -e .
60
+ ```
61
+
62
+ ## Setup
63
+
64
+ First, create a `config.yaml` file in a desired location and add desired configs. Make a new Python file called `load_config.py` and put it somewhere into your project.
65
+ Here is an example project structure:
66
+ ```
67
+ ├── src
68
+ │ └── your_module
69
+ │ ├── main.py
70
+ │ └──utils
71
+ │ └── load_config.py
72
+ ├── data
73
+ │ └── config.yaml
74
+ ├── README.md
75
+ ├── pyproject.toml
76
+ └── .gitignore
77
+
78
+ ```
79
+
80
+
81
+ Paste the following code into your `load_config.py`:
82
+
83
+ ```python
84
+ from pathlib import Path
85
+ from typing import Type, TypeVar
86
+
87
+ from heracless import load_config as _load_config
88
+
89
+ # CONFIG_YAML_PATH is a global variable that sets the path of your YAML config file
90
+ # Edit this to your config file path
91
+ CONFIG_YAML_PATH = None
92
+
93
+ Config = TypeVar("Config")
94
+
95
+ def load_config(frozen: bool = True) -> Config:
96
+ """
97
+ Load the configuration from the specified directory and return a Config object.
98
+
99
+ Args:
100
+ frozen (bool, optional): Whether the configuration should be frozen. Defaults to True.
101
+
102
+ Returns:
103
+ Config: The loaded configuration object.
104
+
105
+ Raises:
106
+ FileNotFoundError: If the configuration file does not exist.
107
+ yaml.YAMLError: If there is an error parsing the YAML configuration file.
108
+
109
+ Note:
110
+ CONFIG_YAML_PATH is a global variable that sets the path of your YAML config file.
111
+ """
112
+
113
+ file_path = Path(__file__).resolve()
114
+ yaml_config_path = CONFIG_YAML_PATH
115
+ return _load_config(file_path, yaml_config_path, frozen=frozen)
116
+ ```
117
+
118
+ After creating the `load_config.py` file, set the `CONFIG_YAML_PATH` variable to the path of your `config.yaml` file. For example:
119
+
120
+ ```python
121
+ CONFIG_YAML_PATH = "/path/to/your/config.yaml"
122
+ ```
123
+
124
+
125
+ ## Helper Functions in Heracless
126
+
127
+ This document describes the helper functions in the `helper.py` module of the Heracless project.
128
+
129
+ ### `mutate_config`
130
+
131
+ This function takes a `Config` object, a name, and a value, and returns a new `Config` object with the value at the name updated.
132
+
133
+ ```python
134
+ from your_project import load_config
135
+
136
+ from heracless.utils.helper import mutate_config
137
+
138
+ config = load_config()
139
+ new_config = mutate_config(config, "name", "new_value")
140
+ ```
141
+
142
+ ### `as_dict`
143
+
144
+ This function converts a `Config` object to a dictionary.
145
+
146
+ ```python
147
+ from your_project import load_config
148
+
149
+ from heracless.utils.helper import as_dict
150
+
151
+ config = load_config()
152
+ config_dict = as_dict(config)
153
+ ```
154
+
155
+ ### `from_dict`
156
+
157
+ This function creates a `Config` object from a dictionary. You can specify whether the `Config` object should be frozen.
158
+
159
+ ```python
160
+ from heracless.utils.helper import from_dict
161
+
162
+ config_dict = {...} # A dictionary representing the configuration
163
+ config = from_dict(config_dict, frozen=True)
164
+ ```
165
+
166
+ ## Version
167
+
168
+ Heracless 0.4 <br>
169
+ Written in Python 3.11
170
+
171
+ ## Future
172
+
173
+ - [ ] Add config variants
174
+ - [ ] Add None type support
175
+ - [x] Web app
176
+
177
+ ## Author
178
+
179
+ Felix Schelling<br>
180
+ GitHub: [felixscode](https://github.com/felixscode)<br>
181
+ Website: [heracless.io](https://heracless.io)<br>
182
+ Personal website: [felixschelling.de](https://felixschelling.de)<br>
183
+ Written with ❤️ in Mexico
184
+
185
+
@@ -0,0 +1,165 @@
1
+ # ⚔️ Heracless ⚔️
2
+
3
+ A simple and minimalistic Config Manager using YAML.
4
+ [![GitHub](https://img.shields.io/badge/GitHub-Repository-blue?logo=github)](https://github.com/felixscode/heracless)
5
+ [![PyPi](https://img.shields.io/badge/PyPi-Package-blue?logo=pypi)](https://pypi.org/project/heracless/)
6
+ [![Documentation](https://img.shields.io/badge/Documentation-Read-green?logo=readthedocs)](https://heracless.io/docs)
7
+
8
+
9
+ ## Table of Contents
10
+ - [Description](#description)
11
+ - [Installation](#installation)
12
+ - [Setup](#setup)
13
+ - [CLI Tool](#cli-tool)
14
+ - [Helper Functions in Heracless](#helper-functions-in-heracless)
15
+ - [mutate_config](#mutate_config)
16
+ - [as_dict](#as_dict)
17
+ - [from_dict](#from_dict)
18
+ - [Version](#version)
19
+ - [Future](#future)
20
+ - [Author](#author)
21
+
22
+ ## Description
23
+
24
+ Heracless aims to make working with config files in Python easy. It parses a config file into a dataclass and creates types as a Python stub file (.pyi) which can be used for type hints. Generated types also make autocompletion dreamy!
25
+
26
+ ## Installation
27
+
28
+ Heracless is available as a pip package:
29
+
30
+ ```bash
31
+ pip install heracless
32
+ ```
33
+
34
+ If you want to build from source, run:
35
+
36
+ ```bash
37
+ git clone https://github.com/felixscode/heracless.git
38
+ cd heracless
39
+ pip install -e .
40
+ ```
41
+
42
+ ## Setup
43
+
44
+ First, create a `config.yaml` file in a desired location and add desired configs. Make a new Python file called `load_config.py` and put it somewhere into your project.
45
+ Here is an example project structure:
46
+ ```
47
+ ├── src
48
+ │ └── your_module
49
+ │ ├── main.py
50
+ │ └──utils
51
+ │ └── load_config.py
52
+ ├── data
53
+ │ └── config.yaml
54
+ ├── README.md
55
+ ├── pyproject.toml
56
+ └── .gitignore
57
+
58
+ ```
59
+
60
+
61
+ Paste the following code into your `load_config.py`:
62
+
63
+ ```python
64
+ from pathlib import Path
65
+ from typing import Type, TypeVar
66
+
67
+ from heracless import load_config as _load_config
68
+
69
+ # CONFIG_YAML_PATH is a global variable that sets the path of your YAML config file
70
+ # Edit this to your config file path
71
+ CONFIG_YAML_PATH = None
72
+
73
+ Config = TypeVar("Config")
74
+
75
+ def load_config(frozen: bool = True) -> Config:
76
+ """
77
+ Load the configuration from the specified directory and return a Config object.
78
+
79
+ Args:
80
+ frozen (bool, optional): Whether the configuration should be frozen. Defaults to True.
81
+
82
+ Returns:
83
+ Config: The loaded configuration object.
84
+
85
+ Raises:
86
+ FileNotFoundError: If the configuration file does not exist.
87
+ yaml.YAMLError: If there is an error parsing the YAML configuration file.
88
+
89
+ Note:
90
+ CONFIG_YAML_PATH is a global variable that sets the path of your YAML config file.
91
+ """
92
+
93
+ file_path = Path(__file__).resolve()
94
+ yaml_config_path = CONFIG_YAML_PATH
95
+ return _load_config(file_path, yaml_config_path, frozen=frozen)
96
+ ```
97
+
98
+ After creating the `load_config.py` file, set the `CONFIG_YAML_PATH` variable to the path of your `config.yaml` file. For example:
99
+
100
+ ```python
101
+ CONFIG_YAML_PATH = "/path/to/your/config.yaml"
102
+ ```
103
+
104
+
105
+ ## Helper Functions in Heracless
106
+
107
+ This document describes the helper functions in the `helper.py` module of the Heracless project.
108
+
109
+ ### `mutate_config`
110
+
111
+ This function takes a `Config` object, a name, and a value, and returns a new `Config` object with the value at the name updated.
112
+
113
+ ```python
114
+ from your_project import load_config
115
+
116
+ from heracless.utils.helper import mutate_config
117
+
118
+ config = load_config()
119
+ new_config = mutate_config(config, "name", "new_value")
120
+ ```
121
+
122
+ ### `as_dict`
123
+
124
+ This function converts a `Config` object to a dictionary.
125
+
126
+ ```python
127
+ from your_project import load_config
128
+
129
+ from heracless.utils.helper import as_dict
130
+
131
+ config = load_config()
132
+ config_dict = as_dict(config)
133
+ ```
134
+
135
+ ### `from_dict`
136
+
137
+ This function creates a `Config` object from a dictionary. You can specify whether the `Config` object should be frozen.
138
+
139
+ ```python
140
+ from heracless.utils.helper import from_dict
141
+
142
+ config_dict = {...} # A dictionary representing the configuration
143
+ config = from_dict(config_dict, frozen=True)
144
+ ```
145
+
146
+ ## Version
147
+
148
+ Heracless 0.4 <br>
149
+ Written in Python 3.11
150
+
151
+ ## Future
152
+
153
+ - [ ] Add config variants
154
+ - [ ] Add None type support
155
+ - [x] Web app
156
+
157
+ ## Author
158
+
159
+ Felix Schelling<br>
160
+ GitHub: [felixscode](https://github.com/felixscode)<br>
161
+ Website: [heracless.io](https://heracless.io)<br>
162
+ Personal website: [felixschelling.de](https://felixschelling.de)<br>
163
+ Written with ❤️ in Mexico
164
+
165
+
@@ -0,0 +1,4 @@
1
+ from heracless.cli_tool import run_cli as _run_cli
2
+ from heracless.fight import fight as load_config
3
+ if __name__ == "__main__":
4
+ _run_cli()
@@ -1,12 +1,13 @@
1
1
  import argparse
2
- from heracless.main import main, dump_in_console, dump_in_file
3
- from yaml import full_load
4
2
  import os
5
- import yaml
6
- from art import text2art
7
3
  import sys
8
4
  from typing import Optional
9
5
 
6
+ from art import text2art
7
+ from yaml import full_load
8
+
9
+ from heracless.fight import dump_in_console, dump_in_file
10
+
10
11
 
11
12
  def parse_args() -> argparse.Namespace:
12
13
  """
@@ -0,0 +1,133 @@
1
+ """
2
+ heracless YAML TO PyObject parser
3
+
4
+ """
5
+
6
+ import os
7
+ from pathlib import Path
8
+ from typing import Callable, Optional, Any
9
+
10
+ from yaml import full_load
11
+
12
+ from heracless.utils.cfg_tree import (Tree, tree_parser, tree_to_config_obj,
13
+ tree_to_string_translator)
14
+ from heracless.utils.utils import path_exists
15
+ from heracless.utils.exceptions import YamlSyntaxError
16
+
17
+ DEFAULT_DIR = Path("./config/config.yaml")
18
+
19
+
20
+ def load_as_dict(cfg_dir: Path, yaml_load_func: Callable[[Any], dict]) -> dict:
21
+ """
22
+ Load a YAML configuration file and return it as a dictionary.
23
+
24
+ :param cfg_dir: Path to the YAML configuration file.
25
+ :param yaml_load_func: Function to load YAML content.
26
+ :return: Dictionary representation of the YAML content.
27
+ :raises ValueError: If the config file is empty.
28
+ :raises FileNotFoundError: If the config file does not exist.
29
+ :raises OSError: If there is an issue reading the file.
30
+ """
31
+ path_exists(cfg_dir)
32
+ if os.stat(cfg_dir).st_size == 0:
33
+ raise ValueError("Empty config file")
34
+ stream = open(cfg_dir, "r")
35
+ try:
36
+ return yaml_load_func(stream)
37
+ except Exception as e:
38
+ raise YamlSyntaxError(str(e))
39
+
40
+
41
+ def dump_in_console(frozen: bool, _: Any, cfg_tree: Tree, *args: Any, **kwargs: Any) -> None:
42
+ """
43
+ Console dumper: prints generated config object type into console.
44
+
45
+ :param frozen: Whether the config object is frozen.
46
+ :param _: Unused parameter.
47
+ :param cfg_tree: Configuration tree.
48
+ :param args: Additional arguments.
49
+ :param kwargs: Additional keyword arguments.
50
+ """
51
+ print(tree_to_string_translator(frozen, cfg_tree))
52
+
53
+
54
+ def dump_dummy(*args: Any, **kwargs: Any) -> None:
55
+ """
56
+ Dummy dumper: can be used to bypass dumping.
57
+
58
+ :param args: Additional arguments.
59
+ :param kwargs: Additional keyword arguments.
60
+ """
61
+
62
+
63
+ def dump_in_file(frozen: bool, cfg_tree: Tree, dump_dir: Path) -> None:
64
+ """
65
+ File dumper: dumps config types into a file.
66
+
67
+ :param frozen: Whether the config object is frozen.
68
+ :param cfg_tree: Configuration tree.
69
+ :param dump_dir: Directory to dump the config file.
70
+ :raises FileNotFoundError: If the dump directory does not exist.
71
+ :raises OSError: If there is an issue writing to the file.
72
+ """
73
+ if not dump_dir.suffix == ".pyi":
74
+ dump_dir = dump_dir.with_suffix(".pyi")
75
+ path_exists(dump_dir)
76
+ with open(dump_dir, "w") as dd:
77
+ string = tree_to_string_translator(frozen, cfg_tree)
78
+ dd.write(string)
79
+
80
+
81
+ def _fight_hydra(
82
+ cfg_dir: Path,
83
+ dump_dir: Path,
84
+ dump_func: Callable[[bool, Tree, Path], None],
85
+ yaml_load_func: Callable[[Any], dict],
86
+ frozen: bool,
87
+ ) -> Optional[Any]:
88
+ """
89
+ Internal function to parse YAML config and dump it using the specified function.
90
+
91
+ :param cfg_dir: Path to the YAML configuration file.
92
+ :param dump_dir: Directory to dump the config file.
93
+ :param dump_func: Function to dump the config.
94
+ :param yaml_load_func: Function to load YAML content.
95
+ :param frozen: Whether the config object is frozen.
96
+ :return: Configuration object or None if the config is empty.
97
+ :raises ValueError: If the config file is empty.
98
+ :raises FileNotFoundError: If the config file does not exist.
99
+ :raises OSError: If there is an issue reading the file.
100
+ """
101
+ cfg_dict = load_as_dict(cfg_dir, yaml_load_func)
102
+ if cfg_dict is None: # in case dict is empty and config
103
+ return None
104
+ cfg_tree = tree_parser(cfg_dict)
105
+ dump_func(
106
+ frozen,
107
+ cfg_tree,
108
+ dump_dir,
109
+ )
110
+ config_obj = tree_to_config_obj(frozen, cfg_tree)
111
+ return config_obj
112
+
113
+
114
+ def fight(cfg_dir: Path, dump_dir: Path, frozen: bool) -> Optional[Any]:
115
+ """
116
+ Parse YAML config and dump it into a file.
117
+
118
+ :param cfg_dir: Path to the YAML configuration file.
119
+ :param dump_dir: Directory to dump the config file.
120
+ :param frozen: Whether the config object is frozen.
121
+ :return: Configuration object or None if the config is empty.
122
+ :raises ValueError: If the config file is empty.
123
+ :raises FileNotFoundError: If the config file does not exist.
124
+ :raises OSError: If there is an issue reading the file.
125
+ """
126
+ dump_func = dump_in_file
127
+ yaml_load_func = full_load
128
+ return _fight_hydra(cfg_dir, dump_dir, dump_func, yaml_load_func, frozen)
129
+
130
+
131
+ if __name__ == "__main__":
132
+ cfg = fight(DEFAULT_DIR, Path("./tmp/test_file.py"), True)
133
+ print(cfg)
@@ -0,0 +1 @@
1
+ from heracless.utils.helper import as_dict, from_dict, mutate_config