rastr 0.1.0__tar.gz → 0.2.0__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.

Potentially problematic release.


This version of rastr might be problematic. Click here for more details.

Files changed (80) hide show
  1. {rastr-0.1.0 → rastr-0.2.0}/.pre-commit-config.yaml +9 -0
  2. {rastr-0.1.0 → rastr-0.2.0}/PKG-INFO +4 -4
  3. {rastr-0.1.0 → rastr-0.2.0}/README.md +1 -1
  4. {rastr-0.1.0 → rastr-0.2.0}/pyproject.toml +19 -0
  5. {rastr-0.1.0 → rastr-0.2.0}/src/rastr/_version.py +2 -2
  6. {rastr-0.1.0 → rastr-0.2.0}/src/rastr/io.py +1 -1
  7. {rastr-0.1.0 → rastr-0.2.0}/src/rastr/meta.py +2 -0
  8. {rastr-0.1.0 → rastr-0.2.0}/src/rastr/raster.py +18 -10
  9. {rastr-0.1.0 → rastr-0.2.0}/tests/rastr/test_raster.py +35 -0
  10. {rastr-0.1.0 → rastr-0.2.0}/uv.lock +17 -0
  11. {rastr-0.1.0 → rastr-0.2.0}/.copier-answers.yml +0 -0
  12. {rastr-0.1.0 → rastr-0.2.0}/.github/ISSUE_TEMPLATE/bug-report.md +0 -0
  13. {rastr-0.1.0 → rastr-0.2.0}/.github/ISSUE_TEMPLATE/enhancement.md +0 -0
  14. {rastr-0.1.0 → rastr-0.2.0}/.github/copilot-instructions.md +0 -0
  15. {rastr-0.1.0 → rastr-0.2.0}/.github/workflows/ci.yml +0 -0
  16. {rastr-0.1.0 → rastr-0.2.0}/.github/workflows/release.yml +0 -0
  17. {rastr-0.1.0 → rastr-0.2.0}/.gitignore +0 -0
  18. {rastr-0.1.0 → rastr-0.2.0}/.python-version +0 -0
  19. {rastr-0.1.0 → rastr-0.2.0}/LICENSE +0 -0
  20. {rastr-0.1.0 → rastr-0.2.0}/docs/index.md +0 -0
  21. {rastr-0.1.0 → rastr-0.2.0}/mkdocs.yml +0 -0
  22. {rastr-0.1.0 → rastr-0.2.0}/requirements.txt +0 -0
  23. {rastr-0.1.0 → rastr-0.2.0}/src/archive/.gitkeep +0 -0
  24. {rastr-0.1.0 → rastr-0.2.0}/src/notebooks/.gitkeep +0 -0
  25. {rastr-0.1.0 → rastr-0.2.0}/src/rastr/__init__.py +0 -0
  26. {rastr-0.1.0 → rastr-0.2.0}/src/rastr/arr/__init__.py +0 -0
  27. {rastr-0.1.0 → rastr-0.2.0}/src/rastr/arr/fill.py +0 -0
  28. {rastr-0.1.0 → rastr-0.2.0}/src/rastr/create.py +0 -0
  29. {rastr-0.1.0 → rastr-0.2.0}/src/rastr/gis/__init__.py +0 -0
  30. {rastr-0.1.0 → rastr-0.2.0}/src/rastr/gis/fishnet.py +0 -0
  31. {rastr-0.1.0 → rastr-0.2.0}/src/rastr/gis/smooth.py +0 -0
  32. {rastr-0.1.0 → rastr-0.2.0}/src/scripts/.gitkeep +0 -0
  33. {rastr-0.1.0 → rastr-0.2.0}/tasks/ABOUT_TASKS.md +0 -0
  34. {rastr-0.1.0 → rastr-0.2.0}/tasks/dev_sync.ps1 +0 -0
  35. {rastr-0.1.0 → rastr-0.2.0}/tasks/scripts/activate_venv.sh +0 -0
  36. {rastr-0.1.0 → rastr-0.2.0}/tasks/scripts/configure_project.sh +0 -0
  37. {rastr-0.1.0 → rastr-0.2.0}/tasks/scripts/dev_sync.sh +0 -0
  38. {rastr-0.1.0 → rastr-0.2.0}/tasks/scripts/install_backend.sh +0 -0
  39. {rastr-0.1.0 → rastr-0.2.0}/tasks/scripts/install_venv.sh +0 -0
  40. {rastr-0.1.0 → rastr-0.2.0}/tasks/scripts/recover_corrupt_venv.sh +0 -0
  41. {rastr-0.1.0 → rastr-0.2.0}/tasks/scripts/sh_runner.ps1 +0 -0
  42. {rastr-0.1.0 → rastr-0.2.0}/tasks/scripts/sync_requirements.sh +0 -0
  43. {rastr-0.1.0 → rastr-0.2.0}/tasks/scripts/sync_template.sh +0 -0
  44. {rastr-0.1.0 → rastr-0.2.0}/tasks/shims/activate_venv +0 -0
  45. {rastr-0.1.0 → rastr-0.2.0}/tasks/shims/activate_venv.cmd +0 -0
  46. {rastr-0.1.0 → rastr-0.2.0}/tasks/shims/activate_venv.ps1 +0 -0
  47. {rastr-0.1.0 → rastr-0.2.0}/tasks/shims/configure_project +0 -0
  48. {rastr-0.1.0 → rastr-0.2.0}/tasks/shims/configure_project.cmd +0 -0
  49. {rastr-0.1.0 → rastr-0.2.0}/tasks/shims/configure_project.ps1 +0 -0
  50. {rastr-0.1.0 → rastr-0.2.0}/tasks/shims/dev_sync +0 -0
  51. {rastr-0.1.0 → rastr-0.2.0}/tasks/shims/dev_sync.cmd +0 -0
  52. {rastr-0.1.0 → rastr-0.2.0}/tasks/shims/dev_sync.ps1 +0 -0
  53. {rastr-0.1.0 → rastr-0.2.0}/tasks/shims/install_backend +0 -0
  54. {rastr-0.1.0 → rastr-0.2.0}/tasks/shims/install_backend.cmd +0 -0
  55. {rastr-0.1.0 → rastr-0.2.0}/tasks/shims/install_backend.ps1 +0 -0
  56. {rastr-0.1.0 → rastr-0.2.0}/tasks/shims/install_venv +0 -0
  57. {rastr-0.1.0 → rastr-0.2.0}/tasks/shims/install_venv.cmd +0 -0
  58. {rastr-0.1.0 → rastr-0.2.0}/tasks/shims/install_venv.ps1 +0 -0
  59. {rastr-0.1.0 → rastr-0.2.0}/tasks/shims/recover_corrupt_venv +0 -0
  60. {rastr-0.1.0 → rastr-0.2.0}/tasks/shims/recover_corrupt_venv.cmd +0 -0
  61. {rastr-0.1.0 → rastr-0.2.0}/tasks/shims/recover_corrupt_venv.ps1 +0 -0
  62. {rastr-0.1.0 → rastr-0.2.0}/tasks/shims/sync_requirements +0 -0
  63. {rastr-0.1.0 → rastr-0.2.0}/tasks/shims/sync_requirements.cmd +0 -0
  64. {rastr-0.1.0 → rastr-0.2.0}/tasks/shims/sync_requirements.ps1 +0 -0
  65. {rastr-0.1.0 → rastr-0.2.0}/tasks/shims/sync_template +0 -0
  66. {rastr-0.1.0 → rastr-0.2.0}/tasks/shims/sync_template.cmd +0 -0
  67. {rastr-0.1.0 → rastr-0.2.0}/tasks/shims/sync_template.ps1 +0 -0
  68. {rastr-0.1.0 → rastr-0.2.0}/tasks/sync_template.ps1 +0 -0
  69. {rastr-0.1.0 → rastr-0.2.0}/tests/assets/.gitkeep +0 -0
  70. {rastr-0.1.0 → rastr-0.2.0}/tests/assets/pga_g_clipped.grd +0 -0
  71. {rastr-0.1.0 → rastr-0.2.0}/tests/assets/pga_g_clipped.tif +0 -0
  72. {rastr-0.1.0 → rastr-0.2.0}/tests/conftest.py +0 -0
  73. {rastr-0.1.0 → rastr-0.2.0}/tests/rastr/.gitkeep +0 -0
  74. {rastr-0.1.0 → rastr-0.2.0}/tests/rastr/gis/test_fishnet.py +0 -0
  75. {rastr-0.1.0 → rastr-0.2.0}/tests/rastr/gis/test_smooth.py +0 -0
  76. {rastr-0.1.0 → rastr-0.2.0}/tests/rastr/regression_test_data/test_plot_raster.png +0 -0
  77. {rastr-0.1.0 → rastr-0.2.0}/tests/rastr/regression_test_data/test_write_raster_to_file.tif +0 -0
  78. {rastr-0.1.0 → rastr-0.2.0}/tests/rastr/test_create.py +0 -0
  79. {rastr-0.1.0 → rastr-0.2.0}/tests/rastr/test_io.py +0 -0
  80. {rastr-0.1.0 → rastr-0.2.0}/tests/rastr/test_meta.py +0 -0
@@ -121,6 +121,15 @@ repos:
121
121
  language: python
122
122
  always_run: true
123
123
  pass_filenames: false
124
+ - repo: local
125
+ hooks:
126
+ - id: import-linter
127
+ name: import-linter
128
+ always_run: true
129
+ entry: uv run --frozen --offline lint-imports
130
+ language: system
131
+ pass_filenames: false
132
+ require_serial: true
124
133
  - repo: https://github.com/codespell-project/codespell
125
134
  rev: v2.4.1
126
135
  hooks:
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rastr
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Geospatial Raster datatype library for Python.
5
5
  Project-URL: Source Code, https://github.com/tonkintaylor/rastr
6
6
  Project-URL: Bug Tracker, https://github.com/tonkintaylor/rastr/issues
7
7
  Project-URL: Releases, https://github.com/tonkintaylor/rastr/releases
8
- Project-URL: Source Archive, https://github.com/tonkintaylor/rastr/archive/ac9cfaefef4030485d30ce79b97a000821338bd2.zip
9
- Author-email: Tonkin & Taylor Limited <Sub-DisciplineData+AnalyticsStaff@tonkintaylor.co.nz>, Nathan McDougall <nmcdougall@tonkintaylor.co.nz>
8
+ Project-URL: Source Archive, https://github.com/tonkintaylor/rastr/archive/551d36604b84f947fffbe328e9e877732c9e35fb.zip
9
+ Author-email: Tonkin & Taylor Limited <Sub-DisciplineData+AnalyticsStaff@tonkintaylor.co.nz>, Nathan McDougall <nmcdougall@tonkintaylor.co.nz>, Ben Karl <bkarl@tonkintaylor.co.nz>
10
10
  License-Expression: MIT
11
11
  License-File: LICENSE
12
12
  Classifier: Programming Language :: Python :: 3 :: Only
@@ -41,4 +41,4 @@ Description-Content-Type: text/markdown
41
41
 
42
42
  Geospatial Raster datatype library for Python.
43
43
 
44
- Currently, only single-banded rasters with square cells are supported.
44
+ Currently, only single-banded, in-memory rasters with square cells are supported.
@@ -7,4 +7,4 @@
7
7
 
8
8
  Geospatial Raster datatype library for Python.
9
9
 
10
- Currently, only single-banded rasters with square cells are supported.
10
+ Currently, only single-banded, in-memory rasters with square cells are supported.
@@ -20,6 +20,7 @@ license-files = [ "LICENSE" ]
20
20
  authors = [
21
21
  { name = "Tonkin & Taylor Limited", email = "Sub-DisciplineData+AnalyticsStaff@tonkintaylor.co.nz" },
22
22
  { name = "Nathan McDougall", email = "nmcdougall@tonkintaylor.co.nz" },
23
+ { name = "Ben Karl", email = "bkarl@tonkintaylor.co.nz" },
23
24
  ]
24
25
  requires-python = ">=3.10"
25
26
  classifiers = [
@@ -53,6 +54,7 @@ dev = [
53
54
  "build>=1.2.2.post1",
54
55
  "deptry>=0.23.0",
55
56
  "folium>=0.20.0",
57
+ "import-linter>=2.3",
56
58
  "ipykernel>=6.30.0",
57
59
  "ipython>=8.37.0",
58
60
  "ipywidgets>=8.1.7",
@@ -177,6 +179,7 @@ lint.per-file-ignores."src/{scripts,notebooks}/**" = [
177
179
  "S101", # Asserts fine in scripts and notebooks
178
180
  "T201", # Prints fine in scripts and notebooks
179
181
  ]
182
+ lint.per-file-ignores."tests/**" = [ "INP" ]
180
183
  lint.per-file-ignores."tests/**/*.py" = [
181
184
  "ANN", # We don't need annotations in tests
182
185
  "D", # We don't need docstrings in tests
@@ -245,3 +248,19 @@ output = "test-reports/coverage.xml"
245
248
  default-groups = [ "dev", "test", "doc" ]
246
249
  required-version = "==0.8.3" # sync with .pre-commit-config.yaml and release.yml
247
250
  link-mode = "symlink"
251
+
252
+ [tool.importlinter]
253
+ root_packages = [ "rastr" ]
254
+
255
+ [[tool.importlinter.contracts]]
256
+ name = "rastr"
257
+ type = "layers"
258
+ layers = [
259
+ "create | io",
260
+ "raster",
261
+ "meta",
262
+ "arr | gis",
263
+ "_version",
264
+ ]
265
+ containers = [ "rastr" ]
266
+ exhaustive = true
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.1.0'
21
- __version_tuple__ = version_tuple = (0, 1, 0)
20
+ __version__ = version = '0.2.0'
21
+ __version_tuple__ = version_tuple = (0, 2, 0)
@@ -9,7 +9,7 @@ from rastr.meta import RasterMeta
9
9
  from rastr.raster import RasterModel
10
10
 
11
11
 
12
- def read_raster_inmem(raster_path: Path, crs: CRS | None = None) -> RasterModel:
12
+ def read_raster_inmem(raster_path: Path | str, crs: CRS | None = None) -> RasterModel:
13
13
  """Read raster data from a file and return an in-memory Raster object."""
14
14
  with rasterio.open(raster_path, mode="r") as dst:
15
15
  # Read the entire array
@@ -11,6 +11,8 @@ class RasterMeta(BaseModel, extra="forbid"):
11
11
  Attributes:
12
12
  cell_size: Cell size in meters.
13
13
  crs: Coordinate reference system.
14
+ transform: The affine transformation associated with the raster. This is based
15
+ on the CRS, the cell size, as well as the offset/origin.
14
16
  """
15
17
 
16
18
  cell_size: float
@@ -74,9 +74,10 @@ class RasterModel(BaseModel):
74
74
  __hash__ = BaseModel.__hash__
75
75
 
76
76
  def __add__(self, other: float | Self) -> Self:
77
+ cls = self.__class__
77
78
  if isinstance(other, float | int):
78
79
  new_arr = self.arr + other
79
- return RasterModel(arr=new_arr, raster_meta=self.raster_meta)
80
+ return cls(arr=new_arr, raster_meta=self.raster_meta)
80
81
  elif isinstance(other, RasterModel):
81
82
  if self.raster_meta != other.raster_meta:
82
83
  msg = (
@@ -91,7 +92,7 @@ class RasterModel(BaseModel):
91
92
  )
92
93
  raise ValueError(msg)
93
94
  new_arr = self.arr + other.arr
94
- return RasterModel(arr=new_arr, raster_meta=self.raster_meta)
95
+ return cls(arr=new_arr, raster_meta=self.raster_meta)
95
96
  else:
96
97
  return NotImplemented
97
98
 
@@ -99,9 +100,10 @@ class RasterModel(BaseModel):
99
100
  return self + other
100
101
 
101
102
  def __mul__(self, other: float | Self) -> Self:
103
+ cls = self.__class__
102
104
  if isinstance(other, float | int):
103
105
  new_arr = self.arr * other
104
- return RasterModel(arr=new_arr, raster_meta=self.raster_meta)
106
+ return cls(arr=new_arr, raster_meta=self.raster_meta)
105
107
  elif isinstance(other, RasterModel):
106
108
  if self.raster_meta != other.raster_meta:
107
109
  msg = (
@@ -113,7 +115,7 @@ class RasterModel(BaseModel):
113
115
  msg = "Rasters must have the same shape to be multiplied"
114
116
  raise ValueError(msg)
115
117
  new_arr = self.arr * other.arr
116
- return RasterModel(arr=new_arr, raster_meta=self.raster_meta)
118
+ return cls(arr=new_arr, raster_meta=self.raster_meta)
117
119
  else:
118
120
  return NotImplemented
119
121
 
@@ -121,9 +123,10 @@ class RasterModel(BaseModel):
121
123
  return self * other
122
124
 
123
125
  def __truediv__(self, other: float | Self) -> Self:
126
+ cls = self.__class__
124
127
  if isinstance(other, float | int):
125
128
  new_arr = self.arr / other
126
- return RasterModel(arr=new_arr, raster_meta=self.raster_meta)
129
+ return cls(arr=new_arr, raster_meta=self.raster_meta)
127
130
  elif isinstance(other, RasterModel):
128
131
  if self.raster_meta != other.raster_meta:
129
132
  msg = (
@@ -135,7 +138,7 @@ class RasterModel(BaseModel):
135
138
  msg = "Rasters must have the same shape to be divided"
136
139
  raise ValueError(msg)
137
140
  new_arr = self.arr / other.arr
138
- return RasterModel(arr=new_arr, raster_meta=self.raster_meta)
141
+ return cls(arr=new_arr, raster_meta=self.raster_meta)
139
142
  else:
140
143
  return NotImplemented
141
144
 
@@ -149,7 +152,8 @@ class RasterModel(BaseModel):
149
152
  return -self + other
150
153
 
151
154
  def __neg__(self) -> Self:
152
- return RasterModel(arr=-self.arr, raster_meta=self.raster_meta)
155
+ cls = self.__class__
156
+ return cls(arr=-self.arr, raster_meta=self.raster_meta)
153
157
 
154
158
  @property
155
159
  def cell_centre_coords(self) -> NDArray[np.float64]:
@@ -422,9 +426,11 @@ class RasterModel(BaseModel):
422
426
 
423
427
  return raster_gdf
424
428
 
425
- def to_file(self, path: Path) -> None:
429
+ def to_file(self, path: Path | str) -> None:
426
430
  """Write the raster to a GeoTIFF file."""
427
431
 
432
+ path = Path(path)
433
+
428
434
  suffix = path.suffix.lower()
429
435
  if suffix in (".tif", ".tiff"):
430
436
  driver = "GTiff"
@@ -455,8 +461,9 @@ class RasterModel(BaseModel):
455
461
  raise OSError(msg) from err
456
462
 
457
463
  def __str__(self) -> str:
464
+ cls = self.__class__
458
465
  mean = np.nanmean(self.arr)
459
- return f"RasterModel(shape={self.arr.shape}, {mean=})"
466
+ return f"{cls.__name__}(shape={self.arr.shape}, {mean=})"
460
467
 
461
468
  def __repr__(self) -> str:
462
469
  return str(self)
@@ -619,6 +626,7 @@ class RasterModel(BaseModel):
619
626
 
620
627
  factor = self.raster_meta.cell_size / new_cell_size
621
628
 
629
+ cls = self.__class__
622
630
  # Use the rasterio dataset with proper context management
623
631
  with self.to_rasterio_dataset() as dataset:
624
632
  # N.B. the new height and width may increase slightly.
@@ -642,7 +650,7 @@ class RasterModel(BaseModel):
642
650
  cell_size=new_cell_size,
643
651
  )
644
652
 
645
- return RasterModel(arr=new_arr, raster_meta=new_raster_meta)
653
+ return cls(arr=new_arr, raster_meta=new_raster_meta)
646
654
 
647
655
  @field_validator("arr")
648
656
  @classmethod
@@ -155,6 +155,31 @@ class TestRasterModel:
155
155
  # Assert
156
156
  np.testing.assert_array_equal(result.arr, np.array([[6, 8], [10, 12]]))
157
157
 
158
+ def test_add_subclass_return_type(self):
159
+ # Arrange
160
+ class MyRaster(RasterModel):
161
+ pass
162
+
163
+ raster_meta = RasterMeta(
164
+ cell_size=1.0,
165
+ crs=CRS.from_epsg(2193),
166
+ transform=Affine(1.0, 0.0, 0.0, 0.0, 1.0, 0.0),
167
+ )
168
+ raster1 = MyRaster(
169
+ arr=np.array([[1, 2], [3, 4]]),
170
+ raster_meta=raster_meta,
171
+ )
172
+ raster2 = MyRaster(
173
+ arr=np.array([[5, 6], [7, 8]]),
174
+ raster_meta=raster_meta,
175
+ )
176
+
177
+ # Act
178
+ result = raster1 + raster2
179
+
180
+ # Assert
181
+ assert isinstance(result, MyRaster)
182
+
158
183
  def test_crs_mismatch(self):
159
184
  # Arrange
160
185
  raster_meta1 = RasterMeta(
@@ -511,6 +536,16 @@ class TestRasterModel:
511
536
  # Assert
512
537
  assert filename.exists()
513
538
 
539
+ def test_string_as_path(self, tmp_path: Path, example_raster: RasterModel):
540
+ # Arrange
541
+ filename = tmp_path / "test_raster.tif"
542
+
543
+ # Act
544
+ example_raster.to_file(filename.as_posix())
545
+
546
+ # Assert
547
+ assert filename.exists()
548
+
514
549
  class TestPlot:
515
550
  def test_cell_array_unchanged(self, example_raster_with_zeros: RasterModel):
516
551
  # Arrange
@@ -889,6 +889,21 @@ wheels = [
889
889
  { url = "https://files.pythonhosted.org/packages/cb/bd/b394387b598ed84d8d0fa90611a90bee0adc2021820ad5729f7ced74a8e2/imageio-2.37.0-py3-none-any.whl", hash = "sha256:11efa15b87bc7871b61590326b2d635439acc321cf7f8ce996f812543ce10eed", size = 315796, upload-time = "2025-01-20T02:42:34.931Z" },
890
890
  ]
891
891
 
892
+ [[package]]
893
+ name = "import-linter"
894
+ version = "2.3"
895
+ source = { registry = "https://pypi.org/simple" }
896
+ dependencies = [
897
+ { name = "click" },
898
+ { name = "grimp" },
899
+ { name = "tomli", marker = "python_full_version < '3.11'" },
900
+ { name = "typing-extensions" },
901
+ ]
902
+ sdist = { url = "https://files.pythonhosted.org/packages/6e/f4/a3a4110b5b34cdb8553be7c60d66b0169624923bb0597d3fe6f655848a36/import_linter-2.3.tar.gz", hash = "sha256:863646106d52ee5489965670f97a2a78f2c8c68d2d20392322bf0d7cc0111aa7", size = 29321, upload-time = "2025-03-11T09:11:36.002Z" }
903
+ wheels = [
904
+ { url = "https://files.pythonhosted.org/packages/15/62/de70aac73cc7112fd9e582b92dd300d9152a6d40a1d6aad290198ebdb183/import_linter-2.3-py3-none-any.whl", hash = "sha256:5b851776782048ff1be214f1e407ef2e3d30dcb23194e8b852772941811a1258", size = 41584, upload-time = "2025-03-11T09:11:35.07Z" },
905
+ ]
906
+
892
907
  [[package]]
893
908
  name = "importlib-metadata"
894
909
  version = "8.7.0"
@@ -2542,6 +2557,7 @@ dev = [
2542
2557
  { name = "build" },
2543
2558
  { name = "deptry" },
2544
2559
  { name = "folium" },
2560
+ { name = "import-linter" },
2545
2561
  { name = "ipykernel" },
2546
2562
  { name = "ipython", version = "8.37.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
2547
2563
  { name = "ipython", version = "9.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
@@ -2592,6 +2608,7 @@ dev = [
2592
2608
  { name = "build", specifier = ">=1.2.2.post1" },
2593
2609
  { name = "deptry", specifier = ">=0.23.0" },
2594
2610
  { name = "folium", specifier = ">=0.20.0" },
2611
+ { name = "import-linter", specifier = ">=2.3" },
2595
2612
  { name = "ipykernel", specifier = ">=6.30.0" },
2596
2613
  { name = "ipython", specifier = ">=8.37.0" },
2597
2614
  { name = "ipywidgets", specifier = ">=8.1.7" },
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
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
File without changes
File without changes
File without changes
File without changes