diff --git a/.gitignore b/.gitignore index f79e65f..e5d0541 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,8 @@ # Python *.py[cod] *.egg-info + +# Output +/dist + +pantograph/pantograph diff --git a/README.md b/README.md index 8ab7584..b533429 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,15 @@ # PyPantograph + Python interface to the Pantograph library + +## Getting started + +Execute +```bash +poetry build +``` +If the build is successful, the following command should succeed and output the current version of Pantograph. +``` bash +python -m pantograph.server +``` + diff --git a/build.py b/build.py new file mode 100644 index 0000000..f5ffad4 --- /dev/null +++ b/build.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 + +import subprocess, shutil, os, stat +from pathlib import Path + +PATH_PANTOGRAPH = Path("./src") +PATH_PY = Path("./pantograph") + +with subprocess.Popen(["make"], cwd=PATH_PANTOGRAPH) as p: + p.wait() + +path_executable = PATH_PY / "pantograph" +shutil.copyfile(PATH_PANTOGRAPH / ".lake/build/bin/pantograph", path_executable) +os.chmod(path_executable, os.stat(path_executable).st_mode | stat.S_IEXEC) diff --git a/pantograph/server.py b/pantograph/server.py new file mode 100644 index 0000000..f06b4e4 --- /dev/null +++ b/pantograph/server.py @@ -0,0 +1,64 @@ +import json, pexpect, pathlib + +def _get_proc_path(): + return pathlib.Path(__file__).parent / "pantograph" + +class Server: + + def __init__(self, + imports=["Init"], + options=[], + timeout=20, + maxread=1000000): + """ + timeout: Amount of time to wait for execution + maxread: Maximum number of characters to read (especially important for large proofs and catalogs) + """ + self.timeout = timeout + self.imports = imports + self.maxread = maxread + self.proc_path = _get_proc_path() + + self.options = options + self.args = " ".join(imports + [f'--{opt}' for opt in options]) + self.proc = None + self.restart() + + def restart(self): + if self.proc is not None: + self.proc.close() + self.proc = pexpect.spawn( + f"{self.proc_path} {self.args}", + encoding="utf-8", + maxread=self.maxread + ) + self.proc.setecho(False) + + def run(self, cmd, payload): + s = json.dumps(payload) + self.proc.sendline(f"{cmd} {s}") + try: + self.proc.expect("{.*}\r\n", timeout=self.timeout) + output = self.proc.match.group() + return json.loads(output) + except pexpect.exceptions.TIMEOUT: + raise pexpect.exceptions.TIMEOUT + + def reset(self): + return self.run("reset", {}) + + def goal_start(self, expr: str): + return self.run('goal.start', {"expr": str(expr)}) + def goal_tactic(self, stateId: int, goalId: int, tactic): + return self.run('goal.tactic', {"stateId": stateId, "goalId": goalId, "tactic": tactic}) + + +def check_version(): + import subprocess + with subprocess.Popen([_get_proc_path(), "--version"], stdout=subprocess.PIPE) as p: + v = p.communicate()[0].decode('utf-8').strip() + print(v) + + +if __name__ == '__main__': + check_version() diff --git a/poetry.lock b/poetry.lock index b6550d4..261ace1 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,7 +1,31 @@ # This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. -package = [] + +[[package]] +name = "pexpect" +version = "4.9.0" +description = "Pexpect allows easy control of interactive console applications." +optional = false +python-versions = "*" +files = [ + {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"}, + {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"}, +] + +[package.dependencies] +ptyprocess = ">=0.5" + +[[package]] +name = "ptyprocess" +version = "0.7.0" +description = "Run a subprocess in a pseudo terminal" +optional = false +python-versions = "*" +files = [ + {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, + {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, +] [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "81b2fa642d7f2d1219cf80112ace12d689d053d81be7f7addb98144d56fc0fb2" +content-hash = "aaae6d4832f97d9ad1b145776be94a2c44a7e77068bb1b3a05241a81dde23909" diff --git a/pyproject.toml b/pyproject.toml index d6c8f70..1450528 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,11 @@ readme = "README.md" [tool.poetry.dependencies] python = "^3.11" +pexpect = "^4.9.0" +[tool.poetry.build] +generate-setup-file = false +script = "build.py" [build-system] requires = ["poetry-core"]