Python’s resource module (Unix/macOS only) reads and sets process resource limits and reports resource usage statistics. import resource. getrlimit: soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE) — returns (soft, hard) tuple; RLIM_INFINITY = -1 means unlimited. setrlimit: resource.setrlimit(resource.RLIMIT_NOFILE, (soft, hard)) — unprivileged processes can raise soft up to hard; only root can raise hard. getrusage: ru = resource.getrusage(resource.RUSAGE_SELF) — returns struct_rusage; key fields: ru_utime (user CPU seconds), ru_stime (system CPU seconds), ru_maxrss (peak RSS in KB on Linux, bytes on macOS), ru_minflt/ru_majflt (minor/major page faults), ru_nvcsw/ru_nivcsw (voluntary/involuntary context switches), ru_inblock/ru_oublock (block I/O ops). RUSAGE_CHILDREN — usage of waited-for children. Common RLIMIT_* constants: RLIMIT_AS (virtual memory), RLIMIT_CORE, RLIMIT_CPU (seconds), RLIMIT_DATA, RLIMIT_FSIZE, RLIMIT_NOFILE, RLIMIT_NPROC, RLIMIT_RSS, RLIMIT_STACK. Windows: not available; use psutil for cross-platform resource monitoring. Claude Code generates memory-limited workers, CPU-time sandboxes, open-fd maximizers, and resource usage reporters.
CLAUDE.md for resource
## resource Stack
- Stdlib: import resource
- Get: soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)
- Set: resource.setrlimit(resource.RLIMIT_NOFILE, (new_soft, hard))
- Usage: ru = resource.getrusage(resource.RUSAGE_SELF)
- ru.ru_utime ru.ru_stime ru.ru_maxrss ru_minflt ru_majflt
- Inf: resource.RLIM_INFINITY # -1, meaning "no limit"
- Note: softlimit ≤ hardlimit; only root can raise hardlimit
resource Unix Resource Limits Pipeline
# app/resourceutil.py — read/set limits, maximize fds, sandbox, usage report
from __future__ import annotations
import os
import platform
import time
from dataclasses import dataclass
_RESOURCE_AVAILABLE = platform.system() != "Windows"
if _RESOURCE_AVAILABLE:
import resource as _resource
# ─────────────────────────────────────────────────────────────────────────────
# 1. Limit read / write helpers
# ─────────────────────────────────────────────────────────────────────────────
@dataclass
class Limit:
name: str
soft: int # -1 = RLIM_INFINITY
hard: int
def is_unlimited(self) -> bool:
return self.soft == -1
def __str__(self) -> str:
soft_s = "unlimited" if self.soft == -1 else str(self.soft)
hard_s = "unlimited" if self.hard == -1 else str(self.hard)
return f"{self.name:<18s} soft={soft_s:<12s} hard={hard_s}"
def get_limit(resource_id: int, name: str = "") -> Limit:
"""
Read the current (soft, hard) limit for a resource constant.
Example:
lim = get_limit(resource.RLIMIT_NOFILE, "NOFILE")
print(lim)
"""
if not _RESOURCE_AVAILABLE:
raise OSError("resource module not available on Windows")
soft, hard = _resource.getrlimit(resource_id)
return Limit(name=name or str(resource_id), soft=soft, hard=hard)
def set_soft_limit(resource_id: int, new_soft: int) -> None:
"""
Set only the soft limit for a resource, keeping the hard limit unchanged.
Raises ValueError if new_soft exceeds hard limit.
Example:
set_soft_limit(resource.RLIMIT_NOFILE, 65536)
"""
if not _RESOURCE_AVAILABLE:
raise OSError("resource module not available on Windows")
_, hard = _resource.getrlimit(resource_id)
if hard != _resource.RLIM_INFINITY and new_soft > hard:
raise ValueError(
f"Requested soft limit {new_soft} exceeds hard limit {hard}"
)
_resource.setrlimit(resource_id, (new_soft, hard))
def maximize_open_files() -> int:
"""
Raise RLIMIT_NOFILE soft limit to the hard limit.
Returns the new soft limit.
Useful before servers that open many connections.
Example:
max_fds = maximize_open_files()
print(f"Can now open {max_fds} file descriptors")
"""
if not _RESOURCE_AVAILABLE:
return 1024 # Windows default
soft, hard = _resource.getrlimit(_resource.RLIMIT_NOFILE)
if hard == _resource.RLIM_INFINITY:
target = 1_048_576 # reasonable cap when hard is unlimited
else:
target = hard
if soft < target:
_resource.setrlimit(_resource.RLIMIT_NOFILE, (target, hard))
return target
def all_limits() -> list[Limit]:
"""
Return current soft/hard limits for all available RLIMIT_* constants.
Example:
for lim in all_limits():
print(lim)
"""
if not _RESOURCE_AVAILABLE:
return []
rlimit_names = [
("RLIMIT_AS", getattr(_resource, "RLIMIT_AS", None)),
("RLIMIT_CORE", getattr(_resource, "RLIMIT_CORE", None)),
("RLIMIT_CPU", getattr(_resource, "RLIMIT_CPU", None)),
("RLIMIT_DATA", getattr(_resource, "RLIMIT_DATA", None)),
("RLIMIT_FSIZE", getattr(_resource, "RLIMIT_FSIZE", None)),
("RLIMIT_MEMLOCK", getattr(_resource, "RLIMIT_MEMLOCK", None)),
("RLIMIT_MSGQUEUE",getattr(_resource, "RLIMIT_MSGQUEUE",None)),
("RLIMIT_NOFILE", getattr(_resource, "RLIMIT_NOFILE", None)),
("RLIMIT_NPROC", getattr(_resource, "RLIMIT_NPROC", None)),
("RLIMIT_RSS", getattr(_resource, "RLIMIT_RSS", None)),
("RLIMIT_RTPRIO", getattr(_resource, "RLIMIT_RTPRIO", None)),
("RLIMIT_SIGPENDING",getattr(_resource,"RLIMIT_SIGPENDING",None)),
("RLIMIT_STACK", getattr(_resource, "RLIMIT_STACK", None)),
]
results = []
for name, rid in rlimit_names:
if rid is None:
continue
try:
soft, hard = _resource.getrlimit(rid)
results.append(Limit(name=name, soft=soft, hard=hard))
except (OSError, ValueError):
continue
return results
# ─────────────────────────────────────────────────────────────────────────────
# 2. Process sandboxing
# ─────────────────────────────────────────────────────────────────────────────
def apply_sandbox(
max_cpu_seconds: int | None = None,
max_memory_bytes: int | None = None,
max_file_size_bytes: int | None = None,
max_open_files: int | None = None,
no_core_dumps: bool = True,
) -> None:
"""
Apply resource limits to restrict the current process.
Intended for child processes before exec or for sandboxing worker threads.
Example:
apply_sandbox(max_cpu_seconds=5, max_memory_bytes=256*1024*1024)
"""
if not _RESOURCE_AVAILABLE:
return
if no_core_dumps and hasattr(_resource, "RLIMIT_CORE"):
_resource.setrlimit(_resource.RLIMIT_CORE, (0, 0))
if max_cpu_seconds is not None and hasattr(_resource, "RLIMIT_CPU"):
_resource.setrlimit(_resource.RLIMIT_CPU,
(max_cpu_seconds, max_cpu_seconds))
if max_memory_bytes is not None:
for res_name in ("RLIMIT_AS", "RLIMIT_DATA"):
rid = getattr(_resource, res_name, None)
if rid is None:
continue
try:
_, hard = _resource.getrlimit(rid)
cap = max_memory_bytes if hard == _resource.RLIM_INFINITY else min(max_memory_bytes, hard)
_resource.setrlimit(rid, (cap, cap))
except (OSError, ValueError):
pass
if max_file_size_bytes is not None and hasattr(_resource, "RLIMIT_FSIZE"):
_resource.setrlimit(_resource.RLIMIT_FSIZE,
(max_file_size_bytes, max_file_size_bytes))
if max_open_files is not None and hasattr(_resource, "RLIMIT_NOFILE"):
try:
_, hard = _resource.getrlimit(_resource.RLIMIT_NOFILE)
cap = max_open_files if hard == _resource.RLIM_INFINITY else min(max_open_files, hard)
_resource.setrlimit(_resource.RLIMIT_NOFILE, (cap, hard))
except (OSError, ValueError):
pass
# ─────────────────────────────────────────────────────────────────────────────
# 3. Resource usage
# ─────────────────────────────────────────────────────────────────────────────
@dataclass
class UsageSnapshot:
user_time: float # seconds
sys_time: float
wall_time: float
max_rss_kb: int # KB on Linux; bytes / 1024 on macOS
minor_faults: int
major_faults: int
voluntary_csw: int
involuntary_csw: int
block_in: int
block_out: int
@property
def cpu_time(self) -> float:
return self.user_time + self.sys_time
def __str__(self) -> str:
return (
f"cpu={self.cpu_time:.3f}s "
f"(user={self.user_time:.3f}s sys={self.sys_time:.3f}s) "
f"wall={self.wall_time:.3f}s "
f"rss={self.max_rss_kb:,d}KB "
f"faults={self.minor_faults}/{self.major_faults} "
f"csw={self.voluntary_csw}/{self.involuntary_csw}"
)
def _rss_kb(raw: int) -> int:
"""Normalize max_rss: bytes on macOS → KB; already KB on Linux."""
if platform.system() == "Darwin":
return raw // 1024
return raw
def get_usage(who: int | None = None) -> UsageSnapshot:
"""
Return a resource usage snapshot for the current process or its children.
who: resource.RUSAGE_SELF (default) or resource.RUSAGE_CHILDREN.
Example:
before = get_usage()
run_heavy_work()
after = get_usage()
"""
if not _RESOURCE_AVAILABLE:
return UsageSnapshot(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
if who is None:
who = _resource.RUSAGE_SELF
ru = _resource.getrusage(who)
return UsageSnapshot(
user_time=ru.ru_utime,
sys_time=ru.ru_stime,
wall_time=time.monotonic(),
max_rss_kb=_rss_kb(ru.ru_maxrss),
minor_faults=ru.ru_minflt,
major_faults=ru.ru_majflt,
voluntary_csw=ru.ru_nvcsw,
involuntary_csw=ru.ru_nivcsw,
block_in=ru.ru_inblock,
block_out=ru.ru_oublock,
)
def delta_usage(before: UsageSnapshot, after: UsageSnapshot) -> UsageSnapshot:
"""
Compute the difference between two usage snapshots.
Example:
before = get_usage()
work()
after = get_usage()
print(delta_usage(before, after))
"""
return UsageSnapshot(
user_time=after.user_time - before.user_time,
sys_time=after.sys_time - before.sys_time,
wall_time=after.wall_time - before.wall_time,
max_rss_kb=max(after.max_rss_kb, before.max_rss_kb), # peak, not delta
minor_faults=after.minor_faults - before.minor_faults,
major_faults=after.major_faults - before.major_faults,
voluntary_csw=after.voluntary_csw - before.voluntary_csw,
involuntary_csw=after.involuntary_csw - before.involuntary_csw,
block_in=after.block_in - before.block_in,
block_out=after.block_out - before.block_out,
)
# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────
if __name__ == "__main__":
print("=== resource demo ===")
if not _RESOURCE_AVAILABLE:
print(" resource not available on Windows — skipping")
raise SystemExit(0)
# ── all_limits ─────────────────────────────────────────────────────────────
print("\n--- all_limits ---")
for lim in all_limits():
print(f" {lim}")
# ── maximize_open_files ────────────────────────────────────────────────────
print("\n--- maximize_open_files ---")
old_soft, old_hard = _resource.getrlimit(_resource.RLIMIT_NOFILE)
new_max = maximize_open_files()
cur_soft, _ = _resource.getrlimit(_resource.RLIMIT_NOFILE)
print(f" before: soft={old_soft} hard={old_hard}")
print(f" after: soft={cur_soft} (maximize returned {new_max})")
# ── get_usage ──────────────────────────────────────────────────────────────
print("\n--- get_usage ---")
before = get_usage()
# Do some CPU work to generate measurable usage
total = sum(i * i for i in range(500_000))
after = get_usage()
diff = delta_usage(before, after)
print(f" before: {before}")
print(f" after: {after}")
print(f" delta: {diff}")
print(f" sum={total}")
# ── apply_sandbox in child ─────────────────────────────────────────────────
print("\n--- apply_sandbox (in child process) ---")
pid = os.fork()
if pid == 0:
# Child: apply limits and try to exceed them
apply_sandbox(
max_cpu_seconds=10,
max_open_files=64,
no_core_dumps=True,
)
nofile_soft, _ = _resource.getrlimit(_resource.RLIMIT_NOFILE)
print(f" child RLIMIT_NOFILE soft: {nofile_soft}")
os._exit(0)
else:
_, status = os.waitpid(pid, 0)
print(f" child exited with status: {os.waitstatus_to_exitcode(status)}")
print("\n=== done ===")
For the psutil alternative — psutil (PyPI) provides cross-platform process memory info (process.memory_info().rss), CPU percent, open files, and connections with a unified API that works on Windows, Linux, and macOS — use psutil when you need cross-platform resource monitoring, live process trees, or percent-based metrics that resource doesn’t expose; use resource when you need to set OS enforcement limits (setrlimit) rather than just observe usage, or when you want zero dependencies and are targeting Linux/macOS only. For the signal alternative — signal.setitimer(signal.ITIMER_VIRTUAL, seconds) sends SIGVTALRM after a CPU-time interval and signal.alarm(n) sends SIGALRM after wall-clock seconds — use signal timers for soft CPU enforcement that triggers a Python exception handler; use resource.setrlimit(RLIMIT_CPU) for hard kernel-enforced CPU limits that send SIGXCPU and cannot be bypassed by user code even after a fork/exec. The Claude Skills 360 bundle includes resource skill sets covering Limit dataclass with get_limit()/set_soft_limit()/maximize_open_files()/all_limits() limit management, apply_sandbox() for CPU/memory/file-size/fd sandboxing, UsageSnapshot with get_usage()/delta_usage() resource accounting, and platform-aware RSS normalization. Start with the free tier to try resource limit patterns and resource pipeline code generation.