jsonpit 0.1.2__tar.gz → 0.1.4__tar.gz
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.
- {jsonpit-0.1.2 → jsonpit-0.1.4}/PKG-INFO +1 -1
- {jsonpit-0.1.2 → jsonpit-0.1.4}/jsonpit/__init__.py +1 -1
- {jsonpit-0.1.2 → jsonpit-0.1.4}/jsonpit/cli.py +35 -5
- {jsonpit-0.1.2 → jsonpit-0.1.4}/jsonpit/store.py +1 -1
- {jsonpit-0.1.2 → jsonpit-0.1.4}/packages/jpit/pyproject.toml +2 -2
- {jsonpit-0.1.2 → jsonpit-0.1.4}/pyproject.toml +1 -1
- {jsonpit-0.1.2 → jsonpit-0.1.4}/tests/test_cli.py +19 -1
- {jsonpit-0.1.2 → jsonpit-0.1.4}/.gitignore +0 -0
- {jsonpit-0.1.2 → jsonpit-0.1.4}/ADELE_SPIRIT.md +0 -0
- {jsonpit-0.1.2 → jsonpit-0.1.4}/GEMINI.md +0 -0
- {jsonpit-0.1.2 → jsonpit-0.1.4}/README.md +0 -0
- {jsonpit-0.1.2 → jsonpit-0.1.4}/jsonpit/canonical.py +0 -0
- {jsonpit-0.1.2 → jsonpit-0.1.4}/jsonpit/changes.py +0 -0
- {jsonpit-0.1.2 → jsonpit-0.1.4}/jsonpit/config.py +0 -0
- {jsonpit-0.1.2 → jsonpit-0.1.4}/jsonpit/exceptions.py +0 -0
- {jsonpit-0.1.2 → jsonpit-0.1.4}/jsonpit/flags.py +0 -0
- {jsonpit-0.1.2 → jsonpit-0.1.4}/jsonpit/fs.py +0 -0
- {jsonpit-0.1.2 → jsonpit-0.1.4}/jsonpit/history.py +0 -0
- {jsonpit-0.1.2 → jsonpit-0.1.4}/jsonpit/item.py +0 -0
- {jsonpit-0.1.2 → jsonpit-0.1.4}/packages/jpit/README.md +0 -0
- {jsonpit-0.1.2 → jsonpit-0.1.4}/packages/jpit/jpit/__init__.py +0 -0
- {jsonpit-0.1.2 → jsonpit-0.1.4}/tests/fixtures/Person.pit +0 -0
- {jsonpit-0.1.2 → jsonpit-0.1.4}/tests/run_all.py +0 -0
- {jsonpit-0.1.2 → jsonpit-0.1.4}/tests/test_canonical.py +0 -0
- {jsonpit-0.1.2 → jsonpit-0.1.4}/tests/test_change_file.py +0 -0
- {jsonpit-0.1.2 → jsonpit-0.1.4}/tests/test_csharp_compatibility.py +0 -0
- {jsonpit-0.1.2 → jsonpit-0.1.4}/tests/test_delete_property_projection.py +0 -0
- {jsonpit-0.1.2 → jsonpit-0.1.4}/tests/test_equal_timestamp_ordering.py +0 -0
- {jsonpit-0.1.2 → jsonpit-0.1.4}/tests/test_master_ticket.py +0 -0
- {jsonpit-0.1.2 → jsonpit-0.1.4}/tests/test_pit_item.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: jsonpit
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: Daemon-free distributed storage over Cloud Drives for developers and AI agents — multi-process, immutable history, zero dependencies.
|
|
5
5
|
Author: Dr. Rainer Burkhardt
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -96,9 +96,14 @@ def discover_pits(
|
|
|
96
96
|
if discovered:
|
|
97
97
|
return discovered
|
|
98
98
|
|
|
99
|
-
# Fallback single pit target
|
|
100
|
-
|
|
101
|
-
|
|
99
|
+
# Fallback single pit target (only return if it physically exists)
|
|
100
|
+
try:
|
|
101
|
+
p_dir, p_name = resolve_pit_target(target, cloud=cloud, root=root)
|
|
102
|
+
if (p_dir / f"{p_name}.pit").is_file() or p_dir.is_dir():
|
|
103
|
+
return [(p_dir, p_name)]
|
|
104
|
+
except Exception:
|
|
105
|
+
pass
|
|
106
|
+
return []
|
|
102
107
|
|
|
103
108
|
|
|
104
109
|
def cmd_grep(args: argparse.Namespace) -> int:
|
|
@@ -213,6 +218,10 @@ def cmd_get(args: argparse.Namespace) -> int:
|
|
|
213
218
|
read_only=True,
|
|
214
219
|
unflagged=True,
|
|
215
220
|
) as pit:
|
|
221
|
+
if not pit.canonical_file.is_file():
|
|
222
|
+
sys.stderr.write(f"[jpit] Pit '{args.pit}' not found at {pit.pit_dir}.\n")
|
|
223
|
+
return 1
|
|
224
|
+
|
|
216
225
|
item = pit.get(args.id, at=at_time, with_deleted=args.with_deleted)
|
|
217
226
|
if item is None:
|
|
218
227
|
sys.stderr.write(f"[jpit] Entity '{args.id}' not found in Pit '{pit.pit_name}'.\n")
|
|
@@ -235,6 +244,10 @@ def cmd_history(args: argparse.Namespace) -> int:
|
|
|
235
244
|
read_only=True,
|
|
236
245
|
unflagged=True,
|
|
237
246
|
) as pit:
|
|
247
|
+
if not pit.canonical_file.is_file():
|
|
248
|
+
sys.stderr.write(f"[jpit] Pit '{args.pit}' not found at {pit.pit_dir}.\n")
|
|
249
|
+
return 1
|
|
250
|
+
|
|
238
251
|
pit_items = pit._historic_items.get(args.id)
|
|
239
252
|
if pit_items is None:
|
|
240
253
|
sys.stderr.write(f"[jpit] No history for '{args.id}' in Pit '{pit.pit_name}'.\n")
|
|
@@ -258,6 +271,10 @@ def cmd_list(args: argparse.Namespace) -> int:
|
|
|
258
271
|
read_only=True,
|
|
259
272
|
unflagged=True,
|
|
260
273
|
) as pit:
|
|
274
|
+
if not pit.canonical_file.is_file():
|
|
275
|
+
sys.stderr.write(f"[jpit] Pit '{args.pit}' not found at {pit.pit_dir}.\n")
|
|
276
|
+
return 1
|
|
277
|
+
|
|
261
278
|
keys = list(pit.keys())
|
|
262
279
|
if args.json:
|
|
263
280
|
entities = [pit[k].to_dict() for k in keys]
|
|
@@ -442,8 +459,16 @@ def cmd_status(args: argparse.Namespace) -> int:
|
|
|
442
459
|
|
|
443
460
|
def build_parser() -> argparse.ArgumentParser:
|
|
444
461
|
common_parser = argparse.ArgumentParser(add_help=False)
|
|
445
|
-
common_parser.add_argument(
|
|
446
|
-
|
|
462
|
+
common_parser.add_argument(
|
|
463
|
+
"-c", "--cloud",
|
|
464
|
+
default=argparse.SUPPRESS,
|
|
465
|
+
help="Cloud drive (OneDrive, Dropbox, etc.)",
|
|
466
|
+
)
|
|
467
|
+
common_parser.add_argument(
|
|
468
|
+
"-r", "--root",
|
|
469
|
+
default=argparse.SUPPRESS,
|
|
470
|
+
help="Root folder / tenant (e.g. AIA, AfricaStage)",
|
|
471
|
+
)
|
|
447
472
|
|
|
448
473
|
parser = argparse.ArgumentParser(
|
|
449
474
|
prog="jpit",
|
|
@@ -532,6 +557,11 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
532
557
|
parser = build_parser()
|
|
533
558
|
args = parser.parse_args(argv)
|
|
534
559
|
|
|
560
|
+
if not hasattr(args, "cloud"):
|
|
561
|
+
args.cloud = "OneDrive"
|
|
562
|
+
if not hasattr(args, "root"):
|
|
563
|
+
args.root = None
|
|
564
|
+
|
|
535
565
|
dispatch = {
|
|
536
566
|
"grep": cmd_grep,
|
|
537
567
|
"get": cmd_get,
|
|
@@ -63,7 +63,7 @@ class JsonPitBase:
|
|
|
63
63
|
backup: bool = False,
|
|
64
64
|
unflagged: bool = False,
|
|
65
65
|
) -> None:
|
|
66
|
-
self.pit_dir =
|
|
66
|
+
self.pit_dir = Path(pit_dir)
|
|
67
67
|
self.pit_name = pit_name
|
|
68
68
|
self.canonical_file = self.pit_dir / f"{pit_name}.pit"
|
|
69
69
|
self.subscriber = subscriber
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "jpit"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.4"
|
|
8
8
|
description = "CLI companion and alias for jsonpit — daemon-free distributed storage over Cloud Drives."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.12"
|
|
@@ -22,7 +22,7 @@ classifiers = [
|
|
|
22
22
|
"Topic :: Database :: Database Engines/Servers",
|
|
23
23
|
]
|
|
24
24
|
dependencies = [
|
|
25
|
-
"jsonpit>=0.1.
|
|
25
|
+
"jsonpit>=0.1.4",
|
|
26
26
|
]
|
|
27
27
|
|
|
28
28
|
[project.scripts]
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "jsonpit"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.4"
|
|
8
8
|
description = "Daemon-free distributed storage over Cloud Drives for developers and AI agents — multi-process, immutable history, zero dependencies."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.12"
|
|
@@ -127,7 +127,8 @@ def test_cli_version_flag(capsys: Any = None) -> None:
|
|
|
127
127
|
main(["-v"])
|
|
128
128
|
except SystemExit as ex:
|
|
129
129
|
assert ex.code == 0
|
|
130
|
-
|
|
130
|
+
from jsonpit import __version__
|
|
131
|
+
assert __version__ in f_out.getvalue()
|
|
131
132
|
|
|
132
133
|
|
|
133
134
|
def test_cli_flexible_option_placement(capsys: Any = None) -> None:
|
|
@@ -144,3 +145,20 @@ def test_cli_flexible_option_placement(capsys: Any = None) -> None:
|
|
|
144
145
|
code2 = main(["grep", "Adele", str(FIXTURE_PATH), "--root", "AIA"])
|
|
145
146
|
assert code2 == 0
|
|
146
147
|
assert "Adele" in f_out2.getvalue()
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def test_read_operation_never_creates_directories(capsys: Any = None) -> None:
|
|
151
|
+
with tempfile.TemporaryDirectory() as tmp_dir:
|
|
152
|
+
non_existent_pit = Path(tmp_dir) / "GhostPit"
|
|
153
|
+
assert not non_existent_pit.exists()
|
|
154
|
+
|
|
155
|
+
f_err = io.StringIO()
|
|
156
|
+
with contextlib.redirect_stderr(f_err):
|
|
157
|
+
code = main(["get", str(non_existent_pit), "GhostEntity"])
|
|
158
|
+
assert code == 1
|
|
159
|
+
assert not non_existent_pit.exists(), "Read operation must never create ghost directory!"
|
|
160
|
+
|
|
161
|
+
with contextlib.redirect_stderr(f_err):
|
|
162
|
+
code = main(["grep", "Ghost", str(non_existent_pit)])
|
|
163
|
+
assert code == 1
|
|
164
|
+
assert not non_existent_pit.exists(), "Grep must never create ghost directory!"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|