Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Have many sources use lazy imports #1577

Merged
merged 1 commit into from
Jul 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

- Improve plottable data endpoint to better fetch adjacent items and annotations ([#1573](../../pull/1573), [#1574](../../pull/1574))), [#1575](../../pull/1575)))
- Support Girder flat-mount paths ([#1576](../../pull/1576))
- Lazily import some modules to speed up large_image import speed ([#1577](../../pull/1577))
- Create or check large images for each item in a folder ([#1572](../../pull/1572))

## 1.29.2

Expand Down
65 changes: 46 additions & 19 deletions sources/gdal/large_image_source_gdal/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,28 +20,11 @@
import struct
import tempfile
import threading
from importlib.metadata import PackageNotFoundError
from importlib.metadata import version as _importlib_version

import numpy as np
import PIL.Image
from osgeo import gdal, gdal_array, gdalconst, osr

try:
gdal.UseExceptions()
except Exception:
pass

# isort: off

# pyproj stopped supporting older pythons, so on those versions its database is
# aging; as such, if on those older versions of python if it is imported before
# gdal, there can be a database version conflict; importing after gdal avoids
# this.
import pyproj

# isort: on

from importlib.metadata import PackageNotFoundError
from importlib.metadata import version as _importlib_version

from large_image.cache_util import LruCacheMetaclass, methodcache
from large_image.constants import (TILE_FORMAT_IMAGE, TILE_FORMAT_NUMPY,
Expand All @@ -62,6 +45,42 @@
# package is not installed
pass

gdal = None
gdal_array = None
gdalconst = None
osr = None
pyproj = None


def _lazyImport():
"""
Import the gdal module. This is done when needed rather than in the
module initialization because it is slow.
"""
global gdal, gdal_array, gdalconst, osr, pyproj

if gdal is None:
try:
from osgeo import gdal, gdal_array, gdalconst, osr

try:
gdal.UseExceptions()
except Exception:
pass

# isort: off

# pyproj stopped supporting older pythons, so on those versions its
# database is aging; as such, if on those older versions of python
# if it is imported before gdal, there can be a database version
# conflict; importing after gdal avoids this.
import pyproj

# isort: on
except ImportError:
msg = 'gdal module not found.'
raise TileSourceError(msg)


class GDALFileTileSource(GDALBaseFileTileSource, metaclass=LruCacheMetaclass):
"""
Expand Down Expand Up @@ -91,6 +110,8 @@ def __init__(self, path, projection=None, unitsPerPixel=None, **kwargs):
specify unitsPerPixel.
"""
super().__init__(path, **kwargs)
_lazyImport()

self.addKnownExtensions()
self._bounds = {}
self._largeImagePath = self._getLargeImagePath()
Expand Down Expand Up @@ -300,6 +321,8 @@ def _proj4Proj(proj):
:returns: a proj4 projection object. None if the specified projection
cannot be created.
"""
_lazyImport()

if isinstance(proj, bytes):
proj = proj.decode()
if not isinstance(proj, str):
Expand Down Expand Up @@ -940,6 +963,8 @@ def isGeospatial(path):
:param path: The path to the file
:returns: True if geospatial.
"""
_lazyImport()

try:
ds = gdal.Open(str(path), gdalconst.GA_ReadOnly)
except Exception:
Expand All @@ -958,6 +983,8 @@ def isGeospatial(path):
@classmethod
def addKnownExtensions(cls):
if not hasattr(cls, '_addedExtensions'):
_lazyImport()

cls._addedExtensions = True
cls.extensions = cls.extensions.copy()
cls.mimeTypes = cls.mimeTypes.copy()
Expand Down
25 changes: 22 additions & 3 deletions sources/multi/large_image_source_multi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
from importlib.metadata import version as _importlib_version
from pathlib import Path

import jsonschema
import numpy as np
import yaml

Expand All @@ -27,6 +26,26 @@
# package is not installed
pass

jsonschema = None
_validator = None


def _lazyImport():
"""
Import the jsonschema module. This is done when needed rather than in the
module initialization because it is slow.
"""
global jsonschema, _validator

if jsonschema is None:
try:
import jsonschema

_validator = jsonschema.Draft6Validator(MultiSourceSchema)
except ImportError:
msg = 'jsonschema module not found.'
raise TileSourceError(msg)


SourceEntrySchema = {
'type': 'object',
Expand Down Expand Up @@ -387,8 +406,6 @@ class MultiFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
_defaultTileSize = 256
_maxOpenHandles = 6

_validator = jsonschema.Draft6Validator(MultiSourceSchema)

def __init__(self, path, **kwargs):
"""
Initialize the tile class. See the base class for other available
Expand All @@ -398,6 +415,8 @@ def __init__(self, path, **kwargs):
"""
super().__init__(path, **kwargs)

_lazyImport()
self._validator = _validator
self._largeImagePath = self._getLargeImagePath()
self._lastOpenSourceLock = threading.RLock()
# 'c' must be first as channels are special because they can have names
Expand Down
19 changes: 18 additions & 1 deletion sources/openjpeg/large_image_source_openjpeg/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
from importlib.metadata import version as _importlib_version
from xml.etree import ElementTree

import glymur
import PIL.Image

import large_image
Expand All @@ -41,6 +40,23 @@
# package is not installed
pass

glymur = None


def _lazyImport():
"""
Import the glymur module. This is done when needed rather than in the module
initialization because it is slow.
"""
global glymur

if glymur is None:
try:
import glymur
except ImportError:
msg = 'glymur module not found.'
raise TileSourceError(msg)


warnings.filterwarnings('ignore', category=UserWarning, module='glymur')

Expand Down Expand Up @@ -88,6 +104,7 @@ def __init__(self, path, **kwargs):
"""
super().__init__(path, **kwargs)

_lazyImport()
self._largeImagePath = str(self._getLargeImagePath())
self._pixelInfo = {}
try:
Expand Down
Loading