Skip to content

Commit

Permalink
Merge branch 'verify-command' into message-formating
Browse files Browse the repository at this point in the history
  • Loading branch information
MasloMaslane committed Apr 5, 2024
2 parents 7ddad9a + aaa58e6 commit c9157b4
Show file tree
Hide file tree
Showing 50 changed files with 763 additions and 150 deletions.
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ install_requires =
dictdiffer
importlib-resources
psutil
packaging

[options.packages.find]
where = src
Expand Down
39 changes: 25 additions & 14 deletions src/sinol_make/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from sinol_make import util, oiejq

__version__ = "1.5.29"
__version__ = "1.6.0.dev3"


def configure_parsers():
Expand Down Expand Up @@ -50,35 +50,40 @@ def main_exn():
parser = configure_parsers()
arguments = []
curr_args = []
commands = util.get_commands()
commands_dict = {command.get_name(): command for command in commands}
for arg in sys.argv[1:]:
if arg in util.get_command_names() and not (len(curr_args) > 0 and curr_args[0] == 'init'):
if arg in commands_dict.keys() and not (len(curr_args) > 0 and curr_args[0] == 'init'):
if curr_args:
arguments.append(curr_args)
curr_args = [arg]
else:
curr_args.append(arg)
if curr_args:
arguments.append(curr_args)
commands = util.get_commands()
if not arguments:
parser.print_help()
exit(1)
check_oiejq()

for curr_args in arguments:
args = parser.parse_args(curr_args)
command_found = False
for command in commands:
if command.get_name() == args.command:
command_found = True
if len(arguments) > 1:
print(f' {command.get_name()} command '.center(util.get_terminal_size()[1], '='))
command.run(args)
if not command_found:
command = commands_dict.get(args.command, None)
if command:
if len(arguments) > 1:
print(f' {command.get_name()} command '.center(util.get_terminal_size()[1], '='))
command.run(args)
else:
parser.print_help()
exit(1)


def main():
new_version = None
try:
if util.is_dev(__version__):
print(util.warning('You are using a development version of sinol-make. '
'It may be unstable and contain bugs.'))
new_version = util.check_for_updates(__version__)
main_exn()
except argparse.ArgumentError as err:
Expand All @@ -92,6 +97,12 @@ def main():
'https://github.com/sio2project/sinol-make/#reporting-bugs-and-contributing-code')
finally:
if new_version is not None:
print(util.warning(
f'New version of sinol-make is available (your version: {__version__}, available version: '
f'{new_version}).\nYou can update it by running `pip3 install sinol-make --upgrade`.'))
if not util.is_dev(new_version):
print(util.warning(
f'New version of sinol-make is available (your version: {__version__}, available version: '
f'{new_version}).\nYou can update it by running `pip3 install sinol-make --upgrade`.'))
elif util.is_dev(new_version):
print(util.warning(
f'New development version of sinol-make is available (your version: {__version__}, available '
f'version: {new_version}).\nYou can update it by running `pip3 install sinol-make --pre --upgrade`.'
))
1 change: 1 addition & 0 deletions src/sinol_make/commands/doc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ def configure_subparser(self, subparser: argparse.ArgumentParser):
' pdflatex - uses pdflatex. Works with .png and .jpg images.\n'
' latex_dvi - uses latex and dvipdf. Works with .ps and .eps images.', default='auto')
parser.add_argument('files', type=str, nargs='*', help='files to compile')
return parser

def run(self, args: argparse.Namespace):
args = util.init_package_command(args)
Expand Down
22 changes: 19 additions & 3 deletions src/sinol_make/commands/export/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def configure_subparser(self, subparser: argparse.ArgumentParser):
parser.add_argument('--export-ocen', dest='export_ocen', action='store_true',
help='Create ocen archive')
parsers.add_compilation_arguments(parser)
return parser

def generate_input_tests(self):
print('Generating tests...')
Expand Down Expand Up @@ -94,6 +95,7 @@ def create_ocen(self, target_dir: str):
Creates ocen archive for sio2.
:param target_dir: Path to exported package.
"""
print('Generating ocen archive...')
attachments_dir = os.path.join(target_dir, 'attachments')
if not os.path.exists(attachments_dir):
os.makedirs(attachments_dir)
Expand All @@ -106,12 +108,27 @@ def create_ocen(self, target_dir: str):
os.makedirs(in_dir)
out_dir = os.path.join(ocen_dir, 'out')
os.makedirs(out_dir)
num_tests = 0
for ext in ['in', 'out']:
for test in glob.glob(os.path.join(tests_dir, ext, f'{self.task_id}0*.{ext}')) + \
glob.glob(os.path.join(tests_dir, ext, f'{self.task_id}*ocen.{ext}')):
shutil.copy(test, os.path.join(ocen_dir, ext, os.path.basename(test)))

shutil.make_archive(os.path.join(attachments_dir, f'{self.task_id}ocen'), 'zip', tmpdir)
num_tests += 1

dlazaw_dir = os.path.join(os.getcwd(), 'dlazaw')
if num_tests == 0:
print(util.warning('No ocen tests found.'))
elif os.path.exists(dlazaw_dir):
print(util.warning('Skipping ocen arhive creation because dlazaw directory exists.'))
else:
shutil.make_archive(os.path.join(attachments_dir, f'{self.task_id}ocen'), 'zip', tmpdir)

if os.path.exists(dlazaw_dir):
print('Archiving dlazaw directory and adding to attachments.')
os.makedirs(os.path.join(tmpdir, 'dlazaw'), exist_ok=True)
shutil.copytree(dlazaw_dir, os.path.join(tmpdir, 'dlazaw', 'dlazaw'))
shutil.make_archive(os.path.join(attachments_dir, 'dlazaw'), 'zip',
os.path.join(tmpdir, 'dlazaw'))

def compile_statement(self):
command = DocCommand()
Expand Down Expand Up @@ -165,7 +182,6 @@ def copy_package_required_files(self, target_dir: str):

self.generate_output_files()
if self.args.export_ocen:
print('Generating ocen archive...')
self.create_ocen(target_dir)

def clear_files(self, target_dir: str):
Expand Down
4 changes: 4 additions & 0 deletions src/sinol_make/commands/gen/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ def configure_subparser(self, subparser):
parsers.add_cpus_argument(parser, 'number of cpus to use to generate output files')
parser.add_argument('-n', '--no-validate', default=False, action='store_true',
help='do not validate test contents')
parser.add_argument('-f', '--fsanitize', default=False, action='store_true',
help='Use -fsanitize=address,undefined for ingen compilation. Warning: this may fail on some '
'systems. To fix this, run `sudo sysctl vm.mmap_rnd_bits = 28`.')
parsers.add_compilation_arguments(parser)
return parser

def run(self, args: argparse.Namespace):
args = util.init_package_command(args)
Expand Down
6 changes: 5 additions & 1 deletion src/sinol_make/commands/ingen/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@ def configure_subparser(self, subparser):
help='path to ingen source file, for example prog/abcingen.cpp')
parser.add_argument('-n', '--no-validate', default=False, action='store_true',
help='do not validate test contents')
parser.add_argument('-f', '--fsanitize', default=False, action='store_true',
help='Use -fsanitize=address,undefined for compilation. Warning: this may fail on some '
'systems. Tof fix this, run `sudo sysctl vm.mmap_rnd_bits = 28`.')
parsers.add_compilation_arguments(parser)
return parser

def run(self, args: argparse.Namespace):
args = util.init_package_command(args)
Expand All @@ -42,7 +46,7 @@ def run(self, args: argparse.Namespace):
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.ingen_exe = compile_ingen(self.ingen, self.args, self.args.compile_mode, self.args.fsanitize)

previous_tests = []
try:
Expand Down
20 changes: 15 additions & 5 deletions src/sinol_make/commands/ingen/ingen_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def get_ingen(task_id, ingen_path=None):
return correct_ingen


def compile_ingen(ingen_path: str, args: argparse.Namespace, compilation_flags='default'):
def compile_ingen(ingen_path: str, args: argparse.Namespace, compilation_flags='default', use_fsanitize=False):
"""
Compiles ingen and returns path to compiled executable.
If ingen_path is shell script, then it will be returned.
Expand All @@ -57,7 +57,7 @@ def compile_ingen(ingen_path: str, args: argparse.Namespace, compilation_flags='

compilers = compiler.verify_compilers(args, [ingen_path])
ingen_exe, compile_log_path = compile.compile_file(ingen_path, package_util.get_executable(ingen_path),
compilers, compilation_flags, use_fsanitize=True,
compilers, compilation_flags, use_fsanitize=use_fsanitize,
additional_flags='-D_INGEN')

if ingen_exe is None:
Expand Down Expand Up @@ -86,11 +86,21 @@ def run_ingen(ingen_exe, working_dir=None):
print(util.bold(' Ingen output '.center(util.get_terminal_size()[1], '=')))
process = subprocess.Popen([ingen_exe], stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
cwd=working_dir, shell=is_shell)
whole_output = ''
while process.poll() is None:
print(process.stdout.readline().decode('utf-8'), end='')

print(process.stdout.read().decode('utf-8'), end='')
out = process.stdout.readline().decode('utf-8')
if out != '':
print(out, end='')
whole_output += out
out = process.stdout.read().decode('utf-8')
whole_output += out
print(out, end='')
exit_code = process.returncode
print(util.bold(' End of ingen output '.center(util.get_terminal_size()[1], '=')))

if util.has_sanitizer_error(whole_output, exit_code):
print(util.warning('Warning: if ingen failed due to sanitizer errors, you can either run '
'`sudo sysctl vm.mmap_rnd_bits = 28` to fix this or disable sanitizers with the '
'--no-fsanitize flag.'))

return exit_code == 0
4 changes: 2 additions & 2 deletions src/sinol_make/commands/init/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ def configure_subparser(self, subparser: argparse.ArgumentParser):
description='Create package from predefined template with given id.'
)
parser.add_argument('task_id', type=str, help='Id of the task to create')
return parser

def download_template(self):
repo = 'https://github.com/sio2project/sinol-make.git'
Expand Down Expand Up @@ -69,8 +70,7 @@ def run(self, args: argparse.Namespace):

self.move_folder()
self.update_config()

self.used_tmpdir.cleanup()

print(util.info(f'Successfully created task "{self.task_id}"'))

14 changes: 12 additions & 2 deletions src/sinol_make/commands/inwer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,11 @@ def configure_subparser(self, subparser: argparse.ArgumentParser):
parser.add_argument('-t', '--tests', type=str, nargs='+',
help='test to verify, for example in/abc{0,1}*')
parsers.add_cpus_argument(parser, 'number of cpus to use when verifying tests')
parser.add_argument('-f', '--fsanitize', default=False, action='store_true',
help='Use -fsanitize=address,undefined for compilation. Warning: this may fail on some '
'systems. Tof fix this, run `sudo sysctl vm.mmap_rnd_bits = 28`.')
parsers.add_compilation_arguments(parser)
return parser

@staticmethod
def verify_test(execution: InwerExecution) -> VerificationResult:
Expand Down Expand Up @@ -92,11 +96,14 @@ def verify_and_print_table(self) -> Dict[str, TestResult]:
thr.start()

keyboard_interrupt = False
sanitizer_error = False
try:
with mp.Pool(self.cpus) as pool:
for i, result in enumerate(pool.imap(self.verify_test, executions)):
table_data.results[result.test_path].set_results(result.valid, result.output)
table_data.i = i
if util.has_sanitizer_error(result.output, 0 if result.valid else 1):
sanitizer_error = True
except KeyboardInterrupt:
keyboard_interrupt = True

Expand All @@ -106,6 +113,10 @@ def verify_and_print_table(self) -> Dict[str, TestResult]:

print("\n".join(inwer_util.print_view(terminal_width, terminal_height, table_data)[0]))

if sanitizer_error:
print(util.warning('Warning: if inwer failed due to sanitizer errors, you can either run '
'`sudo sysctl vm.mmap_rnd_bits = 28` to fix this or disable sanitizers with the '
'--no-fsanitize flag.'))
if keyboard_interrupt:
util.exit_with_error('Keyboard interrupt.')

Expand Down Expand Up @@ -201,7 +212,7 @@ def run(self, args: argparse.Namespace):
print('Verifying tests: ' + util.bold(', '.join(self.tests)))

util.change_stack_size_to_unlimited()
self.inwer_executable = inwer_util.compile_inwer(self.inwer, args, args.compile_mode)
self.inwer_executable = inwer_util.compile_inwer(self.inwer, args, args.compile_mode, args.fsanitize)
results: Dict[str, TestResult] = self.verify_and_print_table()
print('')

Expand All @@ -216,4 +227,3 @@ def run(self, args: argparse.Namespace):
print("Verifying tests order...")
self.verify_tests_order()
print(util.info('Verification successful.'))
exit(0)
4 changes: 2 additions & 2 deletions src/sinol_make/commands/inwer/inwer_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ def get_inwer_path(task_id: str, path=None) -> Union[str, None]:
return None


def compile_inwer(inwer_path: str, args: argparse.Namespace, compilation_flags='default'):
def compile_inwer(inwer_path: str, args: argparse.Namespace, compilation_flags='default', use_fsanitize=False):
"""
Compiles inwer and returns path to compiled executable and path to compile log.
"""
compilers = compiler.verify_compilers(args, [inwer_path])
inwer_exe, compile_log_path = compile.compile_file(inwer_path, package_util.get_executable(inwer_path), compilers,
compilation_flags, use_fsanitize=True,
compilation_flags, use_fsanitize=use_fsanitize,
additional_flags='-D_INWER')

if inwer_exe is None:
Expand Down
1 change: 1 addition & 0 deletions src/sinol_make/commands/outgen/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ def configure_subparser(self, subparser):
parser.add_argument('-n', '--no-validate', default=False, action='store_true',
help='do not validate test contents')
parsers.add_compilation_arguments(parser)
return parser

def generate_outputs(self, outputs_to_generate):
print(f'Generating output files for {len(outputs_to_generate)} tests on {self.args.cpus} cpus.')
Expand Down
9 changes: 9 additions & 0 deletions src/sinol_make/commands/run/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,11 @@ def configure_subparser(self, subparser):
help='path to oiejq executable (default: `~/.local/bin/oiejq`)')
parser.add_argument('-a', '--apply-suggestions', dest='apply_suggestions', action='store_true',
help='apply suggestions from expected scores report')
parser.add_argument('--ignore-expected', dest='ignore_expected', action='store_true',
help='ignore expected scores from config.yml. When this flag is set, '
'the expected scores are not compared with the actual scores.')
parsers.add_compilation_arguments(parser)
return parser

def parse_time(self, time_str):
if len(time_str) < 3: return -1
Expand Down Expand Up @@ -1238,6 +1242,11 @@ def run(self, args):

results, all_results = self.compile_and_run(solutions)
self.check_errors(all_results)
if self.args.ignore_expected:
print(util.warning("Ignoring expected scores."))
self.exit()
return

try:
validation_results = self.validate_expected_scores(results)
except Exception:
Expand Down
Loading

0 comments on commit c9157b4

Please sign in to comment.