Skip to content

Commit bf643e6

Browse files
Frans Welinbrichet
Frans Welin
authored andcommitted
Add maximum size for entire directory as submission option
The previous maximum size option was applied to individual files only. This adds a separate option to limit the maximum size of the entire directory. The option is called max_dir_size and is implemented similarly to max_file_size. The default is no maximum directory size limit.
1 parent 05bb458 commit bf643e6

File tree

3 files changed

+41
-1
lines changed

3 files changed

+41
-1
lines changed

nbgrader/coursedir.py

+11
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,17 @@ def _validate_root(self, proposal: Bunch) -> str:
279279
)
280280
).tag(config=True)
281281

282+
max_dir_size = Integer(
283+
100000,
284+
help=dedent(
285+
"""
286+
Maximum size of directories (in kilobytes; default: 100Mb).
287+
Upon copying directories recursively, larger files will be
288+
ignored with a warning.
289+
"""
290+
)
291+
).tag(config=True)
292+
282293
def format_path(self, nbgrader_step: str, student_id: str, assignment_id: str, escape: bool = False) -> str:
283294
kwargs = dict(
284295
nbgrader_step=nbgrader_step,

nbgrader/exchange/default/exchange.py

+20
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,33 @@ def copy_files(self):
7070
"""Actually do the file transfer."""
7171
raise NotImplementedError
7272

73+
def get_size(self, root):
74+
"""
75+
Return total size of directory in bytes.
76+
"""
77+
total_size = 0
78+
for dirpath, dirnames, filenames in os.walk(root):
79+
for f in filenames:
80+
fp = os.path.join(dirpath, f)
81+
# skip if it is symbolic link
82+
if not os.path.islink(fp):
83+
total_size += os.path.getsize(fp)
84+
return total_size
85+
86+
7387
def do_copy(self, src, dest, log=None):
7488
"""
7589
Copy the src dir to the dest dir, omitting excluded
7690
file/directories, non included files, and too large files, as
7791
specified by the options coursedir.ignore, coursedir.include
7892
and coursedir.max_file_size.
7993
"""
94+
dir_size = self.get_size(src)
95+
max_dir_size = self.coursedir.max_dir_size
96+
if dir_size > 1000 * max_dir_size:
97+
self.log.error("Directory size is too big")
98+
raise RuntimeError(f"Directory size is too big. Size is {dir_size}, maximum size is {1000 * max_dir_size}")
99+
80100
shutil.copytree(src, dest,
81101
ignore=ignore_patterns(exclude=self.coursedir.ignore,
82102
include=self.coursedir.include,

nbgrader/tests/apps/test_nbgrader_submit.py

+10-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import datetime
33
import time
44
import stat
5+
import pytest
56

67
from os.path import join, isfile, exists
78

@@ -231,7 +232,7 @@ def test_submit_include(self, exchange, cache, course_dir):
231232
filename, = os.listdir(join(exchange, "abc101", "inbound"))
232233
assert not exists(join(exchange, "abc101", "inbound", filename, "foo.txt"))
233234

234-
def test_submit_include(self, exchange, cache, course_dir):
235+
def test_submit_max_file_size(self, exchange, cache, course_dir):
235236
self._release_and_fetch("ps1", exchange, cache, course_dir)
236237
self._make_file(join("ps1", "small_file"), contents="x" * 2000)
237238
self._make_file(join("ps1", "large_file"), contents="x" * 2001)
@@ -240,3 +241,11 @@ def test_submit_include(self, exchange, cache, course_dir):
240241
filename, = os.listdir(join(exchange, "abc101", "inbound"))
241242
assert exists(join(exchange, "abc101", "inbound", filename, "small_file"))
242243
assert not exists(join(exchange, "abc101", "inbound", filename, "large_file"))
244+
245+
def test_submit_max_dir_size(self, exchange, cache, course_dir):
246+
self._release_and_fetch("ps1", exchange, cache, course_dir)
247+
self._make_file(join("ps1", "small_file"), contents="x" * 2000)
248+
self._make_file(join("ps1", "large_file"), contents="x" * 2001)
249+
with pytest.raises(RuntimeError):
250+
self._submit("ps1", exchange, cache,
251+
flags=['--CourseDirectory.max_dir_size=3'])

0 commit comments

Comments
 (0)