arrayview 0.31.2__py3-none-any.whl → 0.31.3__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.
@@ -141,7 +141,8 @@ def register_loading_routes(app, *, notify_shells, setup_rgb) -> None:
141
141
  else:
142
142
  for existing in SESSIONS.values():
143
143
  if existing.filepath and os.path.abspath(existing.filepath) == abs_path:
144
- return {"sid": existing.sid, "name": existing.name, "notified": False}
144
+ if acquire_session_leases([existing.sid]):
145
+ return {"sid": existing.sid, "name": existing.name, "notified": False}
145
146
  if not dir_patterns and not os.environ.get("ARRAYVIEW_SKIP_RAM_GUARD"):
146
147
  from ._io import FULL_LOAD_EXTS
147
148
 
arrayview/_session.py CHANGED
@@ -283,6 +283,7 @@ class Session:
283
283
  self.fft_original_data = None
284
284
  self.fft_axes = None
285
285
  self.data_version = 0 # incremented by /reload when file is reloaded
286
+ self.viewer_leases = 1 # one release is required for each tab using this SID
286
287
 
287
288
  self.rgb_axis = None # set by _setup_rgb(); axis index in actual shape
288
289
  self.spatial_shape = data.shape # shape without rgb_axis (set by _setup_rgb)
@@ -16,7 +16,7 @@ from enum import Enum
16
16
 
17
17
  from arrayview._session import _vprint
18
18
  from arrayview._platform import _in_vscode_terminal, _is_vscode_remote
19
- from arrayview._vscode_extension import _configure_vscode_port_preview, _ensure_vscode_extension, _VSCODE_EXT_FRESH_INSTALL
19
+ from arrayview._vscode_extension import _configure_vscode_port_preview, _ensure_vscode_extension
20
20
  from arrayview._vscode_signal import (
21
21
  AckState,
22
22
  _open_via_signal_file,
@@ -133,9 +133,13 @@ def _open_browser(
133
133
  if is_remote:
134
134
  # Remote/tunnel: install extension + write signal file.
135
135
  ext_ok = _ensure_vscode_extension()
136
- if ext_ok and _VSCODE_EXT_FRESH_INSTALL:
137
- time.sleep(1.5)
138
-
136
+ from arrayview import _vscode_extension as _extension_state
137
+ if not ext_ok and _extension_state._VSCODE_EXT_RELOAD_REQUIRED:
138
+ return OpenResult(
139
+ OpenState.FAILED,
140
+ "vscode-extension",
141
+ "ArrayView updated its VS Code opener; reload this VS Code window once, then retry",
142
+ )
139
143
  # URL-based mode: port is forwarded by VS Code and the viewer
140
144
  # connects via WebSocket through the devtunnel.
141
145
  _configure_vscode_port_preview(parsed_port)
@@ -172,8 +176,13 @@ def _open_browser(
172
176
  # Local VS Code terminal (or --window vscode forced): install extension + signal file.
173
177
  _configure_vscode_port_preview(parsed_port)
174
178
  ext_ok = _ensure_vscode_extension()
175
- if ext_ok and _VSCODE_EXT_FRESH_INSTALL:
176
- time.sleep(1.5)
179
+ from arrayview import _vscode_extension as _extension_state
180
+ if not ext_ok and _extension_state._VSCODE_EXT_RELOAD_REQUIRED:
181
+ return OpenResult(
182
+ OpenState.FAILED,
183
+ "vscode-extension",
184
+ "ArrayView updated its VS Code opener; reload this VS Code window once, then retry",
185
+ )
177
186
  # Always write the signal file: the extension may already be
178
187
  # installed even if _ensure failed (e.g. `code` CLI not found).
179
188
  request = _open_via_signal_file(
@@ -5,7 +5,9 @@ from __future__ import annotations
5
5
  import json
6
6
  import os
7
7
  import re
8
+ import signal
8
9
  import subprocess
10
+ import time
9
11
  import zipfile
10
12
  from importlib.resources import files as _pkg_files
11
13
 
@@ -44,7 +46,8 @@ def _vscode_app_bundle() -> str | None:
44
46
 
45
47
  _VSCODE_EXT_INSTALLED = False # cached so we only check once per process
46
48
  _VSCODE_EXT_FRESH_INSTALL = False # True if we just installed it this session
47
- _VSCODE_EXT_VERSION = "0.14.40" # current bundled extension version
49
+ _VSCODE_EXT_RELOAD_REQUIRED = False # installed files are newer than the live host
50
+ _VSCODE_EXT_VERSION = "0.14.41" # current bundled extension version
48
51
 
49
52
  def _bundled_vscode_vsix_version(vsix_path: str) -> str | None:
50
53
  """Return the bundled opener extension version recorded inside the VSIX."""
@@ -92,19 +95,43 @@ def _patch_vscode_extension_metadata(version: str) -> None:
92
95
  )
93
96
 
94
97
 
95
- def _remove_old_extension_versions(current_version: str) -> None:
96
- """Delete extension directories for versions older than current_version.
98
+ def _version_tuple(version: str) -> tuple[int, ...] | None:
99
+ """Return a numeric version tuple, or ``None`` for unknown formats."""
100
+ try:
101
+ return tuple(int(part) for part in version.split("."))
102
+ except (AttributeError, TypeError, ValueError):
103
+ return None
104
+
105
+
106
+ def _extension_bases(*, remote: bool | None = None) -> tuple[str, ...]:
107
+ """Return extension roots for the active VS Code host."""
108
+ local = os.path.expanduser("~/.vscode/extensions")
109
+ remote_base = os.path.expanduser("~/.vscode-server/extensions")
110
+ if remote is True:
111
+ if os.path.isdir(os.path.expanduser("~/.vscode-server")):
112
+ return (remote_base,)
113
+ # `code tunnel` uses ~/.vscode/extensions on the remote host, while
114
+ # Remote-SSH uses ~/.vscode-server/extensions.
115
+ return (local,)
116
+ if remote is False:
117
+ return (local,)
118
+ return (local, remote_base)
119
+
120
+
121
+ def _remove_old_extension_versions(
122
+ current_version: str, *, remote: bool | None = None
123
+ ) -> None:
124
+ """Delete older extension directories without ever deleting a newer build.
97
125
 
98
126
  When multiple versions of arrayview-opener are installed side-by-side,
99
127
  VS Code may load an older version instead of the latest. Removing stale
100
128
  directories ensures the correct version is picked up on the next reload.
129
+ Cleanup is scoped to the active local or remote extension host.
101
130
  """
102
131
  import shutil
103
132
 
104
- for ext_base in (
105
- os.path.expanduser("~/.vscode-server/extensions"),
106
- os.path.expanduser("~/.vscode/extensions"),
107
- ):
133
+ current_key = _version_tuple(current_version)
134
+ for ext_base in _extension_bases(remote=remote):
108
135
  if not os.path.isdir(ext_base):
109
136
  continue
110
137
  try:
@@ -118,6 +145,9 @@ def _remove_old_extension_versions(current_version: str) -> None:
118
145
  version_str = entry[len(prefix) :]
119
146
  if version_str == current_version:
120
147
  continue # keep
148
+ version_key = _version_tuple(version_str)
149
+ if current_key is None or version_key is None or version_key > current_key:
150
+ continue
121
151
  old_dir = os.path.join(ext_base, entry)
122
152
  try:
123
153
  shutil.rmtree(old_dir)
@@ -126,7 +156,12 @@ def _remove_old_extension_versions(current_version: str) -> None:
126
156
  _vprint(f"[ArrayView] could not remove {entry}: {exc}", flush=True)
127
157
 
128
158
 
129
- def _extension_on_disk(version: str, vsix_path: str | None = None) -> bool:
159
+ def _extension_on_disk(
160
+ version: str,
161
+ vsix_path: str | None = None,
162
+ *,
163
+ remote: bool | None = None,
164
+ ) -> bool:
130
165
  """Return True if the extension directory for *version* exists on disk.
131
166
 
132
167
  When *vsix_path* is given, also verifies that the installed extension
@@ -136,10 +171,7 @@ def _extension_on_disk(version: str, vsix_path: str | None = None) -> bool:
136
171
  """
137
172
  import hashlib
138
173
 
139
- for base in (
140
- os.path.expanduser("~/.vscode/extensions"),
141
- os.path.expanduser("~/.vscode-server/extensions"),
142
- ):
174
+ for base in _extension_bases(remote=remote):
143
175
  ext_dir = os.path.join(base, f"arrayview.arrayview-opener-{version}")
144
176
  if not os.path.isdir(ext_dir):
145
177
  continue
@@ -165,6 +197,88 @@ def _extension_on_disk(version: str, vsix_path: str | None = None) -> bool:
165
197
  return False
166
198
 
167
199
 
200
+ def _newer_extension_on_disk(version: str, *, remote: bool) -> str | None:
201
+ """Return the newest installed opener newer than *version*, if any."""
202
+ wanted = _version_tuple(version)
203
+ if wanted is None:
204
+ return None
205
+ prefix = "arrayview.arrayview-opener-"
206
+ newer: list[tuple[tuple[int, ...], str]] = []
207
+ for base in _extension_bases(remote=remote):
208
+ try:
209
+ entries = os.listdir(base)
210
+ except OSError:
211
+ continue
212
+ for entry in entries:
213
+ if not entry.startswith(prefix):
214
+ continue
215
+ candidate = entry[len(prefix) :]
216
+ key = _version_tuple(candidate)
217
+ if key is not None and key > wanted:
218
+ newer.append((key, candidate))
219
+ return max(newer)[1] if newer else None
220
+
221
+
222
+ def _active_extension_version() -> str | None:
223
+ """Return the opener version advertised by this terminal's live host."""
224
+ window_id = os.environ.get("ARRAYVIEW_WINDOW_ID")
225
+ if not window_id:
226
+ ipc = _find_vscode_ipc_hook()
227
+ if ipc:
228
+ import hashlib
229
+
230
+ window_id = hashlib.sha256(ipc.encode()).hexdigest()[:16]
231
+ if not window_id:
232
+ return None
233
+ registration = os.path.expanduser(f"~/.arrayview/window-{window_id}.json")
234
+ try:
235
+ with open(registration) as file:
236
+ value = json.load(file).get("extensionVersion")
237
+ # An existing registration without a version belongs to a legacy host.
238
+ return value if isinstance(value, str) else ""
239
+ except (OSError, TypeError, ValueError):
240
+ return None
241
+
242
+
243
+ def _wait_for_active_extension_version(version: str, timeout: float = 15.0) -> bool:
244
+ """Wait for an updated extension host registration after installation."""
245
+ deadline = time.monotonic() + timeout
246
+ while time.monotonic() < deadline:
247
+ if _active_extension_version() == version:
248
+ return True
249
+ time.sleep(0.1)
250
+ return _active_extension_version() == version
251
+
252
+
253
+ def _run_extension_installer(command: list[str], env: dict[str, str]) -> subprocess.CompletedProcess:
254
+ """Run VS Code's installer without leaving its server-cli child behind."""
255
+ process = subprocess.Popen(
256
+ command,
257
+ stdout=subprocess.PIPE,
258
+ stderr=subprocess.PIPE,
259
+ text=True,
260
+ env=env,
261
+ start_new_session=os.name != "nt",
262
+ )
263
+ try:
264
+ stdout, stderr = process.communicate(timeout=30)
265
+ except subprocess.TimeoutExpired:
266
+ if os.name == "nt":
267
+ process.kill()
268
+ else:
269
+ os.killpg(process.pid, signal.SIGTERM)
270
+ try:
271
+ process.communicate(timeout=2)
272
+ except subprocess.TimeoutExpired:
273
+ if os.name == "nt":
274
+ process.kill()
275
+ else:
276
+ os.killpg(process.pid, signal.SIGKILL)
277
+ process.communicate()
278
+ raise
279
+ return subprocess.CompletedProcess(command, process.returncode, stdout, stderr)
280
+
281
+
168
282
  def _ensure_vscode_extension() -> bool:
169
283
  """Install the bundled arrayview-opener VS Code extension for local VS Code use.
170
284
 
@@ -180,9 +294,9 @@ def _ensure_vscode_extension() -> bool:
180
294
  The authoritative version is read from the bundled VSIX — no hardcoded
181
295
  version constant needed.
182
296
  """
183
- global _VSCODE_EXT_INSTALLED, _VSCODE_EXT_FRESH_INSTALL
184
- if _VSCODE_EXT_INSTALLED:
185
- return True
297
+ global _VSCODE_EXT_INSTALLED, _VSCODE_EXT_FRESH_INSTALL, _VSCODE_EXT_RELOAD_REQUIRED
298
+ _VSCODE_EXT_FRESH_INSTALL = False
299
+ _VSCODE_EXT_RELOAD_REQUIRED = False
186
300
 
187
301
  vsix_path = str(_pkg_files("arrayview").joinpath("arrayview-opener.vsix"))
188
302
  if not os.path.isfile(vsix_path):
@@ -195,8 +309,18 @@ def _ensure_vscode_extension() -> bool:
195
309
  # Fast path: correct version and content already installed — no reinstall
196
310
  # needed. Reinstalling with --force triggers an extension-host reload,
197
311
  # which creates a ~10-15s gap during which the signal file can be missed.
198
- _remove_old_extension_versions(ext_version)
199
- if _extension_on_disk(ext_version, vsix_path):
312
+ is_remote = _is_vscode_remote()
313
+ active_version = _active_extension_version()
314
+ if _extension_on_disk(ext_version, vsix_path, remote=is_remote):
315
+ if active_version not in (None, ext_version):
316
+ _VSCODE_EXT_RELOAD_REQUIRED = True
317
+ _vprint(
318
+ f"[ArrayView] opener v{ext_version} is installed, but this VS Code "
319
+ "window is still running an older extension host; reload this window",
320
+ flush=True,
321
+ )
322
+ return False
323
+ _remove_old_extension_versions(ext_version, remote=is_remote)
200
324
  _VSCODE_EXT_INSTALLED = True
201
325
  _vprint(
202
326
  f"[ArrayView] extension v{ext_version} already installed — skipping reinstall",
@@ -204,6 +328,23 @@ def _ensure_vscode_extension() -> bool:
204
328
  )
205
329
  return True
206
330
 
331
+ newer_version = _newer_extension_on_disk(ext_version, remote=is_remote)
332
+ if newer_version is not None:
333
+ if active_version not in (None, newer_version):
334
+ _VSCODE_EXT_RELOAD_REQUIRED = True
335
+ _vprint(
336
+ f"[ArrayView] newer opener v{newer_version} is installed, but this "
337
+ "VS Code window is still running an older extension host; reload this window",
338
+ flush=True,
339
+ )
340
+ return False
341
+ _VSCODE_EXT_INSTALLED = True
342
+ _vprint(
343
+ f"[ArrayView] newer extension v{newer_version} is installed — keeping it",
344
+ flush=True,
345
+ )
346
+ return True
347
+
207
348
  code = _find_code_cli()
208
349
  if not code:
209
350
  return False
@@ -214,12 +355,8 @@ def _ensure_vscode_extension() -> bool:
214
355
  env["VSCODE_IPC_HOOK_CLI"] = ipc
215
356
 
216
357
  try:
217
- r = subprocess.run(
218
- [code, "--install-extension", vsix_path, "--force"],
219
- capture_output=True,
220
- text=True,
221
- timeout=30,
222
- env=env,
358
+ r = _run_extension_installer(
359
+ [code, "--install-extension", vsix_path, "--force"], env
223
360
  )
224
361
  combined = (r.stdout or "") + (r.stderr or "")
225
362
  install_failed = (
@@ -247,9 +384,23 @@ def _ensure_vscode_extension() -> bool:
247
384
  pass # non-critical
248
385
  _VSCODE_EXT_INSTALLED = True
249
386
  _VSCODE_EXT_FRESH_INSTALL = True
387
+ _remove_old_extension_versions(ext_version, remote=is_remote)
388
+ if active_version not in (None, ext_version):
389
+ if not _wait_for_active_extension_version(ext_version):
390
+ _VSCODE_EXT_RELOAD_REQUIRED = True
391
+ _vprint(
392
+ "[ArrayView] VS Code installed the new opener, but this window "
393
+ "is still running the old extension host; reload this VS Code window",
394
+ flush=True,
395
+ )
396
+ return False
250
397
  return True
398
+ if active_version not in (None, ext_version):
399
+ _VSCODE_EXT_RELOAD_REQUIRED = True
251
400
  print(f"[ArrayView] extension install failed: {combined.strip()!r}", flush=True)
252
401
  except Exception as exc:
402
+ if active_version not in (None, ext_version):
403
+ _VSCODE_EXT_RELOAD_REQUIRED = True
253
404
  print(f"[ArrayView] extension install error: {exc}", flush=True)
254
405
  return False
255
406
 
@@ -49,6 +49,7 @@ class SignalRequest:
49
49
  server_id: str | None
50
50
  ack_path: Path
51
51
  written: bool
52
+ extension_version: str | None = None
52
53
 
53
54
  def __bool__(self) -> bool:
54
55
  return self.written
@@ -129,6 +130,28 @@ def _wait_for_vscode_ack(
129
130
  message=f"ACK {key} does not match request",
130
131
  payload=payload,
131
132
  )
133
+ if request.extension_version is not None:
134
+ try:
135
+ actual_version = tuple(
136
+ int(part) for part in payload["extensionVersion"].split(".")
137
+ )
138
+ required_version = tuple(
139
+ int(part) for part in request.extension_version.split(".")
140
+ )
141
+ except (AttributeError, KeyError, TypeError, ValueError):
142
+ return AckResult(
143
+ AckState.INVALID,
144
+ request.request_id,
145
+ message="ACK extensionVersion is missing or invalid",
146
+ payload=payload,
147
+ )
148
+ if actual_version < required_version:
149
+ return AckResult(
150
+ AckState.INVALID,
151
+ request.request_id,
152
+ message="ACK extensionVersion is older than required",
153
+ payload=payload,
154
+ )
132
155
  result = AckResult(
133
156
  state,
134
157
  request.request_id,
@@ -403,6 +426,10 @@ def _open_via_signal_file(
403
426
  "requestId": request_id,
404
427
  "ackPath": str(ack_path),
405
428
  }
429
+ from arrayview._vscode_extension import _VSCODE_EXT_VERSION
430
+ payload["requiredExtensionVersion"] = _VSCODE_EXT_VERSION
431
+ if _is_vscode_remote():
432
+ payload["remoteOnly"] = True
406
433
  if window_id:
407
434
  payload["windowId"] = window_id
408
435
  if server_id:
@@ -419,6 +446,7 @@ def _open_via_signal_file(
419
446
  server_id,
420
447
  ack_path,
421
448
  written,
449
+ _VSCODE_EXT_VERSION,
422
450
  )
423
451
 
424
452
 
Binary file
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arrayview
3
- Version: 0.31.2
3
+ Version: 0.31.3
4
4
  Summary: Fast multi-dimensional array viewer
5
5
  Project-URL: Home, https://github.com/oscarvanderheide/arrayview
6
6
  Project-URL: Source, https://github.com/oscarvanderheide/arrayview
@@ -18,7 +18,7 @@ arrayview/_platform.py,sha256=U1XPqEjPeqy3l_xJh9Rmn_3d_6Fh_clJUcA0uR6QBGA,19043
18
18
  arrayview/_render.py,sha256=wVvwxQtZ7ldWYZTzlg2CS-esnPHlpSXMW_z22eh-rU0,30296
19
19
  arrayview/_routes_analysis.py,sha256=QTTdZrq6bXBAxqrAPf4-3_ANZPv0b8VXyPK8mW_Xe1s,31629
20
20
  arrayview/_routes_export.py,sha256=Ha8mMO2E9TlRL5x25-ADV8_8xUiC-jRz9N1oWwgKOsg,3898
21
- arrayview/_routes_loading.py,sha256=pqhiOaH-hXdCR8P9X26JHm_psR5gurT1N4vh8BIuwcw,21864
21
+ arrayview/_routes_loading.py,sha256=0lhbFAtTQS3K0LGuOFxxVjtJ9gtqnCU-8vzdL04CL9Q,21931
22
22
  arrayview/_routes_persistence.py,sha256=T03csHb7ppGT-8yMDf227XkqhnhjgSLYj0RW2wmg59g,17176
23
23
  arrayview/_routes_preload.py,sha256=KOsankNguxdIWmFW9iZcY9p2SvUN1mOKY7KkW1qMGcc,1729
24
24
  arrayview/_routes_query.py,sha256=vEwkNWKVkwTOBswGU079gf_C0fWbO4Bgvg0U7jusBCU,5227
@@ -29,7 +29,7 @@ arrayview/_routes_vectorfield.py,sha256=P8avWU8jDH173YQ4Kfpzs8SZz5gIL4ki0-Gc6pGA
29
29
  arrayview/_routes_websocket.py,sha256=6WCK665MGAbCpOhkxQdEnJN7o1CYB4e2_VB4ilgmr-I,18722
30
30
  arrayview/_segmentation.py,sha256=aENWHmefrKDScXhcZL1X3KgYXE784dpMkmFFo83qnrw,7377
31
31
  arrayview/_server.py,sha256=XqrC1jbQ_wke4_YgxZ-1xACmUFPYxq2hEU--namvtqA,10407
32
- arrayview/_session.py,sha256=NXhx1-XbMNqqsF-A57wmmW2rE9jOUbWtXtjgQRl0b7s,21683
32
+ arrayview/_session.py,sha256=_JYLC-Upnxysh114GjIAyZFJRCgTQJTkFcFZFhJb4Ds,21769
33
33
  arrayview/_session_spec.py,sha256=AEqt_5eXIYdlWkJeeVBmG_MqSUgmVDZCTNaMkdRTZ5Y,6969
34
34
  arrayview/_shell.html,sha256=xzHIB-kRX12okubZoqdrgsWDY8CCLteg5jtXxDQru8U,11624
35
35
  arrayview/_synthetic_mri.py,sha256=CNL-2WC835i6WCqkvmWBCO6WQ5w7HE5trNb8OyEfe64,7365
@@ -37,13 +37,13 @@ arrayview/_torch.py,sha256=13sqIMCUu_-b9SQ9KfDCExDz2UMStL87EDVjnbJxAEo,7536
37
37
  arrayview/_vectorfield.py,sha256=eDqMCMEGjO8Jdt-6Auh01cz3WD9dbF7xYUWD9xHAf1E,8115
38
38
  arrayview/_viewer.html,sha256=42OHpTAuXsFLB5AGLJhCRs1qw9oIqjEXJjq3i9EKm9c,1789431
39
39
  arrayview/_vscode.py,sha256=uY3SYJYjgzuYCbdycmm53ZaPNJIPPtGiIQZKBUobMz4,871
40
- arrayview/_vscode_browser.py,sha256=7WjnJwN--bppJmSv8TFjJWG0_IpGwGqHkNI6ubjxgE8,11785
41
- arrayview/_vscode_extension.py,sha256=UZqjMPJASvz8U6moa-cX7xVJhbvwpVSmCHXd-wc_W4Y,13423
42
- arrayview/_vscode_signal.py,sha256=9WZKG3CqZKA9yG6GvSVmkunQs-0DwmRNy5wML4dD0p0,45107
43
- arrayview/arrayview-opener.vsix,sha256=QeeLuH0h0sFN_tBXIRmFOdm4owSrZ4_aLSQPe7KbOR0,21352
40
+ arrayview/_vscode_browser.py,sha256=r5IX9pLF73NykhOIgZXYJTv1CLqiXnBOstHxCYiMBmk,12355
41
+ arrayview/_vscode_extension.py,sha256=SR21698fF6crdqNE0G1Q5rLNdD-CLRvq8cvd0Zv5rf0,19481
42
+ arrayview/_vscode_signal.py,sha256=P-Ia1f8KNZGPiqCtHrgxTSFhYjxiqt4ZOrgibApQ6Dw,46404
43
+ arrayview/arrayview-opener.vsix,sha256=R5_TSw2R6hXb8BeCWREXOZ71ZRFLZbxdrR2ix7HWV7o,22214
44
44
  arrayview/gsap.min.js,sha256=VGihm4idI0E1uqrWezWsiScB9F_jMosn7qs6pPSkeac,72304
45
- arrayview-0.31.2.dist-info/METADATA,sha256=ID8dLoV42W9GRgXawTdg4nupId1kmrlnxMcPNYspMcc,2054
46
- arrayview-0.31.2.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
47
- arrayview-0.31.2.dist-info/entry_points.txt,sha256=FQSz3M11B-zaVLDyXofKUDvMhHASz7OjmoLgI4nn7ko,105
48
- arrayview-0.31.2.dist-info/licenses/LICENSE,sha256=34OivjM9faB2QaPjZpMRTIiSx31V3U9cEyQ6F2k-bH0,1062
49
- arrayview-0.31.2.dist-info/RECORD,,
45
+ arrayview-0.31.3.dist-info/METADATA,sha256=7M_qfaHSxJuUBuagK3QMzT3Ie58CzANAnmsrCOqOHlA,2054
46
+ arrayview-0.31.3.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
47
+ arrayview-0.31.3.dist-info/entry_points.txt,sha256=FQSz3M11B-zaVLDyXofKUDvMhHASz7OjmoLgI4nn7ko,105
48
+ arrayview-0.31.3.dist-info/licenses/LICENSE,sha256=34OivjM9faB2QaPjZpMRTIiSx31V3U9cEyQ6F2k-bH0,1062
49
+ arrayview-0.31.3.dist-info/RECORD,,