Skip to content

SIO3Pack integration #288

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 28 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/Arch.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
runs-on: self-hosted
strategy:
matrix:
python-version: ["3.9", "3.13.1"]
python-version: ["3.10.18", "3.13.5"]
name: pytest-arch-python-${{ matrix.python-version }}
container:
image: archlinux:latest
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/GithubRunner.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
pytest:
strategy:
matrix:
python-version: ["3.9", "3.13"]
python-version: ["3.10", "3.13"]
name: pytest-github-runner-python-${{ matrix.python-version }}
runs-on: ubuntu-latest
steps:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/Ubuntu.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
runs-on: self-hosted
strategy:
matrix:
python-version: ["3.9", "3.13"]
python-version: ["3.10", "3.13"]
name: pytest-ubuntu-python-${{ matrix.python-version }}
container:
image: ubuntu:latest
Expand Down
3 changes: 2 additions & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ classifiers =
packages = find_namespace:
packages_dir = src
include_package_data = True
python_requires = >=3.9
python_requires = >=3.10
install_requires =
argparse
argcomplete
Expand All @@ -30,6 +30,7 @@ install_requires =
importlib-resources
psutil
packaging
sio3pack==1.0.0.dev5

[options.packages.find]
where = src
Expand Down
17 changes: 15 additions & 2 deletions src/sinol_make/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@
import argcomplete

from sinol_make import util, sio2jail
from sinol_make.helpers import cache, oicompare
from sinol_make.helpers import cache, oicompare, paths

# Required for side effects
from sinol_make.task_type.normal import NormalTaskType # noqa
from sinol_make.task_type.interactive import InteractiveTaskType # noqa

# SIO3Pack
from sio3pack.exceptions import SIO3PackException

__version__ = "1.9.8"

__version__ = "2.0.0.dev1"


def configure_parsers():
Expand Down Expand Up @@ -102,6 +105,16 @@ def main():
util.exit_with_error(err)
except SystemExit as err:
exit(err.code)
except SIO3PackException as err:
print(util.color_red("Short description of the error:"))
print(err.message)
print()
print(util.color_red("Full description:"))
print(err.full_message)
print()
with open(paths.get_cache_path('traceback'), 'w') as f:
traceback.print_exc(file=f)
print(util.warning('Full traceback saved to .cache/traceback'))
except Exception:
print(traceback.format_exc())
util.exit_with_error('An error occurred while running the command.\n'
Expand Down
25 changes: 12 additions & 13 deletions src/sinol_make/commands/chkwer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,22 +55,21 @@ def run_test(self, execution: ChkwerExecution) -> RunResult:
"""
Verifies a test and returns the result of chkwer on this test.
"""
output_file = paths.get_chkwer_path(os.path.basename(execution.out_test_path))
with open(execution.in_test_path, 'r') as inf, open(output_file, 'w') as outf:
output_file = paths.get_chkwer_path(os.path.basename(execution.test.out_file.path))
with open(execution.test.in_file.path, 'r') as inf, open(output_file, 'w') as outf:
process = subprocess.Popen([execution.model_exe], stdin=inf, stdout=outf)
process.wait()
ok, points, comment = self.task_type.check_output(execution.in_test_path, output_file, execution.out_test_path)
ok, points, comment = self.task_type.check_output(execution.test.in_file.path, output_file, execution.test.out_file.path)

return RunResult(execution.in_test_path, ok, int(points), comment)
return RunResult(execution.test, ok, int(points), comment)

def run_and_print_table(self) -> Dict[str, TestResult]:
results = {}
sorted_tests = sorted(self.tests, key=lambda test: package_util.get_group(test, self.task_id))
sorted_tests = sorted(self.tests, key=lambda test: test.group)
executions: List[ChkwerExecution] = []
for test in sorted_tests:
results[test] = TestResult(test, self.task_id)
executions.append(ChkwerExecution(test, results[test].test_name, package_util.get_out_from_in(test),
self.checker_executable, self.model_executable))
results[test.test_name] = TestResult(test)
executions.append(ChkwerExecution(test, self.checker_executable, self.model_executable))

has_terminal, terminal_width, terminal_height = util.get_terminal_size()
table_data = TableData(results, 0, self.task_id, self.contest_type.max_score_per_test())
Expand All @@ -84,7 +83,7 @@ def run_and_print_table(self) -> Dict[str, TestResult]:
try:
with mp.Pool(self.cpus) as pool:
for i, result in enumerate(pool.imap(self.run_test, executions)):
table_data.results[result.test_path].set_results(result.points, result.ok, result.comment)
table_data.results[result.test.test_name].set_results(result.points, result.ok, result.comment)
table_data.i = i
except KeyboardInterrupt:
keyboard_interrupt = True
Expand All @@ -108,12 +107,12 @@ def run(self, args):
util.exit_with_error("chkwer can be run only for normal tasks.")

self.cpus = args.cpus or util.default_cpu_count()
self.tests = package_util.get_tests(self.task_id, args.tests)
self.tests = package_util.get_tests(args.tests)

if len(self.tests) == 0:
util.exit_with_error("No tests found.")
else:
print('Will run on tests: ' + util.bold(', '.join(self.tests)))
print('Will run on tests: ' + util.bold(', '.join([test.test_name for test in self.tests])))
util.change_stack_size_to_unlimited()

additional_files = self.task_type.additional_files_to_compile()
Expand All @@ -122,10 +121,10 @@ def run(self, args):
if len(additional_files) != 1:
util.exit_with_error("More than one file to compile found. How is that possible?")
checker_info = additional_files[0]
model_solution = outgen_util.get_correct_solution(self.task_id)
model_solution = package_util.get_correct_solution()
self.checker_executable = self.compile(checker_info[0], checker_info[1], args, "checker",
args.compile_mode)
self.model_executable = self.compile(model_solution, package_util.get_executable(model_solution), args,
self.model_executable = self.compile(model_solution.path, package_util.get_executable(model_solution.path), args,
"model solution", args.compile_mode)
print()

Expand Down
16 changes: 8 additions & 8 deletions src/sinol_make/commands/chkwer/chkwer_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ def print_view(term_width, term_height, table_data: TableData):
column_lengths = [0, len('Points') + 1, 0]
tests = []
for result in results.values():
column_lengths[0] = max(column_lengths[0], len(result.test_name))
tests.append(result.test_path)
tests = sort_tests(tests, table_data.task_id)
column_lengths[0] = max(column_lengths[0], len(result.test.test_name))
tests.append(result.test)
tests = sort_tests(tests)

# 6 is for " | " between columns, 3 for margin.
column_lengths[2] = max(10, term_width - column_lengths[0] - column_lengths[1] - 6 - 3)
Expand All @@ -38,12 +38,12 @@ def print_line_separator():
print_line_separator()

last_group = None
for test_path in tests:
result = results[test_path]
if last_group is not None and last_group != result.test_group:
for test in tests:
result = results[test.test_name]
if last_group is not None and last_group != result.test.group:
print_line_separator()
last_group = result.test_group
print(margin + result.test_name.ljust(column_lengths[0]) + " | ", end='')
last_group = result.test.group
print(margin + test.test_name.ljust(column_lengths[0]) + " | ", end='')

if result.run:
if result.ok:
Expand Down
4 changes: 2 additions & 2 deletions src/sinol_make/commands/doc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,13 @@ def run(self, args: argparse.Namespace):
elif args.latex_compiler == 'auto':
self.compilation_method = 'pdflatex'
for extension in ['ps', 'eps']:
if glob.glob(os.path.join(os.getcwd(), 'doc', f'*.{extension}')) != []:
if glob.glob(os.path.join(os.getcwd(), 'doc', f'*.{extension}')): #TODO: SIO3Pack?
self.compilation_method = 'latex_dvi'
else:
util.exit_with_error("Unrecognized latex compiler")

if args.files == []:
self.files = glob.glob(os.path.join(os.getcwd(), 'doc', '*.tex'))
self.files = glob.glob(os.path.join(os.getcwd(), 'doc', '*.tex')) #TODO: SIO3Pack?
else:
self.files = []
for file in args.files:
Expand Down
18 changes: 9 additions & 9 deletions src/sinol_make/commands/export/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@
import argparse

from sinol_make import util, contest_types
from sinol_make.commands.ingen.ingen_util import get_ingen, compile_ingen, run_ingen, ingen_exists
from sinol_make.commands.ingen.ingen_util import get_ingen_path, compile_ingen, run_ingen, ingen_exists
from sinol_make.helpers import package_util, parsers, paths
from sinol_make.interfaces.BaseCommand import BaseCommand
from sinol_make.commands.outgen import Command as OutgenCommand, compile_correct_solution, get_correct_solution
from sinol_make.commands.outgen import Command as OutgenCommand, compile_correct_solution
from sinol_make.commands.doc import Command as DocCommand
from sinol_make.interfaces.Errors import UnknownContestType
from sinol_make.sio3pack.package import SIO3Package


class Command(BaseCommand):
Expand Down Expand Up @@ -53,8 +54,8 @@ def generate_input_tests(self):
if os.path.exists(os.path.join(os.getcwd(), 'prog')):
shutil.copytree(os.path.join(os.getcwd(), 'prog'), prog_dir)

if ingen_exists(self.task_id):
ingen_path = get_ingen(self.task_id)
if ingen_exists():
ingen_path = get_ingen_path()
ingen_path = os.path.join(prog_dir, os.path.basename(ingen_path))
ingen_exe = compile_ingen(ingen_path, self.args, self.args.compile_mode)
if not run_ingen(ingen_exe, in_dir):
Expand All @@ -75,7 +76,7 @@ def generate_output_files(self):
outputs.append(os.path.join(out_dir, os.path.basename(test).replace('.in', '.out')))
if len(outputs) > 0:
outgen = OutgenCommand()
correct_solution_exe = compile_correct_solution(get_correct_solution(self.task_id), self.args,
correct_solution_exe = compile_correct_solution(package_util.get_correct_solution().path, self.args,
self.args.compile_mode)
outgen.args = self.args
outgen.correct_solution_exe = correct_solution_exe
Expand All @@ -86,12 +87,12 @@ def get_generated_tests(self):
Returns list of generated tests.
Executes ingen to check what tests are generated.
"""
if not ingen_exists(self.task_id):
if not ingen_exists():
return []

in_dir = paths.get_cache_path('export', 'tests', 'in')
tests = glob.glob(os.path.join(in_dir, f'{self.task_id}*.in'))
return [package_util.extract_test_id(test, self.task_id) for test in tests]
return [SIO3Package().get_test_id_from_filename(os.path.basename(test)) for test in tests]

def create_ocen(self, target_dir: str):
"""
Expand Down Expand Up @@ -172,7 +173,7 @@ def copy_package_required_files(self, target_dir: str):
tests_to_copy = []
for ext in ['in', 'out']:
for test in glob.glob(os.path.join(os.getcwd(), ext, f'{self.task_id}*.{ext}')):
if package_util.extract_test_id(test, self.task_id) not in generated_tests:
if SIO3Package().get_test_id_from_filename(os.path.basename(test)) not in generated_tests:
tests_to_copy.append((ext, test))

cache_test_dir = paths.get_cache_path('export', 'tests')
Expand Down Expand Up @@ -269,7 +270,6 @@ def run(self, args: argparse.Namespace):
self.task_id = package_util.get_task_id()
self.export_name = self.task_id
self.task_type_cls = package_util.get_task_type_cls()
package_util.validate_test_names(self.task_id)
try:
self.contest = contest_types.get_contest_type()
except UnknownContestType as e:
Expand Down
13 changes: 7 additions & 6 deletions src/sinol_make/commands/ingen/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import os

from sinol_make import util
from sinol_make.commands.ingen.ingen_util import get_ingen, compile_ingen, run_ingen
from sinol_make.commands.ingen.ingen_util import get_ingen_path, compile_ingen, run_ingen
from sinol_make.helpers import parsers, package_util, paths
from sinol_make.interfaces.BaseCommand import BaseCommand

Expand Down Expand Up @@ -65,9 +65,9 @@ def run(self, args: argparse.Namespace):

self.task_id = package_util.get_task_id()
util.change_stack_size_to_unlimited()
self.ingen = get_ingen(self.task_id, args.ingen_path)
print(f'Using ingen file {os.path.basename(self.ingen)}')
self.ingen_exe = compile_ingen(self.ingen, self.args, self.args.compile_mode, self.args.fsanitize)
self.ingen_path = get_ingen_path(args.ingen_path)
print(f'Using ingen file {os.path.basename(self.ingen_path)}')
self.ingen_exe = compile_ingen(self.ingen_path, self.args, self.args.compile_mode, self.args.fsanitize)

previous_tests = []
try:
Expand All @@ -86,10 +86,11 @@ def run(self, args: argparse.Namespace):
util.exit_with_error('Failed to generate input files.')

self.delete_dangling_files(dates)
package_util.reload_tests()

with open(paths.get_cache_path("input_tests"), "w") as f:
f.write("\n".join(glob.glob(os.path.join(os.getcwd(), "in", f"{self.task_id}*.in"))))
f.write("\n".join(glob.glob(os.path.join(os.getcwd(), "in", f"{self.task_id}*.in")))) # TODO: refactor

if not self.args.no_validate:
tests = sorted(glob.glob(os.path.join(os.getcwd(), "in", f"{self.task_id}*.in")))
tests = sorted(glob.glob(os.path.join(os.getcwd(), "in", f"{self.task_id}*.in"))) # TODO: refactor
package_util.validate_tests(tests, self.args.cpus, 'input')
16 changes: 8 additions & 8 deletions src/sinol_make/commands/ingen/ingen_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,43 +8,43 @@
from sinol_make.helpers import package_util, compiler, compile


def ingen_exists(task_id):
def ingen_exists():
"""
Checks if ingen source file exists.
:param task_id: task id, for example abc
:return: True if exists, False otherwise
"""
return package_util.any_files_matching_pattern(task_id, f'{task_id}ingen.*')
task_id = package_util.get_task_id()
return package_util.any_files_matching_pattern(f'{task_id}ingen.*')


def get_ingen(task_id, ingen_path=None):
def get_ingen_path(ingen_path=None) -> str:
"""
Find ingen source file in `prog/` directory.
If `ingen_path` is specified, then it will be used (if exists).
:param task_id: task id, for example abc.
:param ingen_path: path to ingen source file
:return: path to ingen source file or None if not found
"""

task_id = package_util.get_task_id()
if ingen_path is not None:
if os.path.exists(ingen_path):
return ingen_path
else:
util.exit_with_error(f'Ingen source file {ingen_path} does not exist.')

ingen = package_util.get_files_matching_pattern(task_id, f'{task_id}ingen.*')
ingen = package_util.get_files_matching_pattern(f'{task_id}ingen.*')
if len(ingen) == 0:
util.exit_with_error(f'Ingen source file for task {task_id} does not exist.')

# Sio2 first chooses shell scripts, then non-shell source codes.
correct_ingen = None
for i in ingen:
if os.path.splitext(i)[1] == '.sh':
if os.path.splitext(i.path)[1] == '.sh':
correct_ingen = i
break
if correct_ingen is None:
correct_ingen = ingen[0]
return correct_ingen
return correct_ingen.path


def compile_ingen(ingen_path: str, args: argparse.Namespace, compilation_flags='default', use_fsanitize=False):
Expand Down
Loading
Loading