Skip to content

Commit

Permalink
Add maximum size for entire directory as submission option
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Frans Welin authored and Gehock committed Feb 7, 2023
1 parent ca7b0e5 commit 8f2a071
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 1 deletion.
11 changes: 11 additions & 0 deletions nbgrader/coursedir.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,17 @@ def _validate_root(self, proposal: Bunch) -> str:
)
).tag(config=True)

max_dir_size = Integer(
100000,
help=dedent(
"""
Maximum size of directories (in kilobytes; default: 100Mb).
Upon copying directories recursively, larger files will be
ignored with a warning.
"""
)
).tag(config=True)

def format_path(self, nbgrader_step: str, student_id: str, assignment_id: str, escape: bool = False) -> str:
kwargs = dict(
nbgrader_step=nbgrader_step,
Expand Down
20 changes: 20 additions & 0 deletions nbgrader/exchange/default/exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,33 @@ def copy_files(self):
"""Actually do the file transfer."""
raise NotImplementedError

def get_size(self, root):
"""
Return total size of directory in bytes.
"""
total_size = 0
for dirpath, dirnames, filenames in os.walk(root):
for f in filenames:
fp = os.path.join(dirpath, f)
# skip if it is symbolic link
if not os.path.islink(fp):
total_size += os.path.getsize(fp)
return total_size


def do_copy(self, src, dest, log=None):
"""
Copy the src dir to the dest dir, omitting excluded
file/directories, non included files, and too large files, as
specified by the options coursedir.ignore, coursedir.include
and coursedir.max_file_size.
"""
dir_size = self.get_size(src)
max_dir_size = self.coursedir.max_dir_size
if dir_size > 1000 * max_dir_size:
self.log.error("Directory size is too big")
raise RuntimeError(f"Directory size is too big. Size is {dir_size}, maximum size is {1000 * max_dir_size}")

shutil.copytree(src, dest,
ignore=ignore_patterns(exclude=self.coursedir.ignore,
include=self.coursedir.include,
Expand Down
11 changes: 10 additions & 1 deletion nbgrader/tests/apps/test_nbgrader_submit.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import datetime
import time
import stat
import pytest

from os.path import join, isfile, exists

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

def test_submit_include(self, exchange, cache, course_dir):
def test_submit_max_file_size(self, exchange, cache, course_dir):
self._release_and_fetch("ps1", exchange, cache, course_dir)
self._make_file(join("ps1", "small_file"), contents="x" * 2000)
self._make_file(join("ps1", "large_file"), contents="x" * 2001)
Expand All @@ -240,3 +241,11 @@ def test_submit_include(self, exchange, cache, course_dir):
filename, = os.listdir(join(exchange, "abc101", "inbound"))
assert exists(join(exchange, "abc101", "inbound", filename, "small_file"))
assert not exists(join(exchange, "abc101", "inbound", filename, "large_file"))

def test_submit_max_dir_size(self, exchange, cache, course_dir):
self._release_and_fetch("ps1", exchange, cache, course_dir)
self._make_file(join("ps1", "small_file"), contents="x" * 2000)
self._make_file(join("ps1", "large_file"), contents="x" * 2001)
with pytest.raises(RuntimeError):
self._submit("ps1", exchange, cache,
flags=['--CourseDirectory.max_dir_size=3'])

0 comments on commit 8f2a071

Please sign in to comment.