assignment-codeval 0.0.32__py3-none-any.whl → 0.0.34__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- assignment_codeval/commons.py +5 -2
- assignment_codeval/submissions.py +84 -2
- {assignment_codeval-0.0.32.dist-info → assignment_codeval-0.0.34.dist-info}/METADATA +1 -1
- {assignment_codeval-0.0.32.dist-info → assignment_codeval-0.0.34.dist-info}/RECORD +7 -7
- {assignment_codeval-0.0.32.dist-info → assignment_codeval-0.0.34.dist-info}/WHEEL +0 -0
- {assignment_codeval-0.0.32.dist-info → assignment_codeval-0.0.34.dist-info}/entry_points.txt +0 -0
- {assignment_codeval-0.0.32.dist-info → assignment_codeval-0.0.34.dist-info}/top_level.txt +0 -0
assignment_codeval/commons.py
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
from typing import NoReturn
|
|
2
2
|
|
|
3
3
|
import click
|
|
4
|
+
import re
|
|
4
5
|
import time
|
|
5
6
|
import dataclasses
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
def despace(name):
|
|
9
|
-
# we need to replace spaces with _ to work well with scripts
|
|
10
10
|
# we need to remove the : so that it doesn't mess up docker
|
|
11
|
-
|
|
11
|
+
# (removed rather than replaced to keep existing names stable)
|
|
12
|
+
# everything else that isn't a word character, . or - becomes _ so the
|
|
13
|
+
# name is a single path component that is safe in scripts and the shell
|
|
14
|
+
return re.sub(r"[^\w.-]", "_", name.replace(":", ""))
|
|
12
15
|
|
|
13
16
|
|
|
14
17
|
@dataclasses.dataclass(init=True, repr=True, frozen=True)
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import os
|
|
2
|
+
import posixpath
|
|
2
3
|
import re
|
|
4
|
+
import shlex
|
|
3
5
|
import shutil
|
|
4
6
|
import subprocess
|
|
5
7
|
import time
|
|
@@ -16,6 +18,7 @@ import requests
|
|
|
16
18
|
|
|
17
19
|
from assignment_codeval.canvas_utils import connect_to_canvas, get_course, get_courses, get_assignment
|
|
18
20
|
from assignment_codeval.commons import debug, error, info, warn, despace
|
|
21
|
+
from assignment_codeval.github_connect import _read_desired_commit
|
|
19
22
|
|
|
20
23
|
# Bookkeeping files the tool writes into each student directory. A student must
|
|
21
24
|
# never be able to clobber these via an attachment filename — in particular
|
|
@@ -514,6 +517,64 @@ def upload_submission_comments(submissions_dir, codeval_prefix, delete):
|
|
|
514
517
|
warn(f"no submission found for {student_id} in {course_name}: {assignment_name}")
|
|
515
518
|
|
|
516
519
|
|
|
520
|
+
def _github_fetch_problem(dirpath, submission_dir):
|
|
521
|
+
"""For a GitHub submission whose fetch failed, build the student-facing message.
|
|
522
|
+
|
|
523
|
+
Returns None when the submission is not GitHub-based (no github_repo in
|
|
524
|
+
metadata.txt) or when the checkout matches the submitted commit, so the
|
|
525
|
+
caller proceeds with normal grading.
|
|
526
|
+
|
|
527
|
+
Covers both failure shapes: no checkout at all (clone failed, bad digest)
|
|
528
|
+
and a clone whose gh_success.txt is missing or stale (the submitted commit
|
|
529
|
+
could not be checked out, e.g. it was never pushed) — grading that tree
|
|
530
|
+
would silently grade the wrong commit. github-setup-repo leaves its
|
|
531
|
+
clone/checkout errors (bad digest, failed clone, failed checkout) in
|
|
532
|
+
comments.txt; include them so the student sees why the fetch failed
|
|
533
|
+
instead of a misleading complaint about a missing assignment directory.
|
|
534
|
+
"""
|
|
535
|
+
metadata_path = os.path.join(dirpath, "metadata.txt")
|
|
536
|
+
has_github_repo = False
|
|
537
|
+
if os.path.exists(metadata_path):
|
|
538
|
+
with open(metadata_path, "r") as f:
|
|
539
|
+
has_github_repo = any(
|
|
540
|
+
line.startswith("github_repo=") and line.split("=", 1)[1].strip()
|
|
541
|
+
for line in f)
|
|
542
|
+
if not has_github_repo:
|
|
543
|
+
return None
|
|
544
|
+
|
|
545
|
+
desired = _read_desired_commit(os.path.join(dirpath, "content.txt"))
|
|
546
|
+
recorded = None
|
|
547
|
+
try:
|
|
548
|
+
with open(os.path.join(dirpath, "gh_success.txt"), "r") as f:
|
|
549
|
+
recorded = f.readline().strip() or None
|
|
550
|
+
except OSError:
|
|
551
|
+
pass
|
|
552
|
+
|
|
553
|
+
if os.path.isdir(submission_dir):
|
|
554
|
+
# Mirror github-setup-repo: a recorded checkout is good if it matches
|
|
555
|
+
# the submitted digest, or if the digest is unreadable (leave as-is).
|
|
556
|
+
if recorded and (not desired or recorded == desired):
|
|
557
|
+
return None
|
|
558
|
+
out = (b"your repository was cloned, but the commit digest you submitted in Canvas\n"
|
|
559
|
+
b"could not be checked out, so your submission was not graded. Make sure you\n"
|
|
560
|
+
b"have pushed your commits and submitted only the digest of your final\n"
|
|
561
|
+
b"pushed commit (the 40-character output of `git rev-parse HEAD`) in Canvas.\n")
|
|
562
|
+
else:
|
|
563
|
+
out = (b"your submission could not be fetched from GitHub, so it was not graded.\n"
|
|
564
|
+
b"Make sure you have pushed your commits and submitted only the digest of\n"
|
|
565
|
+
b"your final commit (the 40-character output of `git rev-parse HEAD`) in Canvas.\n")
|
|
566
|
+
try:
|
|
567
|
+
with open(os.path.join(dirpath, "comments.txt"), "rb") as f:
|
|
568
|
+
prior = f.read()
|
|
569
|
+
except OSError:
|
|
570
|
+
prior = b""
|
|
571
|
+
# Include only clone logs written by github-setup-repo, never the result
|
|
572
|
+
# of a previous evaluation run (which would nest on re-runs).
|
|
573
|
+
if prior.startswith((b"cloning", "❌".encode())):
|
|
574
|
+
out += b"\nDetails of the failed fetch:\n" + prior
|
|
575
|
+
return out
|
|
576
|
+
|
|
577
|
+
|
|
517
578
|
@click.command()
|
|
518
579
|
@click.argument('codeval_dir', metavar="CODEVAL_DIR", required=False)
|
|
519
580
|
@click.option("--submissions-dir", help="directory containing submissions COURSE/ASSIGNMENT/STUDENT_ID",
|
|
@@ -559,6 +620,18 @@ def evaluate_submissions(codeval_dir, submissions_dir):
|
|
|
559
620
|
with open(os.path.join(dirpath, "codeval_path.txt"), "w") as f:
|
|
560
621
|
f.write(os.path.abspath(codeval_file))
|
|
561
622
|
|
|
623
|
+
# A GitHub submission with no checkout, or a checkout that isn't the
|
|
624
|
+
# submitted commit, means the fetch failed (bad digest, failed clone
|
|
625
|
+
# or checkout). Say that, rather than falling through to a misleading
|
|
626
|
+
# "<dir> does not exist" message or grading the wrong commit.
|
|
627
|
+
fetch_failure = _github_fetch_problem(dirpath, submission_dir)
|
|
628
|
+
if fetch_failure is not None:
|
|
629
|
+
info("writing results")
|
|
630
|
+
with open(f"{dirpath}/comments.txt", "wb") as fd:
|
|
631
|
+
fd.write(fetch_failure)
|
|
632
|
+
info("continuing")
|
|
633
|
+
continue
|
|
634
|
+
|
|
562
635
|
# First pass: get CTO, CD tags, and collect Z files (don't extract yet)
|
|
563
636
|
compile_timeout = 20
|
|
564
637
|
assignment_working_dir = "."
|
|
@@ -620,7 +693,16 @@ def evaluate_submissions(codeval_dir, submissions_dir):
|
|
|
620
693
|
os.chmod(os.path.join(dest_dir, f.filename), perms)
|
|
621
694
|
|
|
622
695
|
if not move_to_next_submission:
|
|
623
|
-
|
|
696
|
+
# Mount the whole submission and cd to the working directory
|
|
697
|
+
# inside the container: mounting only the working directory would
|
|
698
|
+
# hide the repository's .git (which lives at the submission root)
|
|
699
|
+
# from git-based tests.
|
|
700
|
+
container_working_dir = posixpath.normpath(
|
|
701
|
+
posixpath.join("/submissions", assignment_working_dir.replace("\\", "/")))
|
|
702
|
+
command = raw_command.replace(
|
|
703
|
+
"EVALUATE",
|
|
704
|
+
f"cd {shlex.quote(container_working_dir)} 2>/dev/null || true; "
|
|
705
|
+
"assignment-codeval run-evaluation codeval.txt")
|
|
624
706
|
|
|
625
707
|
with TemporaryDirectory("cedir", dir="/var/tmp") as link_dir:
|
|
626
708
|
submission_link = os.path.join(link_dir, "submissions")
|
|
@@ -647,7 +729,7 @@ def evaluate_submissions(codeval_dir, submissions_dir):
|
|
|
647
729
|
os.path.join(full_assignment_working_dir, ref_file),
|
|
648
730
|
submission_dir)
|
|
649
731
|
|
|
650
|
-
command = command.replace("SUBMISSIONS",
|
|
732
|
+
command = command.replace("SUBMISSIONS", submission_link)
|
|
651
733
|
info(f"command to execute: {command}")
|
|
652
734
|
p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
|
653
735
|
try:
|
|
@@ -3,7 +3,7 @@ assignment_codeval/ai_benchmark.py,sha256=8WkLYD72NNS_82pF3IVpvXgBJDMN8PBYjmw4fV
|
|
|
3
3
|
assignment_codeval/canvas_utils.py,sha256=NF2xw10A1okVtDnP98YRzEiSo7QlqYF-IVAih4-WeLQ,5998
|
|
4
4
|
assignment_codeval/check_grading.py,sha256=rnNdp8T_XWckDfF1zUi3r70BMe9CPNTD-RfjfarI-CM,5889
|
|
5
5
|
assignment_codeval/cli.py,sha256=Vl27zNI7kI0QEZoXTIE2aTrraEJWj9vgJiyVWou3vgU,1357
|
|
6
|
-
assignment_codeval/commons.py,sha256=
|
|
6
|
+
assignment_codeval/commons.py,sha256=hnos85_OZsMv2OmALzFzXQe23HYIsbTnxXjINJUghEQ,1583
|
|
7
7
|
assignment_codeval/convertMD2Html.py,sha256=oNFY63WgxiXfk1v3j8zc1RxCzzQBzj6fXupezogPZfw,8248
|
|
8
8
|
assignment_codeval/create_assignment.py,sha256=tUgCYWhk4zoPZf8p4wyqHYgbEmLcX8XnUNykAv0XEW0,12488
|
|
9
9
|
assignment_codeval/evaluate.py,sha256=8ce59_fk13cp59hCAKywjojxAC1FdU6TKiF4t5eJKoY,42881
|
|
@@ -12,10 +12,10 @@ assignment_codeval/file_utils.py,sha256=vAFnPaA4p6BQabkn034SacBFhV_Mpib2sbZOxvk-
|
|
|
12
12
|
assignment_codeval/github_connect.py,sha256=4Fc9UusgXTqwlvNW-ppVKCfNnQM0Gp17oXWkUK4OWiY,6659
|
|
13
13
|
assignment_codeval/install_assignment.py,sha256=HsXzTex4G-U3t4tb-HyQ2AobBDBPcKeR6oAUDFqBP-A,4599
|
|
14
14
|
assignment_codeval/recent_comments.py,sha256=WelZpODSvZ5hbRn2IofEpqMwrvHx8haOG4KZJCvHYKw,9415
|
|
15
|
-
assignment_codeval/submissions.py,sha256=
|
|
15
|
+
assignment_codeval/submissions.py,sha256=Kg2DBtSSFhN_C1ePUIKUn5VW5TIVw2_7Giu2PpLdSOA,46366
|
|
16
16
|
assignment_codeval/test_template.html,sha256=c0e2lUurSalF4PpLIVWQCXirXeA8PvB4pzWe-kFS-9Y,3382
|
|
17
|
-
assignment_codeval-0.0.
|
|
18
|
-
assignment_codeval-0.0.
|
|
19
|
-
assignment_codeval-0.0.
|
|
20
|
-
assignment_codeval-0.0.
|
|
21
|
-
assignment_codeval-0.0.
|
|
17
|
+
assignment_codeval-0.0.34.dist-info/METADATA,sha256=y6OHiCYpxNb-8DEepajODCVbPQCTGlG-yG7WYDrGDuo,7757
|
|
18
|
+
assignment_codeval-0.0.34.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
19
|
+
assignment_codeval-0.0.34.dist-info/entry_points.txt,sha256=Pk0eAhsXBpCsgkv6HYLSHTRf5x_zUf-Lg8KK9KsK2B8,66
|
|
20
|
+
assignment_codeval-0.0.34.dist-info/top_level.txt,sha256=a_P8W2IWjAELxKlTV2t4rLwWVoTxElxUMvWgWjR_1n4,19
|
|
21
|
+
assignment_codeval-0.0.34.dist-info/RECORD,,
|
|
File without changes
|
{assignment_codeval-0.0.32.dist-info → assignment_codeval-0.0.34.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|