kax 0.1.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.
kax-0.1.0/LICENSE ADDED
File without changes
kax-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,41 @@
1
+ Metadata-Version: 2.3
2
+ Name: kax
3
+ Version: 0.1.0
4
+ Summary: Biblioteca cliente de sistema de almacenamiento en memoria.
5
+ Author: Julio Salazar
6
+ Author-email: julio.salazar@cinvestav.mx
7
+ Requires-Python: >=3.10
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.10
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Requires-Dist: certifi (==2025.6.15)
14
+ Requires-Dist: charset-normalizer (==3.4.2)
15
+ Requires-Dist: idna (==3.10)
16
+ Requires-Dist: paho-mqtt (==2.1.0)
17
+ Requires-Dist: python-dateutil (==2.9.0.post0)
18
+ Requires-Dist: pytz (==2025.2)
19
+ Requires-Dist: requests (==2.32.4)
20
+ Requires-Dist: six (==1.17.0)
21
+ Requires-Dist: tzdata (==2025.2)
22
+ Requires-Dist: urllib3 (==2.5.0)
23
+ Description-Content-Type: text/markdown
24
+
25
+
26
+ # Biblioteca cliente de sistema de almacenamiento basado en memoria
27
+
28
+ Biblioteca cliente para el sistema de almacenamiento en memoria "ESS v1". El cliente es capaz de cargar en datos memoria de forma local, enviar datos al sistema de almacenamiento en memoria y descargar datos del sistema de almacenamiento.
29
+
30
+ UTILIZAR EN SISTEMA OPERATIVO LINUX.
31
+
32
+ ## Authors
33
+
34
+ - [@juliocsp03](https://www.github.com/juliocsp03)
35
+
36
+
37
+ ## License
38
+
39
+ [MIT](https://choosealicense.com/licenses/mit/)
40
+
41
+
kax-0.1.0/README.md ADDED
@@ -0,0 +1,16 @@
1
+
2
+ # Biblioteca cliente de sistema de almacenamiento basado en memoria
3
+
4
+ Biblioteca cliente para el sistema de almacenamiento en memoria "ESS v1". El cliente es capaz de cargar en datos memoria de forma local, enviar datos al sistema de almacenamiento en memoria y descargar datos del sistema de almacenamiento.
5
+
6
+ UTILIZAR EN SISTEMA OPERATIVO LINUX.
7
+
8
+ ## Authors
9
+
10
+ - [@juliocsp03](https://www.github.com/juliocsp03)
11
+
12
+
13
+ ## License
14
+
15
+ [MIT](https://choosealicense.com/licenses/mit/)
16
+
File without changes
kax-0.1.0/kax/acts.py ADDED
@@ -0,0 +1,211 @@
1
+ import requests
2
+ from multiprocessing import shared_memory, Pool
3
+ from . import fsOperations as fso
4
+ from . import mem
5
+ from . import sub
6
+ from . import pub
7
+
8
+ url = "localhost"
9
+ urlStorage = "localhost"
10
+ chunk_size = 1048576
11
+ workers = 1
12
+ originFile = None
13
+ use_workers = True
14
+
15
+ def loadRes(path, s = False, load_to_memory = True):
16
+ mem.remove_shm_from_resource_tracker()
17
+ metadata = fso.getMetadata(path)
18
+ # print(json.dumps(metadata))
19
+ segments = []
20
+ for index, resource in enumerate(metadata["resources"]): # GENERA LOS ESPACIOS DE MEMEORIA
21
+ try: # SI YA EXISTEN SE AGREGA UN CAMPO 'local' CON VALOR 'True'
22
+ shm = shared_memory.SharedMemory(name = resource['hash'], create = False)
23
+ metadata["resources"][index]['local'] = True
24
+ shm.close()
25
+ except FileNotFoundError: # SI NO EXISTEN SE AGREGA UN CAMPO 'local' CON VALOR 'False'
26
+ if (load_to_memory):
27
+ mem.createLocalSharedMemorySpace(resource['hash'], resource['size'])
28
+ metadata["resources"][index]['local'] = False
29
+ except Exception as e:
30
+ return
31
+ for file in metadata["resources"]: # OBTENER LOS SEGMENTOS DE LOS ARCHIVOS QUE NO ESTÁN EN MEMORIA
32
+ if(file['local'] == False or file['local'] == True):
33
+ segments+=fso.array_of_segments(file['size'], file["path"], file['hash'], chunk_size)
34
+
35
+ if (load_to_memory):
36
+ with Pool(processes = workers) as p: # CARGARLOS EN MEMORIA A TRAVÉS DE TRABAJADORES
37
+ p.map(mem.loadToLocalSharedMemory, segments)
38
+
39
+ for index, resource in enumerate(metadata["resources"]):
40
+ if(resource['local'] == False):
41
+ metadata["resources"][index]['local'] = True
42
+ if s:
43
+ return metadata, segments
44
+ else:
45
+ return metadata
46
+
47
+ def loadAndUploadToCloud(path):
48
+ res, segments = loadRes(path, True)
49
+ res = uploadToCloud(res, segments)
50
+ return res
51
+
52
+
53
+ def uploadToCloudFromDisk(path):
54
+ res, segments = loadRes(path, True, False)
55
+ res = uploadToCloud(res, segments, "Disk")
56
+ return res
57
+
58
+ def uploadToCloudFromMemory(res):
59
+ # res, segments = loadRes(path, True, False)
60
+ segments = []
61
+ segments+=fso.array_of_segments(res['resources'][0]['size'], res['resources'][0]["path"], res['resources'][0]['hash'], chunk_size)
62
+ res = uploadToCloud(res, segments, "Memory")
63
+ return res
64
+
65
+ def uploadToCloud(res = None, segments = False, origin = "Memory"):
66
+ global originFile
67
+ originFile = origin
68
+ mem.remove_shm_from_resource_tracker()
69
+ for index, resource in enumerate(res["resources"]):
70
+ # print("Verificando en la nube el recurso: ", resource['hash'])
71
+ verification = verify(resource['hash'])
72
+ # print(verification)
73
+ if verification:
74
+ # print("Verificación: ", verification)
75
+ if (verification['file']['state']=="reserving"):
76
+ sub.subToTopic(resource['hash'])
77
+ verification['file']['state']="ready"
78
+ res['resources'][index] = verification['file']
79
+ elif (verification['file']['state']=="ready"):
80
+ res['resources'][index] = verification['file']
81
+ # print("Archivo listo para consumo")
82
+ pass
83
+ else:
84
+ return False
85
+ else:
86
+ if 'local' in resource: resource.pop("local")
87
+ resource['state'] = "reserving"
88
+ register = registry(resource)
89
+ pub.pubTopic(resource['hash'], "loading")
90
+ if (register["created"]):
91
+ response_reserve = requests.post(f"{urlStorage}/reserve", json={"name":resource['hash'], "size": resource['size']})
92
+ segments_res = list(filter(lambda x : f"{x['segment']}" == resource['hash'], segments))
93
+ if use_workers:
94
+ with Pool(processes = workers) as p:
95
+ p.map(sendToCloud, segments_res)
96
+ else:
97
+ data = None
98
+ for segment in segments_res:
99
+ if (originFile == "Memory"):
100
+ data = mem.getChunk(segment['segment'], segment['start_position'], segment['end_position'])
101
+ else:
102
+ with open(segment['origin'], "rb") as file:
103
+ file.seek(segment['start_position'])
104
+ data = file.read(segment['chunk_size'])
105
+ files = {"files": data}
106
+ response_fill = requests.post(f"{urlStorage}/fill", files = files, data = segment)
107
+ resUpdateState = update(resource['hash'], "ready")
108
+ pub.pubTopic(resource['hash'], "ready")
109
+ originFile = None
110
+ return res
111
+
112
+ def sendToCloud(segment):
113
+ global originFile
114
+ # print("worker: ", os.getpid(), "\tSegment: ", segment['segment'])
115
+ data = None
116
+ if (originFile == "Memory"):
117
+ data = mem.getChunk(segment['segment'], segment['start_position'], segment['end_position'])
118
+ else:
119
+ with open(segment['origin'], "rb") as file:
120
+ file.seek(segment['start_position'])
121
+ data = file.read(segment['chunk_size'])
122
+ files = {"files": data}
123
+ response_fill = requests.post(f"{urlStorage}/fill", files = files, data = segment)
124
+
125
+ def downloadFromCloud(hash):
126
+ mem.remove_shm_from_resource_tracker()
127
+ md = verify(hash)
128
+ md = md['file']
129
+ original_hash = md['hash']
130
+ mem.createLocalSharedMemorySpace(md['hash'], md['size'])
131
+ segments_to_get = fso.array_of_segments_for_server_file(md['size'], original_hash, chunk_size)
132
+ for segment in segments_to_get:
133
+ response = requests.post(f"{urlStorage}/chunk", data = segment)
134
+ mem.loadDowloadedToLocalSharedMemory(md['hash'], segment['start_position'], segment['end_position'], response.content)
135
+ return {"message": True, "file": md['hash']}
136
+
137
+ def verify(hash):
138
+ try:
139
+ response = requests.post(f"{url}/hash", json = {"hash": hash})
140
+ if response.json()['registered']:
141
+ return response.json()
142
+ else:
143
+ return False
144
+ except requests.exceptions.RequestException as err:
145
+ return {"message": "Error"}
146
+
147
+ def update(hash, state):
148
+ try:
149
+ response = requests.post(f"{url}/update-file-state", json = {"hash": hash, "state": state})
150
+ if response.json():
151
+ return response.json()
152
+ else:
153
+ return False
154
+ except requests.exceptions.RequestException as err:
155
+ return {"message": "Error"}
156
+
157
+ def registry(rmd):
158
+ try:
159
+ response = requests.post(f"{url}/register-file", json=rmd)
160
+ return response.json()
161
+ except requests.exceptions.RequestException as err:
162
+ pass
163
+
164
+ def removeShm(shm_name):
165
+ mem.removeShm(shm_name)
166
+
167
+ def getRes(file):
168
+ return mem.getData(file)
169
+
170
+ def saveFile(path, data, name):
171
+ fso.saveFile(path, data, name)
172
+
173
+ def setWorkers(w):
174
+ global workers
175
+ workers = w
176
+
177
+ def setChunkSize(cs):
178
+ global chunk_size
179
+ chunk_size = cs
180
+
181
+ def setUrl(u):
182
+ global url
183
+ url = u
184
+
185
+ def setStorageUrl(u):
186
+ global urlStorage
187
+ urlStorage = u
188
+
189
+ def getWorkers():
190
+ return workers
191
+
192
+ def getChunkSize():
193
+ return chunk_size
194
+
195
+ def getUrl():
196
+ return url
197
+
198
+ def getStorageUrl():
199
+ return urlStorage
200
+
201
+ def setBrokerUrl(url):
202
+ pub.setBrokerUrl(url)
203
+ sub.setBrokerUrl(url)
204
+
205
+ def setBrokerPort(p):
206
+ pub.setBrokerPort(p)
207
+ sub.setBrokerPort(p)
208
+
209
+ def setUseWorkers(value):
210
+ global use_workers
211
+ use_workers = value
@@ -0,0 +1,93 @@
1
+ import os
2
+ import hashlib
3
+
4
+ def getMetadata(res):
5
+ type = None
6
+ data = []
7
+ if (os.path.isdir(res)):
8
+ data = dirs(res)
9
+ type = "directorio"
10
+ elif (os.path.isfile(res)):
11
+ data.append(files(res))
12
+ type = "archivo"
13
+ else:
14
+ print("Entrada inválida")
15
+ return {"type": type, "resources": data}
16
+
17
+ def getSegmentData(segment):
18
+ data = None
19
+ with open(segment['origin'], "rb") as file:
20
+ file.seek(segment['start_position'])
21
+ data = file.read(segment['chunk_size'])
22
+ return data
23
+
24
+ def files(res):
25
+ with open(res, "rb") as file:
26
+ digest = getFileHash(res)
27
+ return {"hash": digest, "path": res, "size": os.path.getsize(res), "extension": os.path.splitext(res)[1], "state": "none", "temperature": "none", "owners": "none" }
28
+
29
+ def getFileHash(path):
30
+ hash_func = getattr(hashlib, "sha256")()
31
+ with open(path, "rb") as file:
32
+ while chunk := file.read(1048576):
33
+ hash_func.update(chunk)
34
+ return hash_func.hexdigest()
35
+
36
+ def saveFile(path, data, name):
37
+ with open(f"{path}{name}", "wb") as file:
38
+ file.write(data)
39
+
40
+ def dirs(res):
41
+ list_metadata = []
42
+ for element in os.listdir(res):
43
+ if os.path.isdir(res + '/' + element):
44
+ list_metadata = list_metadata + dirs(res + '/' + element)
45
+ elif os.path.isfile(res + '/' + element):
46
+ list_metadata.append(files(f"{res}/{element}"))
47
+ return list_metadata
48
+
49
+ def array_of_segments(file_size, path, segment_name, chunk_size):
50
+ # INICIALIZA POSICIÓN EN 0
51
+ position = 0
52
+ # DEFINE EL TAMAÑO DEL SEGMENTO
53
+ chunk = chunk_size
54
+ # OBTIENE EL NOMBRE Y EXTENSIÓN DEL ARCHIVO
55
+ filename, extension = os.path.splitext(os.path.basename(path))
56
+ # DEFINE ARREGLO DE POSICIONES
57
+ positions = []
58
+ while position < file_size:
59
+ # DEFINE POSICIÓN FINAL DE UN CHUNK
60
+ end_position = position + chunk if ((position + chunk) < file_size) else position + (file_size - position)
61
+ # AGREGA ELEMENTO AL ARREGLO DE POSICIONES
62
+ positions.append({"origin": path, "filename": filename, "extension": extension,"start_position": position, "end_position": end_position, "file_size": file_size, "segment": segment_name, "chunk_size": chunk})
63
+ # ACTUALIZA LA POSICIÓN
64
+ position = position + chunk
65
+ # RETORNA LAS POSICIONES
66
+ return positions
67
+
68
+ def array_of_segments_for_server_file(file_size, segment_name, chunk_size):
69
+ # INICIALIZA POSICIÓN EN 0
70
+ position = 0
71
+ # DEFINE EL TAMAÑO DEL SEGMENTO
72
+ chunk = chunk_size
73
+ # OBTIENE EL NOMBRE Y EXTENSIÓN DEL ARCHIVO
74
+ # filename, extension = os.path.splitext(os.path.basename(path))
75
+ # DEFINE ARREGLO DE POSICIONES
76
+ positions = []
77
+ while position < file_size:
78
+ # DEFINE POSICIÓN FINAL DE UN CHUNK
79
+ end_position = position + chunk if ((position + chunk) < file_size) else position + (file_size - position)
80
+ # AGREGA ELEMENTO AL ARREGLO DE POSICIONES
81
+ positions.append({"start_position": position, "end_position": end_position, "file_size": file_size, "segment": segment_name, "chunk_size": chunk})
82
+ # ACTUALIZA LA POSICIÓN
83
+ position = position + chunk
84
+ # RETORNA LAS POSICIONES
85
+ return positions
86
+
87
+
88
+ # if __name__ == '__main__':
89
+ # response = get_metadata("/home/julio/objects")
90
+ # # response = metadata_extractor("/home/julio/objects/one/f.txt")
91
+ # # response = dirs("/home/julio/objects")
92
+ # # print()
93
+ # print(json.dumps(response))
kax-0.1.0/kax/mem.py ADDED
@@ -0,0 +1,71 @@
1
+ from multiprocessing import shared_memory, Pool
2
+ from multiprocessing import resource_tracker
3
+
4
+ def createLocalSharedMemorySpace(name, size):
5
+ # self.remove_shm_from_resource_tracker()
6
+ try:
7
+ shm = shared_memory.SharedMemory(create=True, size = size, name = name)
8
+ # resource_tracker.unregister(shm._name, 'shared_memory')
9
+ # shm.close()
10
+ except Exception as e:
11
+ removeShm(name)
12
+ shm = shared_memory.SharedMemory(create=True, size = size, name = name)
13
+ finally:
14
+ resource_tracker.unregister(shm._name, 'shared_memory')
15
+ shm.close()
16
+ return shm.name
17
+ # shm = shared_memory.SharedMemory(create=True, size = size, name = name)
18
+ # resource_tracker.unregister(shm._name, 'shared_memory')
19
+ # shm.close()
20
+ # return shm.name
21
+
22
+ def loadToLocalSharedMemory(segment):
23
+ shm = shared_memory.SharedMemory(name = segment['segment'], create=False)
24
+ with open(segment['origin'], "rb") as file:
25
+ file.seek(segment['start_position'])
26
+ shm.buf[segment['start_position']:segment['end_position']] = file.read(segment['chunk_size'])
27
+ shm.close()
28
+
29
+ def loadDowloadedToLocalSharedMemory(shm_name, start_position, end_position, data):
30
+ shm = shared_memory.SharedMemory(name = shm_name, create=False)
31
+ shm.buf[start_position:end_position] = data
32
+ shm.close()
33
+
34
+ def getChunk(shm_name, start_position, end_position):
35
+ shm = shared_memory.SharedMemory(name = shm_name, create=False)
36
+ data = bytes(shm.buf[int(start_position):int(end_position)])
37
+ shm.close()
38
+ return data
39
+
40
+ def getData(file):
41
+ shm = shared_memory.SharedMemory(name = file, create=False)
42
+ data = bytes(shm.buf[:])
43
+ resource_tracker.unregister(shm._name, 'shared_memory')
44
+ shm.close()
45
+ return data
46
+
47
+ def removeShm(shm_name):
48
+ shm = shared_memory.SharedMemory(name = shm_name, create=False)
49
+ shm.close()
50
+ shm.unlink()
51
+
52
+ def remove_shm_from_resource_tracker():
53
+ """Monkey-patch multiprocessing.resource_tracker so SharedMemory won't be tracked
54
+
55
+ More details at: https://bugs.python.org/issue38119
56
+ """
57
+
58
+ def fix_register(name, rtype):
59
+ if rtype == "shared_memory":
60
+ return
61
+ return resource_tracker._resource_tracker.register(self, name, rtype)
62
+ resource_tracker.register = fix_register
63
+
64
+ def fix_unregister(name, rtype):
65
+ if rtype == "shared_memory":
66
+ return
67
+ return resource_tracker._resource_tracker.unregister(self, name, rtype)
68
+ resource_tracker.unregister = fix_unregister
69
+
70
+ if "shared_memory" in resource_tracker._CLEANUP_FUNCS:
71
+ del resource_tracker._CLEANUP_FUNCS["shared_memory"]
kax-0.1.0/kax/pub.py ADDED
@@ -0,0 +1,76 @@
1
+ import paho.mqtt.client as mqtt
2
+ import time
3
+
4
+ # broker = "148.247.201.226"
5
+ broker = "localhost"
6
+ port = 1883
7
+ topic = "files/"
8
+ pub_confirmed = False
9
+ def on_connect(client, userdata, flags, rc, properties = None):
10
+ if rc == 0:
11
+ # print("Conectado al broker MQTT!")
12
+ pass
13
+ else:
14
+ # print(f"Fallo al conectar, código de retorno: {rc}")
15
+ pass
16
+
17
+ def on_publish(client, userdata, mid, rc, properties=None):
18
+ global pub_confirmed
19
+ if rc == 0:
20
+ # print(f"Mensaje con ID {mid} publicado y confirmado.")
21
+ pub_confirmed = True
22
+ else:
23
+ # print(f"Fallo al publicar el mensaje con ID {mid}.")
24
+ pub_confirmed = False
25
+
26
+ client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
27
+
28
+ def pubTopic(hash = "test-hash", value = "test value"):
29
+ client.on_connect = on_connect
30
+ client.on_publish = on_publish
31
+ try:
32
+ client.connect(broker, port, 60)
33
+ except Exception as e:
34
+ # print(f"Error al conectar: {e}")
35
+ exit()
36
+
37
+ client.loop_start()
38
+
39
+ try:
40
+ result = client.publish(f"{topic}{hash}", value, retain=True, qos = 1)
41
+ timeout = time.time() + 10
42
+ while not pub_confirmed and time.time() < timeout:
43
+ time.sleep(0.1)
44
+ if (pub_confirmed):
45
+ # print(f"Publicado exitosamente '{value}' en el tema '{topic}{hash}'")
46
+ client.loop_stop()
47
+ client.disconnect()
48
+ # print("Publicador detenido.")
49
+ return {"pub": True}
50
+ else:
51
+ # print(f"Fallo al publicar mensaje al tema {topic}")
52
+ client.loop_stop()
53
+ client.disconnect()
54
+ # print("Publicador detenido.")
55
+ return {"pub": False}
56
+ # if result[0] == 0:
57
+ # print(f"Publicado exitosamente '{value}' en el tema '{topic}{hash}'")
58
+ # return {"pub": True}
59
+ # else:
60
+ # # print(f"Fallo al publicar mensaje al tema {topic}")
61
+ # return {"pub": False}
62
+ # client.loop_stop()
63
+ # client.disconnect()
64
+ # print("Publicador detenido.")
65
+
66
+ except KeyboardInterrupt:
67
+ # print("Fallo inesperado.")
68
+ pass
69
+
70
+ def setBrokerUrl(url):
71
+ global broker
72
+ broker = url
73
+
74
+ def setBrokerPort(p):
75
+ global port
76
+ port = p
kax-0.1.0/kax/sub.py ADDED
@@ -0,0 +1,55 @@
1
+ import paho.mqtt.client as mqtt
2
+ import time
3
+
4
+ # broker = "148.247.201.226"
5
+ broker = "localhost"
6
+ port = 1883
7
+ topic = "files/"
8
+ hash = "ejemplo"
9
+ file_state = None
10
+ waiting = True
11
+
12
+ def on_connect(client, userdata, flags, rc, properties = None):
13
+ if rc == 0:
14
+ client.subscribe(f"{topic}{hash}", qos = 1)
15
+ else:
16
+ # print(f"Fallo al conectar, código: {rc}")
17
+ pass
18
+
19
+ def on_message(client, userdata, msg):
20
+ # print(f"Mensaje recibido: {msg.payload.decode()} en el tema: {msg.topic}")
21
+ if (msg.payload.decode() == "ready"):
22
+ # print("ENTRANDO A CANCELAR")
23
+ global waiting
24
+ waiting = False
25
+ # client.loop_stop()
26
+
27
+ client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
28
+
29
+ def subToTopic(topic_hash):
30
+ global hash
31
+ hash = topic_hash
32
+
33
+ client.on_connect = on_connect
34
+ client.on_message = on_message
35
+
36
+ try:
37
+ client.connect(broker, port)
38
+ # client.loop_forever()
39
+ client.loop_start()
40
+ while waiting:
41
+ time.sleep(0.3)
42
+ except Exception as e:
43
+ # print(f"Error al conectar: {e}")
44
+ exit()
45
+ finally:
46
+ client.loop_stop()
47
+ client.disconnect()
48
+
49
+ def setBrokerUrl(url):
50
+ global broker
51
+ broker = url
52
+
53
+ def setBrokerPort(p):
54
+ global port
55
+ port = p
@@ -0,0 +1,26 @@
1
+ [project]
2
+ name = "kax"
3
+ version = "0.1.0"
4
+ description = "Biblioteca cliente de sistema de almacenamiento en memoria."
5
+ authors = [
6
+ {name = "Julio Salazar", email = "julio.salazar@cinvestav.mx"}
7
+ ]
8
+ readme = "README.md"
9
+ requires-python = ">=3.10"
10
+ dependencies = [
11
+ "certifi==2025.6.15",
12
+ "charset-normalizer==3.4.2",
13
+ "idna==3.10",
14
+ "paho-mqtt==2.1.0",
15
+ "python-dateutil==2.9.0.post0",
16
+ "pytz==2025.2",
17
+ "requests==2.32.4",
18
+ "six==1.17.0",
19
+ "tzdata==2025.2",
20
+ "urllib3==2.5.0"
21
+ ]
22
+
23
+
24
+ [build-system]
25
+ requires = ["poetry-core>=2.0.0,<3.0.0"]
26
+ build-backend = "poetry.core.masonry.api"