diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index bd38197..a8888c9 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -26,3 +26,8 @@ jobs: - name: Run tests run: pytest tests + + - name: Build + run: | + python3 setup.py sdist + python3 setup.py bdist_wheel diff --git a/INSTALL.md b/INSTALL.md index 0f1c187..19b81c8 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -12,17 +12,15 @@ ## Installation and running KaTrain from PyPi * Run `pip3 install katrain` -* The first time you run KaTrain you will see an error about initializing KataGo. +* Run the program by executing `katrain` in a terminal. +* If you see an error about initializing KataGo: * Open the settings dialog by clicking on the gear icon at the bottom right of the window and change the path of the 'katago' - setting under 'engine' to `katago` and then click 'Apply and Save'. - * If katago is not in your path, try `/usr/local/bin/katago`, or the path where you compiled KataGo. -* Run the program by typing `katrain` in the terminal. + setting under 'engine' to `/usr/local/bin/katago`, or the path where you compiled KataGo. ## Installation from sources * This is largely the same as for linux, see [here](#LinuxSources). # Installation from sources for Windows users - * Download the repository by clicking the green *Clone or download* on this page and *Download zip*. Extract the contents. * Make sure you have a python installation, I will assume Anaconda (Python 3.7), available [here](https://www.anaconda.com/products/individual#download-section). * Open 'Anaconda prompt' from the start menu and navigate to where you extracted the zip file using the `cd ` command. diff --git a/katrain.py b/katrain.py index f4bb42b..a49ad0d 100644 --- a/katrain.py +++ b/katrain.py @@ -1,3 +1,4 @@ # for backward compatibility from katrain.__main__ import run_app + run_app() diff --git a/katrain/KataGo/katago.exe b/katrain/KataGo/katago.exe index 017492c..1fedd86 100644 Binary files a/katrain/KataGo/katago.exe and b/katrain/KataGo/katago.exe differ diff --git a/katrain/__main__.py b/katrain/__main__.py index 3030b21..4e8cc8a 100644 --- a/katrain/__main__.py +++ b/katrain/__main__.py @@ -19,7 +19,7 @@ from kivy.storage.jsonstore import JsonStore from kivy.uix.popup import Popup from katrain.core.ai import ai_move -from katrain.core.common import OUTPUT_INFO, OUTPUT_ERROR, OUTPUT_DEBUG, OUTPUT_EXTRA_DEBUG, OUTPUT_KATAGO_STDERR, find_package_resource +from katrain.core.common import OUTPUT_INFO, OUTPUT_ERROR, OUTPUT_DEBUG, OUTPUT_EXTRA_DEBUG, OUTPUT_KATAGO_STDERR, find_package_resource, clean_temp from katrain.core.engine import KataGoEngine from katrain.core.game import Game, IllegalMoveException, KaTrainSGF from katrain.core.sgf_parser import Move, ParseError @@ -30,6 +30,7 @@ from katrain.gui.popups import NewGamePopup, ConfigPopup, LoadSGFPopup __version__ = "1.0.5" + class KaTrainGui(BoxLayout): """Top level class responsible for tying everything together""" @@ -52,7 +53,7 @@ class KaTrainGui(BoxLayout): self._keyboard.bind(on_key_down=self._on_keyboard_down) def log(self, message, level=OUTPUT_INFO): - if level == OUTPUT_KATAGO_STDERR: + if level == OUTPUT_KATAGO_STDERR and 'ERROR' not in self.controls.status.text: if "starting" in message.lower(): self.controls.set_status(f"KataGo engine starting...") if message.startswith("Tuning"): @@ -319,7 +320,6 @@ class KaTrainApp(App): self.icon = ICON # how you're supposed to set an icon self.gui = KaTrainGui() self.title = f"KaTrain v{__version__}" - print(self.get_application_icon()) Window.bind(on_request_close=self.on_request_close) return self.gui @@ -329,6 +329,7 @@ class KaTrainApp(App): def on_request_close(self, *args): if getattr(self, "gui", None) and self.gui.engine: self.gui.engine.shutdown() + clean_temp() def signal_handler(self, *args): if self.gui.debug_level >= OUTPUT_DEBUG: diff --git a/katrain/config.json b/katrain/config.json index 6f77f26..0597624 100644 --- a/katrain/config.json +++ b/katrain/config.json @@ -1,7 +1,7 @@ { "engine": { - "katago": "katrain/KataGo/katago", - "model": "katrain/KataGo/models/b15-1.3.2.txt.gz", + "katago": "", + "model": "katrain/models/b15-1.3.2.txt.gz", "config": "katrain/KataGo/analysis_config.cfg", "threads": 16, "max_visits": 500, diff --git a/katrain/core/common.py b/katrain/core/common.py index cdf841b..8cc39a4 100644 --- a/katrain/core/common.py +++ b/katrain/core/common.py @@ -30,11 +30,23 @@ def evaluation_class(points_lost: float, eval_thresholds: List[float]): return i +resource_scopes = [] + + +def clean_temp(): + for s in resource_scopes: + s.__exit__(None, None, None) + + def find_package_resource(path): if path.startswith("katrain"): parts = path.replace("\\", "/").split("/") - with pkg_resources.path(".".join(parts[:-1]), parts[-1]) as path_obj: - found_path = str(path_obj) - return found_path + try: + path_obj = pkg_resources.path(".".join(parts[:-1]), parts[-1]).__enter__() + resource_scopes.append(path_obj) + return str(path_obj) + except ModuleNotFoundError as e: + print(f"File {path} not found, installation possibly broken") + return "FILENOTFOUND" else: return path # absolute path diff --git a/katrain/core/engine.py b/katrain/core/engine.py index bfcf27f..6d80168 100644 --- a/katrain/core/engine.py +++ b/katrain/core/engine.py @@ -28,8 +28,17 @@ class KataGoEngine: def __init__(self, katrain, config): self.katrain = katrain + executable = config["katago"].strip() + if not executable: + if sys.platform.startswith("win"): + executable = "katrain/KataGo/katago.exe" + elif sys.platform.startswith("linux"): + executable = "katrain/KataGo/katago" + else: # e.g. MacOS after brewing + executable = "katago" - self.command = f"{find_package_resource(config['katago'])} analysis -model {config['model']} -config {config['config']} -analysis-threads {config['threads']}" + modelfile, configfile = find_package_resource(config["model"]), find_package_resource(config["config"]) + self.command = f"{find_package_resource(executable)} analysis -model {modelfile} -config {configfile} -analysis-threads {config['threads']}" if not sys.platform.startswith("win"): self.command = shlex.split(self.command) self.queries = {} # outstanding query id -> start time and callback @@ -47,11 +56,17 @@ class KataGoEngine: try: self.katrain.log(f"Starting KataGo with {self.command}", OUTPUT_DEBUG) self.katago_process = subprocess.Popen(self.command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - except (FileNotFoundError, PermissionError) as e: - self.katrain.log( - f"Starting kata with command '{self.command}' failed with error {e}. Please make sure the 'katago' value under 'engine' in settings points to the correct KataGo executable.", - OUTPUT_ERROR, - ) + except (FileNotFoundError, PermissionError, OSError) as e: + if self.config["katago"].strip(): + self.katrain.log( + f"Starting kata with command '{self.command}' failed with error {e}. If on MacOS, see the manual on how to use brew to install katago first, and add it to your path or 'engine/katago' setting.", + OUTPUT_ERROR, + ) + else: + self.katrain.log( + f"Starting kata with command '{self.command}' failed with error {e}. Please make sure the 'katago' value under 'engine' in settings points to the correct KataGo executable.", + OUTPUT_ERROR, + ) self.analysis_thread = threading.Thread(target=self._analysis_read_thread, daemon=True).start() self.stderr_thread = threading.Thread(target=self._read_stderr_thread, daemon=True).start() diff --git a/katrain/models/__init__.py b/katrain/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/katrain/KataGo/models/b10-1.3.txt.gz b/katrain/models/b10-1.3.txt.gz similarity index 100% rename from katrain/KataGo/models/b10-1.3.txt.gz rename to katrain/models/b10-1.3.txt.gz diff --git a/katrain/KataGo/models/b15-1.3.2.txt.gz b/katrain/models/b15-1.3.2.txt.gz similarity index 100% rename from katrain/KataGo/models/b15-1.3.2.txt.gz rename to katrain/models/b15-1.3.2.txt.gz diff --git a/setup.py b/setup.py index f990cf3..d2bf4d8 100644 --- a/setup.py +++ b/setup.py @@ -4,22 +4,24 @@ import os from setuptools import find_packages, setup package_data = {"": ["*.json", "*.kv"], "katrain": [], "tests": []} -packages = find_packages(exclude=["bots","tests"]) +packages = find_packages(exclude=["bots", "tests"]) version = re.search('^__version__\s*=\s*"(.*)"', open("katrain/__main__.py").read(), re.M).group(1) + def include_data_files(directory): for root, subfolders, files in os.walk(directory): for fn in files: - filename = os.path.join(root, fn) + filename = os.path.join(root.replace("/", os.path.sep), fn) parts = filename.split(os.path.sep) package_data[parts[0]].append(os.path.join(*parts[1:])) include_data_files("katrain/KataGo") +include_data_files("katrain/models") include_data_files("katrain/img/") include_data_files("katrain/img/flaticon") -print(packages,package_data) +print(packages, package_data) with open("README.md", "r") as fh: long_description = fh.read() @@ -34,10 +36,10 @@ setup( author_email="sander.land@gmail.com", url="https://github.com/sanderland/katrain", license="MIT", - install_requires=["kivy"], + install_requires=["kivy", "kivy_deps.glew;platform_system=='Windows'", "kivy_deps.sdl2;platform_system=='Windows'", "kivy_deps.gstreamer;platform_system=='Windows'"], python_requires=">=3.6, <3.8", entry_points={"console_scripts": ["katrain=katrain.__main__:run_app"]}, - classifiers=["Development Status :: 4 - Beta", "Operating System :: Microsoft :: Windows", "Operating System :: POSIX :: Linux", "Programming Language :: Python :: 3",], + classifiers=["Development Status :: 4 - Beta", "Operating System :: Microsoft :: Windows", "Operating System :: POSIX :: Linux", "Programming Language :: Python :: 3"], packages=packages, package_data=package_data, )