pyuncommonkit 0.1.0__tar.gz → 0.1.2__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: pyuncommonkit
3
- Version: 0.1.0
3
+ Version: 0.1.2
4
4
  Summary: Named shared-memory segments for numpy arrays: publish once, attach from any process.
5
5
  Keywords: numpy,npy,mmap,shared-memory,cache
6
6
  Author: Sean D. True
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pyuncommonkit"
3
- version = "0.1.0"
3
+ version = "0.1.2"
4
4
  description = "Named shared-memory segments for numpy arrays: publish once, attach from any process."
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pyuncommonkit"
3
- version = "0.1.0"
3
+ version = "0.1.2"
4
4
  description = "Named shared-memory segments for numpy arrays: publish once, attach from any process."
5
5
  readme = "README.md"
6
6
  authors = [
@@ -16,7 +16,8 @@ dependencies = [
16
16
  ]
17
17
  keywords = ["numpy", "npy", "mmap", "shared-memory", "cache"]
18
18
  # No License :: classifier: PEP 639's `license` expression replaces it. Linux and macOS only:
19
- # Windows is supported in the contract (§12) but has no Python default root or CI yet.
19
+ # Windows has a default root since 0.1.1 and passes the suite on a Windows 11 machine, but has no
20
+ # CI yet, so no Windows classifier.
20
21
  classifiers = [
21
22
  "Development Status :: 4 - Beta",
22
23
  "Intended Audience :: Developers",
@@ -42,10 +42,11 @@ from __future__ import annotations
42
42
  import re
43
43
  from collections.abc import Iterable
44
44
 
45
- #: Segments are addressed by md5, lowercase. Every pattern here is used with
45
+ #: Segments are addressed by a lowercase hex digest: 32 digits (md5) or 64 (BLAKE3,
46
+ #: contract §3.4); the length tells them apart, so one key still names one content. Every pattern here is used with
46
47
  #: ``fullmatch``, never ``match`` with ``$``: ``$`` also matches before a final
47
48
  #: newline, which let ``"...\n"`` through as a digest or token (contract §3.1).
48
- _DIGEST_RE = re.compile(r"[0-9a-f]{32}")
49
+ _DIGEST_RE = re.compile(r"[0-9a-f]{32}|[0-9a-f]{64}")
49
50
 
50
51
  #: Every other component is a bare lowercase token. Restrictive on purpose:
51
52
  #: these become PATH components, and a key is the only thing standing between
@@ -110,7 +111,7 @@ class KeyScheme:
110
111
 
111
112
  self._key_re = re.compile(
112
113
  rf"{re.escape(self.domain)}:{re.escape(self.version)}:"
113
- r"(?P<form>[a-z0-9]+):(?P<digest>[0-9a-f]{32}):"
114
+ r"(?P<form>[a-z0-9]+):(?P<digest>[0-9a-f]{32}|[0-9a-f]{64}):"
114
115
  r"(?P<variant>[a-z0-9]+)")
115
116
 
116
117
  # ------------------------------------------------------------- building
@@ -140,7 +141,7 @@ class KeyScheme:
140
141
  """The store key for one array. Raises on anything it cannot address."""
141
142
  digest = str(digest).translate(_ASCII_LOWER)
142
143
  if not _DIGEST_RE.fullmatch(digest):
143
- raise ValueError(f"not an md5 digest: {digest!r}")
144
+ raise ValueError(f"not a 32- or 64-digit hex digest: {digest!r}")
144
145
  form = self.default_form if form is None else str(form)
145
146
  if form not in self.forms:
146
147
  raise ValueError(
@@ -44,15 +44,21 @@ from .store import SharedArrayStore
44
44
 
45
45
  ENV_PREFIX = "COMMONKIT_STORE_"
46
46
  _DARWIN = sys.platform == "darwin"
47
+ _WINDOWS = sys.platform == "win32"
47
48
 
48
49
  #: Per-platform defaults. macOS: the user cache directory on the internal SSD -- it has no
49
50
  #: /dev/shm, the SSD beat a RAM disk on first attach (0.69 ms against 0.76 ms), and a
50
51
  #: ``ram://`` disk commits its whole size up front as dirty memory. Linux: /dev/shm, which
51
- #: is RAM, so the budget is its only real bound.
52
+ #: is RAM, so the budget is its only real bound. Windows: no /dev/shm either (it would
53
+ #: resolve to ``\dev\shm`` on the current drive), so the per-user local cache, on disk like
54
+ #: macOS and with its budget (contract 10.1; from seantrue/commonkit d8198ae).
52
55
  DEFAULTS = {
53
56
  "ROOT": (os.path.expanduser("~/Library/Caches/commonkit/store") if _DARWIN
57
+ else os.path.join(os.environ.get("LOCALAPPDATA")
58
+ or os.path.expanduser("~/AppData/Local"), "commonkit", "store")
59
+ if _WINDOWS
54
60
  else "/dev/shm/commonkit"),
55
- "MAX_GB": 2.0 if _DARWIN else 8.0,
61
+ "MAX_GB": 2.0 if _DARWIN or _WINDOWS else 8.0,
56
62
  "MIN_FREE_GB": 8.0,
57
63
  "TTL_S": 900.0,
58
64
  "REAP_S": 30.0,
@@ -97,7 +97,7 @@ _NPY_MAGIC = b"\x93NUMPY"
97
97
  #: The reaper's candidates (contract §9.4): ``{form}/{xx}/{digest}.{variant}.npy`` with
98
98
  #: the shard equal to the digest's first two characters. Matched per path component.
99
99
  _SHARD_RE = re.compile(r"[0-9a-f]{2}")
100
- _SEGMENT_NAME_RE = re.compile(r"([0-9a-f]{2})[0-9a-f]{30}\.[a-z0-9]+\.npy")
100
+ _SEGMENT_NAME_RE = re.compile(r"([0-9a-f]{2})(?:[0-9a-f]{30}|[0-9a-f]{62})\.[a-z0-9]+\.npy")
101
101
 
102
102
  #: An orphaned temporary file is swept once it is this old (contract §9.5). A writer
103
103
  #: holds its ``.tmp`` for one ``np.save`` -- milliseconds -- so an hour old means the
@@ -356,7 +356,9 @@ class SharedArrayStore:
356
356
  ``keys.relpath`` is strict about the key shape, so a malformed key raises
357
357
  here rather than producing a path the reaper might later unlink.
358
358
  """
359
- return os.path.join(self.root, _keys.relpath(key))
359
+ # The relative path is `/`-separated (contract 3.6); joined with this platform's
360
+ # separator, so a Windows path isn't `...\store\plumarr/91/...`.
361
+ return os.path.join(self.root, *_keys.relpath(key).split("/"))
360
362
 
361
363
  # ---------------------------------------------------------------- reading
362
364
 
@@ -812,8 +814,8 @@ def _measure_free(root: str) -> int:
812
814
  """Free bytes on ``root``'s volume, 0 on any failure (contract §9.9)."""
813
815
  # Any failure is 0 free, not an exception: catching only OSError let AttributeError
814
816
  # out on a platform without os.statvfs. Windows has none, so it asks shutil, whose
815
- # POSIX branch is this same f_bavail * f_frsize; how Windows measures is not yet
816
- # contract (§12).
817
+ # POSIX branch is this same f_bavail * f_frsize and whose Windows one is
818
+ # GetDiskFreeSpaceExW's free-to-caller figure (§9.9).
817
819
  try:
818
820
  if hasattr(os, "statvfs"):
819
821
  st = os.statvfs(root)
File without changes
File without changes