Skip to content

Commit

Permalink
Merge pull request #304 from IngeniariusSoftware/master
Browse files Browse the repository at this point in the history
compile with errors, true skips, update readme and citation
  • Loading branch information
IngeniariusSoftware committed Aug 16, 2022
2 parents 8182800 + 4c72a9c commit cf7fc45
Show file tree
Hide file tree
Showing 12 changed files with 132 additions and 136 deletions.
8 changes: 3 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,14 @@
*.exe
*.out
*.app
project/bin/c2eo

# c2eo and temp eo files
project/bin/*

#LLVM directory
llvm-clang/**
!llvm-clang/.gitkeep


# User-specific stuff
**/.vscode/**
**/.idea/**
Expand Down Expand Up @@ -111,9 +112,6 @@ project/tests/**/*.eo
# Log files
*.log

# Compiled c2eo
project/bin/c2eo

# release stuff
*.so
*.so.*
Expand Down
2 changes: 2 additions & 0 deletions citation.cff
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ authors:
orcid: "https://orcid.org/0000-0002-5487-0699"
- family-names: "Chuikin"
given-names: "Nikolay"
orcid: "https://orcid.org/0000-0001-9645-5525"
- family-names: "Shipitsin"
given-names: "Maxim"
orcid: "https://orcid.org/0000-0002-4957-5695"
Expand All @@ -14,6 +15,7 @@ authors:
orcid: "https://orcid.org/0000-0001-5530-3752"
- family-names: "Kamensky"
given-names: "Andrey"
orcid: "https://orcid.org/0000-0001-5197-310X"
title: "c2eo: C to EO transpiler"
version: 0.1.7
date-released: 2022-04-19
Expand Down
52 changes: 34 additions & 18 deletions project/scripts/build_eo.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,55 +35,60 @@

class EOBuilder(object):

def __init__(self):
def __init__(self, transpilation_units):
self.path_to_eo_project = settings.get_setting('path_to_eo_project')
self.current_version = settings.get_setting('current_eo_version')
self.path_to_foreign_objects = settings.get_setting('path_to_foreign_objects')
self.path_to_eo = settings.get_setting('path_to_eo')
self.path_to_eo_parse = settings.get_setting('path_to_eo_parse')
self.transpilation_units = transpilation_units
self.errors = set()
self.error_result = {}

def build(self):
tools.pprint('Compilation start\n')
original_path = os.getcwd()
os.chdir(self.path_to_eo_project)
if self.is_good_for_recompilation():
tools.pprint('\nRecompilation eo project starts\n')
result = subprocess.run('mvn compile', shell=True)
else:
tools.pprint('\nFull eo project compilation starts\n')
result = subprocess.run('mvn clean compile', shell=True)
result = self.is_recompilation()
tools.pprint(f'\n{"Recompilation eo project starts" if result else "Full eo project compilation starts"}\n')
cmd = f'mvn {"" if result else "clean"} compile'
process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, universal_newlines=True)
for line in process.stdout:
if line:
print(line, end='')
if 'error:' in line:
self.handle_eo_error(line)
elif process.poll() is not None:
break
os.chdir(original_path)
if result.returncode:
if process.poll():
exit('compilation failed')
return self.errors, self.error_result

def is_good_for_recompilation(self):
def is_recompilation(self):
if not os.path.exists(self.path_to_foreign_objects):
tools.pprint('Compile dir not found', status=tools.WARNING)
return False
else:
tools.pprint('Compile dir found', status=tools.PASS)

tools.pprint('Compile dir found', status=tools.PASS)
if not self.is_actual_object_version():
tools.pprint('Old version detected', status=tools.WARNING)
return False
else:
tools.pprint('Latest version detected', status=tools.PASS)

tools.pprint('Latest version detected', status=tools.PASS)
eo_src_files = tools.search_files_by_patterns(self.path_to_eo, ['*.eo'], recursive=True)
eo_src_files = set(map(lambda x: x.replace(self.path_to_eo, '', 1).replace('.eo', '', 1), eo_src_files))
project_eo_files = tools.search_files_by_patterns(self.path_to_eo_parse, ['*.xmir'],
recursive=True, filters=['!org/eolang'])
project_eo_files = set(map(lambda x: x.replace(self.path_to_eo_parse, '', 1).replace('.xmir', '', 1),
project_eo_files))
difference = project_eo_files - eo_src_files
tools.pprint()
if difference:
tools.pprint('EO project files are incompatible', status=tools.WARNING)
tools.pprint('\nEO project files are incompatible', status=tools.WARNING)
tools.pprint(f'The following files may have been deleted: {sorted(difference, key=str.casefold)}\n')
return False
else:
tools.pprint('EO project files are compatible', status=tools.PASS)

tools.pprint('\nEO project files are compatible', status=tools.PASS)
return True

def is_actual_object_version(self):
Expand All @@ -96,7 +101,18 @@ def is_actual_object_version(self):
return True
return False

def handle_eo_error(self, message):
file, error = message.split('/', maxsplit=1)[1].split('with error:', maxsplit=1)
file, error = file.strip(), error.strip()
for unit in self.transpilation_units:
if unit['unique_name'] in file:
if error not in self.error_result:
self.error_result[error] = {}
self.error_result[error][unit['unique_name']] = set()
self.errors.add(unit['unique_name'])
return


if __name__ == '__main__':
tools.move_to_script_dir(sys.argv[0])
EOBuilder().build()
EOBuilder([]).build()
33 changes: 10 additions & 23 deletions project/scripts/compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
import os

import sys
import time
import argparse
Expand All @@ -39,35 +39,22 @@ class Compiler(object):

def __init__(self, path_to_files, skips_file_name, need_to_prepare_c_code=True):
self.need_to_prepare_c_code = need_to_prepare_c_code
self.skips = settings.get_skips(skips_file_name) if skips_file_name else {}
self.skips_file_name = skips_file_name
self.path_to_tests = path_to_files
self.path_to_c2eo_build = settings.get_setting('path_to_c2eo_build')
self.transpilation_units = []

def compile(self):
start_time = time.time()
self.transpilation_units = Transpiler(self.path_to_tests, '', self.need_to_prepare_c_code).transpile()
skips = self.remove_skip_files()
self.transpilation_units, skip_result = Transpiler(self.path_to_tests, self.skips_file_name,
self.need_to_prepare_c_code).transpile()
if self.transpilation_units:
EOBuilder().build()
passes = set(unit['unique_name'] for unit in self.transpilation_units)
passes -= set(file for value in skips.values() for file in value.keys())
result = {tools.PASS: passes, tools.SKIP: skips}
tools.pprint_result('COMPILE', len(self.transpilation_units), int(time.time() - start_time), result, 0)
return self.transpilation_units

def remove_skip_files(self):
skips = {}
for unit in self.transpilation_units:
for _filter, comment in self.skips.items():
if _filter in unit['name']:
if comment not in skips:
skips[comment] = {}
if os.path.exists(unit['src_eo_file']):
os.remove(unit['src_eo_file'])
skips[comment][unit['unique_name']] = set()
break
return skips
errors, error_result = EOBuilder(self.transpilation_units).build()
passes = set(unit['unique_name'] for unit in self.transpilation_units) - errors
result = {tools.PASS: passes, tools.ERROR: error_result, tools.SKIP: skip_result}
tests_count = len(self.transpilation_units) + sum(map(len, skip_result.values()))
tools.pprint_result('COMPILE', tests_count, int(time.time() - start_time), result, 0)
return self.transpilation_units, skip_result


def create_parser():
Expand Down
2 changes: 1 addition & 1 deletion project/scripts/data/settings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ csmith_args:
skips_for_test: test
skips_for_compile: compile
skips_for_transpile: transpile
current_eo_version: 0.26.0
current_eo_version: 0.27.0
ignored_inspection_warnings: null
ignored_transpilation_warnings:
- Wunused-command-line-argument
Expand Down
5 changes: 1 addition & 4 deletions project/scripts/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,7 @@ def get_meta_code(name, read_as_lines=False):
path = get_setting('path_to_meta')
file = os.path.join(path, f'{name}.txt')
with open(file, 'r') as f:
if read_as_lines:
return f.readlines()
else:
return f.read()
return f.readlines() if read_as_lines else f.read()


def get_skips(name):
Expand Down
54 changes: 23 additions & 31 deletions project/scripts/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
class Tests(object):

def __init__(self, path_to_tests, skips_file_name):
self.skips = settings.get_skips(skips_file_name) if skips_file_name else {}
self.skips_file_name = skips_file_name
self.path_to_tests = path_to_tests
self.path_to_c2eo_build = settings.get_setting('path_to_c2eo_build')
self.path_to_eo_src = settings.get_setting('path_to_eo_src')
Expand All @@ -53,15 +53,16 @@ def __init__(self, path_to_tests, skips_file_name):

def test(self):
start_time = time.time()
self.transpilation_units = Compiler(self.path_to_tests, '').compile()
self.transpilation_units, skip_result = Compiler(self.path_to_tests, self.skips_file_name).compile()
if self.transpilation_units:
self.get_result_for_tests()
with tools.thread_pool() as threads:
results = threads.map(self.compare_test_results, self.transpilation_units)
results = threads.map(compare_test_results, self.transpilation_units)
result = group_comparison_results(results)
result[tools.SKIP] = skip_result
tests_count = len(self.transpilation_units) + sum(map(len, skip_result.values()))
_is_failed = len(result[tools.ERROR]) + len(result[tools.EXCEPTION])
tools.pprint_result('TEST', len(self.transpilation_units), int(time.time() - start_time), result,
_is_failed)
tools.pprint_result('TEST', tests_count, int(time.time() - start_time), result, _is_failed)
return _is_failed

def get_result_for_tests(self):
Expand Down Expand Up @@ -110,27 +111,13 @@ def get_result_for_eo_file(self, unit):
try:
process.communicate(timeout=timeout)
except subprocess.TimeoutExpired:
subprocess.run(f'pkill -TERM -P {process.pid}', shell=True)
process.kill()
with open(unit['result_eo_file'], 'w') as f:
f.write(f'exception: execution time EO file exceeded {timeout} seconds\n')
finally:
self.test_handled_count += 1
tools.print_progress_bar(self.test_handled_count, len(self.transpilation_units))

def compare_test_results(self, unit):
for _filter, comment in self.skips.items():
if _filter in unit['name']:
return unit, True, False, False, comment

with open(unit['result_c_file'], 'r') as f:
c_data = f.readlines()
with open(unit['result_eo_file'], 'r') as f:
eo_data = f.readlines()
is_except, is_equal, log_data = compare_files(c_data, eo_data)
with open(os.path.join(unit['result_path'], f'{unit["name"]}.log'), 'w') as f:
f.writelines(log_data)
return unit, False, is_except, is_equal, log_data


def compare_files(c_data, eo_data):
if is_exception(c_data):
Expand Down Expand Up @@ -158,16 +145,14 @@ def compare_lines(c_data, eo_data):
is_equal = True
log_data = []
for i, (c_line, eo_line) in enumerate(zip(c_data, eo_data)):
c_line = c_line.rstrip()
eo_line = eo_line.rstrip()
c_line, eo_line = c_line.rstrip(), eo_line.rstrip()
ok_line = f'\t{tools.BGreen}Line {i}: {c_line} == {eo_line}{tools.IWhite}\n'
if c_line == eo_line:
log_data.append(ok_line)
continue

is_both_float = tools.is_float(c_line) and tools.is_float(eo_line)
c_line = c_line.replace(',', '.')
eo_line = eo_line.replace(',', '.')
c_line, eo_line = c_line.replace(',', '.'), eo_line.replace(',', '.')
if is_both_float and math.isclose(float(c_line), float(eo_line), abs_tol=0.0001):
log_data.append(ok_line)
else:
Expand All @@ -178,14 +163,10 @@ def compare_lines(c_data, eo_data):


def group_comparison_results(results):
result = {tools.PASS: [], tools.ERROR: [], tools.EXCEPTION: {}, tools.SKIP: {}}
result = {tools.PASS: [], tools.ERROR: [], tools.EXCEPTION: {}}
tools.pprint('Getting results\n', slowly=True)
for unit, is_skip, is_except, is_equal, log_data in results:
if is_skip:
if log_data not in result[tools.SKIP]:
result[tools.SKIP][log_data] = {}
result[tools.SKIP][log_data][unit['unique_name']] = set()
elif is_except:
for unit, is_except, is_equal, log_data in results:
if is_except:
log_data = ''.join(log_data)
if log_data not in result[tools.EXCEPTION]:
result[tools.EXCEPTION][log_data] = {}
Expand All @@ -197,6 +178,17 @@ def group_comparison_results(results):
return result


def compare_test_results(unit):
with open(unit['result_c_file'], 'r') as f:
c_data = f.readlines()
with open(unit['result_eo_file'], 'r') as f:
eo_data = f.readlines()
is_except, is_equal, log_data = compare_files(c_data, eo_data)
with open(os.path.join(unit['result_path'], f'{unit["name"]}.log'), 'w') as f:
f.writelines(log_data)
return unit, is_except, is_equal, log_data


def create_parser():
_parser = argparse.ArgumentParser(description='the script for testing the correctness of the execution of '
'translated files from C to EO')
Expand Down
11 changes: 3 additions & 8 deletions project/scripts/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,11 +155,8 @@ def pprint(*data, slowly=False, status=INFO, end='\n', on_the_next_line=False):
if type(token) == list:
token = ''.join(list(map(str, token)))
for line in str(token).split('\n'):
if status:
print(f'{IWhite}[{get_status(status)}] {line}', end=end)
else:
print(f'{IWhite}{line}', end=end)

status_str = f'[{[{get_status(status)}]}] ' if status else ''
print(f'{IWhite}{status_str}{line}', end=end)
if slowly:
time.sleep(0.01)

Expand Down Expand Up @@ -229,9 +226,7 @@ def pprint_separation_line():

def pprint_truncated_data(data, max_lines):
if type(data) == str:
data = data.split('\n')
data = data[:max_lines]
data = '\n'.join(data)
data = '\n'.join(data.split('\n')[:max_lines])
else:
data = data[:max_lines]
pprint(data, slowly=True, status='')
Expand Down
Loading

1 comment on commit cf7fc45

@0pdd
Copy link
Member

@0pdd 0pdd commented on cf7fc45 Aug 16, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't able to retrieve PDD puzzles from the code base and submit them to github. If you think that it's a bug on our side, please submit it to yegor256/0pdd:

set -x && set -e && set -o pipefail && cd /tmp/0pdd20220816-14-x9jrlm/Z2l0QGdpdGh1Yi5jb206cG9seXN0YXQvYzJlby5naXQ && pdd -v -f /tmp/20220816-4029-syh3v8 [1]: + set -e + set -o pipefail + cd /tmp/0pdd20220816-14-x9jrlm/Z2l0QGdpdGh1Yi5jb206cG9seXN0YXQvYzJlby5naXQ + pdd -v -f...

Please, copy and paste this stack trace to GitHub:

UserError
set -x && set -e && set -o pipefail && cd /tmp/0pdd20220816-14-x9jrlm/Z2l0QGdpdGh1Yi5jb206cG9seXN0YXQvYzJlby5naXQ && pdd -v -f /tmp/20220816-4029-syh3v8 [1]:
+ set -e
+ set -o pipefail
+ cd /tmp/0pdd20220816-14-x9jrlm/Z2l0QGdpdGh1Yi5jb206cG9seXN0YXQvYzJlby5naXQ
+ pdd -v -f /tmp/20220816-4029-syh3v8

My version is 0.21.3
Ruby version is 2.7.5 at x86_64-linux
Reading from root dir /tmp/0pdd20220816-14-x9jrlm/Z2l0QGdpdGh1Yi5jb206cG9seXN0YXQvYzJlby5naXQ
/tmp/0pdd20220816-14-x9jrlm/Z2l0QGdpdGh1Yi5jb206cG9seXN0YXQvYzJlby5naXQ/.gitmodules is a binary file (0 bytes)
/tmp/0pdd20220816-14-x9jrlm/Z2l0QGdpdGh1Yi5jb206cG9seXN0YXQvYzJlby5naXQ/project/scripts/data/skips/test.txt is a binary file (0 bytes)
/tmp/0pdd20220816-14-x9jrlm/Z2l0QGdpdGh1Yi5jb206cG9seXN0YXQvYzJlby5naXQ/project/tests/in_progress/for_main/.gitkeep is a binary file (0 bytes)
/tmp/0pdd20220816-14-x9jrlm/Z2l0QGdpdGh1Yi5jb206cG9seXN0YXQvYzJlby5naXQ/result/eo/c2eo/src/.gitkeep is a binary file (0 bytes)
Reading .gitignore ...
Reading .gitattributes ...
Reading Dockerfile ...
Reading .rultor.yml ...
Reading project/CMakeLists.txt ...
Reading project/scripts/build_c2eo.py ...
Reading project/scripts/build_eo.py ...
Reading project/scripts/code_lines.py ...
Reading project/scripts/c2eo-all.py ...
Reading project/scripts/clang_tidy.py ...
Reading project/scripts/compile.py ...
Reading project/scripts/readme.md ...
Reading project/scripts/clean_before_transpilation.py ...
Reading project/scripts/tools.py ...
Reading project/scripts/update-release.py ...
Reading project/scripts/test.py ...
Reading project/scripts/transpile.py ...
Reading project/scripts/codecov.py ...
Reading project/scripts/data/settings.yml ...
Reading project/scripts/data/meta/run.sh.txt ...
Reading project/scripts/data/meta/plug.txt ...
Reading project/scripts/data/skips/gcc.txt ...
Reading project/scripts/data/skips/testcuite.txt ...
ERROR: project/scripts/data/skips/testcuite.txt; PDD::Error at project/scripts/data/skips/testcuite.txt:2: TODO found, but puzzle can't be parsed, most probably because TODO is not followed by a puzzle marker, as this page explains: https://github.com/cqfn/pdd#how-to-format
If you can't understand the cause of this issue or you don't know how to fix it, please submit a GitHub issue, we will try to help you: https://github.com/cqfn/pdd/issues. This tool is still in its beta version and we will appreciate your feedback. Here is where you can find more documentation: https://github.com/cqfn/pdd/blob/master/README.md.
Exit code is 1

/app/objects/git_repo.rb:73:in `rescue in block in xml'
/app/objects/git_repo.rb:70:in `block in xml'
/app/vendor/ruby-2.7.5/lib/ruby/2.7.0/tempfile.rb:291:in `open'
/app/objects/git_repo.rb:69:in `xml'
/app/objects/puzzles.rb:41:in `deploy'
/app/objects/jobs/job.rb:38:in `proceed'
/app/objects/jobs/job_starred.rb:32:in `proceed'
/app/objects/jobs/job_recorded.rb:31:in `proceed'
/app/objects/jobs/job_emailed.rb:33:in `proceed'
/app/objects/jobs/job_commiterrors.rb:33:in `proceed'
/app/objects/jobs/job_detached.rb:48:in `exclusive'
/app/objects/jobs/job_detached.rb:36:in `block in proceed'
/app/objects/jobs/job_detached.rb:36:in `fork'
/app/objects/jobs/job_detached.rb:36:in `proceed'
/app/0pdd.rb:519:in `process_request'
/app/0pdd.rb:356:in `block in <top (required)>'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1686:in `call'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1686:in `block in compile!'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1023:in `block (3 levels) in route!'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1042:in `route_eval'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1023:in `block (2 levels) in route!'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1071:in `block in process_route'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1069:in `catch'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1069:in `process_route'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1021:in `block in route!'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1018:in `each'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1018:in `route!'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1140:in `block in dispatch!'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1112:in `block in invoke'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1112:in `catch'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1112:in `invoke'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1135:in `dispatch!'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:949:in `block in call!'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1112:in `block in invoke'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1112:in `catch'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1112:in `invoke'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:949:in `call!'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:938:in `call'
/app/vendor/bundle/ruby/2.7.0/gems/rack-protection-2.2.2/lib/rack/protection/xss_header.rb:18:in `call'
/app/vendor/bundle/ruby/2.7.0/gems/rack-protection-2.2.2/lib/rack/protection/path_traversal.rb:16:in `call'
/app/vendor/bundle/ruby/2.7.0/gems/rack-protection-2.2.2/lib/rack/protection/json_csrf.rb:26:in `call'
/app/vendor/bundle/ruby/2.7.0/gems/rack-protection-2.2.2/lib/rack/protection/base.rb:50:in `call'
/app/vendor/bundle/ruby/2.7.0/gems/rack-protection-2.2.2/lib/rack/protection/base.rb:50:in `call'
/app/vendor/bundle/ruby/2.7.0/gems/rack-protection-2.2.2/lib/rack/protection/frame_options.rb:31:in `call'
/app/vendor/bundle/ruby/2.7.0/gems/rack-2.2.4/lib/rack/logger.rb:17:in `call'
/app/vendor/bundle/ruby/2.7.0/gems/rack-2.2.4/lib/rack/common_logger.rb:38:in `call'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:255:in `call'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:248:in `call'
/app/vendor/bundle/ruby/2.7.0/gems/rack-2.2.4/lib/rack/head.rb:12:in `call'
/app/vendor/bundle/ruby/2.7.0/gems/rack-2.2.4/lib/rack/method_override.rb:24:in `call'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:218:in `call'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1993:in `call'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1553:in `block in call'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1769:in `synchronize'
/app/vendor/bundle/ruby/2.7.0/gems/sinatra-2.2.2/lib/sinatra/base.rb:1553:in `call'
/app/vendor/bundle/ruby/2.7.0/gems/rack-2.2.4/lib/rack/handler/webrick.rb:95:in `service'
/app/vendor/ruby-2.7.5/lib/ruby/2.7.0/webrick/httpserver.rb:140:in `service'
/app/vendor/ruby-2.7.5/lib/ruby/2.7.0/webrick/httpserver.rb:96:in `run'
/app/vendor/ruby-2.7.5/lib/ruby/2.7.0/webrick/server.rb:307:in `block in start_thread'

Please sign in to comment.