|
1 | | -# pedantic-python-decorators [](https://travis-ci.com/LostInDarkMath/pedantic-python-decorators) [](https://coveralls.io/github/LostInDarkMath/pedantic-python-decorators?branch=master) [](https://badge.fury.io/py/pedantic) [](https://anaconda.org/conda-forge/pedantic) [](https://GitHub.com/LostInDarkMath/pedantic-python-decorators) [](https://GitHub.com/LostInDarkMath/pedantic-python-decorators) [](https://GitHub.com/LostInDarkMath/pedantic-python-decorators/issues) [](https://GitHub.com/LostInDarkMath/pedantic-python-decorators/pulls) |
| 1 | +# pedantic-python-decorators [](https://coveralls.io/github/LostInDarkMath/pedantic-python-decorators?branch=master) [](https://badge.fury.io/py/pedantic) [](https://anaconda.org/conda-forge/pedantic) [](https://GitHub.com/LostInDarkMath/pedantic-python-decorators) [](https://GitHub.com/LostInDarkMath/pedantic-python-decorators) [](https://GitHub.com/LostInDarkMath/pedantic-python-decorators/issues) [](https://GitHub.com/LostInDarkMath/pedantic-python-decorators/pulls) |
2 | 2 |
|
3 | 3 | This packages includes many decorators that will make you write cleaner Python code. |
4 | 4 |
|
5 | | -## Getting Started |
6 | | -This package requires Python 3.11 or later. |
7 | | -There are multiple options for installing this package. |
8 | | - |
9 | | -### Option 1: Installing with pip from [Pypi](https://pypi.org/) |
10 | | -Run `pip install pedantic`. |
11 | | - |
12 | | -### Option 2: Installing with conda from [conda-forge](conda-forge.org) |
13 | | -Run `conda install -c conda-forge pedantic` |
14 | | - |
15 | | -### Option 3: Installing with pip and git |
16 | | -1. Install [Git](https://git-scm.com/downloads) if you don't have it already. |
17 | | -2. Run `pip install git+https://github.com/LostInDarkMath/pedantic-python-decorators.git@master` |
18 | | - |
19 | | -### Option 4: Offline installation using wheel |
20 | | -1. Download the [latest release here](https://github.com/LostInDarkMath/PythonHelpers/releases/latest) by clicking on `pedantic-python-decorators-x.y.z-py-none-any.whl`. |
21 | | -2. Execute `pip install pedantic-python-decorators-x.y.z-py3-none-any.whl`. |
22 | | - |
23 | 5 | ## The [@pedantic](https://lostindarkmath.github.io/pedantic-python-decorators/pedantic/method_decorators.html#pedantic.method_decorators.pedantic) decorator - Type checking at runtime |
24 | 6 | The `@pedantic` decorator does the following things: |
25 | 7 | - The decorated function can only be called by using keyword arguments. Positional arguments are not accepted. |
@@ -50,98 +32,6 @@ get_sum_of(values=[0, 1.2, 3, 5.4]) # this raises the following runtime error: |
50 | 32 |
|
51 | 33 | ## The [@validate]() decorator |
52 | 34 | As the name suggests, with `@validate` you are able to validate the values that are passed to the decorated function. |
53 | | -That is done in a highly customizable way. |
54 | | -But the highest benefit of this decorator is that it makes it extremely easy to write decoupled easy testable, maintainable and scalable code. |
55 | | -The following example shows the decoupled implementation of a configurable algorithm with the help of `@validate`: |
56 | | -```python |
57 | | -import os |
58 | | -from dataclasses import dataclass |
59 | | - |
60 | | -from pedantic import validate, ExternalParameter, overrides, Validator, Parameter, Min, ReturnAs |
61 | | - |
62 | | - |
63 | | -@dataclass(frozen=True) |
64 | | -class Configuration: |
65 | | - iterations: int |
66 | | - max_error: float |
67 | | - |
68 | | - |
69 | | -class ConfigurationValidator(Validator): |
70 | | - @overrides(Validator) |
71 | | - def validate(self, value: Configuration) -> Configuration: |
72 | | - if value.iterations < 1 or value.max_error < 0: |
73 | | - self.raise_exception(msg=f'Invalid configuration: {value}', value=value) |
74 | | - |
75 | | - return value |
76 | | - |
77 | | - |
78 | | -class ConfigFromEnvVar(ExternalParameter): |
79 | | - """ Reads the configuration from environment variables. """ |
80 | | - |
81 | | - @overrides(ExternalParameter) |
82 | | - def has_value(self) -> bool: |
83 | | - return 'iterations' in os.environ and 'max_error' in os.environ |
84 | | - |
85 | | - @overrides(ExternalParameter) |
86 | | - def load_value(self) -> Configuration: |
87 | | - return Configuration( |
88 | | - iterations=int(os.environ['iterations']), |
89 | | - max_error=float(os.environ['max_error']), |
90 | | - ) |
91 | | - |
92 | | - |
93 | | -class ConfigFromFile(ExternalParameter): |
94 | | - """ Reads the configuration from a config file. """ |
95 | | - |
96 | | - @overrides(ExternalParameter) |
97 | | - def has_value(self) -> bool: |
98 | | - return os.path.isfile('config.csv') |
99 | | - |
100 | | - @overrides(ExternalParameter) |
101 | | - def load_value(self) -> Configuration: |
102 | | - with open(file='config.csv', mode='r') as file: |
103 | | - content = file.readlines() |
104 | | - return Configuration( |
105 | | - iterations=int(content[0].strip('\n')), |
106 | | - max_error=float(content[1]), |
107 | | - ) |
108 | | - |
109 | | - |
110 | | -# choose your configuration source here: |
111 | | -@validate(ConfigFromEnvVar(name='config', validators=[ConfigurationValidator()]), strict=False, return_as=ReturnAs.KWARGS_WITH_NONE) |
112 | | -# @validate(ConfigFromFile(name='config', validators=[ConfigurationValidator()]), strict=False) |
113 | | - |
114 | | -# with strict_mode = True (which is the default) |
115 | | -# you need to pass a Parameter for each parameter of the decorated function |
116 | | -# @validate( |
117 | | -# Parameter(name='value', validators=[Min(5, include_boundary=False)]), |
118 | | -# ConfigFromFile(name='config', validators=[ConfigurationValidator()]), |
119 | | -# ) |
120 | | -def my_algorithm(value: float, config: Configuration) -> float: |
121 | | - """ |
122 | | - This method calculates something that depends on the given value with considering the configuration. |
123 | | - Note how well this small piece of code is designed: |
124 | | - - Fhe function my_algorithm() need a Configuration but has no knowledge where this come from. |
125 | | - - Furthermore, it doesn't care about parameter validation. |
126 | | - - The ConfigurationValidator doesn't know anything about the creation of the data. |
127 | | - - The @validate decorator is the only you need to change, if you want a different configuration source. |
128 | | - """ |
129 | | - print(value) |
130 | | - print(config) |
131 | | - return value |
132 | | - |
133 | | - |
134 | | -if __name__ == '__main__': |
135 | | - # we can call the function with a config like there is no decorator. |
136 | | - # This makes testing extremely easy: no config files, no environment variables or stuff like that |
137 | | - print(my_algorithm(value=2, config=Configuration(iterations=3, max_error=4.4))) |
138 | | - |
139 | | - os.environ['iterations'] = '12' |
140 | | - os.environ['max_error'] = '3.1415' |
141 | | - |
142 | | - # but we also can omit the config and load it implicitly by our custom Parameters |
143 | | - print(my_algorithm(value=42.0)) |
144 | | -``` |
145 | 35 |
|
146 | 36 | ## List of all decorators in this package |
147 | 37 | - [@deprecated](https://lostindarkmath.github.io/pedantic-python-decorators/pedantic/decorators/fn_deco_deprecated.html) |
@@ -196,6 +86,4 @@ enable_pedantic() |
196 | 86 | This package is **not** compatible with compiled source code (e.g. with [Nuitka](https://github.com/Nuitka/Nuitka)). |
197 | 87 | That's because it uses the `inspect` module from the standard library which will raise errors like `OSError: could not get source code` in case of compiled source code. |
198 | 88 |
|
199 | | - |
200 | 89 | Don't forget to check out the [documentation](https://lostindarkmath.github.io/pedantic-python-decorators/pedantic). |
201 | | -Happy coding! |
0 commit comments