Skip to content

Commit

Permalink
Merge pull request #239 from online-judge-tools/fix/197
Browse files Browse the repository at this point in the history
Catch exceptions from list_attributes() and list_dependencies()
  • Loading branch information
kmyk committed May 5, 2020
2 parents 2405e3f + f09dacb commit a2ca9c0
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 23 deletions.
43 changes: 26 additions & 17 deletions onlinejudge_verify/docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ def __init__(self, file_path: pathlib.Path, source_path: pathlib.Path) -> None:
self.file_path = file_path.resolve()
self.source_path = source_path.resolve()
self.parser = FileParser(file_path)
self.required = []

self.brief = self.parser.get_contents_by_tag(r'@brief')

Expand All @@ -115,33 +116,41 @@ def __init__(self, file_path: pathlib.Path, source_path: pathlib.Path) -> None:
else:
self.category = category_list[-1]

# pathlib 型に直し、相対パスである場合は絶対パスに直す
docs_list = self.parser.get_contents_by_tag(r'@docs')
self.docs = [pathlib.Path(path) for path in docs_list]
self.docs = self.to_abspath(self.docs)

# see で指定されるのは URL: パス修正は不要
self.see = self.parser.get_contents_by_tag(r'(?:@see|@sa)', re_escape=False)

# language object を用意しておく
language = onlinejudge_verify.languages.get(self.file_path)
assert language is not None

# language.list_dependencie() をもとに sself.depends を絶対パスの list として持つ
try:
attributes = language.list_attributes(self.file_path, basedir=pathlib.Path.cwd())
self.depends = language.list_dependencies(self.file_path, basedir=pathlib.Path.cwd())
except:
# 失敗したら中断
traceback.print_exc()
attributes = {}
self.depends = []
self.verification_status = VerificationStatus.FAILED
return
self.depends.extend(map(pathlib.Path, self.parser.get_contents_by_tag(r'@depends')))
self.depends = sorted(set(self.to_abspath(self.depends)))

# see で指定されるのは URL: パス修正は不要
self.see = self.parser.get_contents_by_tag(r'(?:@see|@sa)', re_escape=False)
# language.list_attributes() をもとに PROBLEM のデータを取得
try:
attributes = language.list_attributes(self.file_path, basedir=pathlib.Path.cwd())
except:
# 失敗したら中断
traceback.print_exc()
self.verification_status = VerificationStatus.FAILED
return
if 'PROBLEM' in attributes:
self.see.append(attributes['PROBLEM'])

# pathlib 型に直し、相対パスである場合は絶対パスに直す
docs_list = self.parser.get_contents_by_tag(r'@docs')
self.docs = [pathlib.Path(path) for path in docs_list]
self.docs = self.to_abspath(self.docs)

# pathlib 型に直し、相対パスである場合は絶対パスに直す
depends_list = self.parser.get_contents_by_tag(r'@depends')
depends_list.extend(map(str, language.list_dependencies(self.file_path, basedir=pathlib.Path.cwd())))
self.depends = [pathlib.Path(path) for path in depends_list]
self.depends = list(set(self.to_abspath(self.depends)))
self.depends.sort()

self.required = []
# 表示するverification statusを決める
is_verified = get_verification_marker().is_verified(self.file_path.relative_to(self.source_path))
is_failed = get_verification_marker().is_failed(self.file_path.relative_to(self.source_path))
Expand Down
17 changes: 17 additions & 0 deletions onlinejudge_verify/languages/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
class LanguageEnvironment(object):
@abc.abstractmethod
def compile(self, path: pathlib.Path, *, basedir: pathlib.Path, tempdir: pathlib.Path) -> None:
"""
:throws Exception:
"""

raise NotImplementedError

@abc.abstractmethod
Expand All @@ -18,14 +22,27 @@ def get_execute_command(self, path: pathlib.Path, *, basedir: pathlib.Path, temp

class Language(object):
def list_attributes(self, path: pathlib.Path, *, basedir: pathlib.Path) -> Dict[str, str]:
"""
:throws Exception:
"""

return list_special_comments(path)

@abc.abstractmethod
def list_dependencies(self, path: pathlib.Path, *, basedir: pathlib.Path) -> List[pathlib.Path]:
"""
:throws Exception:
"""

raise NotImplementedError

@abc.abstractmethod
def bundle(self, path: pathlib.Path, *, basedir: pathlib.Path) -> bytes:
"""
:throws Exception:
:throws NotImplementedError:
"""

raise NotImplementedError

def is_verification_file(self, path: pathlib.Path, *, basedir: pathlib.Path) -> bool:
Expand Down
25 changes: 19 additions & 6 deletions onlinejudge_verify/marker.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@
import json
import pathlib
import subprocess
import traceback
from typing import *

import onlinejudge_verify.languages
import onlinejudge_verify.utils

_error_timestamp = datetime.datetime.fromtimestamp(0, tz=datetime.timezone(datetime.timedelta()))


class VerificationMarker(object):
json_path: pathlib.Path
Expand All @@ -30,9 +33,15 @@ def get_current_timestamp(self, path: pathlib.Path) -> datetime.datetime:
else:
language = onlinejudge_verify.languages.get(path)
assert language is not None
timestamp = max([x.stat().st_mtime for x in language.list_dependencies(path, basedir=pathlib.Path.cwd())])
system_local_timezone = datetime.datetime.now(datetime.timezone.utc).astimezone().tzinfo
return datetime.datetime.fromtimestamp(timestamp, tz=system_local_timezone).replace(microsecond=0) # microsecond=0 is required because it's erased on timestamps.*.json
try:
depending_files = language.list_dependencies(path, basedir=pathlib.Path.cwd())
except Exception:
traceback.print_exc()
return _error_timestamp
else:
timestamp = max([x.stat().st_mtime for x in depending_files])
system_local_timezone = datetime.datetime.now(datetime.timezone.utc).astimezone().tzinfo
return datetime.datetime.fromtimestamp(timestamp, tz=system_local_timezone).replace(microsecond=0) # microsecond=0 is required because it's erased on timestamps.*.json

def is_verified(self, path: pathlib.Path) -> bool:
path = path.resolve().relative_to(pathlib.Path.cwd())
Expand Down Expand Up @@ -70,7 +79,7 @@ def load_timestamps(self, *, jobs: Optional[int] = None) -> None:
self.new_timestamps = {}

def load(path, timestamp):
if path.exists() and self.get_current_timestamp(path) <= timestamp:
if path.exists() and _error_timestamp < self.get_current_timestamp(path) <= timestamp:
self.mark_verified(path)
return
#「そもそもテストを実行していない」のか「実行した上で失敗した」のか区別できないが、verifyできてない事には変わりないので一旦はfailedとみなす
Expand Down Expand Up @@ -125,11 +134,15 @@ def get_verification_marker(*, jobs: Optional[int] = None) -> VerificationMarker
def _get_last_commit_time_to_verify(path: pathlib.Path) -> datetime.datetime:
language = onlinejudge_verify.languages.get(path)
assert language is not None
depending_files = language.list_dependencies(path, basedir=pathlib.Path.cwd())
try:
depending_files = language.list_dependencies(path, basedir=pathlib.Path.cwd())
except Exception:
traceback.print_exc()
return _error_timestamp
code = ['git', 'log', '-1', '--date=iso', '--pretty=%ad', '--'] + list(map(str, depending_files))
timestamp = subprocess.check_output(code).decode().strip()
if not timestamp:
return datetime.datetime.fromtimestamp(0, tz=datetime.timezone(datetime.timedelta()))
return _error_timestamp
return datetime.datetime.strptime(timestamp, '%Y-%m-%d %H:%M:%S %z')


Expand Down

0 comments on commit a2ca9c0

Please sign in to comment.