Skip to content

Commit 5b82f0b

Browse files
authored
Merge pull request #952 from blankirigaya/fix/446-guard-free-basemap-import
fix: guard against free basemap providers going out of service (#446)
2 parents 515ed4f + 9ecb3ff commit 5b82f0b

1 file changed

Lines changed: 59 additions & 10 deletions

File tree

malariagen_data/anoph/map_params.py

Lines changed: 59 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Parameters for functions plotting maps using ipyleaflet."""
22

3+
import warnings
34
from typing import Dict, Tuple, Union
45

56
import ipyleaflet # type: ignore
@@ -17,22 +18,70 @@
1718

1819
zoom_default: zoom = 3
1920

20-
basemap_abbrevs = {
21-
"mapnik": ipyleaflet.basemaps.OpenStreetMap.Mapnik,
22-
"natgeoworldmap": ipyleaflet.basemaps.Esri.NatGeoWorldMap,
23-
"opentopomap": ipyleaflet.basemaps.OpenTopoMap,
24-
"positron": ipyleaflet.basemaps.CartoDB.Positron,
25-
"satellite": ipyleaflet.basemaps.Gaode.Satellite,
26-
"worldimagery": ipyleaflet.basemaps.Esri.WorldImagery,
27-
"worldstreetmap": ipyleaflet.basemaps.Esri.WorldStreetMap,
28-
"worldtopomap": ipyleaflet.basemaps.Esri.WorldTopoMap,
21+
# Candidate basemap abbreviations — loaded lazily to avoid import failures
22+
# if a provider has been decommissioned upstream.
23+
_basemap_abbrev_candidates = {
24+
"mapnik": lambda: ipyleaflet.basemaps.OpenStreetMap.Mapnik,
25+
"natgeoworldmap": lambda: ipyleaflet.basemaps.Esri.NatGeoWorldMap,
26+
"opentopomap": lambda: ipyleaflet.basemaps.OpenTopoMap,
27+
"positron": lambda: ipyleaflet.basemaps.CartoDB.Positron,
28+
"satellite": lambda: ipyleaflet.basemaps.Gaode.Satellite,
29+
"worldimagery": lambda: ipyleaflet.basemaps.Esri.WorldImagery,
30+
"worldstreetmap": lambda: ipyleaflet.basemaps.Esri.WorldStreetMap,
31+
"worldtopomap": lambda: ipyleaflet.basemaps.Esri.WorldTopoMap,
2932
}
3033

34+
_basemap_abbrevs: dict | None = None
35+
36+
37+
def get_basemap_abbrevs() -> dict:
38+
"""Return available basemap abbreviations, skipping any unavailable providers."""
39+
global _basemap_abbrevs
40+
if _basemap_abbrevs is None:
41+
_basemap_abbrevs = {}
42+
for key, provider_fn in _basemap_abbrev_candidates.items():
43+
try:
44+
_basemap_abbrevs[key] = provider_fn()
45+
except Exception:
46+
warnings.warn(
47+
f"Basemap provider {key!r} is not available and will be skipped.",
48+
stacklevel=2,
49+
)
50+
return _basemap_abbrevs
51+
52+
53+
# Keep basemap_abbrevs as a property-like alias for backwards compatibility.
54+
# Code that does `map_params.basemap_abbrevs` will call get_basemap_abbrevs().
55+
class _BasemapAbbrevProxy:
56+
def __getattr__(self, item):
57+
return getattr(get_basemap_abbrevs(), item)
58+
59+
def __iter__(self):
60+
return iter(get_basemap_abbrevs())
61+
62+
def __getitem__(self, item):
63+
return get_basemap_abbrevs()[item]
64+
65+
def __contains__(self, item):
66+
return item in get_basemap_abbrevs()
67+
68+
def keys(self):
69+
return get_basemap_abbrevs().keys()
70+
71+
def values(self):
72+
return get_basemap_abbrevs().values()
73+
74+
def items(self):
75+
return get_basemap_abbrevs().items()
76+
77+
78+
basemap_abbrevs = _BasemapAbbrevProxy()
79+
3180
basemap: TypeAlias = Annotated[
3281
Union[str, Dict, ipyleaflet.TileLayer, xyzservices.lib.TileProvider],
3382
f"""
3483
Basemap from ipyleaflet or other TileLayer provider. Strings are abbreviations mapped to corresponding
35-
basemaps, available values are {list(basemap_abbrevs.keys())}.
84+
basemaps, available values are {list(_basemap_abbrev_candidates.keys())}.
3685
""",
3786
]
3887

0 commit comments

Comments
 (0)