#36441: Add lazy loading support for GDAL library in `django.contrib.gis.gdal`
-------------------------------------+-------------------------------------
Reporter: Josh Thomas | Owner: Josh
Type: | Thomas
Cleanup/optimization | Status: assigned
Component: GIS | Version: dev
Severity: Normal | Resolution:
Keywords: gdal | Triage Stage: Accepted
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 1
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Comment (by Jacob Walls):
> So at what point would an exception be raised? How does it compare to
GEOS?
I rechecked this, and GEOS isn't lazy-loaded after all, I don't think.
This is the traceback I get on startup when setting `GEOS_LIBRARY_PATH =
"/dev/null"`:
{{{#!py
File "/Users/jwalls/django/django/__init__.py", line 24, in setup
apps.populate(settings.INSTALLED_APPS)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/jwalls/django/django/apps/registry.py", line 116, in
populate
app_config.import_models()
~~~~~~~~~~~~~~~~~~~~~~~~^^
File "/Users/jwalls/django/django/apps/config.py", line 269, in
import_models
self.models_module = import_module(models_module_name)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
File
"/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/importlib/__init__.py",
line 88, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "<frozen importlib._bootstrap>", line 1387, in _gcd_import
File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
File "<frozen importlib._bootstrap>", line 1331, in
_find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 935, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 1026, in exec_module
File "<frozen importlib._bootstrap>", line 488, in
_call_with_frames_removed
File "/Users/jwalls/django/django/contrib/auth/models.py", line 5, in
<module>
from django.contrib.auth.base_user import AbstractBaseUser,
BaseUserManager
File "/Users/jwalls/django/django/contrib/auth/base_user.py", line 43,
in <module>
class AbstractBaseUser(models.Model):
...<120 lines>...
)
File "/Users/jwalls/django/django/db/models/base.py", line 146, in
__new__
new_class.add_to_class("_meta", Options(meta, app_label))
~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/jwalls/django/django/db/models/base.py", line 394, in
add_to_class
value.contribute_to_class(cls, name)
~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
File "/Users/jwalls/django/django/db/models/options.py", line 245, in
contribute_to_class
self.db_table, connection.ops.max_name_length()
^^^^^^^^^^^^^^
File "/Users/jwalls/django/django/utils/connection.py", line 15, in
__getattr__
return getattr(self._connections[self._alias], item)
~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^
File "/Users/jwalls/django/django/utils/connection.py", line 62, in
__getitem__
conn = self.create_connection(alias)
File "/Users/jwalls/django/django/db/utils.py", line 196, in
create_connection
backend = load_backend(db["ENGINE"])
File "/Users/jwalls/django/django/db/utils.py", line 116, in
load_backend
return import_module("%s.base" % backend_name)
File
"/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/importlib/__init__.py",
line 88, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File
"/Users/jwalls/django/django/contrib/gis/db/backends/postgis/base.py",
line 16, in <module>
from .adapter import PostGISAdapter
File
"/Users/jwalls/django/django/contrib/gis/db/backends/postgis/adapter.py",
line 6, in <module>
from django.contrib.gis.geos import GEOSGeometry
File "/Users/jwalls/django/django/contrib/gis/geos/__init__.py", line 6,
in <module>
from .collections import ( # NOQA
...<4 lines>...
)
File "/Users/jwalls/django/django/contrib/gis/geos/collections.py", line
7, in <module>
from django.contrib.gis.geos.geometry import GEOSGeometry,
LinearGeometryMixin
File "/Users/jwalls/django/django/contrib/gis/geos/geometry.py", line
18, in <module>
from
django.contrib.gis.geos.prototypes.io import ewkb_w, wkb_r,
wkb_w, wkt_r, wkt_w
File "/Users/jwalls/django/django/contrib/gis/geos/prototypes/io.py",
line 249, in <module>
class WKBWriter(IOBase):
...<67 lines>...
wkb_writer_set_include_srid(self.ptr, bool(include))
File "/Users/jwalls/django/django/contrib/gis/geos/prototypes/io.py",
line 253, in WKBWriter
geos_version = geos_version_tuple()
File "/Users/jwalls/django/django/contrib/gis/geos/libgeos.py", line
175, in geos_version_tuple
return get_version_tuple(geos_version().decode())
~~~~~~~~~~~~^^
File "/Users/jwalls/django/django/contrib/gis/geos/libgeos.py", line
170, in geos_version
return lgeos.GEOSversion()
^^^^^^^^^^^^^^^^^
File "/Users/jwalls/django/django/utils/functional.py", line 251, in
inner
self._setup()
~~~~~~~~~~~^^
File "/Users/jwalls/django/django/utils/functional.py", line 404, in
_setup
self._wrapped = self._setupfunc()
~~~~~~~~~~~~~~~^^
File "/Users/jwalls/django/django/contrib/gis/geos/libgeos.py", line 62,
in load_geos
_lgeos = CDLL(lib_path)
File
"/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/ctypes/__init__.py",
line 390, in __init__
self._handle = _dlopen(self._name, mode)
~~~~~~~^^^^^^^^^^^^^^^^^^
OSError: dlopen(/dev/null, 0x0006): tried: '/dev/null' (not a file),
'/System/Volumes/Preboot/Cryptexes/OS/dev/null' (no such file),
'/dev/null' (not a file)
}}}
So the effect of the PR would be to hide failures only for GDAL, rather
than to bring the behavior to parity with GEOS.
Josh, do you agree that a simple system check for a working registration
of GDAL registered by `contrib.gis` would be enough to move this forward?
--
Ticket URL: <
https://code.djangoproject.com/ticket/36441#comment:23>