openscad-parser 2.6.0__tar.gz → 2.6.1__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openscad_parser
3
- Version: 2.6.0
3
+ Version: 2.6.1
4
4
  Summary: A PEG parser to read OpenSCAD language source code, with optional AST tree generation.
5
5
  Keywords: openscad,openscad parser,parser
6
6
  Author: Revar Desmera
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "openscad_parser"
7
- version = "2.6.0"
7
+ version = "2.6.1"
8
8
  description = "A PEG parser to read OpenSCAD language source code, with optional AST tree generation."
9
9
  readme = "README.rst"
10
10
  authors = [
@@ -1,4 +1,5 @@
1
1
  import hashlib
2
+ import json
2
3
  import os
3
4
  import pickle
4
5
  import platform
@@ -310,17 +311,83 @@ def _load_from_disk_cache(file_path: str, include_comments: bool, current_mtime:
310
311
 
311
312
 
312
313
  def _save_to_disk_cache(file_path: str, include_comments: bool, mtime: float, ast: list[ASTNode] | None):
313
- """Save a file's AST to disk cache."""
314
+ """Save a file's AST to disk cache and update the manifest."""
314
315
  cache_path = _disk_cache_path(file_path, include_comments)
315
316
  if not cache_path:
316
317
  return # pragma: no cover
317
318
  try:
318
319
  with open(cache_path, 'wb') as f:
319
320
  pickle.dump((mtime, ast), f, protocol=pickle.HIGHEST_PROTOCOL)
321
+ except OSError: # pragma: no cover
322
+ return
323
+ cache_fname = os.path.basename(cache_path)
324
+ _manifest_update(cache_fname, file_path)
325
+ _evict_stale_cache()
326
+
327
+
328
+ def _manifest_path() -> Optional[str]:
329
+ """Get the path to the cache manifest file."""
330
+ cache_dir = _get_disk_cache_dir()
331
+ if not cache_dir:
332
+ return None # pragma: no cover
333
+ return os.path.join(cache_dir, "manifest.json")
334
+
335
+
336
+ def _manifest_load() -> dict[str, str]:
337
+ """Load the manifest: {cache_filename: source_file_path}."""
338
+ path = _manifest_path()
339
+ if not path or not os.path.exists(path):
340
+ return {}
341
+ try:
342
+ with open(path, 'r') as f:
343
+ return json.load(f)
344
+ except (OSError, json.JSONDecodeError):
345
+ return {}
346
+
347
+
348
+ def _manifest_save(manifest: dict[str, str]):
349
+ """Save the manifest to disk."""
350
+ path = _manifest_path()
351
+ if not path:
352
+ return # pragma: no cover
353
+ try:
354
+ with open(path, 'w') as f:
355
+ json.dump(manifest, f)
320
356
  except OSError: # pragma: no cover
321
357
  pass
322
358
 
323
359
 
360
+ def _manifest_update(cache_fname: str, source_path: str):
361
+ """Add or update an entry in the manifest."""
362
+ manifest = _manifest_load()
363
+ manifest[cache_fname] = source_path
364
+ _manifest_save(manifest)
365
+
366
+
367
+ def _evict_stale_cache():
368
+ """Remove cache entries whose source files no longer exist."""
369
+ cache_dir = _get_disk_cache_dir()
370
+ if not cache_dir:
371
+ return # pragma: no cover
372
+ manifest = _manifest_load()
373
+ if not manifest:
374
+ return
375
+ stale_keys = [
376
+ fname for fname, source_path in manifest.items()
377
+ if not os.path.exists(source_path)
378
+ ]
379
+ if not stale_keys:
380
+ return
381
+ for fname in stale_keys:
382
+ cache_file = os.path.join(cache_dir, fname)
383
+ try:
384
+ os.remove(cache_file)
385
+ except OSError:
386
+ pass
387
+ del manifest[fname]
388
+ _manifest_save(manifest)
389
+
390
+
324
391
  def clear_ast_cache():
325
392
  """Clear the in-memory AST cache.
326
393
 
@@ -346,7 +413,7 @@ def clear_disk_cache():
346
413
  cache_dir = _get_disk_cache_dir()
347
414
  if cache_dir and os.path.isdir(cache_dir):
348
415
  for fname in os.listdir(cache_dir):
349
- if fname.endswith('.pickle'):
416
+ if fname.endswith('.pickle') or fname == 'manifest.json':
350
417
  try:
351
418
  os.remove(os.path.join(cache_dir, fname))
352
419
  except OSError: # pragma: no cover