core-lens 0.1.dev132__tar.gz → 0.1.dev133__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.
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/PKG-INFO +1 -1
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/src/core_lens/_version.py +2 -2
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/src/core_lens/base/entity.py +128 -15
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/.github/ISSUE_TEMPLATE/blank-proposal.yaml +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/.github/ISSUE_TEMPLATE/bug-report.yaml +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/.github/ISSUE_TEMPLATE/feature-request.yaml +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/.github/pull_request_template.md +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/.github/workflows/ci.yml +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/.github/workflows/gh-pages.yml +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/.github/workflows/pre-release.yml +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/.github/workflows/release.yml +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/.gitignore +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/.gitmessage +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/.pre-commit-config.yaml +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/.python-version +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/CONTRIBUTING.md +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/LICENSE +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/README.md +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/SKILLS.md +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/benchmarks/README.md +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/benchmarks/bench_aoi.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/benchmarks/bench_export.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/benchmarks/bench_polars_utils.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/benchmarks/bench_result.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/benchmarks/bench_schema.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/benchmarks/bench_season.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/benchmarks/bench_spatial.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/benchmarks/bench_view.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/benchmarks/run_all.sh +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/docs/Makefile +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/docs/make.bat +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/docs/source/concepts.md +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/docs/source/conf.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/docs/source/export.md +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/docs/source/index.rst +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/docs/source/intro.md +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/docs/source/logging.md +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/docs/source/plots.md +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/docs/source/plugins.md +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/docs/source/queries.md +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/docs/source/quickstart.md +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/docs/source/stats.md +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/examples/demo_mws.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/examples/demo_tehsil.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/hooks/mypy.sh +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/hooks/no-parquet-outside-fixtures.sh +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/hooks/pytest.sh +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/pyproject.toml +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/src/core_lens/__init__.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/src/core_lens/__main__.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/src/core_lens/aoi.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/src/core_lens/base/__init__.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/src/core_lens/base/namespaces/__init__.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/src/core_lens/base/namespaces/plot.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/src/core_lens/base/namespaces/stats.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/src/core_lens/base/result.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/src/core_lens/base/view.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/src/core_lens/entities/__init__.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/src/core_lens/entities/mws.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/src/core_lens/entities/tehsil.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/src/core_lens/entities/waterbody.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/src/core_lens/export/__init__.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/src/core_lens/export/formats.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/src/core_lens/py.typed +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/src/core_lens/schema/__init__.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/src/core_lens/schema/detection.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/src/core_lens/schema/profile.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/src/core_lens/utils/__init__.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/src/core_lens/utils/paths.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/src/core_lens/utils/polars_utils.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/src/core_lens/utils/season.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/src/core_lens/utils/spatial.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/tests/fixtures/generate_fixtures.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/tests/unit/conftest.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/tests/unit/test_aoi.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/tests/unit/test_entities.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/tests/unit/test_entity.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/tests/unit/test_export.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/tests/unit/test_main.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/tests/unit/test_plot.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/tests/unit/test_polars_utils.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/tests/unit/test_profile.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/tests/unit/test_result.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/tests/unit/test_schema_detection.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/tests/unit/test_schema_profile.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/tests/unit/test_season.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/tests/unit/test_season_config.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/tests/unit/test_spatial.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/tests/unit/test_stats.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/tests/unit/test_view.py +0 -0
- {core_lens-0.1.dev132 → core_lens-0.1.dev133}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: core-lens
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.dev133
|
|
4
4
|
Summary: Query, analyse, and visualise CoreStack's microwatershed and Earth science data through a clean, composable Python API.
|
|
5
5
|
Project-URL: Homepage, https://github.com/ApoorvaKashyap/core-lens
|
|
6
6
|
Project-URL: Issues, https://github.com/ApoorvaKashyap/core-lens/issues
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.1.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 1, '
|
|
21
|
+
__version__ = version = '0.1.dev133'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 1, 'dev133')
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -11,6 +11,7 @@ Plugin authors import from the public surface::
|
|
|
11
11
|
|
|
12
12
|
from __future__ import annotations
|
|
13
13
|
|
|
14
|
+
import functools
|
|
14
15
|
import pathlib
|
|
15
16
|
from abc import ABC, abstractmethod
|
|
16
17
|
from typing import TYPE_CHECKING, Any
|
|
@@ -35,6 +36,96 @@ if TYPE_CHECKING:
|
|
|
35
36
|
from core_lens.schema.profile import SchemaProfile
|
|
36
37
|
|
|
37
38
|
|
|
39
|
+
# ---------------------------------------------------------------------------
|
|
40
|
+
# Process-level caches — shared across all BaseEntity instances in a process.
|
|
41
|
+
#
|
|
42
|
+
# Both caches are backed by functools.cache (lru_cache with maxsize=None).
|
|
43
|
+
# maxsize=None means unbounded — intentional: evicting an index or schema
|
|
44
|
+
# profile forces an expensive rebuild on the next access.
|
|
45
|
+
#
|
|
46
|
+
# _cached_detect eliminates repeated detect() calls (OPT-2).
|
|
47
|
+
# _cached_build_index eliminates repeated build_bbox_index() calls (OPT-1).
|
|
48
|
+
#
|
|
49
|
+
# Cache diagnostics: _cached_detect.cache_info() / _cached_build_index.cache_info()
|
|
50
|
+
# Cache reset (tests): _cached_detect.cache_clear() / _cached_build_index.cache_clear()
|
|
51
|
+
# ---------------------------------------------------------------------------
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _so_key(storage_options: dict[str, Any]) -> tuple[tuple[str, Any], ...]:
|
|
55
|
+
"""Convert a ``storage_options`` dict to a hashable, order-stable tuple.
|
|
56
|
+
|
|
57
|
+
All args passed to :func:`_cached_detect` and :func:`_cached_build_index`
|
|
58
|
+
must be hashable (``functools.cache`` requirement). ``storage_options``
|
|
59
|
+
is a plain dict, so we convert it here before passing to the cached
|
|
60
|
+
functions. Different cloud credentials for the same path produce
|
|
61
|
+
different cache keys.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
storage_options (dict[str, Any]): Cloud credential / configuration dict.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
tuple[tuple[str, Any], ...]: A sorted tuple of ``(key, value)`` pairs.
|
|
68
|
+
"""
|
|
69
|
+
return tuple(sorted(storage_options.items()))
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@functools.cache
|
|
73
|
+
def _cached_detect(
|
|
74
|
+
static_path: str,
|
|
75
|
+
key_cols: tuple[str, ...],
|
|
76
|
+
geometry_col: str,
|
|
77
|
+
annual_path: str | None,
|
|
78
|
+
fortnightly_path: str | None,
|
|
79
|
+
storage_options_key: tuple[tuple[str, Any], ...],
|
|
80
|
+
) -> "SchemaProfile":
|
|
81
|
+
"""Cached wrapper around :func:`~core_lens.schema.detection.detect`.
|
|
82
|
+
|
|
83
|
+
All arguments must be hashable so they can form the cache key. Pass
|
|
84
|
+
``storage_options`` through :func:`_so_key` before calling this function.
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
SchemaProfile: Detected schema profile for the given entity paths.
|
|
88
|
+
"""
|
|
89
|
+
from core_lens.schema.detection import detect
|
|
90
|
+
|
|
91
|
+
return detect(
|
|
92
|
+
static_path=static_path,
|
|
93
|
+
key_cols=list(key_cols),
|
|
94
|
+
geometry_col=geometry_col,
|
|
95
|
+
annual_path=annual_path,
|
|
96
|
+
fortnightly_path=fortnightly_path,
|
|
97
|
+
storage_options=dict(storage_options_key) or None,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@functools.cache
|
|
102
|
+
def _cached_build_index(
|
|
103
|
+
static_path: str,
|
|
104
|
+
key_cols: tuple[str, ...],
|
|
105
|
+
bbox_cols: tuple[str, str, str, str] | None,
|
|
106
|
+
geometry_col: str,
|
|
107
|
+
geometry_type: str,
|
|
108
|
+
storage_options_key: tuple[tuple[str, Any], ...],
|
|
109
|
+
) -> pl.DataFrame:
|
|
110
|
+
"""Cached wrapper around :func:`~core_lens.utils.spatial.build_bbox_index`.
|
|
111
|
+
|
|
112
|
+
All arguments must be hashable so they can form the cache key. Pass
|
|
113
|
+
``storage_options`` through :func:`_so_key` before calling this function.
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
pl.DataFrame: The bounding-box index DataFrame.
|
|
117
|
+
"""
|
|
118
|
+
logger.debug("Building bbox index for {} (cache miss)", static_path)
|
|
119
|
+
return build_bbox_index(
|
|
120
|
+
static_path=static_path,
|
|
121
|
+
key_cols=list(key_cols),
|
|
122
|
+
bbox_cols=bbox_cols,
|
|
123
|
+
geometry_col=geometry_col,
|
|
124
|
+
geometry_type=geometry_type,
|
|
125
|
+
storage_options=dict(storage_options_key) or None,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
|
|
38
129
|
class BaseEntity(ABC):
|
|
39
130
|
"""Abstract base class for every entity in the core_lens plugin system.
|
|
40
131
|
|
|
@@ -228,19 +319,29 @@ class BaseEntity(ABC):
|
|
|
228
319
|
def schema_profile(self) -> "SchemaProfile":
|
|
229
320
|
"""Validated schema descriptor for this entity's data files.
|
|
230
321
|
|
|
231
|
-
Auto-detected from Parquet file metadata on first access
|
|
232
|
-
|
|
233
|
-
|
|
322
|
+
Auto-detected from Parquet file metadata on first access. The result
|
|
323
|
+
is cached at two levels:
|
|
324
|
+
|
|
325
|
+
1. **Instance level** — ``_schema_profile`` attribute set on ``self``
|
|
326
|
+
so that repeated property access within the same instance is a bare
|
|
327
|
+
attribute lookup with no dict overhead.
|
|
328
|
+
2. **Process level** — :func:`_cached_detect` (backed by
|
|
329
|
+
``functools.cache``) keyed on the resolved paths and entity
|
|
330
|
+
configuration. Different ``AoI()`` calls that point at the same
|
|
331
|
+
data directory share one ``SchemaProfile`` object, avoiding
|
|
332
|
+
redundant ``collect_schema()`` I/O.
|
|
333
|
+
|
|
334
|
+
Override in subclasses to provide an explicit profile instead of
|
|
335
|
+
relying on detection.
|
|
234
336
|
|
|
235
337
|
Returns:
|
|
236
338
|
SchemaProfile: A fully-validated :class:`~core_lens.schema.profile.SchemaProfile`.
|
|
237
339
|
"""
|
|
238
340
|
if not hasattr(self, "_schema_profile"):
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
self._schema_profile: SchemaProfile = detect(
|
|
341
|
+
_so = self._storage_options or {}
|
|
342
|
+
self._schema_profile: SchemaProfile = _cached_detect(
|
|
242
343
|
static_path=self._resolve(self.static_path),
|
|
243
|
-
key_cols=self.key_cols,
|
|
344
|
+
key_cols=tuple(self.key_cols),
|
|
244
345
|
geometry_col=self.geometry_col,
|
|
245
346
|
annual_path=(
|
|
246
347
|
self._resolve(self.annual_path)
|
|
@@ -252,24 +353,36 @@ class BaseEntity(ABC):
|
|
|
252
353
|
if self.fortnightly_path is not None
|
|
253
354
|
else None
|
|
254
355
|
),
|
|
255
|
-
|
|
356
|
+
storage_options_key=_so_key(_so),
|
|
256
357
|
)
|
|
257
358
|
return self._schema_profile
|
|
258
359
|
|
|
259
360
|
@property
|
|
260
361
|
def _index(self) -> pl.DataFrame:
|
|
362
|
+
"""In-memory bounding-box index for spatial pre-filtering.
|
|
363
|
+
|
|
364
|
+
Cached at two levels (same strategy as :attr:`schema_profile`):
|
|
365
|
+
|
|
366
|
+
1. **Instance level** — ``_cached_index`` attribute on ``self``.
|
|
367
|
+
2. **Process level** — :func:`_cached_build_index` (backed by
|
|
368
|
+
``functools.cache``) keyed on the resolved static path, key
|
|
369
|
+
columns, and bbox/geometry schema. All entity instances that
|
|
370
|
+
resolve to the same static file share one index ``pl.DataFrame``,
|
|
371
|
+
eliminating repeated geometry decodes.
|
|
372
|
+
|
|
373
|
+
Returns:
|
|
374
|
+
pl.DataFrame: DataFrame with columns ``(*key_cols, minx, miny, maxx, maxy)``.
|
|
375
|
+
"""
|
|
261
376
|
if not hasattr(self, "_cached_index"):
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
profile = self.schema_profile
|
|
266
|
-
self._cached_index: pl.DataFrame = build_bbox_index(
|
|
377
|
+
profile = self.schema_profile # already cached — no extra I/O
|
|
378
|
+
_so = self._storage_options or {}
|
|
379
|
+
self._cached_index: pl.DataFrame = _cached_build_index(
|
|
267
380
|
static_path=self._resolve(self.static_path),
|
|
268
|
-
key_cols=self.key_cols,
|
|
381
|
+
key_cols=tuple(self.key_cols),
|
|
269
382
|
bbox_cols=profile.bbox_cols,
|
|
270
383
|
geometry_col=profile.geometry_col,
|
|
271
384
|
geometry_type=profile.geometry_type,
|
|
272
|
-
|
|
385
|
+
storage_options_key=_so_key(_so),
|
|
273
386
|
)
|
|
274
387
|
return self._cached_index
|
|
275
388
|
|
|
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
|
|
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
|
|
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
|