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.
Files changed (30) hide show
  1. {jsonpit-0.1.2 → jsonpit-0.1.4}/PKG-INFO +1 -1
  2. {jsonpit-0.1.2 → jsonpit-0.1.4}/jsonpit/__init__.py +1 -1
  3. {jsonpit-0.1.2 → jsonpit-0.1.4}/jsonpit/cli.py +35 -5
  4. {jsonpit-0.1.2 → jsonpit-0.1.4}/jsonpit/store.py +1 -1
  5. {jsonpit-0.1.2 → jsonpit-0.1.4}/packages/jpit/pyproject.toml +2 -2
  6. {jsonpit-0.1.2 → jsonpit-0.1.4}/pyproject.toml +1 -1
  7. {jsonpit-0.1.2 → jsonpit-0.1.4}/tests/test_cli.py +19 -1
  8. {jsonpit-0.1.2 → jsonpit-0.1.4}/.gitignore +0 -0
  9. {jsonpit-0.1.2 → jsonpit-0.1.4}/ADELE_SPIRIT.md +0 -0
  10. {jsonpit-0.1.2 → jsonpit-0.1.4}/GEMINI.md +0 -0
  11. {jsonpit-0.1.2 → jsonpit-0.1.4}/README.md +0 -0
  12. {jsonpit-0.1.2 → jsonpit-0.1.4}/jsonpit/canonical.py +0 -0
  13. {jsonpit-0.1.2 → jsonpit-0.1.4}/jsonpit/changes.py +0 -0
  14. {jsonpit-0.1.2 → jsonpit-0.1.4}/jsonpit/config.py +0 -0
  15. {jsonpit-0.1.2 → jsonpit-0.1.4}/jsonpit/exceptions.py +0 -0
  16. {jsonpit-0.1.2 → jsonpit-0.1.4}/jsonpit/flags.py +0 -0
  17. {jsonpit-0.1.2 → jsonpit-0.1.4}/jsonpit/fs.py +0 -0
  18. {jsonpit-0.1.2 → jsonpit-0.1.4}/jsonpit/history.py +0 -0
  19. {jsonpit-0.1.2 → jsonpit-0.1.4}/jsonpit/item.py +0 -0
  20. {jsonpit-0.1.2 → jsonpit-0.1.4}/packages/jpit/README.md +0 -0
  21. {jsonpit-0.1.2 → jsonpit-0.1.4}/packages/jpit/jpit/__init__.py +0 -0
  22. {jsonpit-0.1.2 → jsonpit-0.1.4}/tests/fixtures/Person.pit +0 -0
  23. {jsonpit-0.1.2 → jsonpit-0.1.4}/tests/run_all.py +0 -0
  24. {jsonpit-0.1.2 → jsonpit-0.1.4}/tests/test_canonical.py +0 -0
  25. {jsonpit-0.1.2 → jsonpit-0.1.4}/tests/test_change_file.py +0 -0
  26. {jsonpit-0.1.2 → jsonpit-0.1.4}/tests/test_csharp_compatibility.py +0 -0
  27. {jsonpit-0.1.2 → jsonpit-0.1.4}/tests/test_delete_property_projection.py +0 -0
  28. {jsonpit-0.1.2 → jsonpit-0.1.4}/tests/test_equal_timestamp_ordering.py +0 -0
  29. {jsonpit-0.1.2 → jsonpit-0.1.4}/tests/test_master_ticket.py +0 -0
  30. {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.2
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
@@ -18,7 +18,7 @@ try:
18
18
  import importlib.metadata
19
19
  __version__ = importlib.metadata.version("jsonpit")
20
20
  except Exception:
21
- __version__ = "0.1.2"
21
+ __version__ = "0.1.4"
22
22
 
23
23
  __all__ = [
24
24
  "Pit",
@@ -96,9 +96,14 @@ def discover_pits(
96
96
  if discovered:
97
97
  return discovered
98
98
 
99
- # Fallback single pit target
100
- p_dir, p_name = resolve_pit_target(target, cloud=cloud, root=root)
101
- return [(p_dir, p_name)]
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("-c", "--cloud", default="OneDrive", help="Cloud drive (OneDrive, Dropbox, etc.)")
446
- common_parser.add_argument("-r", "--root", default=None, help="Root folder / tenant (e.g. AIA, AfricaStage)")
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 = ensure_directory(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.2"
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.2",
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.2"
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
- assert ("0.1.1" in f_out.getvalue()) or ("0.1.2" in f_out.getvalue())
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