standardsforge 0.1.0a4__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.
@@ -0,0 +1,7 @@
1
+ """StandardsForge deterministic local core."""
2
+
3
+ from .errors import StandardsForgeError
4
+ from .service import StandardsForgeService
5
+
6
+ __all__ = ["StandardsForgeError", "StandardsForgeService"]
7
+ __version__ = "0.1.0a4"
@@ -0,0 +1,3 @@
1
+ from .cli import main
2
+
3
+ raise SystemExit(main())
@@ -0,0 +1,98 @@
1
+ from __future__ import annotations
2
+
3
+ import ctypes
4
+ from ctypes import wintypes
5
+
6
+
7
+ JOB_OBJECT_LIMIT_PROCESS_TIME = 0x00000002
8
+ JOB_OBJECT_LIMIT_ACTIVE_PROCESS = 0x00000008
9
+ JOB_OBJECT_LIMIT_PROCESS_MEMORY = 0x00000100
10
+ JOB_OBJECT_LIMIT_JOB_MEMORY = 0x00000200
11
+ JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 0x00002000
12
+ JOB_OBJECT_EXTENDED_LIMIT_INFORMATION = 9
13
+
14
+
15
+ class IO_COUNTERS(ctypes.Structure):
16
+ _fields_ = [
17
+ ("ReadOperationCount", ctypes.c_ulonglong),
18
+ ("WriteOperationCount", ctypes.c_ulonglong),
19
+ ("OtherOperationCount", ctypes.c_ulonglong),
20
+ ("ReadTransferCount", ctypes.c_ulonglong),
21
+ ("WriteTransferCount", ctypes.c_ulonglong),
22
+ ("OtherTransferCount", ctypes.c_ulonglong),
23
+ ]
24
+
25
+
26
+ class JOBOBJECT_BASIC_LIMIT_INFORMATION(ctypes.Structure):
27
+ _fields_ = [
28
+ ("PerProcessUserTimeLimit", ctypes.c_longlong),
29
+ ("PerJobUserTimeLimit", ctypes.c_longlong),
30
+ ("LimitFlags", wintypes.DWORD),
31
+ ("MinimumWorkingSetSize", ctypes.c_size_t),
32
+ ("MaximumWorkingSetSize", ctypes.c_size_t),
33
+ ("ActiveProcessLimit", wintypes.DWORD),
34
+ ("Affinity", ctypes.c_size_t),
35
+ ("PriorityClass", wintypes.DWORD),
36
+ ("SchedulingClass", wintypes.DWORD),
37
+ ]
38
+
39
+
40
+ class JOBOBJECT_EXTENDED_LIMIT_INFORMATION_STRUCT(ctypes.Structure):
41
+ _fields_ = [
42
+ ("BasicLimitInformation", JOBOBJECT_BASIC_LIMIT_INFORMATION),
43
+ ("IoInfo", IO_COUNTERS),
44
+ ("ProcessMemoryLimit", ctypes.c_size_t),
45
+ ("JobMemoryLimit", ctypes.c_size_t),
46
+ ("PeakProcessMemoryUsed", ctypes.c_size_t),
47
+ ("PeakJobMemoryUsed", ctypes.c_size_t),
48
+ ]
49
+
50
+
51
+ class WindowsJob:
52
+ def __init__(self, memory_bytes: int, cpu_seconds: int) -> None:
53
+ kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)
54
+ self._kernel32 = kernel32
55
+ kernel32.CreateJobObjectW.restype = wintypes.HANDLE
56
+ kernel32.CreateJobObjectW.argtypes = [ctypes.c_void_p, wintypes.LPCWSTR]
57
+ self.handle = kernel32.CreateJobObjectW(None, None)
58
+ if not self.handle:
59
+ raise OSError(ctypes.get_last_error(), "CreateJobObjectW failed")
60
+ limits = JOBOBJECT_EXTENDED_LIMIT_INFORMATION_STRUCT()
61
+ limits.BasicLimitInformation.PerProcessUserTimeLimit = cpu_seconds * 10_000_000
62
+ limits.BasicLimitInformation.ActiveProcessLimit = 1
63
+ limits.BasicLimitInformation.LimitFlags = (
64
+ JOB_OBJECT_LIMIT_PROCESS_TIME
65
+ | JOB_OBJECT_LIMIT_ACTIVE_PROCESS
66
+ | JOB_OBJECT_LIMIT_PROCESS_MEMORY
67
+ | JOB_OBJECT_LIMIT_JOB_MEMORY
68
+ | JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
69
+ )
70
+ limits.ProcessMemoryLimit = memory_bytes
71
+ limits.JobMemoryLimit = memory_bytes
72
+ kernel32.SetInformationJobObject.argtypes = [
73
+ wintypes.HANDLE,
74
+ ctypes.c_int,
75
+ ctypes.c_void_p,
76
+ wintypes.DWORD,
77
+ ]
78
+ if not kernel32.SetInformationJobObject(
79
+ self.handle,
80
+ JOB_OBJECT_EXTENDED_LIMIT_INFORMATION,
81
+ ctypes.byref(limits),
82
+ ctypes.sizeof(limits),
83
+ ):
84
+ self.close()
85
+ raise OSError(ctypes.get_last_error(), "SetInformationJobObject failed")
86
+
87
+ def assign(self, process_handle: int) -> None:
88
+ self._kernel32.AssignProcessToJobObject.argtypes = [wintypes.HANDLE, wintypes.HANDLE]
89
+ if not self._kernel32.AssignProcessToJobObject(self.handle, wintypes.HANDLE(process_handle)):
90
+ raise OSError(ctypes.get_last_error(), "AssignProcessToJobObject failed")
91
+
92
+ def terminate(self) -> None:
93
+ self._kernel32.TerminateJobObject(self.handle, 1)
94
+
95
+ def close(self) -> None:
96
+ if getattr(self, "handle", None):
97
+ self._kernel32.CloseHandle(self.handle)
98
+ self.handle = None