-
Notifications
You must be signed in to change notification settings - Fork 671
function to better estimate number of useable CPUs #6290
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Conversation
…esnt work in k8s e.g.
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the WalkthroughAdds a new public utility function get_cpu_count() in fiftyone/core/utils.py that determines CPUs available to the current process using Linux cgroup parsing (cpu.max), then falls back to multiprocessing.process_cpu_count(), os.sched_getaffinity(0), multiprocessing.cpu_count(), and finally 1. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
✨ Finishing Touches🧪 Generate unit tests
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
Status, Documentation and Community
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (5)
fiftyone/core/utils.py (5)
2907-2907
: Broaden Linux check for robustnessOn some Python builds/platforms
sys.platform
can be"linux"
or"linux2"
. Usestartswith("linux")
to be future-proof.Apply this diff:
- if sys.platform == "linux": + if sys.platform.startswith("linux"):
2931-2933
: Cater for broader exceptions when checking affinity
os.sched_getaffinity(0)
may raise exceptions other thanAttributeError
(e.g.,OSError
on some platforms/containers). Catch generic exceptions here since this is a best-effort probe with fallbacks.Apply this diff:
- except AttributeError: + except Exception: pass
2896-2941
: Optional: also respect cpuset and cgroup v1 to better match container limitsIn Kubernetes and some runtimes:
- cpuset can restrict CPUs independently of quota (e.g., pinned cores).
- Older systems use cgroup v1 (
cpu.cfs_quota_us
andcpu.cfs_period_us
).Taking the minimum of cpuset-count and quota-count yields a closer estimate of usable concurrency. This is safe and still falls back to other methods if files are absent.
Apply this diff to extend the Linux block (keeps your existing logic and adds cpuset and cgroup v1):
@@ - if sys.platform == "linux": + if sys.platform.startswith("linux"): try: + # Try cpuset (often the strictest limit) + cpuset_count = None + for _p in ("/sys/fs/cgroup/cpuset.cpus.effective", + "/sys/fs/cgroup/cpuset.cpus"): + try: + with open(_p) as _f: + _spec = _f.read().strip() + if _spec: + def _count_cpuset(spec: str) -> int: + n = 0 + for part in spec.split(","): + part = part.strip() + if not part: + continue + if "-" in part: + a, b = part.split("-", 1) + n += int(b) - int(a) + 1 + else: + n += 1 + return n + cpuset_count = _count_cpuset(_spec) + break + except Exception: + pass + with open("/sys/fs/cgroup/cpu.max") as f: - parts = f.read().strip().split() + parts = f.read().strip().split() # Format: "<quota|max> <period>" if len(parts) >= 2: quota_str, period_str = parts[0], parts[1] if quota_str != "max" and period_str != "0": quota = int(quota_str) period = int(period_str) - # Floor to an integer, but never return 0 - return max(1, quota // period) + quota_count = max(1, quota // period) + # If cpuset is present, take the stricter limit + if isinstance(cpuset_count, int) and cpuset_count > 0: + return max(1, min(cpuset_count, quota_count)) + return quota_count except Exception: pass + + # cgroup v1 fallback for quota + try: + with open("/sys/fs/cgroup/cpu/cpu.cfs_quota_us") as fq, \ + open("/sys/fs/cgroup/cpu/cpu.cfs_period_us") as fp: + quota = int(fq.read().strip()) + period = int(fp.read().strip()) + if quota > 0 and period > 0: + v1_quota_count = max(1, quota // period) + return v1_quota_count + except Exception: + passIf you prefer a smaller change set now, consider landing the cpu.max parsing and
os.process_cpu_count()
fixes first, and we can follow up with cpuset/cgroup v1 support in a separate PR.
2867-2873
: Consider using get_cpu_count() in worker recommendations to honor container limitsBoth
recommend_thread_pool_workers()
andrecommend_process_pool_workers()
currently default tomultiprocessing.cpu_count()
, which ignores cgroup limits. Usingget_cpu_count()
here would propagate the benefit of this PR across the codebase while still respectingfo.config.*_max_*
caps.If acceptable, I can provide a follow-up patch replacing the defaults in these functions with
get_cpu_count()
.Also applies to: 2887-2893
2896-2941
: Add unit tests for Linux cgroup parsing and fallbacksThis function is platform/FS dependent. Add tests that stub file reads and attributes to validate:
- cpu.max with quota/period (e.g., "200000 100000" -> 2)
- cpu.max with "max 100000" falls through to next methods
- absence of cpu.max triggers
os.process_cpu_count()
path- affinity-based path returns
len(affinity)
- final fallback returns 1 when everything else fails
I can draft pytest cases using
monkeypatch
to fake file contents and to shimos.process_cpu_count
,os.sched_getaffinity
, andos.cpu_count
.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
fiftyone/core/utils.py
(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
- GitHub Check: test / test-python (ubuntu-latest-m, 3.9)
- GitHub Check: test / test-python (ubuntu-latest-m, 3.11)
- GitHub Check: test / test-python (ubuntu-latest-m, 3.10)
- GitHub Check: test / test-python (ubuntu-latest-m, 3.12)
- GitHub Check: test-windows / test-python (windows-latest, 3.12)
- GitHub Check: test-windows / test-python (windows-latest, 3.11)
- GitHub Check: test-windows / test-python (windows-latest, 3.9)
- GitHub Check: test-windows / test-python (windows-latest, 3.10)
- GitHub Check: e2e / test-e2e
- GitHub Check: build
@coderabbitai yes do this |
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chainI'll examine the implementation first and then create comprehensive pytest test cases for the Now I'll create comprehensive pytest test cases for the @swheaton Here are comprehensive pytest test cases for the import os
import sys
import pytest
from unittest.mock import mock_open, patch
import fiftyone.core.utils as fou
class TestGetCpuCount:
"""Test cases for get_cpu_count() function with various scenarios."""
def test_linux_cgroup_with_quota_limit(self, monkeypatch):
"""Test Linux cgroup with CPU quota limit set."""
# Mock file reading for cgroup cpu.max
mock_file_content = "150000 100000" # quota=150000, period=100000 -> 1.5 CPUs
monkeypatch.setattr("builtins.open", mock_open(read_data=mock_file_content))
monkeypatch.setattr("os.path.exists", lambda path: path == "/sys/fs/cgroup/cpu.max")
result = fou.get_cpu_count()
assert result == 1 # max(1, 150000/100000) = max(1, 1.5) = 1 (floor)
def test_linux_cgroup_with_higher_quota_limit(self, monkeypatch):
"""Test Linux cgroup with CPU quota limit higher than 1."""
# Mock file reading for cgroup cpu.max
mock_file_content = "300000 100000" # quota=300000, period=100000 -> 3.0 CPUs
monkeypatch.setattr("builtins.open", mock_open(read_data=mock_file_content))
monkeypatch.setattr("os.path.exists", lambda path: path == "/sys/fs/cgroup/cpu.max")
result = fou.get_cpu_count()
assert result == 3 # max(1, 300000/100000) = max(1, 3.0) = 3
def test_linux_cgroup_no_limit(self, monkeypatch):
"""Test Linux cgroup with no CPU quota limit (max value)."""
# Mock file reading for cgroup cpu.max with "max" indicating no limit
mock_file_content = "max 100000"
monkeypatch.setattr("builtins.open", mock_open(read_data=mock_file_content))
monkeypatch.setattr("os.path.exists", lambda path: path == "/sys/fs/cgroup/cpu.max")
# Should fall back to next method
with patch('os.sched_getaffinity', return_value={0, 1, 2, 3}):
result = fou.get_cpu_count()
assert result == 4
def test_linux_cgroup_file_not_exists(self, monkeypatch):
"""Test Linux system where cgroup file doesn't exist."""
monkeypatch.setattr("os.path.exists", lambda path: False)
# Should fall back to sched_getaffinity
with patch('os.sched_getaffinity', return_value={0, 1, 2, 3, 4, 5}):
result = fou.get_cpu_count()
assert result == 6
def test_linux_cgroup_file_read_error(self, monkeypatch):
"""Test Linux cgroup file read error."""
monkeypatch.setattr("os.path.exists", lambda path: path == "/sys/fs/cgroup/cpu.max")
# Mock open to raise an exception
monkeypatch.setattr("builtins.open", lambda *args, **kwargs: (_ for _ in ()).throw(IOError("Permission denied")))
# Should fall back to sched_getaffinity
with patch('os.sched_getaffinity', return_value={0, 1}):
result = fou.get_cpu_count()
assert result == 2
def test_python_313_process_cpu_count(self, monkeypatch):
"""Test Python 3.13+ os.process_cpu_count() availability."""
# Mock no cgroup file
monkeypatch.setattr("os.path.exists", lambda path: False)
# Mock os.process_cpu_count to exist and return a value
with patch('os.process_cpu_count', return_value=4):
result = fou.get_cpu_count()
assert result == 4
def test_python_313_process_cpu_count_none(self, monkeypatch):
"""Test Python 3.13+ os.process_cpu_count() returns None."""
# Mock no cgroup file
monkeypatch.setattr("os.path.exists", lambda path: False)
# Mock os.process_cpu_count to exist but return None
with patch('os.process_cpu_count', return_value=None):
# Should fall back to sched_getaffinity
with patch('os.sched_getaffinity', return_value={0, 1, 2}):
result = fou.get_cpu_count()
assert result == 3
def test_sched_getaffinity_fallback(self, monkeypatch):
"""Test fallback to os.sched_getaffinity when previous methods fail."""
# Mock no cgroup file
monkeypatch.setattr("os.path.exists", lambda path: False)
# Mock os.process_cpu_count to not exist
with patch.object(os, 'process_cpu_count', side_effect=AttributeError):
# Mock sched_getaffinity
with patch('os.sched_getaffinity', return_value={0, 1, 2, 3, 4, 5, 6, 7}):
result = fou.get_cpu_count()
assert result == 8
def test_sched_getaffinity_not_available(self, monkeypatch):
"""Test when sched_getaffinity is not available (non-Unix systems)."""
# Mock no cgroup file
monkeypatch.setattr("os.path.exists", lambda path: False)
# Mock os.process_cpu_count to not exist
with patch.object(os, 'process_cpu_count', side_effect=AttributeError):
# Mock sched_getaffinity to not exist
with patch.object(os, 'sched_getaffinity', side_effect=AttributeError):
# Should fall back to multiprocessing.cpu_count
with patch('multiprocessing.cpu_count', return_value=12):
result = fou.get_cpu_count()
assert result == 12
def test_multiprocessing_cpu_count_fallback(self, monkeypatch):
"""Test final fallback to multiprocessing.cpu_count()."""
# Mock no cgroup file
monkeypatch.setattr("os.path.exists", lambda path: False)
# Mock all previous methods to fail or not exist
with patch.object(os, 'process_cpu_count', side_effect=AttributeError):
with patch.object(os, 'sched_getaffinity', side_effect=AttributeError):
with patch('multiprocessing.cpu_count', return_value=16):
result = fou.get_cpu_count()
assert result == 16
def test_all_methods_fail_return_one(self, monkeypatch):
"""Test when all CPU count methods fail, should return 1."""
# Mock no cgroup file
monkeypatch.setattr("os.path.exists", lambda path: False)
# Mock all methods to fail
with patch.object(os, 'process_cpu_count', side_effect=AttributeError):
with patch.object(os, 'sched_getaffinity', side_effect=AttributeError):
with patch('multiprocessing.cpu_count', side_effect=Exception("CPU count failed")):
result = fou.get_cpu_count()
assert result == 1
def test_cgroup_malformed_content(self, monkeypatch):
"""Test cgroup file with malformed content."""
# Mock file with malformed content
mock_file_content = "invalid content"
monkeypatch.setattr("builtins.open", mock_open(read_data=mock_file_content))
monkeypatch.setattr("os.path.exists", lambda path: path == "/sys/fs/cgroup/cpu.max")
# Should fall back to next method
with patch('os.sched_getaffinity', return_value={0, 1}):
result = fou.get_cpu_count()
assert result == 2
def test_cgroup_zero_period(self, monkeypatch):
"""Test cgroup file with zero period (edge case)."""
# Mock file with zero period
mock_file_content = "100000 0"
monkeypatch.setattr("builtins.open", mock_open(read_data=mock_file_content))
monkeypatch.setattr("os.path.exists", lambda path: path == "/sys/fs/cgroup/cpu.max")
# Should fall back to next method due to division by zero protection
with patch('os.sched_getaffinity', return_value={0, 1, 2}):
result = fou.get_cpu_count()
assert result == 3
def test_cgroup_empty_file(self, monkeypatch):
"""Test cgroup file that is empty."""
# Mock empty file
mock_file_content = ""
monkeypatch.setattr("builtins.open", mock_open(read_data=mock_file_content))
monkeypatch.setattr("os.path.exists", lambda path: path == "/sys/fs/cgroup/cpu.max")
# Should fall back to next method
with patch('os.sched_getaffinity', return_value={0, 1, 2, 3}):
result = fou.get_cpu_count()
assert result == 4
@pytest.mark.parametrize("quota,period,expected", [
("50000", "100000", 1), # 0.5 CPUs -> max(1, 0.5) = 1
("100000", "100000", 1), # 1.0 CPUs -> max(1, 1.0) = 1
("150000", "100000", 1), # 1.5 CPUs -> max(1, 1.5) = 1
("200000", "100000", 2), # 2.0 CPUs -> max(1, 2.0) = 2
("350000", "100000", 3), # 3.5 CPUs -> max(1, 3.5) = 3
("800000", "100000", 8), # 8.0 CPUs -> max(1, 8.0) = 8
])
def test_cgroup_various_quotas(self, monkeypatch, quota, period, expected):
"""Test various CPU quota/period combinations."""
mock_file_content = f"{quota} {period}"
monkeypatch.setattr("builtins.open", mock_open(read_data=mock_file_content))
monkeypatch.setattr("os.path.exists", lambda path: path == "/sys/fs/cgroup/cpu.max")
result = fou.get_cpu_count()
assert result == expected These test cases cover:
The tests use 🏁 Script executed:
Length of output: 1682 🏁 Script executed:
Length of output: 133 🏁 Script executed:
Length of output: 2103 Now that I can see the actual implementation, I need to update the test cases to match the real code. Here are the corrected comprehensive pytest test cases: @swheaton Here are the corrected comprehensive pytest test cases for the import sys
import os
import multiprocessing
import pytest
from unittest.mock import mock_open, patch
import fiftyone.core.utils as fou
class TestGetCpuCount:
"""Test cases for get_cpu_count() function with various scenarios."""
def test_linux_cgroup_with_quota_limit(self, monkeypatch):
"""Test Linux cgroup with CPU quota limit set."""
monkeypatch.setattr("sys.platform", "linux")
# Mock file reading for cgroup cpu.max
mock_file_content = "150000 100000" # quota=150000, period=100000 -> int(150000/100000) = 1
monkeypatch.setattr("builtins.open", mock_open(read_data=mock_file_content))
result = fou.get_cpu_count()
assert result == 1 # max(1, int(150000/100000)) = max(1, 1) = 1
def test_linux_cgroup_with_higher_quota_limit(self, monkeypatch):
"""Test Linux cgroup with CPU quota limit higher than 1."""
monkeypatch.setattr("sys.platform", "linux")
# Mock file reading for cgroup cpu.max
mock_file_content = "300000 100000" # quota=300000, period=100000 -> int(300000/100000) = 3
monkeypatch.setattr("builtins.open", mock_open(read_data=mock_file_content))
result = fou.get_cpu_count()
assert result == 3 # max(1, int(300000/100000)) = max(1, 3) = 3
def test_linux_cgroup_fractional_quota(self, monkeypatch):
"""Test Linux cgroup with fractional CPU quota (truncated by int())."""
monkeypatch.setattr("sys.platform", "linux")
# Mock file reading for cgroup cpu.max
mock_file_content = "250000 100000" # quota=250000, period=100000 -> int(250000/100000) = 2 (truncated from 2.5)
monkeypatch.setattr("builtins.open", mock_open(read_data=mock_file_content))
result = fou.get_cpu_count()
assert result == 2 # max(1, int(250000/100000)) = max(1, 2) = 2
def test_linux_cgroup_no_limit_max(self, monkeypatch):
"""Test Linux cgroup with no CPU quota limit (max value)."""
monkeypatch.setattr("sys.platform", "linux")
# Mock file reading for cgroup cpu.max with "max" indicating no limit
mock_file_content = "max 100000"
monkeypatch.setattr("builtins.open", mock_open(read_data=mock_file_content))
# Should fall back to next method since quota == "max"
with patch('os.sched_getaffinity', return_value={0, 1, 2, 3}):
result = fou.get_cpu_count()
assert result == 4
def test_linux_cgroup_zero_period(self, monkeypatch):
"""Test Linux cgroup with zero period (should fall back)."""
monkeypatch.setattr("sys.platform", "linux")
# Mock file reading for cgroup cpu.max with zero period
mock_file_content = "100000 0"
monkeypatch.setattr("builtins.open", mock_open(read_data=mock_file_content))
# Should fall back to next method since period == "0"
with patch('os.sched_getaffinity', return_value={0, 1, 2}):
result = fou.get_cpu_count()
assert result == 3
def test_linux_cgroup_file_not_found(self, monkeypatch):
"""Test Linux cgroup file not found."""
monkeypatch.setattr("sys.platform", "linux")
# Mock open to raise FileNotFoundError
monkeypatch.setattr("builtins.open", mock_open())
monkeypatch.setattr("builtins.open", lambda *args, **kwargs: (_ for _ in ()).throw(FileNotFoundError()))
# Should fall back to sched_getaffinity
with patch('os.sched_getaffinity', return_value={0, 1, 2, 3, 4, 5}):
result = fou.get_cpu_count()
assert result == 6
def test_linux_cgroup_file_permission_error(self, monkeypatch):
"""Test Linux cgroup file permission error."""
monkeypatch.setattr("sys.platform", "linux")
# Mock open to raise PermissionError
monkeypatch.setattr("builtins.open", lambda *args, **kwargs: (_ for _ in ()).throw(PermissionError("Access denied")))
# Should fall back to sched_getaffinity
with patch('os.sched_getaffinity', return_value={0, 1}):
result = fou.get_cpu_count()
assert result == 2
def test_linux_cgroup_malformed_content(self, monkeypatch):
"""Test Linux cgroup file with malformed content."""
monkeypatch.setattr("sys.platform", "linux")
# Mock file with content that can't be split properly
mock_file_content = "invalid"
monkeypatch.setattr("builtins.open", mock_open(read_data=mock_file_content))
# Should fall back to next method due to ValueError in split()
with patch('os.sched_getaffinity', return_value={0, 1, 2}):
result = fou.get_cpu_count()
assert result == 3
def test_non_linux_platform(self, monkeypatch):
"""Test non-Linux platform skips cgroup check."""
monkeypatch.setattr("sys.platform", "darwin") # macOS
# Should skip cgroup check and try os.process_cpu_count
with patch('os.process_cpu_count', return_value=8):
result = fou.get_cpu_count()
assert result == 8
def test_python_313_process_cpu_count(self, monkeypatch):
"""Test Python 3.13+ os.process_cpu_count() availability."""
monkeypatch.setattr("sys.platform", "darwin") # Non-Linux to skip cgroup
# Mock os.process_cpu_count to exist and return a value
with patch('os.process_cpu_count', return_value=4):
result = fou.get_cpu_count()
assert result == 4
def test_python_313_process_cpu_count_returns_none(self, monkeypatch):
"""Test Python 3.13+ os.process_cpu_count() returns None."""
monkeypatch.setattr("sys.platform", "darwin") # Non-Linux to skip cgroup
# Mock os.process_cpu_count to exist but return None (it actually returns None, not raises)
with patch('os.process_cpu_count', return_value=None):
result = fou.get_cpu_count()
assert result is None # Function returns None directly
def test_process_cpu_count_not_available(self, monkeypatch):
"""Test when os.process_cpu_count is not available (Python < 3.13)."""
monkeypatch.setattr("sys.platform", "darwin") # Non-Linux to skip cgroup
# Mock os.process_cpu_count to not exist
with patch('os.process_cpu_count', side_effect=AttributeError):
# Should fall back to sched_getaffinity
with patch('os.sched_getaffinity', return_value={0, 1, 2, 3, 4, 5, 6, 7}):
result = fou.get_cpu_count()
assert result == 8
def test_sched_getaffinity_fallback(self, monkeypatch):
"""Test fallback to os.sched_getaffinity when previous methods fail."""
monkeypatch.setattr("sys.platform", "darwin") # Non-Linux to skip cgroup
# Mock os.process_cpu_count to not exist
with patch('os.process_cpu_count', side_effect=AttributeError):
# Mock sched_getaffinity
with patch('os.sched_getaffinity', return_value={0, 1, 2, 3}):
result = fou.get_cpu_count()
assert result == 4
def test_sched_getaffinity_not_available(self, monkeypatch):
"""Test when sched_getaffinity is not available (non-Unix systems)."""
monkeypatch.setattr("sys.platform", "win32") # Windows
# Mock os.process_cpu_count to not exist
with patch('os.process_cpu_count', side_effect=AttributeError):
# Mock sched_getaffinity to not exist
with patch('os.sched_getaffinity', side_effect=AttributeError):
# Should fall back to multiprocessing.cpu_count
with patch('multiprocessing.cpu_count', return_value=12):
result = fou.get_cpu_count()
assert result == 12
def test_multiprocessing_cpu_count_not_implemented(self, monkeypatch):
"""Test when multiprocessing.cpu_count raises NotImplementedError."""
monkeypatch.setattr("sys.platform", "win32") # Windows
# Mock all previous methods to fail
with patch('os.process_cpu_count', side_effect=AttributeError):
with patch('os.sched_getaffinity', side_effect=AttributeError):
with patch('multiprocessing.cpu_count', side_effect=NotImplementedError):
result = fou.get_cpu_count()
assert result == 1
def test_multiprocessing_cpu_count_other_exception(self, monkeypatch):
"""Test when multiprocessing.cpu_count raises other exceptions (should bubble up)."""
monkeypatch.setattr("sys.platform", "win32") # Windows
# Mock all previous methods to fail
with patch('os.process_cpu_count', side_effect=AttributeError):
with patch('os.sched_getaffinity', side_effect=AttributeError):
with patch('multiprocessing.cpu_count', side_effect=RuntimeError("Unexpected error")):
# Should raise the RuntimeError, not catch it
with pytest.raises(RuntimeError, match="Unexpected error"):
fou.get_cpu_count()
@pytest.mark.parametrize("quota,period,expected", [
("50000", "100000", 1), # int(50000/100000) = 0 -> max(1, 0) = 1
("100000", "100000", 1), # int(100000/100000) = 1 -> max(1, 1) = 1
("150000", "100000", 1), # int(150000/100000) = 1 -> max(1, 1) = 1
("200000", "100000", 2), # int(200000/100000) = 2 -> max(1, 2) = 2
("250000", "100000", 2), # int(250000/100000) = 2 -> max(1, 2) = 2 (truncated from 2.5)
("350000", "100000", 3), # int(350000/100000) = 3 -> max(1, 3) = 3 (truncated from 3.5)
("800000", "100000", 8), # int(800000/100000) = 8 -> max(1, 8) = 8
])
def test_cgroup_various_quotas(self, monkeypatch, quota, period, expected):
"""Test various CPU quota/period combinations."""
monkeypatch.setattr("sys.platform", "linux")
mock_file_content = f"{quota} {period}"
monkeypatch.setattr("builtins.open", mock_open(read_data=mock_file_content))
result = fou.get_cpu_count()
assert result == expected
def test_linux_with_all_methods_available(self, monkeypatch):
"""Test Linux system where cgroup fails but other methods work."""
monkeypatch.setattr("sys.platform", "linux")
# Mock cgroup file to raise exception
monkeypatch.setattr("builtins.open", lambda *args, **kwargs: (_ for _ in ()).throw(FileNotFoundError()))
# Mock os.process_cpu_count to work
with patch('os.process_cpu_count', return_value=6):
result = fou.get_cpu_count()
assert result == 6
def test_integration_full_fallback_chain(self, monkeypatch):
"""Test the complete fallback chain from cgroup to final fallback."""
monkeypatch.setattr("sys.platform", "linux")
# Mock cgroup to fail
monkeypatch.setattr("builtins.open", lambda *args, **kwargs: (_ for _ in ()).throw(FileNotFoundError()))
# Mock each fallback method in sequence
with patch('os.process_cpu_count', side_effect=AttributeError):
with patch('os.sched_getaffinity', side_effect=AttributeError):
with patch('multiprocessing.cpu_count', return_value=4):
result = fou.get_cpu_count()
assert result == 4 Key corrections made to match the actual implementation:
These test cases now accurately reflect the actual implementation and provide comprehensive coverage of all code paths and edge cases. |
os.cpu_count()
gives the number of CPUs available to the whole node.In kubernetes e.g., this might be much different from the CPU limit that the container is actually operating under.
What changes are proposed in this pull request?
Try in order:
/sys/fs/cgroup/cpu.max
for kubernetes (or possibly others) defined limitsos.sched_getaffinity(0)
os.cpu_count()
How is this patch tested? If it is not, please explain why.
TBD
Release Notes
Is this a user-facing change that should be mentioned in the release notes?
notes for FiftyOne users.
(Details in 1-2 sentences. You can just refer to another PR with a description
if this PR is part of a larger change.)
What areas of FiftyOne does this PR affect?
fiftyone
Python library changesSummary by CodeRabbit