diff --git a/ddss/main.py b/ddss/main.py index c285f96..6343fef 100644 --- a/ddss/main.py +++ b/ddss/main.py @@ -1,24 +1,38 @@ -import sys import asyncio import tempfile import pathlib +from typing import Annotated, Optional +import tyro from .orm import initialize_database from .ds import main as ds from .egg import main as egg from .input import main as input from .output import main as output +from .load import main as load +from .dump import main as dump + +component_map = { + "ds": ds, + "egg": egg, + "input": input, + "output": output, + "load": load, + "dump": dump, +} -async def main(addr): +async def run(addr: str, components: list[str]) -> None: engine, session = await initialize_database(addr) + try: + try: + coroutines = [component_map[component](addr, engine, session) for component in components] + except KeyError as e: + print(f"error: unsupported component {e}") + raise asyncio.CancelledError() + await asyncio.wait( - [ - asyncio.create_task(ds(addr, engine, session)), - asyncio.create_task(egg(addr, engine, session)), - asyncio.create_task(input(addr, engine, session)), - asyncio.create_task(output(addr, engine, session)), - ], + [asyncio.create_task(coro) for coro in coroutines], return_when=asyncio.FIRST_COMPLETED, ) except asyncio.CancelledError: @@ -35,26 +49,46 @@ async def main(addr): } -def cli(): - if len(sys.argv) == 1: +def main( + addr: Annotated[ + Optional[str], + tyro.conf.arg( + aliases=["-a"], + help="Database address URL. If not provided, uses a temporary SQLite database.", + ), + ] = None, + component: Annotated[ + list[str], + tyro.conf.arg( + aliases=["-c"], + help="Components to run.", + ), + ] = ["input", "output", "ds", "egg"], +) -> None: + """DDSS - Distributed Deductive System Sorts + + Run DDSS with an interactive deductive environment. + """ + if addr is None: tmpdir = tempfile.TemporaryDirectory() path = pathlib.Path(tmpdir.name) / "ddss.db" addr = f"sqlite:///{path.as_posix()}" - elif len(sys.argv) == 2 and sys.argv[1] not in ["--help", "-help", "-h", "/help", "/h", "/?"]: - addr = sys.argv[1] - else: - print(f"Usage: {sys.argv[0]} []") - sys.exit(1) + for key, value in sqlalchemy_driver.items(): if addr.startswith(f"{key}://"): addr = addr.replace(f"{key}://", f"{key}+{value}://") if addr.startswith(f"{key}+{value}://"): break else: - print(f"Unsupported database address: {addr}") - sys.exit(1) + print(f"error: unsupported database: {addr}") + raise SystemExit(1) + print(f"addr: {addr}") - asyncio.run(main(addr)) + asyncio.run(run(addr, component)) + + +def cli(): + tyro.cli(main) if __name__ == "__main__": diff --git a/pyproject.toml b/pyproject.toml index 86f69d8..10711d3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,6 +12,7 @@ dependencies = [ "apyds-egg~=0.0.11", "prompt-toolkit~=3.0.52", "sqlalchemy[aiosqlite,aiomysql,postgresql-asyncpg]~=2.0.45", + "tyro~=1.0.3", ] [project.urls] diff --git a/uv.lock b/uv.lock index cac8cfe..38a6bf8 100644 --- a/uv.lock +++ b/uv.lock @@ -16,11 +16,11 @@ wheels = [ [[package]] name = "aiosqlite" -version = "0.22.0" +version = "0.22.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3a/0d/449c024bdabd0678ae07d804e60ed3b9786facd3add66f51eee67a0fccea/aiosqlite-0.22.0.tar.gz", hash = "sha256:7e9e52d72b319fcdeac727668975056c49720c995176dc57370935e5ba162bb9", size = 14707, upload-time = "2025-12-13T18:32:45.762Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/8a/64761f4005f17809769d23e518d915db74e6310474e733e3593cfc854ef1/aiosqlite-0.22.1.tar.gz", hash = "sha256:043e0bd78d32888c0a9ca90fc788b38796843360c855a7262a532813133a0650", size = 14821, upload-time = "2025-12-23T19:25:43.997Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/39/b2181148075272edfbbd6d87e6cd78cc71dca243446fa3b381fd4116950b/aiosqlite-0.22.0-py3-none-any.whl", hash = "sha256:96007fac2ce70eda3ca1bba7a3008c435258a592b8fbf2ee3eeaa36d33971a09", size = 17263, upload-time = "2025-12-13T18:32:44.619Z" }, + { url = "https://files.pythonhosted.org/packages/00/b7/e3bf5133d697a08128598c8d0abc5e16377b51465a33756de24fa7dee953/aiosqlite-0.22.1-py3-none-any.whl", hash = "sha256:21c002eb13823fad740196c5a2e9d8e62f6243bd9e7e4a1f87fb5e44ecb4fceb", size = 17405, upload-time = "2025-12-23T19:25:42.139Z" }, ] [[package]] @@ -239,6 +239,7 @@ dependencies = [ { name = "apyds-egg" }, { name = "prompt-toolkit" }, { name = "sqlalchemy", extra = ["aiomysql", "aiosqlite", "postgresql-asyncpg"] }, + { name = "tyro" }, ] [package.optional-dependencies] @@ -260,9 +261,19 @@ requires-dist = [ { name = "pytest-cov", marker = "extra == 'dev'", specifier = "~=7.0.0" }, { name = "ruff", marker = "extra == 'dev'", specifier = "~=0.14.10" }, { name = "sqlalchemy", extras = ["aiomysql", "aiosqlite", "postgresql-asyncpg"], specifier = "~=2.0.45" }, + { name = "tyro", specifier = "~=1.0.3" }, ] provides-extras = ["dev"] +[[package]] +name = "docstring-parser" +version = "0.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/9d/c3b43da9515bd270df0f80548d9944e389870713cc1fe2b8fb35fe2bcefd/docstring_parser-0.17.0.tar.gz", hash = "sha256:583de4a309722b3315439bb31d64ba3eebada841f2e2cee23b99df001434c912", size = 27442, upload-time = "2025-07-21T07:35:01.868Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/55/e2/2537ebcff11c1ee1ff17d8d0b6f4db75873e3b0fb32c2d4a2ee31ecb310a/docstring_parser-0.17.0-py3-none-any.whl", hash = "sha256:cf2569abd23dce8099b300f9b4fa8191e9582dda731fd533daf54c4551658708", size = 36896, upload-time = "2025-07-21T07:35:00.684Z" }, +] + [[package]] name = "greenlet" version = "3.3.0" @@ -542,6 +553,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/77/b8/0135fadc89e73be292b473cb820b4f5a08197779206b33191e801feeae40/tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b", size = 14408, upload-time = "2025-10-08T22:01:46.04Z" }, ] +[[package]] +name = "typeguard" +version = "4.4.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c7/68/71c1a15b5f65f40e91b65da23b8224dad41349894535a97f63a52e462196/typeguard-4.4.4.tar.gz", hash = "sha256:3a7fd2dffb705d4d0efaed4306a704c89b9dee850b688f060a8b1615a79e5f74", size = 75203, upload-time = "2025-06-18T09:56:07.624Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1b/a9/e3aee762739c1d7528da1c3e06d518503f8b6c439c35549b53735ba52ead/typeguard-4.4.4-py3-none-any.whl", hash = "sha256:b5f562281b6bfa1f5492470464730ef001646128b180769880468bd84b68b09e", size = 34874, upload-time = "2025-06-18T09:56:05.999Z" }, +] + [[package]] name = "typing-extensions" version = "4.15.0" @@ -551,6 +574,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, ] +[[package]] +name = "tyro" +version = "1.0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "docstring-parser" }, + { name = "typeguard" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c8/c5/2513339a36f300f477e4742706b977d3a8784485c8df037b64712ccd0c5e/tyro-1.0.3.tar.gz", hash = "sha256:6b3d73af2a5bb87247cc98b3e0d0bd5443010617e38250e6780b9f8998e1541e", size = 450742, upload-time = "2025-12-21T09:11:37.184Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/34/ac/8719fcb891c690394751114952e65f6f9f31259b6f62a10d7825b81f887e/tyro-1.0.3-py3-none-any.whl", hash = "sha256:d16758525b4c6ddd06fabb15ef821702b47aa829e0181be80ce71aa99acf99d9", size = 180664, upload-time = "2025-12-21T09:11:38.454Z" }, +] + [[package]] name = "wcwidth" version = "0.2.14"