codegraph-engine 2.1.1__py3-none-any.whl → 2.1.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.
codegraph/__init__.py CHANGED
@@ -1,6 +1,6 @@
1
1
  """CodeGraph MCP: local, evidence-backed codebase intelligence."""
2
2
 
3
- __version__ = "2.1.1"
3
+ __version__ = "2.1.2"
4
4
  from codegraph.errors import (
5
5
  CodeGraphError,
6
6
  ErrorCode,
@@ -16,7 +16,8 @@ from .cache import (
16
16
  )
17
17
  from .coalescer import RequestCoalescer, get_global_coalescer
18
18
  from .debouncer import FileChangeDebouncer
19
- from .governor import ResourceGovernor, get_global_governor, get_process_memory_mb
19
+ from .governor import ResourceGovernor, get_global_governor
20
+ from .monitor import get_process_memory_mb
20
21
  from .policy import (
21
22
  ActivityMode,
22
23
  PressureLevel,
@@ -5,13 +5,12 @@ throttling background indexing, managing process memory, and cooperatively yield
5
5
  """
6
6
  from __future__ import annotations
7
7
 
8
- import resource
9
- import sys
10
8
  import threading
11
9
  import time
12
10
  from collections.abc import Iterator
13
11
  from contextlib import contextmanager
14
12
 
13
+ from .monitor import get_process_memory_mb
15
14
  from .policy import (
16
15
  ActivityMode,
17
16
  PressureLevel,
@@ -22,18 +21,6 @@ from .policy import (
22
21
  )
23
22
 
24
23
 
25
- def get_process_memory_mb() -> float:
26
- """Return estimated resident process memory in megabytes portably."""
27
- try:
28
- usage = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
29
- if sys.platform == "darwin":
30
- return round(usage / (1024 * 1024), 2)
31
- else:
32
- return round(usage / 1024, 2)
33
- except Exception:
34
- return 0.0
35
-
36
-
37
24
  class ResourceGovernor:
38
25
  """Thread-safe central resource governor and priority scheduler."""
39
26
 
@@ -97,17 +84,17 @@ class ResourceGovernor:
97
84
  with self._lock:
98
85
  active_inter = self._active_interactive
99
86
 
100
- if limit_mb and mem_mb >= limit_mb * 0.90:
87
+ if mem_mb is not None and limit_mb and mem_mb >= limit_mb * 0.90:
101
88
  return PressureLevel.CRITICAL
102
89
  if active_inter >= self.policy.max_interactive_workers * 2:
103
90
  return PressureLevel.CRITICAL
104
91
 
105
- if limit_mb and mem_mb >= limit_mb * 0.75:
92
+ if mem_mb is not None and limit_mb and mem_mb >= limit_mb * 0.75:
106
93
  return PressureLevel.HIGH
107
94
  if active_inter > self.policy.max_interactive_workers:
108
95
  return PressureLevel.HIGH
109
96
 
110
- if limit_mb and mem_mb >= limit_mb * 0.60:
97
+ if mem_mb is not None and limit_mb and mem_mb >= limit_mb * 0.60:
111
98
  return PressureLevel.ELEVATED
112
99
  if active_inter > 0:
113
100
  return PressureLevel.ELEVATED
@@ -0,0 +1,102 @@
1
+ """Cross-platform process resource monitoring for CodeGraph.
2
+
3
+ Provides portable resident memory (RSS) measurements across Windows, macOS,
4
+ and Linux without requiring Unix-only APIs at import or runtime.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import sys
9
+ from typing import Any
10
+
11
+
12
+ def _get_unix_memory_mb() -> float | None:
13
+ """Read process RSS using standard library resource module on Unix."""
14
+ try:
15
+ import resource # Unix-only standard library module
16
+
17
+ usage = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
18
+ if sys.platform == "darwin":
19
+ # macOS ru_maxrss is returned in bytes
20
+ return round(usage / (1024 * 1024), 2)
21
+ else:
22
+ # Linux and BSD ru_maxrss is returned in kilobytes
23
+ return round(usage / 1024, 2)
24
+ except Exception:
25
+ return None
26
+
27
+
28
+ def _get_psutil_memory_mb() -> float | None:
29
+ """Read process RSS using psutil if installed."""
30
+ try:
31
+ import psutil # type: ignore[import-untyped]
32
+
33
+ rss_bytes = float(psutil.Process().memory_info().rss)
34
+ return round(rss_bytes / (1024 * 1024), 2)
35
+ except Exception:
36
+ return None
37
+
38
+
39
+ def _get_windows_memory_mb_ctypes() -> float | None:
40
+ """Read process RSS on Windows using standard library ctypes (zero dependencies)."""
41
+ try:
42
+ import ctypes
43
+ from ctypes import wintypes
44
+
45
+ class PROCESS_MEMORY_COUNTERS(ctypes.Structure):
46
+ _fields_ = [
47
+ ("cb", wintypes.DWORD),
48
+ ("PageFaultCount", wintypes.DWORD),
49
+ ("PeakWorkingSetSize", ctypes.c_size_t),
50
+ ("WorkingSetSize", ctypes.c_size_t),
51
+ ("QuotaPeakPagedPoolUsage", ctypes.c_size_t),
52
+ ("QuotaPagedPoolUsage", ctypes.c_size_t),
53
+ ("QuotaPeakNonPagedPoolUsage", ctypes.c_size_t),
54
+ ("QuotaNonPagedPoolUsage", ctypes.c_size_t),
55
+ ("PagefileUsage", ctypes.c_size_t),
56
+ ("PeakPagefileUsage", ctypes.c_size_t),
57
+ ]
58
+
59
+ windll: Any = getattr(ctypes, "windll", None)
60
+ if windll is None:
61
+ return None
62
+
63
+ handle = windll.kernel32.GetCurrentProcess()
64
+ counters = PROCESS_MEMORY_COUNTERS()
65
+ counters.cb = ctypes.sizeof(PROCESS_MEMORY_COUNTERS)
66
+
67
+ # On Windows 7+, K32GetProcessMemoryInfo is in kernel32; otherwise in psapi
68
+ get_info = getattr(windll.kernel32, "K32GetProcessMemoryInfo", None)
69
+ if get_info is None:
70
+ psapi = getattr(windll, "psapi", None)
71
+ if psapi is not None:
72
+ get_info = getattr(psapi, "GetProcessMemoryInfo", None)
73
+
74
+ if get_info is not None and bool(get_info(handle, ctypes.byref(counters), counters.cb)):
75
+ # WorkingSetSize is the resident set size in bytes
76
+ return round(float(counters.WorkingSetSize) / (1024 * 1024), 2)
77
+ except Exception:
78
+ return None
79
+ return None
80
+
81
+
82
+ def get_process_memory_mb() -> float | None:
83
+ """Return estimated resident process memory in megabytes cross-platform.
84
+
85
+ Returns:
86
+ float: Estimated memory in MB if measurement is supported and succeeds.
87
+ None: If measurement is unsupported or fails (degrades gracefully to UNKNOWN).
88
+ """
89
+ if sys.platform == "win32":
90
+ # 1. On Windows: try psutil first, then ctypes Windows API
91
+ mem = _get_psutil_memory_mb()
92
+ if mem is not None:
93
+ return mem
94
+ return _get_windows_memory_mb_ctypes()
95
+
96
+ # 2. On Unix-like platforms (macOS, Linux): try standard resource module
97
+ mem = _get_unix_memory_mb()
98
+ if mem is not None:
99
+ return mem
100
+
101
+ # 3. Fallback to psutil on any platform if resource module failed or is unavailable
102
+ return _get_psutil_memory_mb()
@@ -113,7 +113,7 @@ class ResourceState:
113
113
  queued_tasks: int = 0
114
114
  recent_cpu_time_ms: float = 0.0
115
115
  recent_wall_time_ms: float = 0.0
116
- estimated_memory_mb: float = 0.0
116
+ estimated_memory_mb: float | None = None
117
117
  pressure_level: str = PressureLevel.NORMAL.value
118
118
  activity_mode: str = ActivityMode.IDLE.value
119
119
  indexing_active: bool = False
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codegraph-engine
3
- Version: 2.1.1
3
+ Version: 2.1.2
4
4
  Summary: Evidence-backed local codebase intelligence for MCP clients
5
5
  Author: CodeGraph contributors
6
6
  License-Expression: MIT
@@ -11,6 +11,8 @@ Requires-Dist: pydantic>=2.7
11
11
  Requires-Dist: typer>=0.12
12
12
  Provides-Extra: mcp
13
13
  Requires-Dist: mcp<2,>=1.0; extra == "mcp"
14
+ Provides-Extra: system
15
+ Requires-Dist: psutil>=5.9; extra == "system"
14
16
  Provides-Extra: dev
15
17
  Requires-Dist: pytest>=8; extra == "dev"
16
18
  Requires-Dist: pytest-cov>=5; extra == "dev"
@@ -1,4 +1,4 @@
1
- codegraph/__init__.py,sha256=_fvuBpqIOyFtG6zV5ouDbAzcHh6kaZOODpnBo5zKE8A,845
1
+ codegraph/__init__.py,sha256=79b7HvT1to9TpPMt04Kt_qVbBIwZwVr06vQh9ArQxHU,845
2
2
  codegraph/agent.py,sha256=We0TWEXSMBBz6t2NQEGPB6gv-2P1IwyCmxoFGPN_CBA,911
3
3
  codegraph/architecture.py,sha256=m6RZgXDMoDkSx8KO0xuQHtz5rT0h5B7Jjh1yVE2H2LI,12069
4
4
  codegraph/audit.py,sha256=j6fFL6b4vKfnAdUYU7goqQBv4ysrxy-ecBJvT8rYoSI,3301
@@ -43,20 +43,21 @@ codegraph/mcp/__init__.py,sha256=BmjrnksGkbG7TPqwbyQvgYj9uei8pFSFpfkRpaGVdJU,63
43
43
  codegraph/mcp/server.py,sha256=CnBdY5Qa9T5LkAFpA3TwwpTkGetkauY7JmiGO2U9ocg,31395
44
44
  codegraph/memory/__init__.py,sha256=yfQgSZ9Tii6PPZiLW0ImwC2k-Bg6m7mnUm_K1laBems,58
45
45
  codegraph/memory/store.py,sha256=K3iD9VWF5if4OmYCTb5KxtZ3zj45kfEKmz8yivWzP-M,1490
46
- codegraph/resources/__init__.py,sha256=14FNWGRA0ZXr_m-smhFUxY8dvhgH2f1EqmyXaV6TxHA,1276
46
+ codegraph/resources/__init__.py,sha256=KPpsePEf6cNVvpMI4tnIbYVSw6Q77kaOP6wcmEko7tE,1296
47
47
  codegraph/resources/cache.py,sha256=mdhlrXjhbBGChiF_rR1SnMN27oVxjpx9f4zu5jV-hbI,4054
48
48
  codegraph/resources/coalescer.py,sha256=bKbTmX8q-R0FjNJjZQrh3Us2ZcuYsIKTrH0WimWcxQs,2510
49
49
  codegraph/resources/debouncer.py,sha256=LjDv7uGxixlH-N7vVNkphvlhvtGh6t4jGKFAGg_nX7A,3084
50
- codegraph/resources/governor.py,sha256=mk9pgxlflvrvm87aZQqexqUt5H6aiJlOlY-AjwiaWWg,8971
51
- codegraph/resources/policy.py,sha256=pJScYxmhJiSk8c9FlfORTpxf68ZpM2UwNgd4U_LhUWQ,4026
50
+ codegraph/resources/governor.py,sha256=JFZrjwXb19YtPUGoVg2I9yiKBg8Dbw3q1H8Q2J9RW68,8681
51
+ codegraph/resources/monitor.py,sha256=TZMpxvtNS4nwG1w511LAkpef4XThQjNCYZP8qu__Iy0,3753
52
+ codegraph/resources/policy.py,sha256=Xv0Bd1ZzvCcXhAObrbABxTAxdKxFoUUgCrVNSLY5u5w,4034
52
53
  codegraph/search/__init__.py,sha256=LFdgDMGE_cGWFFBk--uoRappyC_LnKvfKedogv-p5kQ,504
53
54
  codegraph/search/hybrid.py,sha256=pqdRZSwe7wEEETxmUjV5Ns7_oxus60jggnOPNRfFpg0,9617
54
55
  codegraph/search/semantic.py,sha256=QZaJVEoOZIZrp4O6SmUAzMiqMPfopgmPSpqzL3T7GVg,836
55
56
  codegraph/security/__init__.py,sha256=QsucVn1YOuC1IFrd9-EHeZd9kdHv8XUQgRZ9bTz_6_4,142
56
57
  codegraph/security/paths.py,sha256=BtiVvDgMzfL7_Dj3COt6B2Aui4A7hH13QwPUgl5-7No,1241
57
- codegraph_engine-2.1.1.dist-info/licenses/LICENSE,sha256=I6wm9APPQhaH4Cj9H9z8I2jahgYh7ik-9x7wFrmsGOo,1079
58
- codegraph_engine-2.1.1.dist-info/METADATA,sha256=A5yelC6QUOe9FKbvQfbQnMHra-hAPoxfQZY19e8B4bI,13240
59
- codegraph_engine-2.1.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
60
- codegraph_engine-2.1.1.dist-info/entry_points.txt,sha256=U2xQ3yhqOaaDyFtV9p7N0FDref1VEk56RE_Bnvrnycc,48
61
- codegraph_engine-2.1.1.dist-info/top_level.txt,sha256=RqBj9sPbifZTb9aeHtnbxTgKJvfHIQdmYE1Brv8Wdkg,10
62
- codegraph_engine-2.1.1.dist-info/RECORD,,
58
+ codegraph_engine-2.1.2.dist-info/licenses/LICENSE,sha256=I6wm9APPQhaH4Cj9H9z8I2jahgYh7ik-9x7wFrmsGOo,1079
59
+ codegraph_engine-2.1.2.dist-info/METADATA,sha256=1TV7fwB6BD6GdRxajmumnqA-z5iuDRmd9Uslo8b6za4,13309
60
+ codegraph_engine-2.1.2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
61
+ codegraph_engine-2.1.2.dist-info/entry_points.txt,sha256=U2xQ3yhqOaaDyFtV9p7N0FDref1VEk56RE_Bnvrnycc,48
62
+ codegraph_engine-2.1.2.dist-info/top_level.txt,sha256=RqBj9sPbifZTb9aeHtnbxTgKJvfHIQdmYE1Brv8Wdkg,10
63
+ codegraph_engine-2.1.2.dist-info/RECORD,,