Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
135 commits
Select commit Hold shift + click to select a range
f994b10
chore: set up configs for Toad
dannyhammer Oct 8, 2024
8b037cd
Add scipy 1.9.0 to server's requirements.txt (#245)
dannyhammer Oct 9, 2024
6ff5ad8
Merge branch 'AndyGrant:master' into master
dannyhammer Oct 9, 2024
658586e
chore: set up configs for Toad
dannyhammer Oct 8, 2024
a28e60c
feat: adds support for Discord Webhooks (#1)
dannyhammer Oct 22, 2024
01c2dda
Clarify use of relative paths in benching
AndyGrant Nov 12, 2024
66b8c24
Remove scipy version requirement (#251)
gahtan-syarif Nov 13, 2024
20dbb67
More accurate error bars for small sample sizes (#246)
gahtan-syarif Nov 13, 2024
32e23a6
Assorted small simplifications (#247)
cj5716 Nov 13, 2024
a27e717
Explicitly import stats subpackage (#253)
gahtan-syarif Nov 13, 2024
2cca03e
Collapse Test/Tune/Datagen views heavily
AndyGrant Nov 13, 2024
4a4cc64
Remove duplicated urls for default-able endpoints
AndyGrant Nov 13, 2024
cfeb3c4
feat: support for xboard
dannyhammer Nov 19, 2024
4d3624c
Merge branch 'AndyGrant:master' into master
dannyhammer Nov 19, 2024
9c2ea92
chore: gave yukari back
dannyhammer Nov 20, 2024
3e52b50
feat: enabled/added some more engines. for fun
dannyhammer Nov 20, 2024
daad2de
feat: webhooks now only ping the test author (username is case-insens…
dannyhammer Nov 20, 2024
b909883
chore: added ByteKnight
dannyhammer Nov 21, 2024
b46989e
chore: updated Toad config
dannyhammer Nov 27, 2024
64b8fc2
Change some Yukari defaults (#2)
Ravenslofty Nov 27, 2024
a8d477c
Allow --threads auto to just use max physical cores
AndyGrant Dec 2, 2024
5312239
add pawnocchio.json (#3)
JonathanHallstrom Dec 13, 2024
eacd230
Merge branch 'AndyGrant:master' into master
dannyhammer Dec 13, 2024
1e86db5
fix: added Pawnocchio
dannyhammer Dec 13, 2024
06d4e2c
Fix workload creation submission endpoint
AndyGrant Dec 13, 2024
a590439
Merge branch 'AndyGrant:master' into master
dannyhammer Dec 13, 2024
b37ccd8
fix: updated paths in create_workload.py (#257)
dannyhammer Dec 13, 2024
64ff060
Merge branch 'AndyGrant:master' into master
dannyhammer Dec 13, 2024
af104fc
update pawnocchio config (#4)
JonathanHallstrom Dec 13, 2024
a44bdb6
feat: auto-refresh Index and Workload pages every 30s
dannyhammer Dec 14, 2024
231c591
chore: updated Toad.json nps
dannyhammer Dec 17, 2024
6a54cc7
chore: added Lichess book, updated Yukari/Toad configs' default book,…
dannyhammer Dec 17, 2024
0a982ac
chore: update ByteKnight nps (#5)
Dec 17, 2024
a0f0ead
Change some Yukari defaults (#6)
Ravenslofty Dec 22, 2024
cc47c4d
fix oopsie (#7)
Ravenslofty Dec 22, 2024
88f67a1
chore: updated nps for Toad
dannyhammer Dec 22, 2024
e183a65
chore: update Toad.json
dannyhammer Dec 24, 2024
3041789
chore: purple and gold logo because why not
dannyhammer Dec 24, 2024
8746bdb
feat: added support for specifying nightly dependencies for Rust engines
dannyhammer Dec 24, 2024
091ef00
chore: updated Toad and Yukari configs for faster-fails and win_adj
dannyhammer Dec 25, 2024
9ea2e51
fix: fixed workload.html `MODIFY` path
dannyhammer Dec 25, 2024
b03d962
fix: fixed protocol/network mismatch in tune workloads
dannyhammer Dec 26, 2024
20d28da
chore: reduced Toad nps by running on a slower reference system
dannyhammer Dec 26, 2024
1f93dbd
Expose scaling options in workload creation
AndyGrant Dec 21, 2024
37a81fc
HACK: Set scale_nps when defaulted to 0
AndyGrant Dec 21, 2024
9f2278f
Fix url mapping errors during simplification
AndyGrant Dec 21, 2024
3f14927
Update Weiss config from 8/40 to 10/60
AndyGrant Dec 21, 2024
920b2cd
WIP: WSGI handling of Watchers + Graceful exits Linux/Windows
AndyGrant Dec 21, 2024
7a536ee
Cleanup Watcher/WSGI/Graceful
AndyGrant Dec 21, 2024
e389633
Only load config once per process; resolve import ordering.
AndyGrant Dec 21, 2024
77d80f5
Return API errors when trying to download partial PGNs
AndyGrant Dec 23, 2024
0d7f8e9
Kick client/server to v33
AndyGrant Dec 28, 2024
1603b17
chore: set up configs for Toad
dannyhammer Dec 28, 2024
19c2445
chore: set up configs for Toad
dannyhammer Oct 8, 2024
974b670
feat: adds support for Discord Webhooks (#1)
dannyhammer Oct 22, 2024
2047a93
feat: support for xboard
dannyhammer Nov 19, 2024
c67566c
chore: gave yukari back
dannyhammer Nov 20, 2024
d76a146
feat: enabled/added some more engines. for fun
dannyhammer Nov 20, 2024
1eeb816
feat: webhooks now only ping the test author (username is case-insens…
dannyhammer Nov 20, 2024
a1eaada
chore: added ByteKnight
dannyhammer Nov 21, 2024
e78dea7
chore: updated Toad config
dannyhammer Nov 27, 2024
5d1e8d6
Change some Yukari defaults (#2)
Ravenslofty Nov 27, 2024
d4088f9
add pawnocchio.json (#3)
JonathanHallstrom Dec 13, 2024
39726ea
fix: added Pawnocchio
dannyhammer Dec 13, 2024
8f89015
update pawnocchio config (#4)
JonathanHallstrom Dec 13, 2024
c5ae7f1
feat: auto-refresh Index and Workload pages every 30s
dannyhammer Dec 14, 2024
4e46a0b
chore: updated Toad.json nps
dannyhammer Dec 17, 2024
69fa601
chore: added Lichess book, updated Yukari/Toad configs' default book,…
dannyhammer Dec 17, 2024
3622d42
chore: update ByteKnight nps (#5)
Dec 17, 2024
83f990d
Change some Yukari defaults (#6)
Ravenslofty Dec 22, 2024
8ede1fa
fix oopsie (#7)
Ravenslofty Dec 22, 2024
5430212
chore: updated nps for Toad
dannyhammer Dec 22, 2024
a69b3fe
chore: update Toad.json
dannyhammer Dec 24, 2024
c50318b
chore: purple and gold logo because why not
dannyhammer Dec 24, 2024
1618b49
feat: added support for specifying nightly dependencies for Rust engines
dannyhammer Dec 24, 2024
c7ef2ee
chore: updated Toad and Yukari configs for faster-fails and win_adj
dannyhammer Dec 25, 2024
c366c44
fix: fixed protocol/network mismatch in tune workloads
dannyhammer Dec 26, 2024
21390a8
chore: reduced Toad nps by running on a slower reference system
dannyhammer Dec 26, 2024
884fa43
Merge branch 'AndyGrant-master'
dannyhammer Dec 28, 2024
eeef48c
Dog (#9)
folkertvanheusden Dec 28, 2024
71bd625
chore: added Stash config & updated Toad default bounds
dannyhammer Dec 29, 2024
cb7c97f
Dog requires at least g++ v12 (#11)
folkertvanheusden Dec 30, 2024
314f1a2
add bannou (#10)
87flowers Dec 30, 2024
cdb7d4a
g++14 for Dog (#12)
folkertvanheusden Dec 30, 2024
948328a
Dog (#13)
folkertvanheusden Dec 30, 2024
a2eff24
Dog2 (#16)
folkertvanheusden Dec 31, 2024
af9c4c3
Dog4 (#17)
folkertvanheusden Dec 31, 2024
d0ac2e4
bannou: switch source to github (#18)
87flowers Jan 2, 2025
373ca6b
compilers for Dog (#20)
folkertvanheusden Jan 2, 2025
891643a
Add UHO_Lichess_4852_v1.epd
AndyGrant Jan 2, 2025
e9c0e6e
Ignore openbench_watchers.lock
AndyGrant Jan 3, 2025
23685e6
Clear openbench.exit on startup; Check openbench.exit outside the try…
AndyGrant Jan 3, 2025
63e72d1
Handle missing PGNs on early Cutechess exits
AndyGrant Jan 3, 2025
34ef6f7
Kill processes, without file paths, to appease Windows' Taskkill
AndyGrant Jan 3, 2025
25ff470
Use workload_url instead of explicit links
AndyGrant Jan 3, 2025
04cda80
Redirect mismatched types when viewing workloads
AndyGrant Jan 3, 2025
2a96c55
Add OpenBenchFatalWorkerException to force worker re-init.
AndyGrant Jan 3, 2025
22ccf40
Trigger OpenBenchFatalWorkerException when config meaningfully changes
AndyGrant Jan 3, 2025
9f9f1ba
Attempt to resolve cascading reimports on worker updates (#265)
AndyGrant Jan 4, 2025
5af494d
Merge remote-tracking branch 'upstream/master'
dannyhammer Jan 4, 2025
8865c42
Update Ethereal/Torch/Weiss to UHO_Lichess_4852_v1; Add Minimal for W…
AndyGrant Jan 4, 2025
1663aaf
chore: added `uci` to Yukari.json protocols
dannyhammer Jan 6, 2025
a8edd63
Fix reading private credentials on some setups (#266)
xu-shawn Jan 9, 2025
4c934a3
chore: removed base/dev branch defaults
dannyhammer Jan 13, 2025
81286b3
allow --no-client-downloads to reject initial and incremental updates
AndyGrant Jan 30, 2025
8ab94f2
Update 1k fudging button to 4k
AndyGrant Jan 30, 2025
e2c73d3
bannou: set more reasonable defaults (#21)
87flowers Feb 2, 2025
f5d47a9
added GrandChess.json and updated config.json (#22)
urisinger Feb 2, 2025
f81d840
Add Client option --noisy, to reject all time-based workloads
AndyGrant Feb 7, 2025
4674156
Update to Stockfish 17 (ob_17). Default Net nn-1111cefa1111.nnue
AndyGrant Feb 7, 2025
9bb8f09
update pawnocchios config (#23)
JonathanHallstrom Feb 17, 2025
f7d82ae
update yukari NPS (#24)
Ravenslofty Feb 19, 2025
768d387
Add genfens_engine.py, to test genfens
AndyGrant Mar 4, 2025
06569bf
Convert fens to epds for genfens, to allow half/full move counters
AndyGrant Mar 4, 2025
7dcc90d
Kick to v37
AndyGrant Mar 4, 2025
dd628b6
Expect OperationError: database is locked, in PGN/Artifact watchers
AndyGrant Mar 4, 2025
993dad1
Updates to Torch default buttons
AndyGrant Mar 25, 2025
2c0561a
Add Caps.json (I hope it works :p) (#25)
toanth Apr 3, 2025
89b969f
feat: adding the soon to be 3500 40/15 hce engine (#26)
TheRealGioviok Apr 4, 2025
e891cf6
Add chessatron to config file and engine folder (#27)
dallinson Apr 6, 2025
ac514c8
Script + API to delete state nets from a user
AndyGrant Apr 14, 2025
69ba69f
Remove debug statement
AndyGrant Apr 14, 2025
cb4a9bd
Final version
AndyGrant Apr 14, 2025
7a0af18
Fixed unknown function for scripts (#284)
Luecx Apr 14, 2025
a3309a9
Collapse lengthy engine options into popups on workload displays
AndyGrant Apr 14, 2025
d984db2
Add --contains to delete_networks.py
AndyGrant Apr 14, 2025
87b1bf0
Update Stash config (#287)
mhouppin Apr 30, 2025
89c9820
allow archive2nps to breakdown per result-id. Add node counters
AndyGrant May 6, 2025
37e0248
Fix safari date display on mobile (#289)
xu-shawn May 8, 2025
0d72b9d
Add Rose (#28)
87flowers May 12, 2025
782bbc5
Merge remote-tracking branch 'upstream/master'
dannyhammer May 12, 2025
816dd9d
Update config.json (#29)
JonathanHallstrom May 31, 2025
1376f15
Update Pawnocchio.json (#30)
JonathanHallstrom May 31, 2025
c64f5f0
Add Episteme (#31)
aletheiaaaaa Jun 11, 2025
d41e200
Update Episteme.json
aletheiaaaaa Jun 28, 2025
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
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,8 @@ Media/*
Media/PGNs/*
Media/Networks/*
Media/Events/*

*.lock
*.epd

webhooks.json
4 changes: 4 additions & 0 deletions Books/UHO_Lichess_4852_v1.epd.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"sha": "7A7F6470615A69C6CF23D565417701D38732876F480AF90D67B42ABADE35644A",
"source": "https://raw.githubusercontent.com/AndyGrant/openbench-books/master/UHO_Lichess_4852_v1.epd.zip"
}
21 changes: 11 additions & 10 deletions Client/bench.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@

# The sole purpose of this module is to invoke run_benchmark().
#
# - binary : Path to, and including, the Binary File
# - network : Path to Network File, or None
# - binary : Relative path to, and including, the Binary File
# - network : Relative path to a private engine's Network File, or None
# - private : True or False; Private NNUE engines require special care
# - threads : Number of concurrent benches to run
# - sets : Number of times to repeat this experiment
Expand All @@ -37,8 +37,9 @@
import subprocess
import sys

from utils import kill_process_by_name
from utils import OpenBenchBadBenchException
## Local imports must only use "import x", never "from x import ..."

import utils

MAX_BENCH_TIME_SECONDS = 60

Expand Down Expand Up @@ -103,8 +104,8 @@ def multi_core_bench(binary, network, private, threads):
return [outqueue.get(timeout=MAX_BENCH_TIME_SECONDS) for ii in range(threads)]

except queue.Empty: # Force kill the engine, thus causing the processes to finish
kill_process_by_name(binary)
raise OpenBenchBadBenchException('[%s] Bench Exceeded Max Duration' % (binary))
utils.kill_process_by_name(binary)
raise utils.OpenBenchBadBenchException('[%s] Bench Exceeded Max Duration' % (binary))

finally: # Join everything to avoid zombie processes
for process in processes:
Expand All @@ -120,12 +121,12 @@ def run_benchmark(binary, network, private, threads, sets, expected=None):
benches.append(bench); speeds.append(speed)

if len(set(benches)) != 1:
raise OpenBenchBadBenchException('[%s] Non-Deterministic Benches' % (engine))
raise utils.OpenBenchBadBenchException('[%s] Non-Deterministic Benches' % (engine))

if None in benches or None in speeds:
raise OpenBenchBadBenchException('[%s] Failed to Execute Benchmark' % (engine))
raise utils.OpenBenchBadBenchException('[%s] Failed to Execute Benchmark' % (engine))

if expected and expected != benches[0]:
raise OpenBenchBadBenchException('[%s] Wrong Bench: %d' % (engine, benches[0]))
raise utils.OpenBenchBadBenchException('[%s] Wrong Bench: %d' % (engine, benches[0]))

return int(sum(speeds) / len(speeds)), benches[0]
return sum(speeds) // len(speeds), benches[0]
16 changes: 12 additions & 4 deletions Client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,11 @@ def parse_arguments():
)

# Create and parse all arguments into a raw format
p.add_argument('-U', '--username', help=help_user , required=req_user )
p.add_argument('-P', '--password', help=help_pass , required=req_pass )
p.add_argument('-S', '--server' , help=help_server , required=req_server)
p.add_argument( '--clean' , help='Force New Client', action='store_true')
p.add_argument('-U', '--username' , help=help_user , required=req_user )
p.add_argument('-P', '--password' , help=help_pass , required=req_pass )
p.add_argument('-S', '--server' , help=help_server , required=req_server)
p.add_argument( '--clean' , help='Force New Client' , action='store_true')
p.add_argument( '--no-client-downloads', help='NEVER download a client', action='store_true')

# Override, to possibly print worker.py's help as well as client.py's
p.print_help = lambda: custom_help(p.format_help())
Expand Down Expand Up @@ -160,6 +161,9 @@ def download_client_files(args):

args = parse_arguments()

if args.no_client_downloads and not has_worker():
raise Exception('Client missing, and --no-client-downloads provided')

if args.clean or not has_worker():
print ('[NOTE] Downloading Client...')
try_forever(download_client_files, [args], 'Failed to download Client files')
Expand All @@ -174,6 +178,10 @@ def download_client_files(args):
worker.run_openbench_worker(args)

except BadVersionException:

if args.no_client_downloads:
raise Exception('Client update requested, but --no-client-downloads provided')

print ('[NOTE] Downloading newer version of Client...')
try_forever(download_client_files, [args], 'Failed to download Client files')

Expand Down
95 changes: 42 additions & 53 deletions Client/genfens.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@
# #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

# The sole purpose of this module is to invoke create_genfens_opening_book().
# The main purpose of this module is to invoke create_genfens_opening_book().
# Refer to Client/worker.py, or Scripts/genfens_engine.py for the arguments.
#
# This will execute engines with commands like the following:
# We will execute engines with commands like the following:
# ./engine "genfens N seed S book <None|Books/book.epd> <?extra>" "quit"
#
# This work is split over many engines. If a workload requires 1024 openings,
# and there are 16 threads, then each thread will generate 64 openings. The
# openings are saved to Books/openbench.genfens.epd
# and there are 16 threads, then each thread will generate 64 openings.
#
# create_genfens_opening_book() may raise utils.OpenBenchFailedGenfensException.
# This occurs when longer than 15 seconds has elapsed since getting an opening.
Expand All @@ -38,8 +38,9 @@
import time
import multiprocessing

from utils import kill_process_by_name
from utils import OpenBenchFailedGenfensException
## Local imports must only use "import x", never "from x import ..."

import utils

def genfens_required_openings_each(config):

Expand All @@ -50,38 +51,22 @@ def genfens_required_openings_each(config):

return math.ceil(total_games / config.threads)

def genfens_command_args(config, binary_name, network):

binary = os.path.join('Engines', binary_name)
private = config.workload['test']['dev']['private']
N = genfens_required_openings_each(config)
book = genfens_book_input_name(config)
extra_args = config.workload['test']['genfens_args']

return (binary, network, private, N, book, extra_args)

def genfens_book_input_name(config):

book_name = config.workload['test']['book']['name']
book_none = book_name.upper() == 'NONE'
book_name = config.workload['test']['book']['name']
book_none = book_name.upper() == 'NONE'

return 'None' if book_none else os.path.join('Books', book_name)

def genfens_seed(config, N_per_thread, thread_index):

x = config.workload['test']['book_seed']
y = config.workload['test']['book_index']

return (x << 32) + (y + N_per_thread * thread_index)
def genfens_command_builder(args, index):

def genfens_command_builder(binary, network, private, N, book, extra_args, seed):
command = ['./%s' % (args['engine'])]

command = ['./%s' % (binary)]
if args['network'] and args['private']:
command += ['setoption name EvalFile value %s' % (args['network'])]

if network and private:
command += ['setoption name EvalFile value %s' % (network)]

command += ['genfens %d seed %d book %s %s' % (N, seed, book, extra_args), 'quit']
fstr = 'genfens %d seed %d book %s %s'
command += [fstr % (args['N'], args['seeds'][index], args['book'], args['extra']), 'quit']

return command

Expand All @@ -101,51 +86,55 @@ def genfens_single_threaded(command, queue):

def genfens_progress_bar(curr, total):

prev_progress = int(50 * (curr - 1) / total)
curr_progress = int(50 * (curr - 0) / total)
prev_progress = 50 * (curr - 1) // total
curr_progress = 50 * (curr - 0) // total

if curr_progress != prev_progress:
bar_text = '=' * curr_progress + ' ' * (50 - curr_progress)
print ('\r[%s] %d/%d' % (bar_text, curr, total), end='', flush=True)

def create_genfens_opening_book(config, binary_name, network):
def convert_fen_to_epd(fen):

# Input : rnbqkbnr/pppp2pp/4pp2/8/2P2P2/P7/1P1PP1PP/RNBQKBNR b KQkq - 0 3
# Output : rnbqkbnr/pppp2pp/4pp2/8/2P2P2/P7/1P1PP1PP/RNBQKBNR b KQkq - hmvc 0; fmvn 3;

halfmove, fullmove = fen.split()[4:]

# Format: ./engine "genfens N seed S book <None|book.epd>" "quit"
N = genfens_required_openings_each(config)
seeds = config.workload['test']['genfens_seeds']
args = genfens_command_args(config, binary_name, network)
return ' '.join(fen.split()[:4]) + ' hmvc %d; fmvn %d;' % (int(halfmove), int(fullmove))

def create_genfens_opening_book(args):

N = args['N']
threads = args['threads']
start_time = time.time()
output = multiprocessing.Queue()
print ('\nGenerating %d Openings using %d Threads...' % (N * config.threads, config.threads))

print ('\nGenerating %d Openings using %d Threads...' % (N * threads, threads))

# Split the work over many threads. Ensure the seed varies by the thread,
# number in accordance with how many openings each thread will generate

processes = [
multiprocessing.Process(
target=genfens_single_threaded,
args=(genfens_command_builder(*args, seeds[ii]), output))
for ii in range(config.threads)
args=(genfens_command_builder(args, index), output))
for index in range(threads)
]

for process in processes:
process.start()

# Parse the Queue and save the content into Books/openbench.genfens.epd
with open(os.path.join('Books', 'openbench.genfens.epd'), 'w') as fout:

try: # Each process will deposit exactly N results into the Queue
for iteration in range(N * config.threads):
fout.write(output.get(timeout=15) + '\n')
genfens_progress_bar(iteration+1, N * config.threads)
try: # Each process will deposit exactly N results into the Queue
for iteration in range(N * threads):
args['output'].write(convert_fen_to_epd(output.get(timeout=15)) + '\n')
genfens_progress_bar(iteration+1, N * threads)

except queue.Empty: # Force kill the engine, thus causing the processes to finish
kill_process_by_name(binary_name)
raise OpenBenchFailedGenfensException('[%s] Stalled during genfens' % (binary_name))
except queue.Empty: # Force kill the engine, thus causing the processes to finish
utils.kill_process_by_name(binary_name)
raise utils.OpenBenchFailedGenfensException('[%s] Stalled during genfens' % (binary_name))

finally: # Join everything to avoid zombie processes
for process in processes:
process.join()
finally: # Join everything to avoid zombie processes
for process in processes:
process.join()

print('\nFinished Building Opening Book in %.3f seconds' % (time.time() - start_time))
2 changes: 2 additions & 0 deletions Client/pgn_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import re
import sys

## Local imports must only use "import x", never "from x import ..."

# For use externally
REGEX_COMMENT_VERBOSE = r'(book|[+-]?M?\d+(?:\.\d+)? \d+/\d+ \d+ \d+)'
REGEX_COMMENT_COMPACT = r'(book|[+-]?M?\d+(?:\.\d+)?) \d+/\d+ \d+ \d+'
Expand Down
15 changes: 15 additions & 0 deletions Client/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,17 @@
import tempfile
import zipfile

## Local imports must only use "import x", never "from x import ..."

IS_WINDOWS = platform.system() == 'Windows' # Don't touch this
IS_LINUX = platform.system() != 'Windows' # Don't touch this


class OpenBenchFatalWorkerException(Exception):
def __init__(self, message):
self.message = 'Restarting Worker: ' + message
super().__init__(self.message)

class OpenBenchBuildFailedException(Exception):
def __init__(self, message, logs):
self.message = message
Expand Down Expand Up @@ -73,9 +81,16 @@ def __init__(self, message):
self.message = message
super().__init__(self.message)

class OpenBenchMisssingPGNException(Exception):
def __init__(self, message):
self.message = message
super().__init__(self.message)


def kill_process_by_name(process_name):

process_name = os.path.basename(process_name)

if IS_LINUX:
subprocess.run(['pkill', '-f', process_name])

Expand Down
Loading