iniUts 2.0.2__py3-none-any.whl
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.
- iniUts/__init__.py +13 -0
- iniUts/config_parser_ini.py +55 -0
- iniUts/envar.py +17 -0
- iniUts/iniUts.py +193 -0
- iniUts/secret.py +58 -0
- iniuts-2.0.2.dist-info/METADATA +339 -0
- iniuts-2.0.2.dist-info/RECORD +10 -0
- iniuts-2.0.2.dist-info/WHEEL +5 -0
- iniuts-2.0.2.dist-info/licenses/LICENCE.txt +8 -0
- iniuts-2.0.2.dist-info/top_level.txt +1 -0
iniUts/__init__.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import configparser as cp
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class IniCp:
|
|
5
|
+
|
|
6
|
+
def __init__(self, ini_file, encoding=None):
|
|
7
|
+
self.ini_file = ini_file
|
|
8
|
+
self.encoding = encoding
|
|
9
|
+
self.read_ini()
|
|
10
|
+
|
|
11
|
+
def read_ini(self):
|
|
12
|
+
config = cp.RawConfigParser(
|
|
13
|
+
allow_no_value=True, comment_prefixes=(";", "#"), strict=False
|
|
14
|
+
)
|
|
15
|
+
config.optionxform = str
|
|
16
|
+
if self.encoding:
|
|
17
|
+
with open(self.ini_file, "r", encoding=self.encoding) as f:
|
|
18
|
+
config.read_string(f.read())
|
|
19
|
+
else:
|
|
20
|
+
config.read(self.ini_file)
|
|
21
|
+
|
|
22
|
+
self.config_parser = config
|
|
23
|
+
|
|
24
|
+
def write(self, section, key, value):
|
|
25
|
+
if section not in self.config_parser.sections():
|
|
26
|
+
self.config_parser[section] = {}
|
|
27
|
+
self.config_parser[section][key] = value
|
|
28
|
+
self.config_parser.write(open(self.ini_file, "w", encoding=self.encoding))
|
|
29
|
+
|
|
30
|
+
def read(self, section, key):
|
|
31
|
+
if section not in self.config_parser.sections():
|
|
32
|
+
raise Exception("Section not found!")
|
|
33
|
+
if key not in self.config_parser[section]:
|
|
34
|
+
raise Exception("Key not found!")
|
|
35
|
+
return self.config_parser[section][key]
|
|
36
|
+
|
|
37
|
+
def getSections(self):
|
|
38
|
+
return list(self.config_parser.sections())
|
|
39
|
+
|
|
40
|
+
def getKeys(self, section):
|
|
41
|
+
if section not in self.config_parser.sections():
|
|
42
|
+
raise Exception("Section not found!")
|
|
43
|
+
|
|
44
|
+
return list(self.config_parser[section])
|
|
45
|
+
|
|
46
|
+
def section2Dict(self, section):
|
|
47
|
+
dc = dict(self.config_parser[section])
|
|
48
|
+
|
|
49
|
+
return {x: (y or None) for x, y in dc.items()}
|
|
50
|
+
|
|
51
|
+
def __iter__(self):
|
|
52
|
+
sections = self.getSections()
|
|
53
|
+
for sect in sections:
|
|
54
|
+
# Retorna uma tupla (chave, valor) para cada iteração
|
|
55
|
+
yield sect, self.section2Dict(sect)
|
iniUts/envar.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Envar:
|
|
6
|
+
def __init__(self, key: str, default: Optional[str] = None):
|
|
7
|
+
self.key = key
|
|
8
|
+
self.default = default
|
|
9
|
+
|
|
10
|
+
def get_value(self):
|
|
11
|
+
if self.default is not None:
|
|
12
|
+
return os.getenv(self.key, self.default)
|
|
13
|
+
else:
|
|
14
|
+
value = os.getenv(self.key)
|
|
15
|
+
if not value:
|
|
16
|
+
raise Exception(f"envar '{self.key}' not found!")
|
|
17
|
+
return value
|
iniUts/iniUts.py
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
import re
|
|
3
|
+
import types
|
|
4
|
+
from iniUts.secret import decrypt, encrypt
|
|
5
|
+
from iniUts.config_parser_ini import IniCp
|
|
6
|
+
from iniUts.envar import Envar
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class IniUts:
|
|
10
|
+
|
|
11
|
+
def __init__(
|
|
12
|
+
self, ini_prd, ini_dev=None, in_prd=True, encryption_key=None, encoding=None
|
|
13
|
+
):
|
|
14
|
+
self.cp_prd = IniCp(ini_prd, encoding=encoding)
|
|
15
|
+
self.cp_dev = IniCp(ini_dev, encoding=encoding) if ini_dev else None
|
|
16
|
+
self.in_prd = in_prd
|
|
17
|
+
self.encryption_key = encryption_key
|
|
18
|
+
self.checkKeys()
|
|
19
|
+
|
|
20
|
+
def refresh(self):
|
|
21
|
+
self.cp_prd = IniCp(self.cp_prd.ini_file, encoding=self.cp_prd.encoding)
|
|
22
|
+
self.cp_dev = (
|
|
23
|
+
IniCp(self.cp_dev.ini_file, encoding=self.cp_dev.encoding)
|
|
24
|
+
if self.cp_dev
|
|
25
|
+
else None
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
# TODAS AS CHAVES DE DEV DEVE CONTER EM PRD
|
|
29
|
+
def checkKeys(self):
|
|
30
|
+
if self.cp_dev:
|
|
31
|
+
# VALIDA AS SESSOES
|
|
32
|
+
sections_dev = self.cp_dev.getSections()
|
|
33
|
+
sections_prd = self.cp_prd.getSections()
|
|
34
|
+
not_sections_in_prd = set(sections_dev) - set(sections_prd)
|
|
35
|
+
if not_sections_in_prd:
|
|
36
|
+
raise Exception(
|
|
37
|
+
f"could not find {not_sections_in_prd} section at production file, dev ini file must contain same sections as in production ini file"
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
# VALIDA AS CHAVES
|
|
41
|
+
for sect in sections_dev:
|
|
42
|
+
keys_dev = self.cp_dev.getKeys(sect)
|
|
43
|
+
keys_prd = self.cp_prd.getKeys(sect)
|
|
44
|
+
not_keys_in_prd = set(keys_dev) - set(keys_prd)
|
|
45
|
+
if not_keys_in_prd:
|
|
46
|
+
raise Exception(
|
|
47
|
+
f"could not find {not_keys_in_prd} keys in section '{sect}' at production file, dev ini file must contain same sections as in production ini file"
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
def format_data(self, dtClass, k, v):
|
|
51
|
+
cls = dtClass.__annotations__[k]
|
|
52
|
+
if k in dtClass.__CRYPTED_KEYS__:
|
|
53
|
+
v = decrypt(v, self.encryption_key)
|
|
54
|
+
|
|
55
|
+
if cls == tuple:
|
|
56
|
+
name = f"{str(dtClass)}_{k}"
|
|
57
|
+
if name not in self.delimiters:
|
|
58
|
+
isFormatDefined = k in [
|
|
59
|
+
x for x in dir(dtClass) if not re.search("__.*__", x)
|
|
60
|
+
]
|
|
61
|
+
delimiter = getattr(dtClass, k) or "," if isFormatDefined else ","
|
|
62
|
+
self.delimiters[name] = delimiter
|
|
63
|
+
|
|
64
|
+
v = tuple(v.split(self.delimiters[name]))
|
|
65
|
+
elif cls == datetime:
|
|
66
|
+
name = f"{str(dtClass)}_{k}"
|
|
67
|
+
if name not in self.dateFormats:
|
|
68
|
+
isFormatDefined = k in [
|
|
69
|
+
x for x in dir(dtClass) if not re.search("__.*__", x)
|
|
70
|
+
]
|
|
71
|
+
delimiter = getattr(dtClass, k) if isFormatDefined else "%Y-%m-%d"
|
|
72
|
+
self.dateFormats[name] = delimiter
|
|
73
|
+
|
|
74
|
+
v = datetime.strptime(v, self.dateFormats[name])
|
|
75
|
+
elif cls == bool:
|
|
76
|
+
val = v.strip().lower()
|
|
77
|
+
v = True if val and val in ["true", "1", "y"] else False
|
|
78
|
+
v = False if val in ["false", "", "0", "n"] else True
|
|
79
|
+
|
|
80
|
+
else:
|
|
81
|
+
v = cls(v) if v else None
|
|
82
|
+
return v
|
|
83
|
+
|
|
84
|
+
# COLOCA TODOS COMO NONE INICIALMENTE
|
|
85
|
+
def setup_initial_values(self, dtClass):
|
|
86
|
+
for k in dtClass.__annotations__:
|
|
87
|
+
if not hasattr(dtClass, k):
|
|
88
|
+
setattr(dtClass, k, None)
|
|
89
|
+
return dtClass
|
|
90
|
+
|
|
91
|
+
def section2DataClass(
|
|
92
|
+
self, section, dtClass, skip_missing=False, empty_as_null=False
|
|
93
|
+
):
|
|
94
|
+
dtClass = self.setup_initial_values(dtClass)
|
|
95
|
+
|
|
96
|
+
dtClass.save = types.MethodType(save, dtClass)
|
|
97
|
+
dtClass.__SECTION__ = section
|
|
98
|
+
dtClass.__ENVARS__ = [
|
|
99
|
+
x for x in dtClass.__annotations__ if isinstance(getattr(dtClass, x), Envar)
|
|
100
|
+
]
|
|
101
|
+
dtClass.__INIUTS__ = self
|
|
102
|
+
dtClass.__CRYPTED_KEYS__ = [
|
|
103
|
+
x.replace("&_", "") for x in self.cp_prd.getKeys(section) if "&_" in x
|
|
104
|
+
]
|
|
105
|
+
dict_prd = {
|
|
106
|
+
k.replace("&_", ""): v for k, v in self.cp_prd.section2Dict(section).items()
|
|
107
|
+
}
|
|
108
|
+
dict_dev = (
|
|
109
|
+
{
|
|
110
|
+
k.replace("&_", ""): v
|
|
111
|
+
for k, v in self.cp_dev.section2Dict(section).items()
|
|
112
|
+
}
|
|
113
|
+
if self.cp_dev and section in self.cp_dev.getSections()
|
|
114
|
+
else {}
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
# ENCRIPTA VARIAVEIS INICIAIS
|
|
118
|
+
for k in dtClass.__CRYPTED_KEYS__:
|
|
119
|
+
# ENCRIPTA VARIAVEIS INICIAIS NO ARQUIVO DE DEV
|
|
120
|
+
if self.cp_dev:
|
|
121
|
+
if (
|
|
122
|
+
k in dict_dev.keys()
|
|
123
|
+
and dict_dev[k]
|
|
124
|
+
and dict_dev[k].startswith("&_")
|
|
125
|
+
):
|
|
126
|
+
cripted_value = encrypt(
|
|
127
|
+
dict_dev[k].replace("&_", ""), self.encryption_key
|
|
128
|
+
)
|
|
129
|
+
dict_dev[k] = cripted_value
|
|
130
|
+
self.cp_dev.write(section, "&_" + k, cripted_value)
|
|
131
|
+
|
|
132
|
+
# ENCRIPTA VARIAVEIS INICIAIS NO ARQUIVO DE PRD
|
|
133
|
+
if k in dict_prd.keys() and dict_prd[k] and dict_prd[k].startswith("&_"):
|
|
134
|
+
cripted_value = encrypt(
|
|
135
|
+
dict_prd[k].replace("&_", ""), self.encryption_key
|
|
136
|
+
)
|
|
137
|
+
dict_prd[k] = cripted_value
|
|
138
|
+
self.cp_prd.write(section, "&_" + k, cripted_value)
|
|
139
|
+
|
|
140
|
+
for key in dtClass.__annotations__:
|
|
141
|
+
if key in dtClass.__ENVARS__:
|
|
142
|
+
v = getattr(dtClass, key).get_value()
|
|
143
|
+
v = self.format_data(dtClass, key, v)
|
|
144
|
+
setattr(dtClass, key, v)
|
|
145
|
+
continue
|
|
146
|
+
if key in dict_prd.keys():
|
|
147
|
+
if key in dict_dev.keys() and not self.in_prd:
|
|
148
|
+
v = dict_dev.get(key)
|
|
149
|
+
else:
|
|
150
|
+
v = dict_prd.get(key)
|
|
151
|
+
v = self.format_data(dtClass, key, v)
|
|
152
|
+
setattr(dtClass, key, v)
|
|
153
|
+
continue
|
|
154
|
+
raise Exception(
|
|
155
|
+
f"Cound not find '{key}' key at section '{section}' in ini file"
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
def link(self, section, skip_missing=False, empty_as_null=False):
|
|
159
|
+
def wrap(function):
|
|
160
|
+
self.section2DataClass(section, function, skip_missing, empty_as_null)
|
|
161
|
+
return function
|
|
162
|
+
|
|
163
|
+
return wrap
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def save(self):
|
|
167
|
+
ini = self.__INIUTS__
|
|
168
|
+
types_to_str = [str, int, float, bool]
|
|
169
|
+
is_str = lambda t: any([t == x for x in types_to_str])
|
|
170
|
+
|
|
171
|
+
for k, t in self.__annotations__.items():
|
|
172
|
+
if k in self.__ENVARS__:
|
|
173
|
+
continue
|
|
174
|
+
|
|
175
|
+
if is_str(t):
|
|
176
|
+
value = str(getattr(self, k))
|
|
177
|
+
elif t == tuple:
|
|
178
|
+
value = getattr(self, k)
|
|
179
|
+
delimiter = ini.delimiters[f"{str(self)}_{k}"]
|
|
180
|
+
value = delimiter.join(value)
|
|
181
|
+
elif t == datetime:
|
|
182
|
+
value = getattr(self, k)
|
|
183
|
+
dateFormat = ini.dateFormats[f"{str(self)}_{k}"]
|
|
184
|
+
value = value.strftime(dateFormat)
|
|
185
|
+
|
|
186
|
+
if k in self.__CRYPTED_KEYS__:
|
|
187
|
+
k = "&_" + k
|
|
188
|
+
value = encrypt(value, ini.encryption_key)
|
|
189
|
+
|
|
190
|
+
if not ini.in_prd and k in ini.cp_dev.getKeys(self.__SECTION__):
|
|
191
|
+
ini.cp_dev.write(self.__SECTION__, k, value)
|
|
192
|
+
else:
|
|
193
|
+
ini.cp_prd.write(self.__SECTION__, k, value)
|
iniUts/secret.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import base64
|
|
3
|
+
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
|
4
|
+
from cryptography.hazmat.primitives import hashes
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# Retorna 32 bytes (SHA-256) da senha (usada como chave final)
|
|
8
|
+
def _derive_key_from_password(password: str) -> bytes:
|
|
9
|
+
# Hash SHA-256 da senha -> chave de 32 bytes
|
|
10
|
+
digest = hashes.Hash(hashes.SHA256())
|
|
11
|
+
digest.update(password.encode("utf-8"))
|
|
12
|
+
return digest.finalize()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# Encripta texto (str) com senha (str). Retorna string Base64 contendo: nonce + ciphertext + tag
|
|
16
|
+
def encrypt(text: str, password: str) -> str:
|
|
17
|
+
"""
|
|
18
|
+
// text: texto em claro
|
|
19
|
+
// password: senha/segredo que será transformado em hash (SHA-256) para usar como chave
|
|
20
|
+
// retorna: base64(nonce || ciphertext || tag)
|
|
21
|
+
"""
|
|
22
|
+
key = _derive_key_from_password(password) # 32 bytes
|
|
23
|
+
aesgcm = AESGCM(key)
|
|
24
|
+
nonce = os.urandom(12) # nonce/IV recomendado para GCM: 12 bytes
|
|
25
|
+
plaintext = text.encode("utf-8")
|
|
26
|
+
ciphertext = aesgcm.encrypt(nonce, plaintext, None) # dados adicionais (AAD) = None
|
|
27
|
+
payload = (
|
|
28
|
+
nonce + ciphertext
|
|
29
|
+
) # armazenar nonce + ciphertext (ciphertext já inclui tag no AESGCM)
|
|
30
|
+
return base64.b64encode(payload).decode("utf-8")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# Decripta string Base64 gerada por encrypt()
|
|
34
|
+
def decrypt(token_b64: str, password: str) -> str:
|
|
35
|
+
"""
|
|
36
|
+
// token_b64: string retornada por encrypt (base64(nonce||ciphertext||tag))
|
|
37
|
+
// password: mesma senha usada na encriptação
|
|
38
|
+
// retorna: texto em claro (str) ou levanta exceção se inválido
|
|
39
|
+
"""
|
|
40
|
+
key = _derive_key_from_password(password)
|
|
41
|
+
data = base64.b64decode(token_b64)
|
|
42
|
+
if len(data) < 12:
|
|
43
|
+
raise ValueError("Payload inválido")
|
|
44
|
+
nonce = data[:12]
|
|
45
|
+
ciphertext = data[12:]
|
|
46
|
+
aesgcm = AESGCM(key)
|
|
47
|
+
plaintext = aesgcm.decrypt(nonce, ciphertext, None)
|
|
48
|
+
return plaintext.decode("utf-8")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
# Exemplo de uso:
|
|
52
|
+
if __name__ == "__main__":
|
|
53
|
+
senha = "minha-senha-secreta"
|
|
54
|
+
texto = "mensagem ultra secreta"
|
|
55
|
+
cifrado = encrypt(texto, senha)
|
|
56
|
+
print("Cifrado:", cifrado)
|
|
57
|
+
dec = decrypt(cifrado, senha)
|
|
58
|
+
print("Decifrado:", dec)
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: iniUts
|
|
3
|
+
Version: 2.0.2
|
|
4
|
+
Summary: Ini file manipulator
|
|
5
|
+
Home-page:
|
|
6
|
+
Author: Melque Lima
|
|
7
|
+
Author-email: melque_ex@yahoo.com.br
|
|
8
|
+
License: MIT
|
|
9
|
+
Keywords: iniUts
|
|
10
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
+
Classifier: Intended Audience :: Education
|
|
12
|
+
Classifier: Operating System :: Microsoft :: Windows :: Windows 10
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENCE.txt
|
|
17
|
+
Requires-Dist: cryptography>=3.4.7
|
|
18
|
+
Dynamic: author
|
|
19
|
+
Dynamic: author-email
|
|
20
|
+
Dynamic: classifier
|
|
21
|
+
Dynamic: description
|
|
22
|
+
Dynamic: description-content-type
|
|
23
|
+
Dynamic: keywords
|
|
24
|
+
Dynamic: license
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
Dynamic: requires-dist
|
|
27
|
+
Dynamic: summary
|
|
28
|
+
|
|
29
|
+
# Ini Uts
|
|
30
|
+
#
|
|
31
|
+
### Installation
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
pip install iniUts
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## GitHub
|
|
38
|
+
https://github.com/ZdekPyPi/IniUts
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
## Usage
|
|
42
|
+
#
|
|
43
|
+
<!-- //==================================================== -->
|
|
44
|
+
## read
|
|
45
|
+
##### test.ini file
|
|
46
|
+
```ini
|
|
47
|
+
[Person]
|
|
48
|
+
name = myname
|
|
49
|
+
age = 31
|
|
50
|
+
amount = 20.3
|
|
51
|
+
friends = friend1,friend2,friend3
|
|
52
|
+
dob = 1991-12-23
|
|
53
|
+
```
|
|
54
|
+
##### python code
|
|
55
|
+
```py
|
|
56
|
+
from iniUts import IniUts
|
|
57
|
+
|
|
58
|
+
ini = IniUts('test.ini')
|
|
59
|
+
data = ini.read('Person','name')
|
|
60
|
+
|
|
61
|
+
print(result)
|
|
62
|
+
```
|
|
63
|
+
##### output
|
|
64
|
+
```py
|
|
65
|
+
"myname"
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
<!-- //==================================================== -->
|
|
69
|
+
## write
|
|
70
|
+
##### test.ini file
|
|
71
|
+
```ini
|
|
72
|
+
[PERSON]
|
|
73
|
+
name = myname
|
|
74
|
+
```
|
|
75
|
+
##### python code
|
|
76
|
+
```py
|
|
77
|
+
from iniUts import IniUts
|
|
78
|
+
|
|
79
|
+
ini = IniUts('test.ini')
|
|
80
|
+
ini.write('PERSON','last_name','mylastname')
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
##### test.ini file
|
|
84
|
+
```ini
|
|
85
|
+
[PERSON]
|
|
86
|
+
name = myname
|
|
87
|
+
last_name = mylastname
|
|
88
|
+
```
|
|
89
|
+
<!-- //==================================================== -->
|
|
90
|
+
## getKeys
|
|
91
|
+
##### test.ini file
|
|
92
|
+
```ini
|
|
93
|
+
[PERSON]
|
|
94
|
+
name = myname
|
|
95
|
+
last_name = mylastname
|
|
96
|
+
```
|
|
97
|
+
##### python code
|
|
98
|
+
```py
|
|
99
|
+
from iniUts import IniUts
|
|
100
|
+
|
|
101
|
+
ini = IniUts('test.ini')
|
|
102
|
+
keys = ini.getKeys("PERSON")
|
|
103
|
+
print(keys)
|
|
104
|
+
```
|
|
105
|
+
##### output
|
|
106
|
+
```py
|
|
107
|
+
['name','last_name']
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
<!-- //==================================================== -->
|
|
111
|
+
## Section2Dict
|
|
112
|
+
##### test.ini file
|
|
113
|
+
```ini
|
|
114
|
+
[PERSON]
|
|
115
|
+
name = myname
|
|
116
|
+
age = 31
|
|
117
|
+
amount = 20.3
|
|
118
|
+
friends = friend1,friend2,friend3
|
|
119
|
+
dob = 1991-12-23
|
|
120
|
+
```
|
|
121
|
+
##### python code
|
|
122
|
+
```py
|
|
123
|
+
from iniUts import IniUts
|
|
124
|
+
|
|
125
|
+
ini = IniUts('test.ini')
|
|
126
|
+
ini.Section2Dict('PERSON')
|
|
127
|
+
print(Person)
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
##### output
|
|
131
|
+
```py
|
|
132
|
+
{
|
|
133
|
+
"name" = "myname"
|
|
134
|
+
"age" = "31"
|
|
135
|
+
"amount" = "20.3"
|
|
136
|
+
"friends" = "friend1,friend2,friend3"
|
|
137
|
+
"dob" = "1991-12-23"
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
<!-- //==================================================== -->
|
|
142
|
+
## link
|
|
143
|
+
##### test.ini file
|
|
144
|
+
```ini
|
|
145
|
+
[PERSON]
|
|
146
|
+
name = myname
|
|
147
|
+
age = 31
|
|
148
|
+
amount = 20.3
|
|
149
|
+
friends = friend1,friend2,friend3
|
|
150
|
+
dob = 1991-12-23
|
|
151
|
+
```
|
|
152
|
+
##### python code
|
|
153
|
+
```py
|
|
154
|
+
from iniUts import IniUts
|
|
155
|
+
from datetime import datetime
|
|
156
|
+
from dataclasses import dataclass
|
|
157
|
+
|
|
158
|
+
ini = IniUts('test.ini')
|
|
159
|
+
|
|
160
|
+
@ini.link('PERSON')
|
|
161
|
+
class Person():
|
|
162
|
+
name : str
|
|
163
|
+
age : int
|
|
164
|
+
amount : float
|
|
165
|
+
friends: tuple = ','
|
|
166
|
+
dob : datetime = "%Y-%m-%d"
|
|
167
|
+
|
|
168
|
+
print(Person.name)
|
|
169
|
+
print(Person.age)
|
|
170
|
+
print(Person.amount)
|
|
171
|
+
print(Person.friends)
|
|
172
|
+
print(Person.dob)
|
|
173
|
+
|
|
174
|
+
```
|
|
175
|
+
##### output
|
|
176
|
+
```py
|
|
177
|
+
myname
|
|
178
|
+
31
|
|
179
|
+
20.3
|
|
180
|
+
("friend1","friend2","friend3")
|
|
181
|
+
datetime.datetime(1991, 12, 2, 0, 0)
|
|
182
|
+
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
# ENCRYPTION
|
|
186
|
+
|
|
187
|
+
<!-- //==================================================== -->
|
|
188
|
+
## Using Encryption
|
|
189
|
+
##### test.ini file
|
|
190
|
+
```ini
|
|
191
|
+
[CREDENTIALS]
|
|
192
|
+
username = myuser
|
|
193
|
+
&_password = &_mypassword123
|
|
194
|
+
&_api_key = &_secret_api_key_12345
|
|
195
|
+
```
|
|
196
|
+
##### python code
|
|
197
|
+
```py
|
|
198
|
+
from iniUts import IniUts
|
|
199
|
+
|
|
200
|
+
# Initialize with encryption key
|
|
201
|
+
ini = IniUts('test.ini', encryption_key="my_secure_encryption_key_32_chars")
|
|
202
|
+
|
|
203
|
+
@ini.link('CREDENTIALS')
|
|
204
|
+
class Credentials():
|
|
205
|
+
username: str
|
|
206
|
+
password: str
|
|
207
|
+
api_key : str
|
|
208
|
+
|
|
209
|
+
print(Credentials.username)
|
|
210
|
+
print(Credentials.password)
|
|
211
|
+
print(Credentials.api_key)
|
|
212
|
+
|
|
213
|
+
# Save encrypted values back to file
|
|
214
|
+
Credentials.password = "new_secure_password"
|
|
215
|
+
Credentials.save()
|
|
216
|
+
```
|
|
217
|
+
##### output
|
|
218
|
+
```py
|
|
219
|
+
myuser
|
|
220
|
+
mypassword123
|
|
221
|
+
secret_api_key_12345
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
**Note:** When using encryption, the values in the INI file will be encrypted. The encryption key must be provided every time you read or write to the file.
|
|
225
|
+
|
|
226
|
+
# ENVIORNMENT CHANGING
|
|
227
|
+
|
|
228
|
+
<!-- //==================================================== -->
|
|
229
|
+
## Link
|
|
230
|
+
##### prd.ini file
|
|
231
|
+
```ini
|
|
232
|
+
[PERSON]
|
|
233
|
+
name = myName # Will be changed in DEV
|
|
234
|
+
age = 31
|
|
235
|
+
amount = 20.3
|
|
236
|
+
friends = friend1,friend2,friend3
|
|
237
|
+
dob = 1991-12-23
|
|
238
|
+
|
|
239
|
+
[CONFIG]
|
|
240
|
+
ip = <some_ip>
|
|
241
|
+
path = <some_path> # Will be changed in DEV
|
|
242
|
+
|
|
243
|
+
```
|
|
244
|
+
##### dev.ini file
|
|
245
|
+
```ini
|
|
246
|
+
[PERSON] #change only PERSON name
|
|
247
|
+
name = myOtherName
|
|
248
|
+
|
|
249
|
+
[CONFIG] #change only CONFIG path
|
|
250
|
+
path = <another_path>
|
|
251
|
+
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
##### python code
|
|
255
|
+
```py
|
|
256
|
+
from iniUts import IniUts,envar
|
|
257
|
+
from datetime import datetime
|
|
258
|
+
from dataclasses import dataclass
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
ini = IniUts('prd.ini','dev.ini',in_prd=True) #CHANGE S WILL BE MADE IF IN DEVELOPMENT MODE
|
|
262
|
+
|
|
263
|
+
@ini.link('PERSON')
|
|
264
|
+
class Person():
|
|
265
|
+
name : str
|
|
266
|
+
age : int
|
|
267
|
+
amount : float
|
|
268
|
+
friends: tuple = ','
|
|
269
|
+
dob : datetime = "%Y-%m-%d"
|
|
270
|
+
mode : envar(key='MODE',default='DEV')
|
|
271
|
+
|
|
272
|
+
@ini.link('CONFIG')
|
|
273
|
+
class Config():
|
|
274
|
+
ip : str
|
|
275
|
+
path : str
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
print(Person.name)
|
|
279
|
+
print(Person.age)
|
|
280
|
+
print(Config.ip)
|
|
281
|
+
print(Config.path)
|
|
282
|
+
|
|
283
|
+
```
|
|
284
|
+
##### output
|
|
285
|
+
```py
|
|
286
|
+
#==================== IN PRD
|
|
287
|
+
myName
|
|
288
|
+
31
|
|
289
|
+
<some_ip>
|
|
290
|
+
<some_path>
|
|
291
|
+
#==================== IN DEV
|
|
292
|
+
myOtherName
|
|
293
|
+
16
|
|
294
|
+
<some_ip>
|
|
295
|
+
<some_path>
|
|
296
|
+
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
Change Log
|
|
305
|
+
==========
|
|
306
|
+
|
|
307
|
+
1.0.0 (2023-04-26)
|
|
308
|
+
------------------
|
|
309
|
+
- First Release
|
|
310
|
+
|
|
311
|
+
1.0.1 (2023-04-26)
|
|
312
|
+
------------------
|
|
313
|
+
- Bug Fixed
|
|
314
|
+
|
|
315
|
+
1.0.2 (2023-04-26)
|
|
316
|
+
------------------
|
|
317
|
+
- Upercase Bug Fixed
|
|
318
|
+
|
|
319
|
+
1.0.3 (2023-04-26)
|
|
320
|
+
------------------
|
|
321
|
+
- Upercase Write Bug Fixed
|
|
322
|
+
|
|
323
|
+
1.0.4 (2023-04-28)
|
|
324
|
+
------------------
|
|
325
|
+
- Update Class bug fixed
|
|
326
|
+
|
|
327
|
+
1.0.6 (2023-04-28)
|
|
328
|
+
------------------
|
|
329
|
+
- empty_as_null
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
1.0.7 (2023-04-28)
|
|
333
|
+
------------------
|
|
334
|
+
- changing enviornment
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
1.3.0 (2025-12-01)
|
|
338
|
+
------------------
|
|
339
|
+
- encription and decription
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
iniUts/__init__.py,sha256=_qvnk6lBUg9LtHPoOz4R-6L103Vo2wwGwMvwbVlR3LA,234
|
|
2
|
+
iniUts/config_parser_ini.py,sha256=ToPJZTsS-5Oh2U-7UVEi7SzaN0KOUjr0Fy_sT1DVv4M,1777
|
|
3
|
+
iniUts/envar.py,sha256=URXpMed5Yg8PQxqAMwlA8u3lMQdBjRBgweRqJyCVelA,459
|
|
4
|
+
iniUts/iniUts.py,sha256=LfHzb8uD7It4jMyyT_d_9FDzvMdnPkrdSCLe2DWLkZ8,7225
|
|
5
|
+
iniUts/secret.py,sha256=pCRamcKp0oidOOxyyjjpmA7AQyxllOc4ARBaUIrqt3U,2092
|
|
6
|
+
iniuts-2.0.2.dist-info/licenses/LICENCE.txt,sha256=GUH9WcKnuaaIagVs7XjWYP1X3egwKzk9rPq_8JVK3Lw,1051
|
|
7
|
+
iniuts-2.0.2.dist-info/METADATA,sha256=UmbXdm8Ju3Zrcx4Dgv2Yzs3vFVCwJ0aDdHQmwvQ_yZY,5735
|
|
8
|
+
iniuts-2.0.2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
9
|
+
iniuts-2.0.2.dist-info/top_level.txt,sha256=1FCLwhTWytEuBDJNbNfuzVBmR3EOHnYNuGLtuuupjPc,7
|
|
10
|
+
iniuts-2.0.2.dist-info/RECORD,,
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
|
|
2
|
+
Copyright 2021 Melque Lima
|
|
3
|
+
|
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
5
|
+
|
|
6
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
7
|
+
|
|
8
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
iniUts
|