PyRAMObjects 0.1.0__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.
PyRAM.py ADDED
@@ -0,0 +1,623 @@
1
+ from PyRAMErrors import (
2
+ PyRAMError, ComponentNotInstalledError,
3
+ DiskAlreadyExistsError, DiskNotFoundError,
4
+ DiskCreationError, IncorrectSymbolInPath,
5
+ NoDiskInPath, FolderNotFile, FileNotFound,
6
+ PathNotFound, NotEnoughRAMError,
7
+ DiskNotAlreadyExistsError, FolderAlreadyExistsError,
8
+ FolderNotAlreadyExistsError, ProcessAlreadyExistsError,
9
+ ProcessNotAlreadyExistsError, WindowsDiskDeleteError,
10
+ ZeroOrNegativeRAMError, FailedToFormatDisk,
11
+ WindowsDiskFormatError, ForeignDiskError, InputError,
12
+ IncorrectDiskName
13
+ )
14
+ from typing import Any
15
+ from pathlib import Path
16
+ import subprocess
17
+ import sys
18
+ import os
19
+ import platform
20
+ from warnings import deprecated
21
+ import warnings
22
+ from dataclasses import dataclass
23
+ import ctypes
24
+ import shutil
25
+ import re
26
+
27
+ MARKER = ".pyram_owned"
28
+ FILE_READ_ONLY_VOLUME = 0x00080000
29
+ IMDISK_UNIT_MIN, IMDISK_UNIT_MAX = 100, 199
30
+
31
+ if platform.system() != "Windows":
32
+ raise RuntimeError("Library only for Windows")
33
+
34
+ try:
35
+ import psutil
36
+ psutil_instaled = True
37
+ except(ImportError, ImportWarning):
38
+ psutil_instaled = False
39
+
40
+ try:
41
+ import winfspy
42
+ winfspy_installed = True
43
+ except(ImportError, ImportWarning):
44
+ warnings.warn("Warning: winfspy not installed!\nPls download winfspy on site https://winfsp.dev/ and download library winfspy.", RuntimeWarning)
45
+ winfspy_installed = False
46
+
47
+ def _find_imdisk() -> str:
48
+ exe = shutil.which("imdisk")
49
+ if exe:
50
+ return exe
51
+ for base in (os.environ.get("SystemRoot", r"C:\Windows"),):
52
+ for sub in ("System32", "Sysnative"):
53
+ p = os.path.join(base, sub, "imdisk.exe")
54
+ if os.path.isfile(p):
55
+ return p
56
+ raise ComponentNotInstalledError("ImDisk not found. Install ImDisk Toolkit: https://sourceforge.net/projects/imdisk-toolkit/")
57
+
58
+ class MemoryObject:
59
+ def __init__(self):
60
+ self.objects = {}
61
+
62
+ def write(self, path, value) -> None:
63
+ self.objects[path] = value
64
+
65
+ def read(self, path) -> Any:
66
+ try:
67
+ return self.objects[path]
68
+ except(KeyError):
69
+ raise KeyError(f"Object '{path}', not found!")
70
+
71
+ def delete(self, path) -> None:
72
+ del self.objects[path]
73
+
74
+ def object_sum(self, path1, path2) -> Any:
75
+ object1 = self.read(path=path1)
76
+ object2 = self.read(path=path2)
77
+
78
+ try:
79
+ return object1 + object2
80
+ except(TypeError):
81
+ raise TypeError(f"Can not concatenate '{object1}:{type(object1)}' and '{object2}:{type(object2)}'")
82
+
83
+ def object_sub(self, path1, path2) -> Any:
84
+ object1 = self.read(path=path1)
85
+ object2 = self.read(path=path2)
86
+
87
+ try:
88
+ return object1 - object2
89
+ except(TypeError):
90
+ raise TypeError(f"Can not subtract '{object1}:{type(object1)}' and '{object2}:{type(object2)}'")
91
+
92
+ def object_div(self, path1, path2) -> int | float | bool | complex:
93
+ object1 = self.read(path=path1)
94
+ object2 = self.read(path=path2)
95
+
96
+ try:
97
+ return object1 / object2
98
+ except(TypeError):
99
+ raise TypeError(f"Can not divide '{object1}:{type(object1)}' and '{object2}:{type(object2)}'")
100
+
101
+ def object_mul(self, path1, path2) -> Any:
102
+ object1 = self.read(path=path1)
103
+ object2 = self.read(path=path2)
104
+
105
+ try:
106
+ return object1 * object2
107
+ except(TypeError):
108
+ raise TypeError(f"Can not multiply '{object1}:{type(object1)}' and '{object2}:{type(object2)}'")
109
+
110
+ def object_equal(self, path1, path2) -> bool:
111
+ object1 = self.read(path=path1)
112
+ object2 = self.read(path=path2)
113
+
114
+ if object1 == object2:
115
+ return True
116
+ elif object1 != object2:
117
+ return False
118
+ else:
119
+ raise RuntimeError(f"Objects cannot be compared, {object1}:{type(object1)} and {object2}:{type(object2)}")
120
+
121
+ class MemoryPythonFolder:
122
+ def __init__(self):
123
+ self.filesystem = {}
124
+ self.default_disk = "C"
125
+
126
+ def write(self, path: str, value=None) -> None:
127
+ disk = path.split(":")[0]
128
+
129
+ if len(disk) > 1:
130
+ raise NoDiskInPath("Pls enter disk, for example 'C:/example/popato.txt'")
131
+
132
+ path_without_disk = path.split(":")[1]
133
+ blocked_symbol = {":", ";", ","}
134
+ clean_path = Path(path)
135
+ list_of_parts = clean_path.parts
136
+ # file_name = clean_path.name
137
+
138
+ if any(symbol in path_without_disk for symbol in blocked_symbol):
139
+ raise IncorrectSymbolInPath("Pls do not use ':', ';', ',' in path, for example 'C:/example/popato.txt'")
140
+
141
+ current = self.filesystem
142
+
143
+ for part in list_of_parts[:-1]:
144
+ if current == part:
145
+ current = current[part]
146
+ continue
147
+ current = current.setdefault(part, {})
148
+
149
+ if list_of_parts[-1] is not None and value is not None:
150
+ current[list_of_parts[-1]] = value
151
+ elif list_of_parts[-1] is not None and value is None:
152
+ current[list_of_parts[-1]] = {}
153
+
154
+ return self.filesystem
155
+
156
+ def read(self, path: str) -> Any:
157
+ disk = path.split(":")[0]
158
+
159
+ if len(disk) > 1:
160
+ raise NoDiskInPath("Pls enter disk, for example 'C:/example/popato.txt'")
161
+
162
+ path_without_disk = path.split(":")[1]
163
+ blocked_symbol = {":", ";", ","}
164
+ clean_path = Path(path)
165
+ list_of_parts = clean_path.parts
166
+
167
+ if any(symbol in path_without_disk for symbol in blocked_symbol):
168
+ raise IncorrectSymbolInPath("Pls do not use ':', ';', ',' in path, for example 'C:/example/popato.txt'")
169
+
170
+ current = self.filesystem
171
+
172
+ for part in list_of_parts:
173
+ try:
174
+ current = current[part]
175
+ except(KeyError):
176
+ raise FileNotFound(f"File {path} not found")
177
+
178
+ return current
179
+
180
+ def listdir(self, path: str) -> Any:
181
+ disk = path.split(":")[0]
182
+
183
+ if len(disk) > 1:
184
+ raise NoDiskInPath("Pls enter disk, for example 'C:/example/popato.txt'")
185
+
186
+ path_without_disk = path.split(":")[1]
187
+ blocked_symbol = {":", ";", ","}
188
+ clean_path = Path(path)
189
+ list_of_parts = clean_path.parts
190
+
191
+ if any(symbol in path_without_disk for symbol in blocked_symbol):
192
+ raise IncorrectSymbolInPath("Pls do not use ':', ';', ',' in path, for example 'C:/example/popato.txt'")
193
+
194
+ current = self.filesystem
195
+
196
+ for part in list_of_parts:
197
+ try:
198
+ current = current[part]
199
+ except(KeyError):
200
+ raise FileNotFound(f"File {path} not found")
201
+
202
+ if isinstance(current, str):
203
+ raise FolderNotFile("Path must end by folder, not file!")
204
+
205
+ return list(current.keys())
206
+
207
+ def delete(self, path: str) -> None:
208
+ disk = path.split(":")[0]
209
+
210
+ if len(disk) > 1:
211
+ raise NoDiskInPath("Pls enter disk, for example 'C:/example/popato.txt'")
212
+
213
+ path_without_disk = path.split(":")[1]
214
+ blocked_symbol = {":", ";", ","}
215
+ clean_path = Path(path)
216
+ list_of_parts = clean_path.parts
217
+
218
+ if any(symbol in path_without_disk for symbol in blocked_symbol):
219
+ raise IncorrectSymbolInPath("Pls do not use ':', ';', ',' in path, for example 'C:/example/popato.txt'")
220
+
221
+ current = self.filesystem
222
+
223
+ for part in list_of_parts[:-1]:
224
+ try:
225
+ current = current[part]
226
+ except KeyError:
227
+ raise PathNotFound(f"Path '{path}' not found")
228
+
229
+ try:
230
+ del current[list_of_parts[-1]]
231
+ except KeyError:
232
+ raise FileNotFound(f"File '{path}' not found")
233
+
234
+ class MemoryStorage:
235
+ def __init__(self, cleanup_on_exit: bool = False):
236
+ self.ImDiskPath = _find_imdisk()
237
+ self._cleanup_on_exit = cleanup_on_exit
238
+ self.disks = {}
239
+ self.folders = {}
240
+ self.process = {}
241
+
242
+ for d in self.get_all_drives():
243
+ drive = d.rstrip("\\")
244
+ self.disks[drive] = self._unit_by_letter(disk_name=drive)
245
+
246
+ def _is_owned(self, drive: str) -> bool:
247
+ unit = self._unit_by_letter(drive)
248
+ if unit is None:
249
+ return False
250
+ if not (IMDISK_UNIT_MIN <= unit <= IMDISK_UNIT_MAX):
251
+ return False
252
+
253
+ return True
254
+
255
+ def _is_admin(self) -> bool:
256
+ try:
257
+ return bool(ctypes.windll.shell32.IsUserAnAdmin())
258
+ except Exception:
259
+ return False
260
+
261
+ def check_disk_formatted(self, disk_name: str) -> bool:
262
+ if psutil_instaled == False:
263
+ raise ComponentNotInstalledError("Psutil not installed, please do it")
264
+
265
+ disk_name = self._normalizate_disk_name(disk_name)
266
+
267
+ partitions = psutil.disk_partitions(all=True)
268
+
269
+ for p in partitions:
270
+ if p.device.startswith(disk_name) or p.mountpoint.startswith(disk_name):
271
+ if p.fstype and p.fstype.upper() != 'RAW':
272
+ return True
273
+ else:
274
+ return False
275
+
276
+ return False
277
+
278
+ def _normalizate_disk_name(self, name: str) -> str:
279
+ if not isinstance(name, str):
280
+ raise InputError("Disk name must be string!")
281
+
282
+ name = name.upper()
283
+
284
+ if not name.endswith(":"):
285
+ name += ":"
286
+
287
+ return name
288
+
289
+ def _formatdr(self, disk_name: str) -> bool:
290
+ success = re.fullmatch(r"[A-Z]:?", disk_name) and len(disk_name) == 2
291
+ return bool(success)
292
+
293
+ def _imdisk(self, *args, timeout=60) -> subprocess.CompletedProcess[str]:
294
+ exe = self.ImDiskPath
295
+ r = subprocess.run([exe, *args], capture_output=True, text=True, timeout=timeout)
296
+ return r
297
+
298
+ def get_all_drives(self) -> list:
299
+ drives = os.listdrives()
300
+ return drives
301
+
302
+ def get_windows_disk(self) -> str:
303
+ return os.environ.get('SystemDrive')
304
+
305
+ def get_disk_info(self, disk_name: str) -> dict:
306
+ disk_name = self._normalizate_disk_name(name=disk_name)
307
+
308
+ owner = None
309
+ is_formatted = None
310
+ total_bytes = None
311
+ used_bytes = None
312
+ free_bytes = None
313
+ used_perstange = None
314
+ partitions = None
315
+ fs_type = None
316
+ mountpoint = None
317
+ opts = None
318
+
319
+ try:
320
+ disk_value = self.disks[disk_name]
321
+ except(KeyError):
322
+ raise DiskNotAlreadyExistsError(f"Disk: '{disk_name.rstrip(":")}' not already exists")
323
+
324
+ if self.get_windows_disk() == disk_name:
325
+ owner = "System"
326
+ elif isinstance(disk_value, subprocess.Popen) or self._is_owned(drive=disk_name):
327
+ owner = "PyRAM"
328
+ else:
329
+ owner = "Unknown"
330
+
331
+ is_formatted = self.check_disk_formatted(disk_name=disk_name)
332
+
333
+ if is_formatted and psutil_instaled:
334
+ try:
335
+ usage = psutil.disk_usage(disk_name)
336
+
337
+ total_bytes = usage.total
338
+ used_bytes = usage.used
339
+ free_bytes = usage.free
340
+ used_perstange = usage.percent
341
+ except:
342
+ pass
343
+
344
+ elif not psutil_instaled:
345
+ warnings.warn("Psutil not installed, some data may be unavailable")
346
+
347
+ if psutil_instaled:
348
+ partitions = psutil.disk_partitions(all=True)
349
+
350
+ for p in partitions:
351
+ if p.device.upper().rstrip(r"\\") == disk_name:
352
+ try:
353
+ fs_type = p.fstype
354
+ mountpoint = p.mountpoint
355
+ opts = p.opts
356
+ except:
357
+ pass
358
+
359
+ return DiskInfo(drive=disk_name, owner=owner,is_formatted=is_formatted, drive_opts=opts, fs=fs_type, label=self.get_drive_label(drive_letter=disk_name),
360
+ total_bytes=total_bytes, used_bytes=used_bytes, free_bytes=free_bytes, used_percent=used_perstange,
361
+ readonly=self.is_drive_readonly(drive_letter=disk_name), mountpoint=mountpoint)
362
+
363
+ def get_drive_label(self, drive_letter: str) -> str:
364
+ path = f"{drive_letter.rstrip('\\').rstrip(':')}:\\"
365
+
366
+ volume_name_buffer = ctypes.create_unicode_buffer(260)
367
+
368
+ success = ctypes.windll.kernel32.GetVolumeInformationW(
369
+ path,
370
+ volume_name_buffer,
371
+ len(volume_name_buffer),
372
+ None, None, None, None, 0
373
+ )
374
+
375
+ if success:
376
+ return volume_name_buffer.value or "Local disk"
377
+ else:
378
+ return "Unknown disk"
379
+
380
+ def is_drive_readonly(self, drive_letter: str) -> bool:
381
+ path = f"{drive_letter.rstrip('\\').rstrip(':')}:\\"
382
+
383
+ flags = ctypes.c_ulong(0)
384
+
385
+ success = ctypes.windll.kernel32.GetVolumeInformationW(
386
+ path, None, 0, None, None, ctypes.byref(flags), None, 0
387
+ )
388
+
389
+ if success:
390
+ return bool(flags.value & FILE_READ_ONLY_VOLUME)
391
+ else:
392
+ return None
393
+
394
+ def _imdisk_units(self) -> list[int]:
395
+ r = self._imdisk("-l")
396
+ return [int(n) for n in re.findall(r"ImDisk(\d+)", r.stdout)]
397
+
398
+ def _free_unit(self) -> int:
399
+ used = set(self._imdisk_units())
400
+ for u in range(IMDISK_UNIT_MIN, IMDISK_UNIT_MAX + 1):
401
+ if u not in used:
402
+ return u
403
+ raise PyRAMError("No free ImDisk units in PyRAM range")
404
+
405
+ def _unit_by_letter(self, disk_name: str) -> int | None:
406
+ target = self._normalizate_disk_name(disk_name)
407
+ for u in self._imdisk_units():
408
+ if self._letter_by_unit(u) == target:
409
+ return u
410
+ return None
411
+
412
+ def _letter_by_unit(self, unit: int) -> str | None:
413
+ r = self._imdisk("-l", "-u", str(unit))
414
+ m = re.search(r"^Drive letter:\s*([A-Za-z])\s*$", r.stdout, re.MULTILINE)
415
+ return m.group(1).upper() + ":" if m else None
416
+
417
+ def create_disk_imdisk(self, disk_name: str = "Z:", memory_mb_size: int = 2048, file_system: str = "ntfs") -> bool:
418
+ disk_name = self._normalizate_disk_name(disk_name)
419
+
420
+ if self._formatdr(disk_name=disk_name) == False:
421
+ raise IncorrectDiskName(f"Disk name not correct: '{disk_name}'")
422
+
423
+ if disk_name in self.disks:
424
+ raise DiskAlreadyExistsError(f"Disk: '{disk_name.rstrip(":")}' already exists")
425
+
426
+ if psutil_instaled == True:
427
+ bytes_val = psutil.virtual_memory().total
428
+ mb_val = bytes_val / (1024 ** 2)
429
+
430
+ if memory_mb_size > mb_val:
431
+ raise NotEnoughRAMError("You cannot specify more RAM than you actually have!")
432
+
433
+ elif psutil_instaled == False:
434
+ warnings.warn("Psutil not installed, can not check RAM restrictions", RuntimeWarning)
435
+
436
+ if memory_mb_size <= 0:
437
+ raise ZeroOrNegativeRAMError(f"RAM is zero or negative: '{memory_mb_size}'")
438
+
439
+ unit = self._free_unit()
440
+ r = self._imdisk(
441
+ "-a",
442
+ "-u", str(unit),
443
+ "-s", f"{memory_mb_size}M",
444
+ "-m", disk_name,
445
+ "-p", f"/fs:{file_system} /q /y /v:PyRAM",
446
+ )
447
+
448
+ if r.returncode != 0:
449
+ raise PyRAMError(f"imdisk failed ({r.returncode}): {r.stderr or r.stdout}")
450
+
451
+ self.disks[disk_name] = unit
452
+
453
+ if not self.format_imdisk(disk_name, file_system):
454
+ warnings.warn(f"Failed to format RAM disk '{disk_name.rstrip(":")}'\nYou need to run the process as an administrator.", RuntimeWarning)
455
+
456
+ return True
457
+
458
+ def remove_disk_imdisk(self, disk_name: str, force: bool = True) -> bool:
459
+ disk_name = self._normalizate_disk_name(disk_name)
460
+
461
+ if self._formatdr(disk_name=disk_name) == False:
462
+ raise IncorrectDiskName(f"Disk name not correct: '{disk_name}'")
463
+
464
+ sys_drive = self.get_windows_disk()
465
+
466
+ if sys_drive == disk_name:
467
+ raise WindowsDiskDeleteError("You can not delete main Windows disk")
468
+
469
+ unit = self._unit_by_letter(disk_name)
470
+ if unit is None:
471
+ raise DiskNotAlreadyExistsError(f"Disk '{disk_name.rstrip(":")}' not found in ImDisk")
472
+ if not (IMDISK_UNIT_MIN <= unit <= IMDISK_UNIT_MAX):
473
+ raise ForeignDiskError(f"Disk '{disk_name.rstrip(":")}' was not created by PyRAM")
474
+
475
+ r = self._imdisk("-D" if force else "-d", "-u", str(unit))
476
+ if r.returncode != 0:
477
+ raise PyRAMError(f"imdisk failed ({r.returncode}): {r.stderr or r.stdout}")
478
+
479
+ self.disks.pop(disk_name, None)
480
+
481
+ return True
482
+
483
+ def format_imdisk(self, disk_name: str, file_system: str = "NTFS") -> bool:
484
+ disk_name = self._normalizate_disk_name(disk_name)
485
+
486
+ if self._formatdr(disk_name=disk_name) == False:
487
+ raise IncorrectDiskName(f"Disk name not correct: '{disk_name}'")
488
+
489
+ if disk_name == self.get_windows_disk():
490
+ raise WindowsDiskFormatError("You can not format main Windows disk")
491
+
492
+ fs = file_system.upper()
493
+ if fs not in {"NTFS", "FAT32", "EXFAT"}:
494
+ raise ValueError("Unsupported file system")
495
+
496
+ unit = self._unit_by_letter(disk_name)
497
+ if unit is None:
498
+ raise DiskNotAlreadyExistsError(f"Disk {disk_name.rstrip(':')} not found in ImDisk")
499
+ if not (IMDISK_UNIT_MIN <= unit <= IMDISK_UNIT_MAX):
500
+ raise ForeignDiskError(f"Disk {disk_name.rstrip(':')} was not created by PyRAM")
501
+
502
+ r = subprocess.run(
503
+ ["format.com", disk_name, f"/FS:{fs}", "/Q", "/Y", "/V:PyRAM"],
504
+ capture_output=True, text=True, timeout=60,
505
+ )
506
+ if r.returncode != 0:
507
+ self._imdisk("-D", "-u", str(unit))
508
+ raise PyRAMError(f"format failed ({r.returncode}): {r.stdout or r.stderr}")
509
+
510
+ return True
511
+
512
+ def get_all_disks(self) -> dict:
513
+ return self.disks
514
+
515
+ def get_all_folders(self) -> dict:
516
+ return self.folders
517
+
518
+ def get_all_processes(self) -> dict:
519
+ return self.process
520
+
521
+ @deprecated("Experimental method, not stable. Please use ramdisk methods")
522
+ def create_disk_winfspy(self, disk_name: str = "Z:") -> subprocess.Popen:
523
+ if winfspy_installed == False:
524
+ raise ComponentNotInstalledError("winfspy not installed!")
525
+
526
+ if disk_name in self.disks:
527
+ raise DiskAlreadyExistsError(f"Disk: '{disk_name.rstrip(":")}' already exists")
528
+
529
+ process = subprocess.Popen([
530
+ sys.executable,
531
+ "-m", "winfspy.memfs",
532
+ disk_name,
533
+ ])
534
+
535
+ self.process[disk_name] = process
536
+
537
+ return process
538
+
539
+ @deprecated("Experimental method, not stable. Please use ramdisk methods")
540
+ def create_folder_winfspy(self, path: str) -> subprocess.Popen:
541
+ if winfspy_installed == False:
542
+ raise ComponentNotInstalledError("winfspy not installed!")
543
+
544
+ clead_path = Path(path)
545
+
546
+ if clead_path in self.folders:
547
+ raise FolderAlreadyExistsError(f"Folder: {path} already exists")
548
+
549
+ process = subprocess.Popen([
550
+ sys.executable,
551
+ "-m", "winfspy.memfs",
552
+ clead_path,
553
+ ])
554
+
555
+ self.process[clead_path] = process
556
+
557
+ return process
558
+
559
+ @deprecated("Experimental method, not stable. Please use ramdisk methods")
560
+ def remove_storage_winfspy(self, pointer: str) -> bool:
561
+ if len(pointer) <= 2:
562
+ pointer = self._normalizate_disk_name(pointer)
563
+
564
+ if winfspy_installed == False:
565
+ raise ComponentNotInstalledError("winfspy not installed!")
566
+
567
+ if pointer not in self.disks and pointer not in self.folders:
568
+ raise ProcessNotAlreadyExistsError(f"Process: {pointer.rstrip(":")} not already exists")
569
+
570
+ process: subprocess.Popen = self.process.get(pointer)
571
+
572
+ if process is None:
573
+ return False
574
+
575
+ process.terminate()
576
+ try:
577
+ process.wait(timeout=5)
578
+ except subprocess.TimeoutExpired:
579
+ process.kill()
580
+
581
+ if len(pointer) >= 2:
582
+ del self.folders[pointer]
583
+ else:
584
+ del self.disks[pointer]
585
+
586
+ def _owned_disks(self) -> list[str]:
587
+ result = []
588
+ for u in self._imdisk_units():
589
+ if IMDISK_UNIT_MIN <= u <= IMDISK_UNIT_MAX:
590
+ letter = self._letter_by_unit(u)
591
+ if letter:
592
+ result.append(letter)
593
+ return result
594
+
595
+ def close(self, force: bool = False) -> None:
596
+ for drive in self._owned_disks():
597
+ try:
598
+ self.remove_disk_imdisk(drive, force=force)
599
+ except PyRAMError as e:
600
+ warnings.warn(f"Could not remove {drive}: {e}", RuntimeWarning)
601
+
602
+ def __enter__(self):
603
+ return self
604
+
605
+ def __exit__(self, exc_type, exc, tb):
606
+ if self._cleanup_on_exit:
607
+ self.close(force=True)
608
+ return False
609
+
610
+ @dataclass(frozen=True)
611
+ class DiskInfo:
612
+ drive: str
613
+ owner: str
614
+ is_formatted: bool
615
+ drive_opts: str | None
616
+ fs: str | None
617
+ label: str | None
618
+ total_bytes: int | None
619
+ used_bytes: int | None
620
+ free_bytes: int | None
621
+ used_percent: float | None
622
+ readonly: bool | None
623
+ mountpoint: str | None
PyRAMErrors.py ADDED
@@ -0,0 +1,68 @@
1
+ class PyRAMError(Exception):
2
+ """A fundamental error in the PyRAM library."""
3
+
4
+ class ComponentNotInstalledError(PyRAMError):
5
+ """A required component is not installed."""
6
+
7
+ class DiskAlreadyExistsError(PyRAMError):
8
+ """Disk already exists."""
9
+
10
+ class DiskNotFoundError(PyRAMError):
11
+ """Disk not found."""
12
+
13
+ class DiskCreationError(PyRAMError):
14
+ """Failed to create disk."""
15
+
16
+ class IncorrectSymbolInPath(PyRAMError):
17
+ """Character error in the path."""
18
+
19
+ class NoDiskInPath(PyRAMError):
20
+ """No disk in the path."""
21
+
22
+ class FileNotFound(PyRAMError):
23
+ """File not found."""
24
+
25
+ class FolderNotFile(PyRAMError):
26
+ """A folder, not a file."""
27
+
28
+ class PathNotFound(PyRAMError):
29
+ """Path not found."""
30
+
31
+ class NotEnoughRAMError(PyRAMError):
32
+ """Not enough RAM."""
33
+
34
+ class DiskNotAlreadyExistsError(PyRAMError):
35
+ """Disk does not exist."""
36
+
37
+ class FolderAlreadyExistsError(PyRAMError):
38
+ """Folder already exists."""
39
+
40
+ class FolderNotAlreadyExistsError(PyRAMError):
41
+ """Folder does not exist."""
42
+
43
+ class ProcessAlreadyExistsError(PyRAMError):
44
+ """Process already exists."""
45
+
46
+ class ProcessNotAlreadyExistsError(PyRAMError):
47
+ """Process does not exist."""
48
+
49
+ class WindowsDiskDeleteError(PyRAMError):
50
+ """Windows disk deletion error"""
51
+
52
+ class WindowsDiskFormatError(PyRAMError):
53
+ """Windows disk formattion error"""
54
+
55
+ class ZeroOrNegativeRAMError(PyRAMError):
56
+ """Zero or negative RAM."""
57
+
58
+ class FailedToFormatDisk(PyRAMError):
59
+ """Disk formatting error"""
60
+
61
+ class ForeignDiskError(PyRAMError):
62
+ """Not PyRAM created"""
63
+
64
+ class InputError(PyRAMError):
65
+ """incorrect answer"""
66
+
67
+ class IncorrectDiskName(PyRAMError):
68
+ """Incorrect disk name"""
@@ -0,0 +1,133 @@
1
+ Metadata-Version: 2.4
2
+ Name: PyRAMObjects
3
+ Version: 0.1.0
4
+ Summary: Safe Python API for ImDisk RAM disks on Windows plus in-memory objects
5
+ Author: lolololox
6
+ License: MIT
7
+ Keywords: ramdisk,imdisk,windows,in-memory
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Operating System :: Microsoft :: Windows
10
+ Classifier: Programming Language :: Python :: 3.13
11
+ Requires-Python: >=3.13
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: psutil>=5.9
15
+ Dynamic: license-file
16
+
17
+ # PyRAM
18
+
19
+ A Python library for managing RAM disks on Windows. Based on ImDisk with RamDisk and WinFs with WinFsPy.
20
+ Project target it is:
21
+
22
+ 1. Create more simple API than original library
23
+ 2. Do more stable than original library
24
+ 3. Give additional functional than original library
25
+
26
+ > [!CAUTION]
27
+ > **IMPORTANT:**
28
+ >
29
+ > 1. Early version of the library, bugs are possible
30
+ > 2. Disks may be removed with a delay
31
+ > 3. API can changes every update, please check actual documentation
32
+ > 4. Administrator privileges are required for the library to function properly
33
+ > 5. ONLY Windows
34
+ > 6. PyRAM only manages disks it created (ImDisk units 100-199) and refuses to touch others.
35
+
36
+ ## Features
37
+
38
+ - Create RAM disks.
39
+ - Remove RAM disks.
40
+ - Track disks managed by a `MemoryStorage` instance.
41
+ - Simple API with minimal setup.
42
+ - Format RAM disks
43
+ - Virtual in-memory file system
44
+
45
+ ## Requirements
46
+
47
+ - Windows
48
+ - Python
49
+ - ImDisk
50
+ - WinFs(Not strictly required, but necessary for the library to additional function)
51
+ - WinFsPy(Not strictly required, but necessary for the library to additional features)
52
+ - Python 3.12 and higer
53
+
54
+ RAM disk operations may require administrator privileges.
55
+
56
+ ## Installation
57
+
58
+ Install ImDisk and the required Python dependencies before using PyRAM.
59
+ In short time meybe you can install PyRAM from PaPi(Pip install PyRAMObjects)
60
+
61
+ ## Quick Start
62
+
63
+ ```python
64
+ from PyRAM import MemoryStorage
65
+
66
+ storage = MemoryStorage()
67
+
68
+ # Create disk without parameters
69
+ storage.create_disk_ramdisk() # uses default parameters disk_name = "Z:", memory_mb_size = 2048, file_system = "ntfs"
70
+
71
+ # Create a 512 MB RAM disk
72
+ storage.create_disk_ramdisk(
73
+ disk_name="X:",
74
+ memory_mb_size=512,
75
+ file_system = "ntfs"
76
+ )
77
+
78
+ # Remove the RAM disk
79
+ storage.remove_disk_ramdisk(disk_name="X:")
80
+
81
+ with MemoryStorage(cleanup_on_exit=True) as storage1: # cleaning all PyRAM disks when app closing, becouse cleanup_on_exit=True
82
+ storage1.create_disk_imdisk(disk_name="K:", memory_mb_size=512)
83
+ ```
84
+
85
+ ## Usage Notes
86
+
87
+ - RAM disks use volatile memory. Data stored on them should be considered temporary.
88
+ - A drive letter must be available when creating a RAM disk.
89
+ - Creating, formatting, or removing a RAM disk may require elevated permissions.
90
+ - The library's disk registry tracks disks managed by the current `MemoryStorage` instance.
91
+
92
+ ## License
93
+
94
+ MIT
95
+
96
+ ## Documentation
97
+
98
+ - classes
99
+
100
+ 1. MemoryObject. Just dict with dicts
101
+ 2. MemoryPythonFolder. Virtual file system, where you can save only string data types
102
+ 3. MemoryStorage. Class where access create folders(experimental only WinFsPy) and disks in ram and see it in explorer
103
+ 4. DiskInfo. Just dataclass, has no methods
104
+
105
+ ### MemoryObject methods
106
+
107
+ 1. write. Save value
108
+ 2. read. Get value
109
+ 3. delete. delete value
110
+ 4. object_sum/object_sub/object_div/object_mul/object_equal. Operations with objects
111
+
112
+ ### MemoryPythonFolder methods
113
+
114
+ 1. write. Save value
115
+ 2. read. Get value
116
+ 3. listdir. Get list of folders
117
+ 4. delete. Delete value
118
+
119
+ ### MemoryStorage methods
120
+
121
+ 1. get_all_drives. Get all drives
122
+ 2. remove_disk_imdisk. Create disk in RAM using ImDisk
123
+ 3. remove_disk_imdisk. Delete disk, that created using ImDisk
124
+ 4. format_ramdisk. Format disk
125
+ 5. get_all_disks/get_all_folders/get_all_processes. Get all disks/folders/processes
126
+ 6. create_disk_winfspy. Create disk in RAM using WinFsPy
127
+ 7. create_folder_winfspy. Create folder in RAM using WinFsPy
128
+ 8. remove_storage_winfspy. Remove storage, that created using WinFsPy
129
+ 9. get_windows_disk. Get Windows disk
130
+ 10. check_disk_formatted. Check disk formatted or not
131
+ 11. get_drive_label. Get disk label
132
+ 12. is_drive_readonly. Check disk read-only or not
133
+ 13. get_disk_info. Get info about disk
@@ -0,0 +1,7 @@
1
+ PyRAM.py,sha256=QffA21icHcDKqLhqNCPHbgI9H_pC5MRiusVvJbqUtvE,21720
2
+ PyRAMErrors.py,sha256=1mm3sr5El-pnpRJEelaqqDWeuLJAD8kVw_Hei6C6JMs,1745
3
+ pyramobjects-0.1.0.dist-info/licenses/LICENSE,sha256=NGSda9nG6kWtoYMffS-hwJx92ADfBIm2-__O30Vw3aw,1109
4
+ pyramobjects-0.1.0.dist-info/METADATA,sha256=ga2y4HJbIEkjDhuzUuFulLSuSkvFeOVR2Vh93Ux29XQ,4258
5
+ pyramobjects-0.1.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
6
+ pyramobjects-0.1.0.dist-info/top_level.txt,sha256=4MvBKbML7j0d0jNmPgd8tqR17pUbaHeVSXyitjyvzjY,18
7
+ pyramobjects-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.2.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,20 @@
1
+ <!-- markdownlint-disable MD041 -->
2
+ Copyright (c) 2026 lolololox
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ SOFTWARE.
@@ -0,0 +1,2 @@
1
+ PyRAM
2
+ PyRAMErrors