aboutsummaryrefslogtreecommitdiff
path: root/src_py
diff options
context:
space:
mode:
authorbozo.kopic <bozo@kopic.xyz>2022-01-03 00:58:17 +0100
committerbozo.kopic <bozo@kopic.xyz>2022-01-03 00:58:17 +0100
commitae33b2c75ec0e03fd1d80241a4c4b29acadb517c (patch)
treefa86a159bafb6ea5c1605cd66a67f8be5a24ba3e /src_py
parent1a73bd946b8636dbcbb2970c8f8e919b888074ab (diff)
calculate native implementation
Diffstat (limited to 'src_py')
-rw-r--r--src_py/opcut/main.py90
-rw-r--r--src_py/opcut/server.py64
2 files changed, 110 insertions, 44 deletions
diff --git a/src_py/opcut/main.py b/src_py/opcut/main.py
index df9987f..475288e 100644
--- a/src_py/opcut/main.py
+++ b/src_py/opcut/main.py
@@ -15,25 +15,13 @@ import opcut.output
import opcut.server
-log_schema_id: str = 'hat-json://logging.yaml#'
params_schema_id: str = 'opcut://opcut.yaml#/definitions/params'
result_schema_id: str = 'opcut://opcut.yaml#/definitions/result'
@click.group()
-@click.option('--log',
- default=None,
- metavar='PATH',
- type=Path,
- help=f"logging configuration file path {log_schema_id}")
-def main(log: typing.Optional[Path]):
+def main():
"""Application main entry point"""
- if not log:
- return
-
- log_conf = json.decode_file(log)
- common.json_schema_repo.validate(log_schema_id, log_conf)
- logging.config.dictConfig(log_conf)
@main.command()
@@ -41,30 +29,35 @@ def main(log: typing.Optional[Path]):
default=common.Method.FORWARD_GREEDY,
type=common.Method,
help="calculate method")
-@click.option('--params',
- default=None,
- metavar='PATH',
- type=Path,
- help=f"calculate parameters file path ({params_schema_id})")
-@click.option('--result',
+@click.option('--output',
default=None,
metavar='PATH',
type=Path,
help=f"result file path ({result_schema_id})")
+@click.argument('params',
+ required=False,
+ default=None,
+ metavar='PATH',
+ type=Path)
def calculate(method: common.Method,
- params: typing.Optional[Path],
- result: typing.Optional[Path]):
+ output: typing.Optional[Path],
+ params: typing.Optional[Path]):
"""Calculate result"""
- params = (json.decode_file(params) if params
+ params = (json.decode_file(params) if params and params != Path('-')
else json.decode_stream(sys.stdin))
common.json_schema_repo.validate(params_schema_id, params)
params = common.params_from_json(params)
- res = opcut.csp.calculate(params, method)
+ try:
+ res = opcut.csp.calculate(params, method)
+
+ except common.UnresolvableError:
+ sys.exit(42)
+
res = common.result_to_json(res)
- if result:
- json.encode_file(res, result)
+ if output and output != Path('-'):
+ json.encode_file(res, output)
else:
json.encode_stream(res, sys.stdout)
@@ -77,32 +70,33 @@ def calculate(method: common.Method,
@click.option('--panel',
default=None,
help="panel identifier")
-@click.option('--result',
- default=None,
- metavar='PATH',
- type=Path,
- help=f"result file path ({result_schema_id})")
@click.option('--output',
default=None,
metavar='PATH',
type=Path,
help="result file path")
+@click.argument('result',
+ required=False,
+ default=None,
+ metavar='PATH',
+ type=Path)
def generate_output(output_type: common.OutputType,
panel: typing.Optional[str],
- result: typing.Optional[Path],
- output: typing.Optional[Path]):
+ output: typing.Optional[Path],
+ result: typing.Optional[Path]):
"""Generate output"""
- result = (json.decode_file(result) if result
+ result = (json.decode_file(result) if result and result != Path('-')
else json.decode_stream(sys.stdin))
common.json_schema_repo.validate(result_schema_id, result)
result = common.result_from_json(result)
out = opcut.output.generate_output(result, output_type, panel)
- if output:
- out.write_bytes(out)
+ if output and output != Path('-'):
+ output.write_bytes(out)
else:
- sys.stdout.detach().write(out)
+ stdout, sys.stdout = sys.stdout.detach(), None
+ stdout.write(out)
@main.command()
@@ -113,10 +107,29 @@ def generate_output(output_type: common.OutputType,
default=8080,
type=int,
help="listening TCP port")
+@click.option('--log-level',
+ default='INFO',
+ type=click.Choice(['CRITICAL', 'ERROR', 'WARNING', 'INFO',
+ 'DEBUG', 'NOTSET']),
+ help="log level")
def server(host: str,
- port: int):
+ port: int,
+ log_level: str):
"""Run server"""
- loop = aio.init_asyncio()
+ logging.config.dictConfig({
+ 'version': 1,
+ 'formatters': {
+ 'console': {
+ 'format': "[%(asctime)s %(levelname)s %(name)s] %(message)s"}},
+ 'handlers': {
+ 'console': {
+ 'class': 'logging.StreamHandler',
+ 'formatter': 'console',
+ 'level': log_level}},
+ 'root': {
+ 'level': log_level,
+ 'handlers': ['console']},
+ 'disable_existing_loggers': False})
async def run():
server = await opcut.server.create(host, port)
@@ -127,6 +140,7 @@ def server(host: str,
finally:
await aio.uncancellable(server.async_close())
+ loop = aio.init_asyncio()
with contextlib.suppress(asyncio.CancelledError):
aio.run_asyncio(run(), loop=loop)
diff --git a/src_py/opcut/server.py b/src_py/opcut/server.py
index 92a4186..5e726ad 100644
--- a/src_py/opcut/server.py
+++ b/src_py/opcut/server.py
@@ -1,11 +1,13 @@
from pathlib import Path
+import asyncio
+import subprocess
+import sys
from hat import aio
+from hat import json
import aiohttp.web
from opcut import common
-import opcut.csp
-import opcut.output
static_dir: Path = common.package_path / 'ui'
@@ -63,12 +65,13 @@ class Server(aio.Resource):
return aiohttp.web.Response(status=400,
text="Invalid request")
+ native = request.query.get('native') == 'true'
method = common.Method(request.query['method'])
params = common.params_from_json(data)
try:
- result = await self._executor(opcut.csp.calculate, params, method)
- return aiohttp.web.json_response(common.result_to_json(result))
+ result = await _calculate(native, method, params)
+ return aiohttp.web.json_response(result)
except common.UnresolvableError:
return aiohttp.web.Response(status=400,
@@ -88,8 +91,7 @@ class Server(aio.Resource):
panel = request.query.get('panel')
result = common.result_from_json(data)
- output = await self._executor(opcut.output.generate_output, result,
- output_type, panel)
+ output = await _generate_output(output_type, panel, result)
if output_type == common.OutputType.PDF:
content_type = 'application/pdf'
@@ -102,3 +104,53 @@ class Server(aio.Resource):
return aiohttp.web.Response(body=output,
content_type=content_type)
+
+
+async def _calculate(native, method, params):
+ args = [*_get_calculate_cmd(native), '--method', method.value]
+ stdint_data = json.encode(common.params_to_json(params)).encode('utf-8')
+
+ p = await asyncio.create_subprocess_exec(*args,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ stdout_data, stderr_data = await p.communicate(stdint_data)
+
+ if p.returncode == 0:
+ return json.decode(stdout_data.decode('utf-8'))
+
+ if p.returncode == 42:
+ raise common.UnresolvableError()
+
+ raise Exception(stderr_data.decode('utf-8'))
+
+
+async def _generate_output(output_type, panel, result):
+ args = [*_get_generate_output_cmd(), '--output-type', output_type.value,
+ *(['--panel', panel] if panel else [])]
+ stdint_data = json.encode(common.result_to_json(result)).encode('utf-8')
+
+ p = await asyncio.create_subprocess_exec(*args,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ stdout_data, stderr_data = await p.communicate(stdint_data)
+
+ if p.returncode == 0:
+ return stdout_data
+
+ raise Exception(stderr_data.decode('utf-8'))
+
+
+def _get_calculate_cmd(native):
+ if native and sys.platform == 'linux':
+ return [str(common.package_path / 'bin/linux-opcut-calculate')]
+
+ elif native and sys.platform == 'win32':
+ return [str(common.package_path / 'bin/windows-opcut-calculate.exe')]
+
+ return [sys.executable, '-m', 'opcut', 'calculate']
+
+
+def _get_generate_output_cmd():
+ return [sys.executable, '-m', 'opcut', 'generate-output']