pyisyntax 0.1.1__tar.gz → 0.1.3__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 (76) hide show
  1. pyisyntax-0.1.3/CHANGELOG.md +57 -0
  2. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/MANIFEST.in +1 -0
  3. {pyisyntax-0.1.1/pyisyntax.egg-info → pyisyntax-0.1.3}/PKG-INFO +12 -1
  4. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/README.md +6 -0
  5. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax/lowlevel/io_management.py +1 -1
  6. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax/lowlevel/libisyntax.py +9 -6
  7. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax/wrapper.py +12 -14
  8. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/builder.py +1 -1
  9. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/examples/isyntax_example.c +1 -3
  10. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/examples/isyntax_to_tiff.c +2 -2
  11. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/isyntax/isyntax.c +11 -0
  12. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/isyntax/isyntax_reader.c +1 -1
  13. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/isyntax/isyntax_streamer.c +0 -15
  14. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/libisyntax.c +10 -6
  15. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/libisyntax.h +3 -2
  16. {pyisyntax-0.1.1 → pyisyntax-0.1.3/pyisyntax.egg-info}/PKG-INFO +12 -1
  17. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/pyisyntax.egg-info/SOURCES.txt +1 -0
  18. pyisyntax-0.1.3/pyisyntax.egg-info/top_level.txt +1 -0
  19. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/pyproject.toml +12 -2
  20. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/tests/test_lowlevel.py +2 -6
  21. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/tests/test_wrapper.py +23 -4
  22. pyisyntax-0.1.1/pyisyntax.egg-info/top_level.txt +0 -2
  23. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/LICENSE +0 -0
  24. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax/__init__.py +0 -0
  25. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax/lowlevel/__init__.py +0 -0
  26. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/__init__.py +0 -0
  27. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/pyisyntax.c +0 -0
  28. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/src/python_platform_utils.c +0 -0
  29. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/src/python_platform_utils.h +0 -0
  30. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/src/win32_utils.c +0 -0
  31. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/LICENSE.txt +0 -0
  32. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/README.md +0 -0
  33. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/isyntax/isyntax.h +0 -0
  34. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/isyntax/isyntax_dwt.c +0 -0
  35. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/isyntax/isyntax_reader.h +0 -0
  36. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/isyntax/isyntax_streamer.h +0 -0
  37. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/platform/arena.h +0 -0
  38. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/platform/common.h +0 -0
  39. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/platform/intrinsics.h +0 -0
  40. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/platform/linux_platform.c +0 -0
  41. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/platform/linux_utils.c +0 -0
  42. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/platform/platform.c +0 -0
  43. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/platform/platform.h +0 -0
  44. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/platform/win32_utils.c +0 -0
  45. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/platform/win32_utils.h +0 -0
  46. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/platform/work_queue.c +0 -0
  47. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/platform/work_queue.h +0 -0
  48. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/third_party/ltalloc.cc +0 -0
  49. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/third_party/ltalloc.h +0 -0
  50. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/third_party/stb_ds.h +0 -0
  51. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/third_party/stb_image.h +0 -0
  52. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/third_party/stb_image_write.h +0 -0
  53. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/third_party/stb_sprintf.h +0 -0
  54. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/third_party/yxml.c +0 -0
  55. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/third_party/yxml.h +0 -0
  56. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/utils/benaphore.c +0 -0
  57. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/utils/benaphore.h +0 -0
  58. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/utils/block_allocator.c +0 -0
  59. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/utils/block_allocator.h +0 -0
  60. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/utils/libtiff_api.h +0 -0
  61. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/utils/mathutils.c +0 -0
  62. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/utils/mathutils.h +0 -0
  63. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/utils/memrw.c +0 -0
  64. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/utils/memrw.h +0 -0
  65. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/utils/stringutils.c +0 -0
  66. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/utils/stringutils.h +0 -0
  67. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/utils/timerutils.c +0 -0
  68. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/isyntax_build/vendor/libisyntax/src/utils/timerutils.h +0 -0
  69. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/pyisyntax.egg-info/dependency_links.txt +0 -0
  70. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/pyisyntax.egg-info/not-zip-safe +0 -0
  71. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/pyisyntax.egg-info/requires.txt +0 -0
  72. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/setup.cfg +0 -0
  73. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/setup.py +0 -0
  74. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/tests/__init__.py +0 -0
  75. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/tests/conftest.py +0 -0
  76. {pyisyntax-0.1.1 → pyisyntax-0.1.3}/tests/data/DOWNLOAD.txt +0 -0
@@ -0,0 +1,57 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.3] - 2024-03-28
11
+
12
+ ### Added
13
+
14
+ - This CHANGELOG file.
15
+
16
+ ### Fixed
17
+
18
+ - Gracefully exit the contextmanager when an ISyntax file fails to open
19
+ ([#8](https://github.com/anibali/pyisyntax/issues/8)).
20
+ - Release reference to the associated cache object when an ISyntax object is closed
21
+ so that it can be garbage collected and destroyed
22
+ ([#9](https://github.com/anibali/pyisyntax/issues/9)).
23
+
24
+ ## [0.1.2] - 2024-03-25
25
+
26
+ ### Added
27
+
28
+ - Configuration option to set the cache size
29
+ ([#5](https://github.com/anibali/pyisyntax/issues/5)).
30
+
31
+ ### Changed
32
+
33
+ - Nest the `_pyisyntax` binary library file under the `isyntax` top-level
34
+ namespace.
35
+ - Bump the `libisyntax` submodule commit and match their API changes
36
+ (thanks @erikogabrielsson).
37
+
38
+ ## [0.1.1] - 2024-03-02
39
+
40
+ ### Changed
41
+
42
+ - Return `None` when an associated image/colour profile is not present.
43
+ - Reduce the number of provided wheels by using `py_limited_api`.
44
+ Multiple Python versions will continue being tested during CI through
45
+ the use of `tox`.
46
+
47
+ ## [0.1.0] - 2024-02-26
48
+
49
+ ### Added
50
+
51
+ - This is the initial release.
52
+
53
+ [unreleased]: https://github.com/anibali/pyisyntax/compare/v0.1.3...HEAD
54
+ [0.1.3]: https://github.com/anibali/pyisyntax/compare/v0.1.2...v0.1.3
55
+ [0.1.2]: https://github.com/anibali/pyisyntax/compare/v0.1.1...v0.1.2
56
+ [0.1.1]: https://github.com/anibali/pyisyntax/compare/v0.1.0...v0.1.1
57
+ [0.1.0]: https://github.com/anibali/pyisyntax/releases/tag/v0.1.0
@@ -1,5 +1,6 @@
1
1
  include README.md
2
2
  include LICENSE
3
+ include CHANGELOG.md
3
4
 
4
5
  graft isyntax_build
5
6
  prune isyntax_build/vendor/libisyntax
@@ -1,9 +1,14 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyisyntax
3
- Version: 0.1.1
3
+ Version: 0.1.3
4
4
  Summary: Python bindings for libisyntax
5
5
  Author-email: Aiden Nibali <dismaldenizen@gmail.com>
6
6
  License: MIT
7
+ Project-URL: homepage, https://github.com/anibali/pyisyntax
8
+ Project-URL: source, https://github.com/anibali/pyisyntax
9
+ Project-URL: download, https://pypi.org/project/pyisyntax/#files
10
+ Project-URL: tracker, https://github.com/anibali/pyisyntax/issues
11
+ Project-URL: release notes, https://github.com/anibali/pyisyntax/blob/master/CHANGELOG.md
7
12
  Requires-Python: >=3.10
8
13
  Description-Content-Type: text/markdown
9
14
  License-File: LICENSE
@@ -23,6 +28,12 @@ Requires-Dist: typing_extensions; extra == "dev"
23
28
  A Python library for working with pathology images in the iSyntax file format,
24
29
  powered by [libisyntax](https://github.com/amspath/libisyntax).
25
30
 
31
+ ## Installation
32
+
33
+ ```console
34
+ $ pip install pyisyntax
35
+ ```
36
+
26
37
  ## Usage
27
38
 
28
39
  Read and display a region of the WSI via
@@ -3,6 +3,12 @@
3
3
  A Python library for working with pathology images in the iSyntax file format,
4
4
  powered by [libisyntax](https://github.com/amspath/libisyntax).
5
5
 
6
+ ## Installation
7
+
8
+ ```console
9
+ $ pip install pyisyntax
10
+ ```
11
+
6
12
  ## Usage
7
13
 
8
14
  Read and display a region of the WSI via
@@ -3,7 +3,7 @@ from dataclasses import dataclass
3
3
  from io import BufferedIOBase, RawIOBase
4
4
  from typing import Generic, NewType, TypeVar
5
5
 
6
- from _pyisyntax import ffi, lib
6
+ from isyntax._pyisyntax import ffi, lib
7
7
 
8
8
  VoidPtr = NewType("VoidPtr", object)
9
9
  T = TypeVar("T")
@@ -3,8 +3,7 @@ from enum import IntEnum
3
3
  from pathlib import Path
4
4
  from typing import TYPE_CHECKING, NewType
5
5
 
6
- from _pyisyntax import ffi, lib
7
-
6
+ from isyntax._pyisyntax import ffi, lib
8
7
  from isyntax.lowlevel.io_management import init_python_io_hooks, register_io
9
8
 
10
9
  if TYPE_CHECKING:
@@ -108,12 +107,16 @@ def get_tile_height(isyntax: ISyntaxPtr) -> int:
108
107
  return lib.libisyntax_get_tile_height(isyntax)
109
108
 
110
109
 
111
- def get_wsi_image_index(isyntax: ISyntaxPtr) -> int:
112
- return lib.libisyntax_get_wsi_image_index(isyntax)
110
+ def get_wsi_image(isyntax: ISyntaxPtr) -> ISyntaxImagePtr:
111
+ return lib.libisyntax_get_wsi_image(isyntax)
112
+
113
+
114
+ def get_label_image(isyntax: ISyntaxPtr) -> ISyntaxImagePtr:
115
+ return lib.libisyntax_get_label_image(isyntax)
113
116
 
114
117
 
115
- def get_image(isyntax: ISyntaxPtr, wsi_image_index: int) -> ISyntaxImagePtr:
116
- return lib.libisyntax_get_image(isyntax, wsi_image_index)
118
+ def get_macro_image(isyntax: ISyntaxPtr) -> ISyntaxImagePtr:
119
+ return lib.libisyntax_get_macro_image(isyntax)
117
120
 
118
121
 
119
122
  def image_get_level_count(wsi_image: ISyntaxImagePtr) -> int:
@@ -78,23 +78,28 @@ class ISyntaxImage:
78
78
 
79
79
 
80
80
  class ISyntax:
81
- def __init__(self, f: RawIOBase | BufferedIOBase, n_bytes: int) -> None:
81
+ def __init__(self, f: RawIOBase | BufferedIOBase, n_bytes: int, cache_size: int = 2000) -> None:
82
+ # Start in the "closed" for a graceful contextmanager exit when the file
83
+ # fails to open.
84
+ self.closed = True
82
85
  self.io_handle = register_io(f, n_bytes)
83
86
  self.ptr = libisyntax.open_from_registered_handle(self.io_handle, is_init_allocators=False)
84
87
  self.closed = False
88
+ self._cache_size = cache_size
85
89
  self._cache = None
86
90
 
87
91
  @classmethod
88
- def open(cls: type["ISyntax"], filename: str | Path) -> "ISyntax":
92
+ def open(cls: type["ISyntax"], filename: str | Path, cache_size: int = 2000) -> "ISyntax":
89
93
  filename = Path(filename)
90
94
  f = filename.open("rb")
91
95
  n_bytes = os.fstat(f.fileno()).st_size
92
- return cls(f, n_bytes)
96
+ return cls(f, n_bytes, cache_size)
93
97
 
94
98
  def close(self) -> None:
95
99
  if self.closed:
96
100
  return
97
101
  libisyntax.close(self.ptr)
102
+ self._cache = None
98
103
  self.closed = True
99
104
 
100
105
  @property
@@ -105,20 +110,13 @@ class ISyntax:
105
110
  def tile_height(self) -> int:
106
111
  return libisyntax.get_tile_height(self.ptr)
107
112
 
108
- @property
109
- def wsi_index(self) -> int:
110
- return libisyntax.get_wsi_image_index(self.ptr)
111
-
112
- def get_image(self, image_index: int) -> ISyntaxImage:
113
- return ISyntaxImage(libisyntax.get_image(self.ptr, image_index))
114
-
115
113
  @property
116
114
  def wsi(self) -> ISyntaxImage:
117
- return self.get_image(self.wsi_index)
115
+ return ISyntaxImage(libisyntax.get_wsi_image(self.ptr))
118
116
 
119
117
  def get_cache(self) -> ISyntaxCache:
120
118
  if self._cache is None:
121
- self._cache = ISyntaxCache(cache_size=2000)
119
+ self._cache = ISyntaxCache(cache_size=self._cache_size)
122
120
  self._cache.inject(self)
123
121
  return self._cache
124
122
 
@@ -180,14 +178,14 @@ class ISyntax:
180
178
  except libisyntax.LibISyntaxFatalError:
181
179
  return None
182
180
 
183
- def read_icc_profile(self, image_index: int) -> memoryview | None:
181
+ def read_icc_profile(self) -> memoryview | None:
184
182
  """Reads the ICC color profile for an image.
185
183
 
186
184
  Returns:
187
185
  The ICC color profile, or None if it can't be read.
188
186
  """
189
187
  try:
190
- return libisyntax.read_icc_profile(self.ptr, self.get_image(image_index).ptr)
188
+ return libisyntax.read_icc_profile(self.ptr, self.wsi.ptr)
191
189
  except libisyntax.LibISyntaxFatalError:
192
190
  return None
193
191
 
@@ -43,7 +43,7 @@ def create_ffibuilder() -> FFI:
43
43
  libraries = []
44
44
 
45
45
  ffibuilder.set_source(
46
- "_pyisyntax",
46
+ "isyntax._pyisyntax",
47
47
  resources.read_text(isyntax_build, "pyisyntax.c"),
48
48
  sources=paths_to_strings(
49
49
  libisyntax_src/"libisyntax.c",
@@ -15,9 +15,7 @@
15
15
  #define LOG_VAR(fmt, var) printf("%s: %s=" fmt "\n", __FUNCTION__, #var, var)
16
16
 
17
17
  void print_isyntax_levels(isyntax_t* isyntax) {
18
- int wsi_image_idx = libisyntax_get_wsi_image_index(isyntax);
19
- LOG_VAR("%d", wsi_image_idx);
20
- const isyntax_image_t* wsi_image = libisyntax_get_image(isyntax, wsi_image_idx);
18
+ const isyntax_image_t* wsi_image = libisyntax_get_wsi_image(isyntax);
21
19
 
22
20
  for (int i = 0; i < libisyntax_image_get_level_count(wsi_image); ++i) {
23
21
  const isyntax_level_t* level = libisyntax_image_get_level(wsi_image, i);
@@ -1,7 +1,7 @@
1
1
  /*
2
2
  BSD 2-Clause License
3
3
 
4
- Copyright (c) 2019-2023, Pieter Valkema, Alexandr Virodov, Jonas Teuwen
4
+ Copyright (c) 2019-2024, Pieter Valkema, Alexandr Virodov, Jonas Teuwen
5
5
 
6
6
  Redistribution and use in source and binary forms, with or without
7
7
  modification, are permitted provided that the following conditions are met:
@@ -445,7 +445,7 @@ int main(int argc, char **argv) {
445
445
  return -1;
446
446
  }
447
447
 
448
- const isyntax_image_t *image = libisyntax_get_image(isyntax, 0);
448
+ const isyntax_image_t *image = libisyntax_get_wsi_image(isyntax);
449
449
 
450
450
  // Check if the page we selected actually exists.
451
451
  int32_t level_count = libisyntax_image_get_level_count(image);
@@ -3327,6 +3327,17 @@ bool isyntax_open(isyntax_t* isyntax, const char* filename, bool init_allocators
3327
3327
  isyntax->is_block_allocator_owned = false;
3328
3328
  }
3329
3329
 
3330
+ // Initialize dummy blocks with 'background' coefficients, to use for filling in margins at the edges (in case the neighboring codeblock doesn't exist)
3331
+ if (!isyntax->black_dummy_coeff) {
3332
+ isyntax->black_dummy_coeff = (icoeff_t*)calloc(1, isyntax->block_width * isyntax->block_height * sizeof(icoeff_t));
3333
+ }
3334
+ if (!isyntax->white_dummy_coeff) {
3335
+ isyntax->white_dummy_coeff = (icoeff_t*)malloc(isyntax->block_width * isyntax->block_height * sizeof(icoeff_t));
3336
+ for (i32 i = 0; i < isyntax->block_width * isyntax->block_height; ++i) {
3337
+ isyntax->white_dummy_coeff[i] = 255;
3338
+ }
3339
+ }
3340
+
3330
3341
  success = true;
3331
3342
 
3332
3343
  free(read_buffer);
@@ -128,7 +128,7 @@ static void isyntax_openslide_load_tile_coefficients_ll_or_h(isyntax_cache_t* ca
128
128
 
129
129
  isyntax_hulsken_decompress(codeblock_data, codeblock->block_size,
130
130
  isyntax->block_width, isyntax->block_height,
131
- codeblock->coefficient, 1,
131
+ codeblock->coefficient, wsi->compressor_version,
132
132
  is_ll ? tile->color_channels[color].coeff_ll : tile->color_channels[color].coeff_h);
133
133
  free(codeblock_data);
134
134
  }
@@ -51,19 +51,6 @@ static void submit_tile_completed(isyntax_streamer_t* streamer, void* tile_pixel
51
51
 
52
52
  }
53
53
 
54
- static void isyntax_init_dummy_codeblocks(isyntax_t* isyntax) {
55
- // Blocks with 'background' coefficients, to use for filling in margins at the edges (in case the neighboring codeblock doesn't exist)
56
- if (!isyntax->black_dummy_coeff) {
57
- isyntax->black_dummy_coeff = (icoeff_t*)calloc(1, isyntax->block_width * isyntax->block_height * sizeof(icoeff_t));
58
- }
59
- if (!isyntax->white_dummy_coeff) {
60
- isyntax->white_dummy_coeff = (icoeff_t*)malloc(isyntax->block_width * isyntax->block_height * sizeof(icoeff_t));
61
- for (i32 i = 0; i < isyntax->block_width * isyntax->block_height; ++i) {
62
- isyntax->white_dummy_coeff[i] = 255;
63
- }
64
- }
65
- }
66
-
67
54
  static i32 isyntax_load_all_tiles_in_level(isyntax_streamer_t* streamer, i32 scale) {
68
55
  i32 tiles_loaded = 0;
69
56
  i32 tile_index = 0;
@@ -125,8 +112,6 @@ static void isyntax_do_first_load(isyntax_streamer_t* streamer) {
125
112
  i32 tiles_loaded = 0;
126
113
  isyntax->total_rgb_transform_time = 0.0f;
127
114
 
128
- isyntax_init_dummy_codeblocks(isyntax);
129
-
130
115
  i32 scale = wsi->max_scale;
131
116
  isyntax_level_t* current_level = wsi->levels + scale;
132
117
  i32 codeblocks_per_color = isyntax_get_chunk_codeblocks_per_color_for_level(scale, true); // most often 1 + 4 + 16 (for scale n, n-1, n-2) + 1 (LL block)
@@ -1,7 +1,7 @@
1
1
  /*
2
2
  BSD 2-Clause License
3
3
 
4
- Copyright (c) 2019-2023, Pieter Valkema
4
+ Copyright (c) 2019-2024, Pieter Valkema
5
5
 
6
6
  Redistribution and use in source and binary forms, with or without
7
7
  modification, are permitted provided that the following conditions are met:
@@ -42,7 +42,7 @@
42
42
 
43
43
  #define CHECK_LIBISYNTAX_OK(_libisyntax_call) do { \
44
44
  isyntax_error_t result = _libisyntax_call; \
45
- assert(result == LIBISYNTAX_OK); \
45
+ ASSERT(result == LIBISYNTAX_OK); \
46
46
  } while(0);
47
47
 
48
48
  static platform_thread_info_t thread_infos[MAX_THREAD_COUNT];
@@ -249,12 +249,16 @@ int32_t libisyntax_get_tile_height(const isyntax_t* isyntax) {
249
249
  return isyntax->tile_height;
250
250
  }
251
251
 
252
- int32_t libisyntax_get_wsi_image_index(const isyntax_t* isyntax) {
253
- return isyntax->wsi_image_index;
252
+ const isyntax_image_t* libisyntax_get_wsi_image(const isyntax_t* isyntax) {
253
+ return isyntax->images + isyntax->wsi_image_index;
254
254
  }
255
255
 
256
- const isyntax_image_t* libisyntax_get_image(const isyntax_t* isyntax, int32_t wsi_image_index) {
257
- return &isyntax->images[wsi_image_index];
256
+ const isyntax_image_t* libisyntax_get_label_image(const isyntax_t* isyntax) {
257
+ return isyntax->images + isyntax->label_image_index;
258
+ }
259
+
260
+ const isyntax_image_t* libisyntax_get_macro_image(const isyntax_t* isyntax) {
261
+ return isyntax->images + isyntax->label_image_index;
258
262
  }
259
263
 
260
264
  int32_t libisyntax_image_get_level_count(const isyntax_image_t* image) {
@@ -47,8 +47,9 @@ void libisyntax_close(isyntax_t* isyntax);
47
47
  //== Getters API ==
48
48
  int32_t libisyntax_get_tile_width(const isyntax_t* isyntax);
49
49
  int32_t libisyntax_get_tile_height(const isyntax_t* isyntax);
50
- int32_t libisyntax_get_wsi_image_index(const isyntax_t* isyntax);
51
- const isyntax_image_t* libisyntax_get_image(const isyntax_t* isyntax, int32_t wsi_image_index);
50
+ const isyntax_image_t* libisyntax_get_wsi_image(const isyntax_t* isyntax);
51
+ const isyntax_image_t* libisyntax_get_label_image(const isyntax_t* isyntax);
52
+ const isyntax_image_t* libisyntax_get_macro_image(const isyntax_t* isyntax);
52
53
  int32_t libisyntax_image_get_level_count(const isyntax_image_t* image);
53
54
  const isyntax_level_t* libisyntax_image_get_level(const isyntax_image_t* image, int32_t index);
54
55
 
@@ -1,9 +1,14 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyisyntax
3
- Version: 0.1.1
3
+ Version: 0.1.3
4
4
  Summary: Python bindings for libisyntax
5
5
  Author-email: Aiden Nibali <dismaldenizen@gmail.com>
6
6
  License: MIT
7
+ Project-URL: homepage, https://github.com/anibali/pyisyntax
8
+ Project-URL: source, https://github.com/anibali/pyisyntax
9
+ Project-URL: download, https://pypi.org/project/pyisyntax/#files
10
+ Project-URL: tracker, https://github.com/anibali/pyisyntax/issues
11
+ Project-URL: release notes, https://github.com/anibali/pyisyntax/blob/master/CHANGELOG.md
7
12
  Requires-Python: >=3.10
8
13
  Description-Content-Type: text/markdown
9
14
  License-File: LICENSE
@@ -23,6 +28,12 @@ Requires-Dist: typing_extensions; extra == "dev"
23
28
  A Python library for working with pathology images in the iSyntax file format,
24
29
  powered by [libisyntax](https://github.com/amspath/libisyntax).
25
30
 
31
+ ## Installation
32
+
33
+ ```console
34
+ $ pip install pyisyntax
35
+ ```
36
+
26
37
  ## Usage
27
38
 
28
39
  Read and display a region of the WSI via
@@ -1,3 +1,4 @@
1
+ CHANGELOG.md
1
2
  LICENSE
2
3
  MANIFEST.in
3
4
  README.md
@@ -0,0 +1 @@
1
+ isyntax
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pyisyntax"
3
- version = "0.1.1"
3
+ version = "0.1.3"
4
4
  description = "Python bindings for libisyntax"
5
5
  readme = { content-type = "text/markdown", file = "README.md" }
6
6
  authors = [{ name = "Aiden Nibali", email = "dismaldenizen@gmail.com" }]
@@ -11,6 +11,13 @@ dependencies = [
11
11
  "numpy",
12
12
  ]
13
13
 
14
+ [project.urls]
15
+ homepage = "https://github.com/anibali/pyisyntax"
16
+ source = "https://github.com/anibali/pyisyntax"
17
+ download = "https://pypi.org/project/pyisyntax/#files"
18
+ tracker = "https://github.com/anibali/pyisyntax/issues"
19
+ "release notes" = "https://github.com/anibali/pyisyntax/blob/master/CHANGELOG.md"
20
+
14
21
  [project.optional-dependencies]
15
22
  dev = [
16
23
  "conda-lock",
@@ -62,6 +69,9 @@ pydocstyle.convention = "google"
62
69
  [tool.pyright]
63
70
  typeCheckingMode = "standard"
64
71
 
72
+ [tool.pytest.ini_options]
73
+ addopts = "-Werror"
74
+
65
75
  [tool.tox]
66
76
  legacy_tox_ini = """
67
77
  [tox]
@@ -72,7 +82,7 @@ env_list = lint, type, py{310,311,312}
72
82
  deps =
73
83
  pytest
74
84
  pytest-mock
75
- commands = pytest {posargs:tests}
85
+ commands = pytest --import-mode=importlib {posargs:tests}
76
86
 
77
87
  [testenv:lint]
78
88
  skip_install = true
@@ -23,7 +23,7 @@ def isyntax(sample_isyntax_file: Path) -> Iterator[ISyntaxPtr]:
23
23
 
24
24
  @pytest.fixture()
25
25
  def wsi_image(isyntax: ISyntaxPtr) -> ISyntaxImagePtr:
26
- return libisyntax.get_image(isyntax, 0)
26
+ return libisyntax.get_wsi_image(isyntax)
27
27
 
28
28
 
29
29
  @pytest.fixture()
@@ -59,10 +59,6 @@ def test_libisyntax_get_tile_height(isyntax: ISyntaxPtr) -> None:
59
59
  assert libisyntax.get_tile_height(isyntax) == expected
60
60
 
61
61
 
62
- def test_libisyntax_get_wsi_image_index(isyntax: ISyntaxPtr) -> None:
63
- assert libisyntax.get_wsi_image_index(isyntax) == 0
64
-
65
-
66
62
  def test_libisyntax_image_get_level_count(wsi_image: ISyntaxImagePtr) -> None:
67
63
  expected = 8
68
64
  assert libisyntax.image_get_level_count(wsi_image) == expected
@@ -159,7 +155,7 @@ def test_libisyntax_read_macro_image_jpeg(isyntax: ISyntaxPtr, mocker: MockerFix
159
155
 
160
156
  def test_libisyntax_read_icc_profile(isyntax: ISyntaxPtr, mocker: MockerFixture) -> None:
161
157
  free_spy = mocker.spy(libisyntax, "free")
162
- wsi_image = libisyntax.get_image(isyntax, 0)
158
+ wsi_image = libisyntax.get_wsi_image(isyntax)
163
159
  buf = libisyntax.read_icc_profile(isyntax, wsi_image)
164
160
  version_major = buf[8]
165
161
  version_minor = buf[9] >> 4
@@ -1,3 +1,5 @@
1
+ import gc
2
+ import io
1
3
  from collections.abc import Iterator
2
4
  from pathlib import Path
3
5
 
@@ -22,10 +24,6 @@ class TestISyntax:
22
24
  expected = 256
23
25
  assert isyntax.tile_height == expected
24
26
 
25
- def test_wsi_index(self, isyntax: ISyntax) -> None:
26
- expected = 0
27
- assert isyntax.wsi_index == expected
28
-
29
27
  def test_level_count(self, isyntax: ISyntax) -> None:
30
28
  expected = 8
31
29
  assert isyntax.level_count == expected
@@ -89,6 +87,7 @@ class TestISyntax:
89
87
  def test_read_label_image_jpeg(self, isyntax: ISyntax, mocker: MockerFixture) -> None:
90
88
  free_spy = mocker.spy(libisyntax, "free")
91
89
  jpeg_data = isyntax.read_label_image_jpeg()
90
+ assert jpeg_data is not None
92
91
  jpeg_magic = b"\xff\xd8"
93
92
  assert jpeg_data[:2] == jpeg_magic
94
93
  expected_size = 60348
@@ -102,6 +101,7 @@ class TestISyntax:
102
101
  def test_read_macro_image_jpeg(self, isyntax: ISyntax, mocker: MockerFixture) -> None:
103
102
  free_spy = mocker.spy(libisyntax, "free")
104
103
  jpeg_data = isyntax.read_macro_image_jpeg()
104
+ assert jpeg_data is not None
105
105
  jpeg_magic = b"\xff\xd8"
106
106
  assert jpeg_data[:2] == jpeg_magic
107
107
  expected_size = 49625
@@ -111,3 +111,22 @@ class TestISyntax:
111
111
  free_spy.assert_not_called()
112
112
  del jpeg_data
113
113
  free_spy.assert_called_once()
114
+
115
+ def test_close_is_idempotent(self, isyntax: ISyntax) -> None:
116
+ isyntax.close()
117
+ isyntax.close()
118
+ assert isyntax.closed
119
+
120
+ def test_cache_is_destroyed_on_close(self, isyntax: ISyntax, mocker: MockerFixture) -> None:
121
+ destroy_spy = mocker.spy(libisyntax, "cache_destroy")
122
+ # Reading image data causes the cache to be created.
123
+ isyntax.read_region(500, 500, 1, 1, level=4)
124
+ destroy_spy.assert_not_called()
125
+ isyntax.close()
126
+ gc.collect()
127
+ destroy_spy.assert_called_once()
128
+
129
+ def test_closes_after_error_from_contextmanager(self) -> None:
130
+ f = io.BytesIO()
131
+ with pytest.raises(libisyntax.LibISyntaxFatalError), ISyntax(f, 0):
132
+ pass
@@ -1,2 +0,0 @@
1
- _pyisyntax
2
- isyntax
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes