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 ADDED
@@ -0,0 +1,13 @@
1
+ from iniUts.iniUts import IniUts
2
+ from iniUts.config_parser_ini import IniCp
3
+ from iniUts.secret import decrypt, encrypt
4
+ from iniUts.envar import Envar
5
+
6
+
7
+ __all__ = [
8
+ 'IniUts',
9
+ 'IniCp',
10
+ 'decrypt',
11
+ 'encrypt',
12
+ 'Envar'
13
+ ]
@@ -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,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.10.2)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -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