Skip to content

Commit 5d3ae7a

Browse files
committed
[GR-64339] Add implementation of shutil.disk_usage() based on TruffleFile API
PullRequest: graalpython/3854
2 parents 72ed223 + 75bfbba commit 5d3ae7a

File tree

7 files changed

+150
-77
lines changed

7 files changed

+150
-77
lines changed

graalpython/com.oracle.graal.python.test/src/tests/test_posix.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,6 @@ def test_fstat(self):
380380
with open(TEST_FULL_PATH2, 0) as fd: # follows symlink
381381
self.assertEqual(inode, os.fstat(fd).st_ino)
382382

383-
@unittest.skipIf(__graalpython__.posix_module_backend() == 'java', 'statvfs emulation is not supported')
384383
def test_statvfs(self):
385384
res = os.statvfs(TEST_FULL_PATH1)
386385
with open(TEST_FULL_PATH1, 0) as fd:
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
2+
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3+
#
4+
# The Universal Permissive License (UPL), Version 1.0
5+
#
6+
# Subject to the condition set forth below, permission is hereby granted to any
7+
# person obtaining a copy of this software, associated documentation and/or
8+
# data (collectively the "Software"), free of charge and under any and all
9+
# copyright rights in the Software, and any and all patent rights owned or
10+
# freely licensable by each licensor hereunder covering either (i) the
11+
# unmodified Software as contributed to or provided by such licensor, or (ii)
12+
# the Larger Works (as defined below), to deal in both
13+
#
14+
# (a) the Software, and
15+
#
16+
# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
17+
# one is included with the Software each a "Larger Work" to which the Software
18+
# is contributed by such licensors),
19+
#
20+
# without restriction, including without limitation the rights to copy, create
21+
# derivative works of, display, perform, and distribute the Software and make,
22+
# use, sell, offer for sale, import, export, have made, and have sold the
23+
# Software and the Larger Work(s), and to sublicense the foregoing rights on
24+
# either these or other terms.
25+
#
26+
# This license is subject to the following condition:
27+
#
28+
# The above copyright notice and either this complete permission notice or at a
29+
# minimum a reference to the UPL must be included in all copies or substantial
30+
# portions of the Software.
31+
#
32+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
34+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
35+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
36+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
37+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
38+
# SOFTWARE.
39+
40+
import unittest
41+
import shutil
42+
import io
43+
import os
44+
import pathlib
45+
import re
46+
47+
48+
class TestShUtil(unittest.TestCase):
49+
50+
def test_disk_usage_returns_a_tuple(self):
51+
usage = shutil.disk_usage(__file__)
52+
self.assertIsInstance(usage, tuple)
53+
self.assertGreater(usage.total, 0)
54+
self.assertGreater(usage.used, 0)
55+
self.assertGreater(usage.free, 0)
56+
self.assertGreater(usage.total, usage.used)
57+
self.assertGreater(usage.total, usage.free)
58+
59+
def test_disk_usage_accepts_path_as_a_string(self):
60+
usage = shutil.disk_usage(__file__)
61+
self.assertIsInstance(usage, tuple)
62+
63+
def test_disk_usage_accepts_path_as_bytes(self):
64+
usage = shutil.disk_usage(__file__.encode("utf-8"))
65+
self.assertIsInstance(usage, tuple)
66+
67+
def test_disk_usage_accepts_path_as_a_pathlike(self):
68+
usage = shutil.disk_usage(pathlib.PurePath(__file__))
69+
self.assertIsInstance(usage, tuple)
70+
71+
def test_disk_usage_accepts_file_descriptor(self):
72+
with io.open(__file__) as file:
73+
fd = file.fileno()
74+
usage = shutil.disk_usage(fd)
75+
self.assertIsInstance(usage, tuple)
76+
77+
def test_disk_usage_supports_path_to_file(self):
78+
usage = shutil.disk_usage(__file__)
79+
self.assertIsInstance(usage, tuple)
80+
81+
def test_disk_usage_supports_path_to_directory(self):
82+
usage = shutil.disk_usage(os.path.dirname(__file__))
83+
self.assertIsInstance(usage, tuple)
84+
85+
def test_disk_usage_raises_exception_when_given_path_type_is_not_supported(self):
86+
with self.assertRaisesRegex(TypeError, r'path should be string, bytes, os.PathLike or integer, not list'):
87+
shutil.disk_usage([])
88+
89+
def test_disk_usage_raises_exception_when_given_path_does_not_exist(self):
90+
not_existing_path = __file__ + '.not-existing'
91+
not_existing_file_name = os.path.basename(not_existing_path)
92+
93+
with self.assertRaisesRegex(FileNotFoundError, rf"No such file or directory: '.+{not_existing_file_name}'"):
94+
shutil.disk_usage(not_existing_path)
95+
96+
def test_disk_usage_raises_exception_when_there_is_no_open_file_with_given_file_descriptor(self):
97+
not_existing_file_descriptor = 1_000_000 # expect the current process to not open 1M files
98+
99+
with self.assertRaisesRegex(OSError, r'(B|b)ad file descriptor: 1000000'):
100+
shutil.disk_usage(not_existing_file_descriptor)

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PosixModuleBuiltins.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,6 @@ public void postInitialize(Python3Core core) {
355355
((PDict) environAttr).setDictStorage(environ.getDictStorage());
356356

357357
if (posixLib.getBackend(posixSupport).toJavaStringUncached().equals("java")) {
358-
posix.setAttribute(toTruffleStringUncached("statvfs"), PNone.NO_VALUE);
359358
posix.setAttribute(toTruffleStringUncached("geteuid"), PNone.NO_VALUE);
360359
posix.setAttribute(toTruffleStringUncached("getegid"), PNone.NO_VALUE);
361360

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/multiprocessing/MultiprocessingGraalPyModuleBuiltins.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ PNone doit(VirtualFrame frame, TruffleString name,
127127
@Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) {
128128
Semaphore prev = getContext().getSharedMultiprocessingData().removeNamedSemaphore(name);
129129
if (prev == null) {
130-
throw constructAndRaiseNode.get(inliningTarget).raiseFileNotFoundError(frame, ErrorMessages.NO_SUCH_FILE_OR_DIR, "semaphores", name);
130+
throw constructAndRaiseNode.get(inliningTarget).raiseFileNotFoundError(frame, ErrorMessages.NO_SUCH_FILE_OR_DIR_WITH_LABEL, "semaphores", name);
131131
}
132132
return PNone.NONE;
133133
}

0 commit comments

Comments
 (0)