GeoAlchemy2 0.17.0__py3-none-any.whl → 0.18.0__py3-none-any.whl

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.
geoalchemy2/comparator.py CHANGED
@@ -37,7 +37,7 @@ Using the ORM::
37
37
  Session.query(Cls).order_by(Cls.geom.distance_box('POINT(0 0)')).limit(10)
38
38
  """
39
39
 
40
- from typing import Union
40
+ from typing import Any
41
41
 
42
42
  from sqlalchemy import types as sqltypes
43
43
  from sqlalchemy.dialects.postgresql import DOUBLE_PRECISION
@@ -65,7 +65,7 @@ SAME: custom_op = custom_op("~=")
65
65
  DISTANCE_CENTROID: custom_op = custom_op("<->")
66
66
  DISTANCE_BOX: custom_op = custom_op("<#>")
67
67
 
68
- _COMPARATOR_INPUT_TYPE = Union[str, WKBElement, WKTElement]
68
+ _COMPARATOR_INPUT_TYPE = str | WKBElement | WKTElement
69
69
 
70
70
 
71
71
  class BaseComparator(UserDefinedType.Comparator):
@@ -80,13 +80,13 @@ class BaseComparator(UserDefinedType.Comparator):
80
80
 
81
81
  key = None
82
82
 
83
- def __getattr__(self, name):
83
+ def __getattr__(self, name: str) -> Any:
84
84
  # Function names that don't start with "ST_" are rejected.
85
85
  # This is not to mess up with SQLAlchemy's use of
86
86
  # hasattr/getattr on Column objects.
87
87
 
88
88
  if not name.lower().startswith("st_"):
89
- raise AttributeError
89
+ raise AttributeError(f"Attribute {name} not found")
90
90
 
91
91
  # We create our own _FunctionGenerator here, and use it in place of
92
92
  # SQLAlchemy's "func" object. This is to be able to "bind" the
geoalchemy2/elements.py CHANGED
@@ -35,6 +35,11 @@ class _SpatialElement:
35
35
 
36
36
  """
37
37
 
38
+ # Define __slots__ to restrict attributes in this class.
39
+ # This is done intentionally to improve performance by preventing
40
+ # the creation of a dynamic __dict__ for each instance.
41
+ __slots__ = ("srid", "data", "extended")
42
+
38
43
  def __init__(self, data, srid: int = -1, extended: Optional[bool] = None) -> None:
39
44
  self.srid = srid
40
45
  self.data = data
@@ -114,8 +119,16 @@ class WKTElement(_SpatialElement):
114
119
  wkt_element_1 = WKTElement('POINT(5 45)')
115
120
  wkt_element_2 = WKTElement('POINT(5 45)', srid=4326)
116
121
  wkt_element_3 = WKTElement('SRID=4326;POINT(5 45)', extended=True)
122
+
123
+ Note::
124
+ This class uses ``__slots__`` to restrict its attributes and improve memory efficiency by
125
+ preventing the creation of a dynamic ``__dict__`` for each instance.
126
+ If you require dynamic attributes or support for weak references, use the
127
+ ``DynamicWKTElement`` subclass, which provides these capabilities.
117
128
  """
118
129
 
130
+ __slots__ = ()
131
+
119
132
  _REMOVE_SRID = re.compile("(SRID=([0-9]+); ?)?(.*)")
120
133
  SPLIT_WKT_PATTERN = re.compile(r"((SRID=\d+) *; *)?([\w ]+) *(\([-\d\. ,\(\)]+\))")
121
134
 
@@ -160,6 +173,15 @@ class WKTElement(_SpatialElement):
160
173
  return WKTElement(self.data, self.srid, self.extended)
161
174
 
162
175
 
176
+ class DynamicWKTElement(WKTElement):
177
+ """This is a subclass of ``WKTElement`` that allows dynamic attributes.
178
+
179
+ It is useful when you need to add attributes dynamically to the object.
180
+ """
181
+
182
+ __slots__ = ("__dict__", "__weakref__")
183
+
184
+
163
185
  class WKBElement(_SpatialElement):
164
186
  """Instances of this class wrap a WKB or EWKB value.
165
187
 
@@ -172,13 +194,21 @@ class WKBElement(_SpatialElement):
172
194
 
173
195
  Note: you can create ``WKBElement`` objects from Shapely geometries
174
196
  using the :func:`geoalchemy2.shape.from_shape` function.
197
+
198
+ Note::
199
+ This class uses ``__slots__`` to restrict its attributes and improve memory efficiency by
200
+ preventing the creation of a dynamic ``__dict__`` for each instance.
201
+ If you require dynamic attributes or support for weak references, use the
202
+ ``DynamicWKBElement`` subclass, which provides these capabilities.
175
203
  """
176
204
 
205
+ __slots__ = ()
206
+
177
207
  geom_from: str = "ST_GeomFromWKB"
178
208
  geom_from_extended_version: str = "ST_GeomFromEWKB"
179
209
 
180
210
  def __init__(
181
- self, data: Union[str, bytes, memoryview], srid: int = -1, extended: Optional[bool] = None
211
+ self, data: str | bytes | memoryview, srid: int = -1, extended: Optional[bool] = None
182
212
  ) -> None:
183
213
  if srid == -1 or extended is None or extended:
184
214
  # read srid from the EWKB
@@ -216,14 +246,18 @@ class WKBElement(_SpatialElement):
216
246
  srid = int(wkb_srid)
217
247
  _SpatialElement.__init__(self, data, srid, extended)
218
248
 
249
+ @staticmethod
250
+ def _wkb_to_hex(data: Union[str, bytes, memoryview]) -> str:
251
+ """Convert WKB to hex string."""
252
+ if isinstance(data, str):
253
+ # SpatiaLite case
254
+ return data.lower()
255
+ return str(binascii.hexlify(data), encoding="utf-8").lower()
256
+
219
257
  @property
220
258
  def desc(self) -> str:
221
259
  """This element's description string."""
222
- if isinstance(self.data, str):
223
- # SpatiaLite case
224
- return self.data.lower()
225
- desc = str(binascii.hexlify(self.data), encoding="utf-8").lower()
226
- return desc
260
+ return self._wkb_to_hex(self.data)
227
261
 
228
262
  @staticmethod
229
263
  def _data_from_desc(desc) -> bytes:
@@ -277,7 +311,7 @@ class WKBElement(_SpatialElement):
277
311
  )
278
312
  wkb_type_int |= 536870912 # Set SRID bit to 1 and keep all other bits
279
313
 
280
- data: Union[str, memoryview]
314
+ data: str | memoryview
281
315
  if isinstance(self.data, str):
282
316
  wkb_type_hex = binascii.hexlify(
283
317
  wkb_type_int.to_bytes(4, "little" if byte_order else "big")
@@ -303,13 +337,30 @@ class WKBElement(_SpatialElement):
303
337
  return WKBElement(self.data, self.srid)
304
338
 
305
339
 
340
+ class DynamicWKBElement(WKBElement):
341
+ """This is a subclass of ``WKBElement`` that allows dynamic attributes.
342
+
343
+ It is useful when you need to add attributes dynamically to the object.
344
+ """
345
+
346
+ __slots__ = ("__dict__", "__weakref__")
347
+
348
+
306
349
  class RasterElement(_SpatialElement):
307
350
  """Instances of this class wrap a ``raster`` value.
308
351
 
309
352
  Raster values read from the database are converted to instances of this type. In
310
353
  most cases you won't need to create ``RasterElement`` instances yourself.
354
+
355
+ Note::
356
+ This class uses ``__slots__`` to restrict its attributes and improve memory efficiency by
357
+ preventing the creation of a dynamic ``__dict__`` for each instance.
358
+ If you require dynamic attributes or support for weak references, use the
359
+ ``DynamicRasterElement`` subclass, which provides these capabilities.
311
360
  """
312
361
 
362
+ __slots__ = ()
363
+
313
364
  geom_from_extended_version: str = "raster"
314
365
 
315
366
  def __init__(self, data: Union[str, bytes, memoryview]) -> None:
@@ -337,9 +388,20 @@ class RasterElement(_SpatialElement):
337
388
  return desc
338
389
 
339
390
 
391
+ class DynamicRasterElement(RasterElement):
392
+ """This is a subclass of ``RasterElement`` that allows dynamic attributes.
393
+
394
+ It is useful when you need to add attributes dynamically to the object.
395
+ """
396
+
397
+ __slots__ = ("__dict__", "__weakref__")
398
+
399
+
340
400
  class CompositeElement(FunctionElement):
341
401
  """Instances of this class wrap a Postgres composite type."""
342
402
 
403
+ __slots__ = ("name", "type")
404
+
343
405
  inherit_cache: bool = False
344
406
  """The cache is disabled for this class."""
345
407
 
@@ -361,6 +423,9 @@ __all__: List[str] = [
361
423
  "RasterElement",
362
424
  "WKBElement",
363
425
  "WKTElement",
426
+ "DynamicRasterElement",
427
+ "DynamicWKBElement",
428
+ "DynamicWKTElement",
364
429
  ]
365
430
 
366
431
 
geoalchemy2/functions.py CHANGED
@@ -76,6 +76,7 @@ from sqlalchemy.ext.compiler import compiles
76
76
  from sqlalchemy.sql import annotation
77
77
  from sqlalchemy.sql import functions
78
78
  from sqlalchemy.sql.elements import ColumnElement
79
+ from sqlalchemy.sql.selectable import FromClause
79
80
 
80
81
  from geoalchemy2 import elements
81
82
  from geoalchemy2._functions import _FUNCTIONS
@@ -134,11 +135,11 @@ class TableRowElement(ColumnElement):
134
135
  inherit_cache: bool = False
135
136
  """The cache is disabled for this class."""
136
137
 
137
- def __init__(self, selectable: bool) -> None:
138
+ def __init__(self, selectable: FromClause) -> None:
138
139
  self.selectable = selectable
139
140
 
140
141
  @property
141
- def _from_objects(self) -> List[bool]:
142
+ def _from_objects(self) -> List[FromClause]:
142
143
  return [self.selectable]
143
144
 
144
145