Skip to content

Commit 86047c6

Browse files
authored
refactor: switch to sqlite (#17)
This will be needed to support application-wide configurations, for example to enable reusing OpenAI assistants. Another benefit is that is allows us to reduce our reliance on (and the number of) commits.
1 parent 12f467f commit 86047c6

File tree

15 files changed

+265
-145
lines changed

15 files changed

+265
-145
lines changed

poetry.lock

Lines changed: 13 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@ readme = 'README.md'
77
dynamic = ['version']
88
requires-python = '>=3.12'
99
dependencies = [
10-
'gitpython >=3.1.44,<4',
10+
'gitpython (>=3.1.44,<4)',
11+
"xdg-base-dirs (>=6.0.2,<7.0.0)",
1112
]
1213

1314
[project.optional-dependencies]
14-
openai = ['openai >=1.64.0,<2']
15+
openai = ['openai (>=1.64.0,<2)']
1516

1617
[project.scripts]
1718
git-draft = 'git_draft.__main__:main'

src/git_draft/__main__.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import textwrap
88

99
from .assistants import load_assistant
10-
from .common import open_editor
10+
from .common import Store, open_editor
1111
from .manager import Manager
1212

1313

@@ -41,9 +41,9 @@ def callback(_option, _opt, _value, parser) -> None:
4141
)
4242

4343

44-
add_command("discard", help="discard all drafts associated with a branch")
44+
add_command("discard", help="discard the current draft")
4545
add_command("finalize", help="apply the current draft to the original branch")
46-
add_command("generate", help="draft a new change from a prompt")
46+
add_command("generate", help="start a new draft from a prompt")
4747

4848
parser.add_option(
4949
"-a",
@@ -61,7 +61,7 @@ def callback(_option, _opt, _value, parser) -> None:
6161
parser.add_option(
6262
"-d",
6363
"--delete",
64-
help="delete the draft after finalizing or discarding",
64+
help="delete draft after finalizing or discarding",
6565
action="store_true",
6666
)
6767
parser.add_option(
@@ -76,6 +76,12 @@ def callback(_option, _opt, _value, parser) -> None:
7676
help="reset index before generating a new draft",
7777
action="store_true",
7878
)
79+
parser.add_option(
80+
"-s",
81+
"--sync",
82+
help="commit prior worktree changes separately",
83+
action="store_true",
84+
)
7985

8086

8187
EDITOR_PLACEHOLDER = """\
@@ -86,7 +92,7 @@ def callback(_option, _opt, _value, parser) -> None:
8692
def main() -> None:
8793
(opts, _args) = parser.parse_args()
8894

89-
manager = Manager.enclosing()
95+
manager = Manager.create(Store.persistent())
9096

9197
command = getattr(opts, "command", "generate")
9298
if command == "generate":

src/git_draft/assistants/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
"""Assistant interfaces and built-in implementations
2+
3+
* https://aider.chat/docs/leaderboards/
4+
"""
5+
16
from typing import Any, Mapping
27

38
from .common import Assistant, Session, Toolbox

src/git_draft/assistants/common.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66

77

88
class Toolbox(Protocol):
9+
# TODO: Something similar to https://aider.chat/docs/repomap.html,
10+
# including inferring the most important files, and allowing returning
11+
# signature-only versions.
12+
913
def list_files(self) -> Sequence[PurePosixPath]: ...
1014
def read_file(self, path: PurePosixPath) -> str: ...
1115
def write_file(self, path: PurePosixPath, contents: str) -> None: ...

src/git_draft/assistants/openai.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ class OpenAIAssistant(Assistant):
8787
8888
* https://platform.openai.com/docs/assistants/tools/function-calling
8989
* https://platform.openai.com/docs/assistants/deep-dive#runs-and-run-steps
90+
* https://platform.openai.com/docs/api-reference/assistants-streaming/events
9091
* https://github.com/openai/openai-python/blob/main/src/openai/resources/beta/threads/runs/runs.py
9192
"""
9293

src/git_draft/common.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
1+
from __future__ import annotations
2+
3+
import contextlib
4+
import functools
15
import os
6+
from pathlib import Path
7+
import random
28
import shutil
9+
import sqlite3
310
import subprocess
11+
import string
412
import sys
513
import tempfile
14+
from typing import ContextManager
15+
import xdg_base_dirs
616

717

818
_default_editors = ["vim", "emacs", "nano"]
@@ -43,3 +53,50 @@ def open_editor(placeholder="") -> str:
4353

4454
with open(temp.name, mode="r") as reader:
4555
return reader.read()
56+
57+
58+
NAMESPACE = "git-draft"
59+
60+
61+
def _ensure_state_home() -> Path:
62+
path = xdg_base_dirs.xdg_state_home() / NAMESPACE
63+
path.mkdir(parents=True, exist_ok=True)
64+
return path
65+
66+
67+
class Store:
68+
_name = "db.v1.sqlite3"
69+
70+
def __init__(self, conn: sqlite3.Connection) -> None:
71+
self._connection = conn
72+
73+
@classmethod
74+
def persistent(cls) -> Store:
75+
path = _ensure_state_home() / cls._name
76+
conn = sqlite3.connect(str(path), autocommit=False)
77+
return cls(conn)
78+
79+
@classmethod
80+
def in_memory(cls) -> Store:
81+
return cls(sqlite3.connect(":memory:"))
82+
83+
def cursor(self) -> ContextManager[sqlite3.Cursor]:
84+
return contextlib.closing(self._connection.cursor())
85+
86+
87+
_query_root = Path(__file__).parent / "queries"
88+
89+
90+
@functools.cache
91+
def sql(name: str) -> str:
92+
path = _query_root / f"{name}.sql"
93+
with open(path) as reader:
94+
return reader.read()
95+
96+
97+
_random = random.Random()
98+
_alphabet = string.ascii_lowercase + string.digits
99+
100+
101+
def random_id(n: int) -> str:
102+
return "".join(_random.choices(_alphabet, k=n))

0 commit comments

Comments
 (0)