pg0-embedded 0.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pg0/__init__.py +538 -0
- pg0/py.typed +0 -0
- pg0_embedded-0.1.1.dist-info/METADATA +143 -0
- pg0_embedded-0.1.1.dist-info/RECORD +5 -0
- pg0_embedded-0.1.1.dist-info/WHEEL +4 -0
pg0/__init__.py
ADDED
|
@@ -0,0 +1,538 @@
|
|
|
1
|
+
"""
|
|
2
|
+
pg0 - Embedded PostgreSQL for Python
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
from pg0 import Pg0
|
|
6
|
+
|
|
7
|
+
# Start PostgreSQL
|
|
8
|
+
pg = Pg0()
|
|
9
|
+
pg.start()
|
|
10
|
+
print(pg.uri)
|
|
11
|
+
pg.stop()
|
|
12
|
+
|
|
13
|
+
# Or use context manager
|
|
14
|
+
with Pg0() as pg:
|
|
15
|
+
print(pg.uri)
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import json
|
|
21
|
+
import os
|
|
22
|
+
import platform
|
|
23
|
+
import shutil
|
|
24
|
+
import stat
|
|
25
|
+
import subprocess
|
|
26
|
+
import sys
|
|
27
|
+
import tempfile
|
|
28
|
+
import urllib.request
|
|
29
|
+
from dataclasses import dataclass
|
|
30
|
+
from pathlib import Path
|
|
31
|
+
from typing import Optional
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
__version__ = "0.1.0"
|
|
35
|
+
|
|
36
|
+
# GitHub repo for pg0 releases
|
|
37
|
+
PG0_REPO = "vectorize-io/pg0"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class Pg0Error(Exception):
|
|
41
|
+
"""Base exception for pg0 errors."""
|
|
42
|
+
pass
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class Pg0NotFoundError(Pg0Error):
|
|
46
|
+
"""pg0 binary not found and could not be installed."""
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class Pg0NotRunningError(Pg0Error):
|
|
51
|
+
"""PostgreSQL instance is not running."""
|
|
52
|
+
pass
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class Pg0AlreadyRunningError(Pg0Error):
|
|
56
|
+
"""PostgreSQL instance is already running."""
|
|
57
|
+
pass
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@dataclass
|
|
61
|
+
class InstanceInfo:
|
|
62
|
+
"""Information about a PostgreSQL instance."""
|
|
63
|
+
name: str
|
|
64
|
+
running: bool
|
|
65
|
+
pid: Optional[int] = None
|
|
66
|
+
port: Optional[int] = None
|
|
67
|
+
version: Optional[str] = None
|
|
68
|
+
username: Optional[str] = None
|
|
69
|
+
database: Optional[str] = None
|
|
70
|
+
data_dir: Optional[str] = None
|
|
71
|
+
uri: Optional[str] = None
|
|
72
|
+
|
|
73
|
+
@classmethod
|
|
74
|
+
def from_dict(cls, data: dict) -> "InstanceInfo":
|
|
75
|
+
return cls(
|
|
76
|
+
name=data.get("name", "default"),
|
|
77
|
+
running=data.get("running", False),
|
|
78
|
+
pid=data.get("pid"),
|
|
79
|
+
port=data.get("port"),
|
|
80
|
+
version=data.get("version"),
|
|
81
|
+
username=data.get("username"),
|
|
82
|
+
database=data.get("database"),
|
|
83
|
+
data_dir=data.get("data_dir"),
|
|
84
|
+
uri=data.get("uri"),
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _get_install_dir() -> Path:
|
|
89
|
+
"""Get the directory where pg0 binary should be installed."""
|
|
90
|
+
# Use ~/.local/bin on Unix, or a pg0-specific dir
|
|
91
|
+
if sys.platform == "win32":
|
|
92
|
+
base = Path(os.environ.get("LOCALAPPDATA", Path.home() / "AppData" / "Local"))
|
|
93
|
+
return base / "pg0" / "bin"
|
|
94
|
+
else:
|
|
95
|
+
return Path.home() / ".local" / "bin"
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _get_platform() -> str:
|
|
99
|
+
"""Get the platform string for downloading the correct binary."""
|
|
100
|
+
system = platform.system().lower()
|
|
101
|
+
machine = platform.machine().lower()
|
|
102
|
+
|
|
103
|
+
if system == "darwin":
|
|
104
|
+
# macOS - only Apple Silicon supported, Intel uses Rosetta
|
|
105
|
+
return "darwin-aarch64"
|
|
106
|
+
elif system == "linux":
|
|
107
|
+
# Detect architecture
|
|
108
|
+
if machine in ("x86_64", "amd64"):
|
|
109
|
+
arch_str = "x86_64"
|
|
110
|
+
elif machine in ("aarch64", "arm64"):
|
|
111
|
+
arch_str = "aarch64"
|
|
112
|
+
else:
|
|
113
|
+
raise Pg0NotFoundError(f"Unsupported Linux architecture: {machine}")
|
|
114
|
+
|
|
115
|
+
# Detect libc (musl vs glibc)
|
|
116
|
+
# Check for musl by looking for the musl loader
|
|
117
|
+
import subprocess
|
|
118
|
+
try:
|
|
119
|
+
result = subprocess.run(
|
|
120
|
+
["ldd", "--version"],
|
|
121
|
+
capture_output=True,
|
|
122
|
+
text=True,
|
|
123
|
+
timeout=5,
|
|
124
|
+
)
|
|
125
|
+
output = result.stdout + result.stderr
|
|
126
|
+
if "musl" in output.lower():
|
|
127
|
+
return f"linux-{arch_str}-musl"
|
|
128
|
+
except (FileNotFoundError, subprocess.TimeoutExpired):
|
|
129
|
+
pass
|
|
130
|
+
|
|
131
|
+
# Check for musl loader file
|
|
132
|
+
musl_loaders = [
|
|
133
|
+
f"/lib/ld-musl-{arch_str}.so.1",
|
|
134
|
+
"/lib/ld-musl-x86_64.so.1",
|
|
135
|
+
"/lib/ld-musl-aarch64.so.1",
|
|
136
|
+
]
|
|
137
|
+
for loader in musl_loaders:
|
|
138
|
+
if Path(loader).exists():
|
|
139
|
+
return f"linux-{arch_str}-musl"
|
|
140
|
+
|
|
141
|
+
# Default to glibc
|
|
142
|
+
return f"linux-{arch_str}-gnu"
|
|
143
|
+
elif system == "windows":
|
|
144
|
+
return "windows-x86_64"
|
|
145
|
+
else:
|
|
146
|
+
raise Pg0NotFoundError(f"Unsupported platform: {system}")
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _get_latest_version() -> str:
|
|
150
|
+
"""Get the latest pg0 version from GitHub."""
|
|
151
|
+
url = f"https://api.github.com/repos/{PG0_REPO}/releases/latest"
|
|
152
|
+
try:
|
|
153
|
+
with urllib.request.urlopen(url, timeout=30) as response:
|
|
154
|
+
data = json.loads(response.read().decode())
|
|
155
|
+
return data["tag_name"]
|
|
156
|
+
except Exception as e:
|
|
157
|
+
raise Pg0NotFoundError(f"Failed to fetch latest version: {e}")
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def install(version: Optional[str] = None, force: bool = False) -> Path:
|
|
161
|
+
"""
|
|
162
|
+
Install the pg0 binary.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
version: Version to install (default: latest)
|
|
166
|
+
force: Force reinstall even if already installed
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
Path to the installed binary
|
|
170
|
+
"""
|
|
171
|
+
install_dir = _get_install_dir()
|
|
172
|
+
binary_name = "pg0.exe" if sys.platform == "win32" else "pg0"
|
|
173
|
+
binary_path = install_dir / binary_name
|
|
174
|
+
|
|
175
|
+
# Check if already installed
|
|
176
|
+
if binary_path.exists() and not force:
|
|
177
|
+
return binary_path
|
|
178
|
+
|
|
179
|
+
# Get version
|
|
180
|
+
if version is None:
|
|
181
|
+
version = _get_latest_version()
|
|
182
|
+
|
|
183
|
+
# Get platform
|
|
184
|
+
plat = _get_platform()
|
|
185
|
+
|
|
186
|
+
# Build download URL
|
|
187
|
+
ext = ".exe" if sys.platform == "win32" else ""
|
|
188
|
+
filename = f"pg0-{plat}{ext}"
|
|
189
|
+
url = f"https://github.com/{PG0_REPO}/releases/download/{version}/{filename}"
|
|
190
|
+
|
|
191
|
+
print(f"Installing pg0 {version}...")
|
|
192
|
+
|
|
193
|
+
# Create install directory
|
|
194
|
+
install_dir.mkdir(parents=True, exist_ok=True)
|
|
195
|
+
|
|
196
|
+
# Download binary
|
|
197
|
+
try:
|
|
198
|
+
with tempfile.NamedTemporaryFile(delete=False) as tmp:
|
|
199
|
+
tmp_path = Path(tmp.name)
|
|
200
|
+
|
|
201
|
+
with urllib.request.urlopen(url, timeout=120) as response:
|
|
202
|
+
tmp_path.write_bytes(response.read())
|
|
203
|
+
|
|
204
|
+
# Move to install location
|
|
205
|
+
shutil.move(str(tmp_path), str(binary_path))
|
|
206
|
+
|
|
207
|
+
# Make executable on Unix
|
|
208
|
+
if sys.platform != "win32":
|
|
209
|
+
binary_path.chmod(binary_path.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
|
|
210
|
+
|
|
211
|
+
print(f"Installed pg0 to {binary_path}")
|
|
212
|
+
return binary_path
|
|
213
|
+
|
|
214
|
+
except Exception as e:
|
|
215
|
+
# Cleanup
|
|
216
|
+
if tmp_path.exists():
|
|
217
|
+
tmp_path.unlink()
|
|
218
|
+
raise Pg0NotFoundError(f"Failed to install pg0: {e}")
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def _find_pg0() -> str:
|
|
222
|
+
"""Find the pg0 binary, installing if necessary."""
|
|
223
|
+
# Check PATH first
|
|
224
|
+
path = shutil.which("pg0")
|
|
225
|
+
if path:
|
|
226
|
+
return path
|
|
227
|
+
|
|
228
|
+
# Check our install location
|
|
229
|
+
install_dir = _get_install_dir()
|
|
230
|
+
binary_name = "pg0.exe" if sys.platform == "win32" else "pg0"
|
|
231
|
+
binary_path = install_dir / binary_name
|
|
232
|
+
|
|
233
|
+
if binary_path.exists():
|
|
234
|
+
return str(binary_path)
|
|
235
|
+
|
|
236
|
+
# Auto-install
|
|
237
|
+
installed_path = install(version=None)
|
|
238
|
+
return str(installed_path)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def _run_pg0(*args: str, check: bool = True) -> subprocess.CompletedProcess:
|
|
242
|
+
"""Run a pg0 command."""
|
|
243
|
+
pg0_path = _find_pg0()
|
|
244
|
+
try:
|
|
245
|
+
result = subprocess.run(
|
|
246
|
+
[pg0_path, *args],
|
|
247
|
+
capture_output=True,
|
|
248
|
+
text=True,
|
|
249
|
+
)
|
|
250
|
+
if check and result.returncode != 0:
|
|
251
|
+
stderr = result.stderr.strip()
|
|
252
|
+
if "already running" in stderr.lower():
|
|
253
|
+
raise Pg0AlreadyRunningError(stderr)
|
|
254
|
+
elif "no running instance" in stderr.lower() or "not running" in stderr.lower():
|
|
255
|
+
raise Pg0NotRunningError(stderr)
|
|
256
|
+
else:
|
|
257
|
+
raise Pg0Error(stderr or f"pg0 command failed with code {result.returncode}")
|
|
258
|
+
return result
|
|
259
|
+
except FileNotFoundError:
|
|
260
|
+
raise Pg0NotFoundError("pg0 binary not found")
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
class Pg0:
|
|
264
|
+
"""
|
|
265
|
+
Embedded PostgreSQL instance.
|
|
266
|
+
|
|
267
|
+
Args:
|
|
268
|
+
name: Instance name (allows multiple instances)
|
|
269
|
+
port: Port to listen on
|
|
270
|
+
username: Database username
|
|
271
|
+
password: Database password
|
|
272
|
+
database: Database name
|
|
273
|
+
data_dir: Custom data directory
|
|
274
|
+
config: Dict of PostgreSQL configuration options
|
|
275
|
+
|
|
276
|
+
Example:
|
|
277
|
+
# Simple usage
|
|
278
|
+
pg = Pg0()
|
|
279
|
+
pg.start()
|
|
280
|
+
print(pg.uri)
|
|
281
|
+
pg.stop()
|
|
282
|
+
|
|
283
|
+
# Context manager
|
|
284
|
+
with Pg0(port=5433, database="myapp") as pg:
|
|
285
|
+
print(pg.uri)
|
|
286
|
+
|
|
287
|
+
# Custom config
|
|
288
|
+
pg = Pg0(config={"shared_buffers": "512MB"})
|
|
289
|
+
"""
|
|
290
|
+
|
|
291
|
+
def __init__(
|
|
292
|
+
self,
|
|
293
|
+
name: str = "default",
|
|
294
|
+
port: int = 5432,
|
|
295
|
+
username: str = "postgres",
|
|
296
|
+
password: str = "postgres",
|
|
297
|
+
database: str = "postgres",
|
|
298
|
+
data_dir: Optional[str] = None,
|
|
299
|
+
config: Optional[dict[str, str]] = None,
|
|
300
|
+
):
|
|
301
|
+
self.name = name
|
|
302
|
+
self.port = port
|
|
303
|
+
self.username = username
|
|
304
|
+
self.password = password
|
|
305
|
+
self.database = database
|
|
306
|
+
self.data_dir = data_dir
|
|
307
|
+
self.config = config or {}
|
|
308
|
+
|
|
309
|
+
def start(self) -> InstanceInfo:
|
|
310
|
+
"""
|
|
311
|
+
Start the PostgreSQL instance.
|
|
312
|
+
|
|
313
|
+
Returns:
|
|
314
|
+
InstanceInfo with connection details
|
|
315
|
+
|
|
316
|
+
Raises:
|
|
317
|
+
Pg0AlreadyRunningError: If instance is already running
|
|
318
|
+
Pg0Error: If start fails
|
|
319
|
+
"""
|
|
320
|
+
args = [
|
|
321
|
+
"start",
|
|
322
|
+
"--name", self.name,
|
|
323
|
+
"--port", str(self.port),
|
|
324
|
+
"--username", self.username,
|
|
325
|
+
"--password", self.password,
|
|
326
|
+
"--database", self.database,
|
|
327
|
+
]
|
|
328
|
+
|
|
329
|
+
if self.data_dir:
|
|
330
|
+
args.extend(["--data-dir", self.data_dir])
|
|
331
|
+
|
|
332
|
+
for key, value in self.config.items():
|
|
333
|
+
args.extend(["-c", f"{key}={value}"])
|
|
334
|
+
|
|
335
|
+
_run_pg0(*args)
|
|
336
|
+
return self.info()
|
|
337
|
+
|
|
338
|
+
def stop(self) -> None:
|
|
339
|
+
"""
|
|
340
|
+
Stop the PostgreSQL instance.
|
|
341
|
+
|
|
342
|
+
Note: Does not raise an error if the instance is not running.
|
|
343
|
+
"""
|
|
344
|
+
_run_pg0("stop", "--name", self.name, check=False)
|
|
345
|
+
|
|
346
|
+
def drop(self, force: bool = True) -> None:
|
|
347
|
+
"""
|
|
348
|
+
Drop the PostgreSQL instance (stop if running, delete all data).
|
|
349
|
+
|
|
350
|
+
Args:
|
|
351
|
+
force: Skip confirmation prompt (default True for programmatic use)
|
|
352
|
+
|
|
353
|
+
Warning:
|
|
354
|
+
This permanently deletes all data for this instance!
|
|
355
|
+
"""
|
|
356
|
+
args = ["drop", "--name", self.name]
|
|
357
|
+
if force:
|
|
358
|
+
args.append("--force")
|
|
359
|
+
_run_pg0(*args, check=False)
|
|
360
|
+
|
|
361
|
+
def info(self) -> InstanceInfo:
|
|
362
|
+
"""
|
|
363
|
+
Get information about the PostgreSQL instance.
|
|
364
|
+
|
|
365
|
+
Returns:
|
|
366
|
+
InstanceInfo with current status and connection details
|
|
367
|
+
"""
|
|
368
|
+
result = _run_pg0("info", "--name", self.name, "-o", "json", check=False)
|
|
369
|
+
data = json.loads(result.stdout)
|
|
370
|
+
return InstanceInfo.from_dict(data)
|
|
371
|
+
|
|
372
|
+
@property
|
|
373
|
+
def uri(self) -> Optional[str]:
|
|
374
|
+
"""Get the connection URI if running."""
|
|
375
|
+
return self.info().uri
|
|
376
|
+
|
|
377
|
+
@property
|
|
378
|
+
def running(self) -> bool:
|
|
379
|
+
"""Check if the instance is running."""
|
|
380
|
+
return self.info().running
|
|
381
|
+
|
|
382
|
+
def psql(self, *args: str) -> subprocess.CompletedProcess:
|
|
383
|
+
"""
|
|
384
|
+
Run psql with the given arguments.
|
|
385
|
+
|
|
386
|
+
Args:
|
|
387
|
+
*args: Arguments to pass to psql (e.g., "-c", "SELECT 1")
|
|
388
|
+
|
|
389
|
+
Returns:
|
|
390
|
+
CompletedProcess with stdout/stderr
|
|
391
|
+
|
|
392
|
+
Example:
|
|
393
|
+
result = pg.psql("-c", "SELECT version();")
|
|
394
|
+
print(result.stdout)
|
|
395
|
+
"""
|
|
396
|
+
return _run_pg0("psql", "--name", self.name, *args)
|
|
397
|
+
|
|
398
|
+
def execute(self, sql: str) -> str:
|
|
399
|
+
"""
|
|
400
|
+
Execute a SQL command and return the output.
|
|
401
|
+
|
|
402
|
+
Args:
|
|
403
|
+
sql: SQL command to execute
|
|
404
|
+
|
|
405
|
+
Returns:
|
|
406
|
+
Command output as string
|
|
407
|
+
|
|
408
|
+
Example:
|
|
409
|
+
output = pg.execute("SELECT version();")
|
|
410
|
+
"""
|
|
411
|
+
result = self.psql("-c", sql)
|
|
412
|
+
return result.stdout
|
|
413
|
+
|
|
414
|
+
def __enter__(self) -> "Pg0":
|
|
415
|
+
"""Context manager entry - starts PostgreSQL."""
|
|
416
|
+
self.start()
|
|
417
|
+
return self
|
|
418
|
+
|
|
419
|
+
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
|
|
420
|
+
"""Context manager exit - stops PostgreSQL."""
|
|
421
|
+
try:
|
|
422
|
+
self.stop()
|
|
423
|
+
except Pg0NotRunningError:
|
|
424
|
+
pass
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
def list_instances() -> list[InstanceInfo]:
|
|
428
|
+
"""
|
|
429
|
+
List all pg0 instances.
|
|
430
|
+
|
|
431
|
+
Returns:
|
|
432
|
+
List of InstanceInfo for all known instances
|
|
433
|
+
"""
|
|
434
|
+
result = _run_pg0("list", "-o", "json", check=False)
|
|
435
|
+
data = json.loads(result.stdout)
|
|
436
|
+
return [InstanceInfo.from_dict(item) for item in data]
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
def start(
|
|
440
|
+
name: str = "default",
|
|
441
|
+
port: int = 5432,
|
|
442
|
+
username: str = "postgres",
|
|
443
|
+
password: str = "postgres",
|
|
444
|
+
database: str = "postgres",
|
|
445
|
+
**config: str,
|
|
446
|
+
) -> InstanceInfo:
|
|
447
|
+
"""
|
|
448
|
+
Start a PostgreSQL instance (convenience function).
|
|
449
|
+
|
|
450
|
+
Args:
|
|
451
|
+
name: Instance name
|
|
452
|
+
port: Port to listen on
|
|
453
|
+
username: Database username
|
|
454
|
+
password: Database password
|
|
455
|
+
database: Database name
|
|
456
|
+
**config: PostgreSQL configuration options
|
|
457
|
+
|
|
458
|
+
Returns:
|
|
459
|
+
InstanceInfo with connection details
|
|
460
|
+
|
|
461
|
+
Example:
|
|
462
|
+
info = pg0.start(port=5433, shared_buffers="512MB")
|
|
463
|
+
print(info.uri)
|
|
464
|
+
"""
|
|
465
|
+
pg = Pg0(
|
|
466
|
+
name=name,
|
|
467
|
+
port=port,
|
|
468
|
+
username=username,
|
|
469
|
+
password=password,
|
|
470
|
+
database=database,
|
|
471
|
+
config=config,
|
|
472
|
+
)
|
|
473
|
+
return pg.start()
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
def stop(name: str = "default") -> None:
|
|
477
|
+
"""
|
|
478
|
+
Stop a PostgreSQL instance (convenience function).
|
|
479
|
+
|
|
480
|
+
Args:
|
|
481
|
+
name: Instance name to stop
|
|
482
|
+
"""
|
|
483
|
+
_run_pg0("stop", "--name", name, check=False)
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
def drop(name: str = "default", force: bool = True) -> None:
|
|
487
|
+
"""
|
|
488
|
+
Drop a PostgreSQL instance (convenience function).
|
|
489
|
+
|
|
490
|
+
Stops the instance if running and deletes all data.
|
|
491
|
+
|
|
492
|
+
Args:
|
|
493
|
+
name: Instance name to drop
|
|
494
|
+
force: Skip confirmation prompt (default True for programmatic use)
|
|
495
|
+
|
|
496
|
+
Warning:
|
|
497
|
+
This permanently deletes all data for this instance!
|
|
498
|
+
"""
|
|
499
|
+
args = ["drop", "--name", name]
|
|
500
|
+
if force:
|
|
501
|
+
args.append("--force")
|
|
502
|
+
_run_pg0(*args, check=False)
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
def info(name: str = "default") -> InstanceInfo:
|
|
506
|
+
"""
|
|
507
|
+
Get information about a PostgreSQL instance (convenience function).
|
|
508
|
+
|
|
509
|
+
Args:
|
|
510
|
+
name: Instance name
|
|
511
|
+
|
|
512
|
+
Returns:
|
|
513
|
+
InstanceInfo with current status
|
|
514
|
+
"""
|
|
515
|
+
result = _run_pg0("info", "--name", name, "-o", "json", check=False)
|
|
516
|
+
data = json.loads(result.stdout)
|
|
517
|
+
return InstanceInfo.from_dict(data)
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
# Keep PostgreSQL as alias for backwards compatibility
|
|
521
|
+
PostgreSQL = Pg0
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
__all__ = [
|
|
525
|
+
"Pg0",
|
|
526
|
+
"PostgreSQL", # alias
|
|
527
|
+
"InstanceInfo",
|
|
528
|
+
"Pg0Error",
|
|
529
|
+
"Pg0NotFoundError",
|
|
530
|
+
"Pg0NotRunningError",
|
|
531
|
+
"Pg0AlreadyRunningError",
|
|
532
|
+
"install",
|
|
533
|
+
"list_instances",
|
|
534
|
+
"start",
|
|
535
|
+
"stop",
|
|
536
|
+
"drop",
|
|
537
|
+
"info",
|
|
538
|
+
]
|
pg0/py.typed
ADDED
|
File without changes
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pg0-embedded
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Python API for pg0 - embedded PostgreSQL
|
|
5
|
+
Project-URL: Homepage, https://github.com/vectorize-io/pg0
|
|
6
|
+
Project-URL: Repository, https://github.com/vectorize-io/pg0
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Keywords: database,embedded,pgvector,postgresql
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Requires-Python: >=3.8
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# pg0 - Embedded PostgreSQL for Python
|
|
22
|
+
|
|
23
|
+
Zero-config PostgreSQL with pgvector support. Just `pip install` and go.
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install pg0-embedded
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Quick Start
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
from pg0 import Pg0
|
|
35
|
+
|
|
36
|
+
# Start PostgreSQL (auto-installs on first run)
|
|
37
|
+
pg = Pg0()
|
|
38
|
+
pg.start()
|
|
39
|
+
|
|
40
|
+
print(pg.uri) # postgresql://postgres:postgres@localhost:5432/postgres
|
|
41
|
+
|
|
42
|
+
pg.stop()
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Context Manager
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
from pg0 import Pg0
|
|
49
|
+
|
|
50
|
+
with Pg0() as pg:
|
|
51
|
+
print(pg.uri)
|
|
52
|
+
pg.execute("CREATE EXTENSION IF NOT EXISTS vector")
|
|
53
|
+
pg.execute("SELECT version()")
|
|
54
|
+
# Automatically stopped
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Custom Configuration
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
from pg0 import Pg0
|
|
61
|
+
|
|
62
|
+
pg = Pg0(
|
|
63
|
+
port=5433,
|
|
64
|
+
username="myuser",
|
|
65
|
+
password="mypass",
|
|
66
|
+
database="mydb",
|
|
67
|
+
config={
|
|
68
|
+
"shared_buffers": "512MB",
|
|
69
|
+
"maintenance_work_mem": "1GB",
|
|
70
|
+
}
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
with pg:
|
|
74
|
+
print(pg.uri)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Multiple Instances
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
from pg0 import Pg0, list_instances
|
|
81
|
+
|
|
82
|
+
# Run multiple PostgreSQL instances
|
|
83
|
+
app = Pg0(name="app", port=5432)
|
|
84
|
+
test = Pg0(name="test", port=5433)
|
|
85
|
+
|
|
86
|
+
app.start()
|
|
87
|
+
test.start()
|
|
88
|
+
|
|
89
|
+
for instance in list_instances():
|
|
90
|
+
print(f"{instance.name}: {instance.uri}")
|
|
91
|
+
|
|
92
|
+
app.stop()
|
|
93
|
+
test.stop()
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## API Reference
|
|
97
|
+
|
|
98
|
+
### Pg0 Class
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
pg = Pg0(
|
|
102
|
+
name="default", # Instance name
|
|
103
|
+
port=5432, # Port
|
|
104
|
+
username="postgres", # Username
|
|
105
|
+
password="postgres", # Password
|
|
106
|
+
database="postgres", # Database
|
|
107
|
+
data_dir=None, # Custom data directory
|
|
108
|
+
config={}, # PostgreSQL config options
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
pg.start() # Start PostgreSQL -> InstanceInfo
|
|
112
|
+
pg.stop() # Stop PostgreSQL
|
|
113
|
+
pg.info() # Get instance info -> InstanceInfo
|
|
114
|
+
pg.uri # Connection URI (property)
|
|
115
|
+
pg.running # Is running (property)
|
|
116
|
+
pg.execute(sql) # Execute SQL -> str
|
|
117
|
+
pg.psql(*args) # Run psql command
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Module Functions
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
import pg0
|
|
124
|
+
|
|
125
|
+
pg0.start(port=5432, ...) # Start instance -> InstanceInfo
|
|
126
|
+
pg0.stop(name="default") # Stop instance
|
|
127
|
+
pg0.info(name="default") # Get info -> InstanceInfo
|
|
128
|
+
pg0.list_instances() # List all -> [InstanceInfo]
|
|
129
|
+
pg0.install(version=None) # Install pg0 binary
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### InstanceInfo
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
info.name # Instance name
|
|
136
|
+
info.running # Is running
|
|
137
|
+
info.pid # Process ID
|
|
138
|
+
info.port # Port
|
|
139
|
+
info.uri # Connection URI
|
|
140
|
+
info.username # Username
|
|
141
|
+
info.database # Database
|
|
142
|
+
info.data_dir # Data directory
|
|
143
|
+
```
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
pg0/__init__.py,sha256=C8i9Lat5e2c8RD70yuzamyYt_cOZY6urGxSRufZTZ8U,14282
|
|
2
|
+
pg0/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
pg0_embedded-0.1.1.dist-info/METADATA,sha256=pya-8UDnu6xWrLhgrZ9dQfRVi-7AQ3s1fTb_6lQ6hAA,3200
|
|
4
|
+
pg0_embedded-0.1.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
5
|
+
pg0_embedded-0.1.1.dist-info/RECORD,,
|