GeoAlchemy2 0.16.0__py3-none-any.whl → 0.17.1__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.
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: GeoAlchemy2
3
- Version: 0.16.0
3
+ Version: 0.17.1
4
4
  Summary: Using SQLAlchemy with Spatial Databases
5
5
  Home-page: https://geoalchemy-2.readthedocs.io/en/stable/
6
6
  Author: Eric Lemoine
@@ -28,6 +28,18 @@ Requires-Dist: SQLAlchemy>=1.4
28
28
  Requires-Dist: packaging
29
29
  Provides-Extra: shapely
30
30
  Requires-Dist: Shapely>=1.7; extra == "shapely"
31
+ Dynamic: author
32
+ Dynamic: author-email
33
+ Dynamic: classifier
34
+ Dynamic: description
35
+ Dynamic: home-page
36
+ Dynamic: keywords
37
+ Dynamic: license
38
+ Dynamic: project-url
39
+ Dynamic: provides-extra
40
+ Dynamic: requires-dist
41
+ Dynamic: requires-python
42
+ Dynamic: summary
31
43
 
32
44
  ============
33
45
  GeoAlchemy 2
@@ -0,0 +1,35 @@
1
+ geoalchemy2/__init__.py,sha256=aGo3WcjOBcqrvqYXBuzCff1d_WiFUFObgtcyR11gg6M,2043
2
+ geoalchemy2/_functions.py,sha256=e8v584Fx_fmh8XnJvkuYEXrcyPDiWV95Mu2yw6L-LHY,62863
3
+ geoalchemy2/_functions_helpers.py,sha256=V_e58pX6aalDD3HeECZbUen__2vHhEhMZty2qIZSBGA,2680
4
+ geoalchemy2/alembic_helpers.py,sha256=3U0Co-VdkkZ44MaRDG-ukN93dRk6qWDz2BOgcEOpTl4,27906
5
+ geoalchemy2/comparator.py,sha256=WUDXn10doDlJVnYiWbVIAhhBu7N5AO9BI8sNf2mhpA4,8100
6
+ geoalchemy2/elements.py,sha256=5yd_7dUQGbrLvgYvo6A2YYAFwTvP4_BskIdBCB0DrlM,13002
7
+ geoalchemy2/exc.py,sha256=Nn9bRKB_35skWDMkEf4_Y2GL6gvguPVycPZcbOfa69g,226
8
+ geoalchemy2/functions.py,sha256=8VzEoE1jANlOmWM4Y6i8eGPwYSwRMLZLulqpyyCgkMs,10375
9
+ geoalchemy2/functions.pyi,sha256=bQUzlBCuJXGiDVPHKwGCO5tDYH5KRYxgnERGn37BVn8,108665
10
+ geoalchemy2/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ geoalchemy2/shape.py,sha256=TLeWa6NwXZqBxh3Zj-l96wBB5kPviEhnZ44kyr8v678,2735
12
+ geoalchemy2/utils.py,sha256=OYWYnT64tjp4DWhPdq3KIxHbVti932xPMtGGxajtu-I,488
13
+ geoalchemy2/admin/__init__.py,sha256=O5kr1ETm3lTpEELm16Oxq6344KztOZmUaapf0ZIP6b8,4016
14
+ geoalchemy2/admin/plugin.py,sha256=2U5p4_JdZM7b2MOXXBfP8KhtXlLHu-ASjxTq4mF-thg,4332
15
+ geoalchemy2/admin/dialects/__init__.py,sha256=z4gBrdglClnmp9VdDW0WwEAGNoemMUSvMCeFFvuQcgs,422
16
+ geoalchemy2/admin/dialects/common.py,sha256=p-CGtcooIESy-BiwZ8bmmdAORjuJtTK-sq_X0jnjHB0,3036
17
+ geoalchemy2/admin/dialects/geopackage.py,sha256=PgOhRftDN4I_OIqqOfop7jgmospReCq6qJi6BGW-Gts,13596
18
+ geoalchemy2/admin/dialects/mariadb.py,sha256=4PtkC1Gv4jcAl_grO5lJwxPsIChi4_8g36soZXnMM3w,4267
19
+ geoalchemy2/admin/dialects/mysql.py,sha256=NP0qXGsvssHZKegP8m8-4Vqbx8CosNHTsq6qfSD2MHs,7181
20
+ geoalchemy2/admin/dialects/postgresql.py,sha256=VwB_h3TC8M5aQ6aKE3UYgxHfbEKav3eIHJeLx534Zzg,6191
21
+ geoalchemy2/admin/dialects/sqlite.py,sha256=iW5MBhbxVKGMVghN5Do0iF-_9Pbtrv65NTXmW9mOyZA,13802
22
+ geoalchemy2/types/__init__.py,sha256=Ud1_6gukAs3gSzN3BThZnC-p0naHnTcNEjlyD5Ahm-M,14240
23
+ geoalchemy2/types/dialects/__init__.py,sha256=GYqO6nDtzElvsV22VN1SSUet2elaZG4ZuRrM-asbstM,414
24
+ geoalchemy2/types/dialects/common.py,sha256=eiIKe-sFAdzgvQ7YT0cV29tYAPrs55GXKJi3p1UvjPQ,158
25
+ geoalchemy2/types/dialects/geopackage.py,sha256=nRmN_PnF-CWMEHkWhKVdsybnw3SvXZIBXAHIHXJLTqw,147
26
+ geoalchemy2/types/dialects/mariadb.py,sha256=tPZpfj95XlVSOm8zC52iFbqVrIOdj-EDH5KVJP1Gd9c,1723
27
+ geoalchemy2/types/dialects/mysql.py,sha256=zMNi1920v0XlVaCJWSwtq0b0P5YQRn8y3X_rBxC5KEw,1790
28
+ geoalchemy2/types/dialects/postgresql.py,sha256=7NBKEbDJXMwX8Sgs6o_N2bAUHgjUcjbrdmYOA7sDiRw,1117
29
+ geoalchemy2/types/dialects/sqlite.py,sha256=B-yLzaQcqL_4dXoOPX9D9IXw2ZQlq-Tibv_kqUIBeb4,2104
30
+ GeoAlchemy2-0.17.1.dist-info/COPYING.rst,sha256=-bQKftq9uMOROzF7oN65kYBBIJKxTmBuDoftp27IC3I,1056
31
+ GeoAlchemy2-0.17.1.dist-info/METADATA,sha256=MWAit1vb7ST4fMZOE0nkxAXMDMGyfAsLFZNLumLynGo,2327
32
+ GeoAlchemy2-0.17.1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
33
+ GeoAlchemy2-0.17.1.dist-info/entry_points.txt,sha256=izFHecGE8cNV6IjoLkc0uEmKH13rYbbvozc4fEwRl6Y,70
34
+ GeoAlchemy2-0.17.1.dist-info/top_level.txt,sha256=3kGUTcfBeXd61zFpof6-qiuw1peNF_HuZabgHQgrdis,12
35
+ GeoAlchemy2-0.17.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.4.0)
2
+ Generator: setuptools (75.8.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -0,0 +1,2 @@
1
+ [sqlalchemy.plugins]
2
+ geoalchemy2 = geoalchemy2.admin.plugin:GeoEngine
geoalchemy2/__init__.py CHANGED
@@ -8,6 +8,7 @@ from geoalchemy2 import shape # noqa
8
8
  from geoalchemy2 import types # noqa
9
9
  from geoalchemy2.admin.dialects.geopackage import load_spatialite_gpkg # noqa
10
10
  from geoalchemy2.admin.dialects.sqlite import load_spatialite # noqa
11
+ from geoalchemy2.admin.plugin import GeoEngine # noqa
11
12
  from geoalchemy2.elements import CompositeElement # noqa
12
13
  from geoalchemy2.elements import RasterElement # noqa
13
14
  from geoalchemy2.elements import WKBElement # noqa
@@ -50,6 +51,7 @@ __all__ = [
50
51
  "__version__",
51
52
  "ArgumentError",
52
53
  "CompositeElement",
54
+ "GeoEngine",
53
55
  "Geography",
54
56
  "Geometry",
55
57
  "Raster",
@@ -50,6 +50,7 @@ from typing import List
50
50
 
51
51
  from sqlalchemy.sql import functions
52
52
  from sqlalchemy.sql.elements import ColumnElement
53
+ from sqlalchemy.sql.selectable import FromClause
53
54
 
54
55
  import geoalchemy2.types
55
56
 
@@ -60,8 +61,8 @@ class TableRowElement(ColumnElement):
60
61
  """The cache is disabled for this class."""
61
62
 
62
63
  def __init__(self, selectable: bool) -> None: ...
63
- @property # type: ignore[override]
64
- def _from_objects(self) -> List[bool]: ...
64
+ @property
65
+ def _from_objects(self) -> List[FromClause]: ...
65
66
  '''
66
67
  stub_file_parts = [header]
67
68
 
@@ -20,7 +20,7 @@ def select_dialect(dialect_name):
20
20
  known_dialects = {
21
21
  "geopackage": dialects.geopackage,
22
22
  "mysql": dialects.mysql,
23
- "mariadb": dialects.mysql,
23
+ "mariadb": dialects.mariadb,
24
24
  "postgresql": dialects.postgresql,
25
25
  "sqlite": dialects.sqlite,
26
26
  }
@@ -53,7 +53,7 @@ def setup_ddl_event_listeners():
53
53
  @event.listens_for(Column, "after_parent_attach")
54
54
  def after_parent_attach(column, table):
55
55
  """Automatically add spatial indexes."""
56
- if not isinstance(table, Table):
56
+ if not isinstance(table, Table): # pragma: no cover
57
57
  # For old versions of SQLAlchemy, subqueries might trigger the after_parent_attach event
58
58
  # with a selectable as table, so we want to skip this case.
59
59
  return
@@ -75,7 +75,7 @@ def setup_ddl_event_listeners():
75
75
  try:
76
76
  if column.type._spatial_index_reflected:
77
77
  return
78
- except AttributeError:
78
+ except AttributeError: # pragma: no cover
79
79
  pass
80
80
 
81
81
  kwargs = {
@@ -98,7 +98,7 @@ def setup_ddl_event_listeners():
98
98
  )
99
99
 
100
100
  @event.listens_for(Table, "column_reflect")
101
- def _reflect_geometry_column(inspector, table, column_info):
101
+ def column_reflect(inspector, table, column_info):
102
102
  select_dialect(inspector.bind.dialect.name).reflect_geometry_column(
103
103
  inspector, table, column_info
104
104
  )
@@ -2,6 +2,7 @@
2
2
 
3
3
  from geoalchemy2.admin.dialects import common # noqa
4
4
  from geoalchemy2.admin.dialects import geopackage # noqa
5
+ from geoalchemy2.admin.dialects import mariadb # noqa
5
6
  from geoalchemy2.admin.dialects import mysql # noqa
6
7
  from geoalchemy2.admin.dialects import postgresql # noqa
7
8
  from geoalchemy2.admin.dialects import sqlite # noqa
@@ -85,20 +85,20 @@ def setup_create_drop(table, bind, check_col_management=None):
85
85
 
86
86
 
87
87
  def reflect_geometry_column(inspector, table, column_info):
88
- return
88
+ return # pragma: no cover
89
89
 
90
90
 
91
91
  def before_create(table, bind, **kw):
92
- return
92
+ return # pragma: no cover
93
93
 
94
94
 
95
95
  def after_create(table, bind, **kw):
96
- return
96
+ return # pragma: no cover
97
97
 
98
98
 
99
99
  def before_drop(table, bind, **kw):
100
- return
100
+ return # pragma: no cover
101
101
 
102
102
 
103
103
  def after_drop(table, bind, **kw):
104
- return
104
+ return # pragma: no cover
@@ -71,15 +71,15 @@ def init_geopackage(dbapi_conn, *args):
71
71
  dbapi_conn.execute("SELECT gpkgCreateBaseTables();")
72
72
 
73
73
 
74
- def load_spatialite_gpkg(*args, **kwargs):
74
+ def load_spatialite_gpkg(dbapi_conn, *args, **kwargs):
75
75
  """Load SpatiaLite extension in GeoPackage and initialize internal tables.
76
76
 
77
77
  See :func:`geoalchemy2.admin.dialects.geopackage.load_geopackage_driver` and
78
78
  :func:`geoalchemy2.admin.dialects.geopackage.init_geopackage` functions for details about
79
79
  arguments.
80
80
  """
81
- load_geopackage_driver(*args)
82
- init_geopackage(*args, **kwargs)
81
+ load_geopackage_driver(dbapi_conn)
82
+ init_geopackage(dbapi_conn, **kwargs)
83
83
 
84
84
 
85
85
  def _get_spatialite_attrs(bind, table_name, col_name):
@@ -188,6 +188,10 @@ def reflect_geometry_column(inspector, table, column_info):
188
188
  column_info["type"]._spatial_index_reflected = False
189
189
 
190
190
 
191
+ def connect(dbapi_conn, *args, **kwargs):
192
+ return load_spatialite_gpkg(dbapi_conn, *args, **kwargs)
193
+
194
+
191
195
  def before_create(table, bind, **kw):
192
196
  """Handle spatial indexes during the before_create event."""
193
197
  dialect, gis_cols, regular_cols = setup_create_drop(table, bind)
@@ -0,0 +1,134 @@
1
+ """This module defines specific functions for MariaDB dialect."""
2
+
3
+ from sqlalchemy.ext.compiler import compiles
4
+
5
+ from geoalchemy2 import functions
6
+ from geoalchemy2.admin.dialects.mysql import after_create # noqa
7
+ from geoalchemy2.admin.dialects.mysql import after_drop # noqa
8
+ from geoalchemy2.admin.dialects.mysql import before_create # noqa
9
+ from geoalchemy2.admin.dialects.mysql import before_drop # noqa
10
+ from geoalchemy2.admin.dialects.mysql import reflect_geometry_column # noqa
11
+ from geoalchemy2.elements import WKBElement
12
+ from geoalchemy2.elements import WKTElement
13
+ from geoalchemy2.shape import to_shape
14
+
15
+
16
+ def _cast(param):
17
+ if isinstance(param, memoryview):
18
+ param = param.tobytes()
19
+ if isinstance(param, bytes):
20
+ data_element = WKBElement(param)
21
+ param = to_shape(data_element).wkt.encode("utf-8")
22
+ return param
23
+
24
+
25
+ def before_cursor_execute(
26
+ conn, cursor, statement, parameters, context, executemany, convert=True
27
+ ): # noqa: D417
28
+ """Event handler to cast the parameters properly.
29
+
30
+ Args:
31
+ convert (bool): Trigger the conversion.
32
+ """
33
+ if convert:
34
+ if isinstance(parameters, (tuple, list)):
35
+ parameters = tuple(_cast(x) for x in parameters)
36
+ elif isinstance(parameters, dict):
37
+ for k in parameters:
38
+ parameters[k] = _cast(parameters[k])
39
+
40
+ return statement, parameters
41
+
42
+
43
+ _MARIADB_FUNCTIONS = {
44
+ "ST_AsEWKB": "ST_AsBinary",
45
+ }
46
+
47
+
48
+ def _compiles_mariadb(cls, fn):
49
+ def _compile_mariadb(element, compiler, **kw):
50
+ return "{}({})".format(fn, compiler.process(element.clauses, **kw))
51
+
52
+ compiles(getattr(functions, cls), "mariadb")(_compile_mariadb)
53
+
54
+
55
+ def register_mariadb_mapping(mapping):
56
+ """Register compilation mappings for the given functions.
57
+
58
+ Args:
59
+ mapping: Should have the following form::
60
+
61
+ {
62
+ "function_name_1": "mariadb_function_name_1",
63
+ "function_name_2": "mariadb_function_name_2",
64
+ ...
65
+ }
66
+ """
67
+ for cls, fn in mapping.items():
68
+ _compiles_mariadb(cls, fn)
69
+
70
+
71
+ register_mariadb_mapping(_MARIADB_FUNCTIONS)
72
+
73
+
74
+ def _compile_GeomFromText_MariaDB(element, compiler, **kw):
75
+ element.identifier = "ST_GeomFromText"
76
+ compiled = compiler.process(element.clauses, **kw)
77
+ try:
78
+ clauses = list(element.clauses)
79
+ data_element = WKTElement(clauses[0].value)
80
+ srid = max(0, data_element.srid)
81
+ if srid <= 0:
82
+ srid = max(0, element.type.srid)
83
+ if len(clauses) > 1 and srid > 0:
84
+ clauses[1].value = srid
85
+ except Exception:
86
+ srid = max(0, element.type.srid)
87
+
88
+ if srid > 0:
89
+ res = "{}({}, {})".format(element.identifier, compiled, srid)
90
+ else:
91
+ res = "{}({})".format(element.identifier, compiled)
92
+ return res
93
+
94
+
95
+ def _compile_GeomFromWKB_MariaDB(element, compiler, **kw):
96
+ element.identifier = "ST_GeomFromText"
97
+
98
+ try:
99
+ clauses = list(element.clauses)
100
+ data_element = WKBElement(clauses[0].value)
101
+ srid = max(0, data_element.srid)
102
+ if srid <= 0:
103
+ srid = max(0, element.type.srid)
104
+ if len(clauses) > 1 and srid > 0:
105
+ clauses[1].value = srid
106
+ except Exception:
107
+ srid = max(0, element.type.srid)
108
+ compiled = compiler.process(element.clauses, **kw)
109
+
110
+ if srid > 0:
111
+ res = "{}({}, {})".format(element.identifier, compiled, srid)
112
+ else:
113
+ res = "{}({})".format(element.identifier, compiled)
114
+ return res
115
+
116
+
117
+ @compiles(functions.ST_GeomFromText, "mariadb") # type: ignore
118
+ def _MariaDB_ST_GeomFromText(element, compiler, **kw):
119
+ return _compile_GeomFromText_MariaDB(element, compiler, **kw)
120
+
121
+
122
+ @compiles(functions.ST_GeomFromEWKT, "mariadb") # type: ignore
123
+ def _MariaDB_ST_GeomFromEWKT(element, compiler, **kw):
124
+ return _compile_GeomFromText_MariaDB(element, compiler, **kw)
125
+
126
+
127
+ @compiles(functions.ST_GeomFromWKB, "mariadb") # type: ignore
128
+ def _MariaDB_ST_GeomFromWKB(element, compiler, **kw):
129
+ return _compile_GeomFromWKB_MariaDB(element, compiler, **kw)
130
+
131
+
132
+ @compiles(functions.ST_GeomFromEWKB, "mariadb") # type: ignore
133
+ def _MariaDB_ST_GeomFromEWKB(element, compiler, **kw):
134
+ return _compile_GeomFromWKB_MariaDB(element, compiler, **kw)
@@ -8,9 +8,6 @@ from geoalchemy2 import functions
8
8
  from geoalchemy2.admin.dialects.common import _check_spatial_type
9
9
  from geoalchemy2.admin.dialects.common import _spatial_idx_name
10
10
  from geoalchemy2.admin.dialects.common import setup_create_drop
11
- from geoalchemy2.elements import WKBElement
12
- from geoalchemy2.elements import WKTElement
13
- from geoalchemy2.shape import to_shape
14
11
  from geoalchemy2.types import Geography
15
12
  from geoalchemy2.types import Geometry
16
13
 
@@ -51,7 +48,7 @@ def reflect_geometry_column(inspector, table, column_info):
51
48
  is_nullable = str(nullable_str).lower() == "yes"
52
49
 
53
50
  if geometry_type not in _POSSIBLE_TYPES:
54
- return
51
+ return # pragma: no cover
55
52
 
56
53
  # Check if the column has spatial index
57
54
  has_index_query = """SELECT DISTINCT
@@ -75,6 +72,25 @@ def reflect_geometry_column(inspector, table, column_info):
75
72
  )
76
73
 
77
74
 
75
+ def before_cursor_execute(
76
+ conn, cursor, statement, parameters, context, executemany, convert=True
77
+ ): # noqa: D417
78
+ """Event handler to cast the parameters properly.
79
+
80
+ Args:
81
+ convert (bool): Trigger the conversion.
82
+ """
83
+ if convert:
84
+ if isinstance(parameters, (tuple, list)):
85
+ parameters = tuple(x.tobytes() if isinstance(x, memoryview) else x for x in parameters)
86
+ elif isinstance(parameters, dict):
87
+ for k in parameters:
88
+ if isinstance(parameters[k], memoryview):
89
+ parameters[k] = parameters[k].tobytes()
90
+
91
+ return statement, parameters
92
+
93
+
78
94
  def before_create(table, bind, **kw):
79
95
  """Handle spatial indexes during the before_create event."""
80
96
  dialect, gis_cols, regular_cols = setup_create_drop(table, bind)
@@ -137,7 +153,6 @@ def _compiles_mysql(cls, fn):
137
153
  return "{}({})".format(fn, compiler.process(element.clauses, **kw))
138
154
 
139
155
  compiles(getattr(functions, cls), "mysql")(_compile_mysql)
140
- compiles(getattr(functions, cls), "mariadb")(_compile_mysql)
141
156
 
142
157
 
143
158
  def register_mysql_mapping(mapping):
@@ -172,9 +187,6 @@ def _compile_GeomFromText_MySql(element, compiler, **kw):
172
187
 
173
188
  def _compile_GeomFromWKB_MySql(element, compiler, **kw):
174
189
  element.identifier = "ST_GeomFromWKB"
175
- wkb_data = list(element.clauses)[0].value
176
- if isinstance(wkb_data, memoryview):
177
- list(element.clauses)[0].value = wkb_data.tobytes()
178
190
  compiled = compiler.process(element.clauses, **kw)
179
191
  srid = element.type.srid
180
192
 
@@ -184,50 +196,6 @@ def _compile_GeomFromWKB_MySql(element, compiler, **kw):
184
196
  return "{}({})".format(element.identifier, compiled)
185
197
 
186
198
 
187
- def _compile_GeomFromText_MariaDB(element, compiler, **kw):
188
- element.identifier = "ST_GeomFromText"
189
- compiled = compiler.process(element.clauses, **kw)
190
- try:
191
- clauses = list(element.clauses)
192
- data_element = WKTElement(clauses[0].value)
193
- srid = max(0, data_element.srid)
194
- if srid <= 0:
195
- srid = max(0, element.type.srid)
196
- if len(clauses) > 1 and srid > 0:
197
- clauses[1].value = srid
198
- except Exception:
199
- srid = max(0, element.type.srid)
200
-
201
- if srid > 0:
202
- res = "{}({}, {})".format(element.identifier, compiled, srid)
203
- else:
204
- res = "{}({})".format(element.identifier, compiled)
205
- return res
206
-
207
-
208
- def _compile_GeomFromWKB_MariaDB(element, compiler, **kw):
209
- element.identifier = "ST_GeomFromText"
210
-
211
- try:
212
- clauses = list(element.clauses)
213
- data_element = WKBElement(clauses[0].value)
214
- srid = max(0, data_element.srid)
215
- if srid <= 0:
216
- srid = max(0, element.type.srid)
217
- clauses[0].value = to_shape(data_element).wkt.encode("utf-8")
218
- if len(clauses) > 1 and srid > 0:
219
- clauses[1].value = srid
220
- except Exception:
221
- srid = max(0, element.type.srid)
222
- compiled = compiler.process(element.clauses, **kw)
223
-
224
- if srid > 0:
225
- res = "{}({}, {})".format(element.identifier, compiled, srid)
226
- else:
227
- res = "{}({})".format(element.identifier, compiled)
228
- return res
229
-
230
-
231
199
  @compiles(functions.ST_GeomFromText, "mysql") # type: ignore
232
200
  def _MySQL_ST_GeomFromText(element, compiler, **kw):
233
201
  return _compile_GeomFromText_MySql(element, compiler, **kw)
@@ -238,16 +206,6 @@ def _MySQL_ST_GeomFromEWKT(element, compiler, **kw):
238
206
  return _compile_GeomFromText_MySql(element, compiler, **kw)
239
207
 
240
208
 
241
- @compiles(functions.ST_GeomFromText, "mariadb") # type: ignore
242
- def _MariaDB_ST_GeomFromText(element, compiler, **kw):
243
- return _compile_GeomFromText_MariaDB(element, compiler, **kw)
244
-
245
-
246
- @compiles(functions.ST_GeomFromEWKT, "mariadb") # type: ignore
247
- def _MariaDB_ST_GeomFromEWKT(element, compiler, **kw):
248
- return _compile_GeomFromText_MariaDB(element, compiler, **kw)
249
-
250
-
251
209
  @compiles(functions.ST_GeomFromWKB, "mysql") # type: ignore
252
210
  def _MySQL_ST_GeomFromWKB(element, compiler, **kw):
253
211
  return _compile_GeomFromWKB_MySql(element, compiler, **kw)
@@ -256,13 +214,3 @@ def _MySQL_ST_GeomFromWKB(element, compiler, **kw):
256
214
  @compiles(functions.ST_GeomFromEWKB, "mysql") # type: ignore
257
215
  def _MySQL_ST_GeomFromEWKB(element, compiler, **kw):
258
216
  return _compile_GeomFromWKB_MySql(element, compiler, **kw)
259
-
260
-
261
- @compiles(functions.ST_GeomFromWKB, "mariadb") # type: ignore
262
- def _MariaDB_ST_GeomFromWKB(element, compiler, **kw):
263
- return _compile_GeomFromWKB_MariaDB(element, compiler, **kw)
264
-
265
-
266
- @compiles(functions.ST_GeomFromEWKB, "mariadb") # type: ignore
267
- def _MariaDB_ST_GeomFromEWKB(element, compiler, **kw):
268
- return _compile_GeomFromWKB_MariaDB(element, compiler, **kw)
@@ -51,6 +51,8 @@ def init_spatialite(
51
51
 
52
52
  Args:
53
53
  dbapi_conn: The DBAPI connection.
54
+ transaction: If set to `True` the whole operation will be handled as a single Transaction
55
+ (faster). The default value is `False` (slower, but safer).
54
56
  init_mode: Can be `None` to load all EPSG SRIDs, `'WGS84'` to load only the ones related
55
57
  to WGS84 or `'EMPTY'` to not load any EPSG SRID.
56
58
 
@@ -58,8 +60,6 @@ def init_spatialite(
58
60
 
59
61
  It is possible to load other EPSG SRIDs afterwards using `InsertEpsgSrid(srid)`.
60
62
 
61
- transaction: If set to `True` the whole operation will be handled as a single Transaction
62
- (faster). The default value is `False` (slower, but safer).
63
63
  journal_mode: Change the journal mode to the given value. This can make the table creation
64
64
  much faster. The possible values are the following: <JOURNAL_MODE_VALUES>. See
65
65
  https://www.sqlite.org/pragma.html#pragma_journal_mode for more details.
@@ -127,15 +127,15 @@ def init_spatialite(
127
127
  dbapi_conn.execute("PRAGMA journal_mode = {}".format(current_journal_mode))
128
128
 
129
129
 
130
- def load_spatialite(*args, **kwargs):
130
+ def load_spatialite(dbapi_conn, *args, **kwargs):
131
131
  """Load SpatiaLite extension in SQLite DB and initialize internal tables.
132
132
 
133
133
  See :func:`geoalchemy2.admin.dialects.sqlite.load_spatialite_driver` and
134
134
  :func:`geoalchemy2.admin.dialects.sqlite.init_spatialite` functions for details about
135
135
  arguments.
136
136
  """
137
- load_spatialite_driver(*args)
138
- init_spatialite(*args, **kwargs)
137
+ load_spatialite_driver(dbapi_conn)
138
+ init_spatialite(dbapi_conn, **kwargs)
139
139
 
140
140
 
141
141
  def _get_spatialite_attrs(bind, table_name, col_name):
@@ -260,6 +260,11 @@ def reflect_geometry_column(inspector, table, column_info):
260
260
  column_info["type"]._spatial_index_reflected = False
261
261
 
262
262
 
263
+ def connect(dbapi_conn, *args, **kwargs):
264
+ """Even handler to load spatial extension when a new connection is created."""
265
+ return load_spatialite(dbapi_conn, *args, **kwargs)
266
+
267
+
263
268
  def before_create(table, bind, **kw):
264
269
  """Handle spatial indexes during the before_create event."""
265
270
  dialect, gis_cols, regular_cols = setup_create_drop(table, bind)
@@ -0,0 +1,98 @@
1
+ from collections import defaultdict
2
+ from functools import partial
3
+
4
+ from sqlalchemy import event
5
+ from sqlalchemy.engine import CreateEnginePlugin
6
+
7
+ from geoalchemy2.admin import select_dialect
8
+
9
+
10
+ class GeoEngine(CreateEnginePlugin):
11
+ """A plugin to create `sqlalchemy.Engine` objects with specific events to handle spatial data.
12
+
13
+ This plugin should only be used with the :func:`sqlalchemy.create_engine` function. This plugin
14
+ will automatically attach the relevant event listeners to the new engine depending on its
15
+ dialect. For some specific dialects it's even possible to pass arguments to customize the
16
+ functions called by the listeners. These arguments have the following form:
17
+ `geoalchemy2_<event>_<dialect>_<argument_name>`.
18
+ For example, creating a new SQLite DB and initialize the SpatiaLite extension with only the
19
+ WGS84 SRID can be performed this way:
20
+
21
+ .. code-block:: python
22
+
23
+ db_url = "sqlite:////tmp/test_db.sqlite?geoalchemy2_connect_sqlite_init_mode=WGS84"
24
+ engine = sqlalchemy.create_engine(db_url, plugins=["geoalchemy2"])
25
+
26
+ The names of the parameters can be found in the event listener of each dialect. Note that all
27
+ dialects don't have listeners for all events.
28
+ """
29
+
30
+ def __init__(self, url, kwargs):
31
+ super().__init__(url, kwargs)
32
+
33
+ # Consume the parameters from the URL query
34
+ self.params = defaultdict(lambda: defaultdict(dict))
35
+
36
+ transaction = url.query.get("geoalchemy2_connect_sqlite_transaction", None)
37
+ if transaction is not None:
38
+ self.params["connect"]["sqlite"]["transaction"] = self.str_to_bool(transaction)
39
+
40
+ init_mode = url.query.get("geoalchemy2_connect_sqlite_init_mode", None)
41
+ if init_mode is not None:
42
+ self.params["connect"]["sqlite"]["init_mode"] = init_mode
43
+
44
+ journal_mode = url.query.get("geoalchemy2_connect_sqlite_journal_mode", None)
45
+ if journal_mode is not None:
46
+ self.params["connect"]["sqlite"]["journal_mode"] = journal_mode
47
+
48
+ before_cursor_execute_convert_mysql = url.query.get(
49
+ "geoalchemy2_before_cursor_execute_mysql_convert", None
50
+ )
51
+ if before_cursor_execute_convert_mysql is not None:
52
+ self.params["before_cursor_execute"]["mysql"]["convert"] = self.str_to_bool(
53
+ before_cursor_execute_convert_mysql
54
+ )
55
+
56
+ before_cursor_execute_convert_mariadb = url.query.get(
57
+ "geoalchemy2_before_cursor_execute_mariadb_convert", None
58
+ )
59
+ if before_cursor_execute_convert_mariadb is not None:
60
+ self.params["before_cursor_execute"]["mariadb"]["convert"] = self.str_to_bool(
61
+ before_cursor_execute_convert_mariadb
62
+ )
63
+
64
+ @staticmethod
65
+ def str_to_bool(argument):
66
+ """Cast argument to bool."""
67
+ lowered = str(argument).lower()
68
+ if lowered in ("yes", "y", "true", "t", "1", "enable", "on"):
69
+ return True
70
+ elif lowered in ("no", "n", "false", "f", "0", "disable", "off"):
71
+ return False
72
+ raise ValueError(argument)
73
+
74
+ def update_url(self, url):
75
+ """Update the URL to one that no longer includes specific parameters."""
76
+ return url.difference_update_query(
77
+ [
78
+ "geoalchemy2_connect_sqlite_transaction",
79
+ "geoalchemy2_connect_sqlite_init_mode",
80
+ "geoalchemy2_connect_sqlite_journal_mode",
81
+ "geoalchemy2_before_cursor_execute_mysql_convert",
82
+ "geoalchemy2_before_cursor_execute_mariadb_convert",
83
+ ],
84
+ )
85
+
86
+ def engine_created(self, engine):
87
+ """Attach event listeners after the new Engine object is created."""
88
+ dialect_module = select_dialect(engine.dialect.name)
89
+
90
+ if hasattr(dialect_module, "connect"):
91
+ params = dict(self.params["connect"].get(engine.dialect.name, {}))
92
+ func = partial(dialect_module.connect, **params)
93
+ event.listen(engine, "connect", func)
94
+
95
+ if hasattr(dialect_module, "before_cursor_execute"):
96
+ params = dict(self.params["before_cursor_execute"].get(engine.dialect.name, {}))
97
+ func = partial(dialect_module.before_cursor_execute, **params)
98
+ event.listen(engine, "before_cursor_execute", func, retval=True)
@@ -535,6 +535,7 @@ def create_geo_table(context, revision, op):
535
535
  schema=op.schema,
536
536
  _namespace_metadata=op._namespace_metadata,
537
537
  _constraints_included=op._constraints_included,
538
+ **op.kw,
538
539
  )
539
540
  else:
540
541
  new_op = op
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
@@ -138,7 +139,7 @@ class TableRowElement(ColumnElement):
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
 
geoalchemy2/functions.pyi CHANGED
@@ -3,6 +3,7 @@ from typing import List
3
3
 
4
4
  from sqlalchemy.sql import functions
5
5
  from sqlalchemy.sql.elements import ColumnElement
6
+ from sqlalchemy.sql.selectable import FromClause
6
7
 
7
8
  import geoalchemy2.types
8
9
 
@@ -13,8 +14,8 @@ class TableRowElement(ColumnElement):
13
14
  """The cache is disabled for this class."""
14
15
 
15
16
  def __init__(self, selectable: bool) -> None: ...
16
- @property # type: ignore[override]
17
- def _from_objects(self) -> List[bool]: ...
17
+ @property
18
+ def _from_objects(self) -> List[FromClause]: ...
18
19
 
19
20
  class ST_AsGeoJSON(GenericFunction):
20
21
  """
geoalchemy2/shape.py CHANGED
@@ -48,7 +48,6 @@ def to_shape(element: Union[WKBElement, WKTElement]):
48
48
  lake = Session.query(Lake).get(1)
49
49
  polygon = to_shape(lake.geom)
50
50
  """
51
- assert isinstance(element, (WKBElement, WKTElement))
52
51
  if isinstance(element, WKBElement):
53
52
  data, hex = (
54
53
  (element.data, True) if isinstance(element.data, str) else (bytes(element.data), False)
@@ -245,7 +245,7 @@ class Geometry(_GISType):
245
245
  """ The element class to use. Used by the parent class'
246
246
  ``result_processor`` method. """
247
247
 
248
- cache_ok = False
248
+ cache_ok = True
249
249
  """ Disable cache for this type. """
250
250
 
251
251
 
@@ -275,7 +275,7 @@ class Geography(_GISType):
275
275
  """ The element class to use. Used by the parent class'
276
276
  ``result_processor`` method. """
277
277
 
278
- cache_ok = False
278
+ cache_ok = True
279
279
  """ Disable cache for this type. """
280
280
 
281
281
 
@@ -315,7 +315,7 @@ class Raster(_GISType):
315
315
  """ The element class to use. Used by the parent class'
316
316
  ``result_processor`` method. """
317
317
 
318
- cache_ok = False
318
+ cache_ok = True
319
319
  """ Disable cache for this type. """
320
320
 
321
321
  def __init__(self, spatial_index=True, from_text=None, name=None, nullable=True) -> None:
@@ -2,4 +2,4 @@
2
2
 
3
3
 
4
4
  def bind_processor_process(spatial_type, bindvalue):
5
- return bindvalue
5
+ return bindvalue # pragma: no cover
@@ -1,32 +0,0 @@
1
- geoalchemy2/__init__.py,sha256=Wb5f11AM_hYn56yrxOAOjCT3z76TCtseqjFo2hKsob4,1971
2
- geoalchemy2/_functions.py,sha256=e8v584Fx_fmh8XnJvkuYEXrcyPDiWV95Mu2yw6L-LHY,62863
3
- geoalchemy2/_functions_helpers.py,sha256=hVM9DDTgf-oM27EZu6ycW1y2ENnSxUJixdINetWsu0E,2651
4
- geoalchemy2/alembic_helpers.py,sha256=WYjKiVVyHtlB4DI22z-G0-x8NnG8othAINUwmxBCMcs,27885
5
- geoalchemy2/comparator.py,sha256=WUDXn10doDlJVnYiWbVIAhhBu7N5AO9BI8sNf2mhpA4,8100
6
- geoalchemy2/elements.py,sha256=5yd_7dUQGbrLvgYvo6A2YYAFwTvP4_BskIdBCB0DrlM,13002
7
- geoalchemy2/exc.py,sha256=Nn9bRKB_35skWDMkEf4_Y2GL6gvguPVycPZcbOfa69g,226
8
- geoalchemy2/functions.py,sha256=0he8hy_SAWpXAFPdfg32NMW5G0FaZP7yVl0jn0MRLBQ,10320
9
- geoalchemy2/functions.pyi,sha256=rKCKdDSVTgeUQHItkOKqJSEuyKmVJ30bpjLpCPwMI5g,108636
10
- geoalchemy2/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
- geoalchemy2/shape.py,sha256=TZJIhCN4p4ZVwZMXidxYxTMeeyReAyoFzyYwspJRqSA,2792
12
- geoalchemy2/utils.py,sha256=OYWYnT64tjp4DWhPdq3KIxHbVti932xPMtGGxajtu-I,488
13
- geoalchemy2/admin/__init__.py,sha256=s8L5C9pYC5dTxhIp9j-JzWeg9kEJevXUW8Pq4_L4aZ8,3984
14
- geoalchemy2/admin/dialects/__init__.py,sha256=XodzuBbWKkkSQzM5EL3I33azuE-y_go0nVOlmIyJ13g,367
15
- geoalchemy2/admin/dialects/common.py,sha256=8OkGa7T2Gxz2vDVnC9nva8Ma0YqBwGxHWfBJoZF1cNQ,2936
16
- geoalchemy2/admin/dialects/geopackage.py,sha256=UaJignKtlo13oHxq92yescVJtXFEXkm4fy5WzR6sLT8,13469
17
- geoalchemy2/admin/dialects/mysql.py,sha256=b_34MDv2I615YdZI3h_KoH4cyoMG2cTKQm1Slh5p2qQ,9043
18
- geoalchemy2/admin/dialects/postgresql.py,sha256=VwB_h3TC8M5aQ6aKE3UYgxHfbEKav3eIHJeLx534Zzg,6191
19
- geoalchemy2/admin/dialects/sqlite.py,sha256=UYQuNDDy-1-lsvYlqyKy01UtHgLF4iEN42fE5D-9QQk,13597
20
- geoalchemy2/types/__init__.py,sha256=r2gscVpcxebsEgZnSA6V9pSvO6V9DSzGkDRfSxrmXdk,14243
21
- geoalchemy2/types/dialects/__init__.py,sha256=GYqO6nDtzElvsV22VN1SSUet2elaZG4ZuRrM-asbstM,414
22
- geoalchemy2/types/dialects/common.py,sha256=gxKaRQhIODJhu_yBurlvFNAqGh3LfPNu_DcObEG1wlU,138
23
- geoalchemy2/types/dialects/geopackage.py,sha256=nRmN_PnF-CWMEHkWhKVdsybnw3SvXZIBXAHIHXJLTqw,147
24
- geoalchemy2/types/dialects/mariadb.py,sha256=tPZpfj95XlVSOm8zC52iFbqVrIOdj-EDH5KVJP1Gd9c,1723
25
- geoalchemy2/types/dialects/mysql.py,sha256=zMNi1920v0XlVaCJWSwtq0b0P5YQRn8y3X_rBxC5KEw,1790
26
- geoalchemy2/types/dialects/postgresql.py,sha256=7NBKEbDJXMwX8Sgs6o_N2bAUHgjUcjbrdmYOA7sDiRw,1117
27
- geoalchemy2/types/dialects/sqlite.py,sha256=B-yLzaQcqL_4dXoOPX9D9IXw2ZQlq-Tibv_kqUIBeb4,2104
28
- GeoAlchemy2-0.16.0.dist-info/COPYING.rst,sha256=-bQKftq9uMOROzF7oN65kYBBIJKxTmBuDoftp27IC3I,1056
29
- GeoAlchemy2-0.16.0.dist-info/METADATA,sha256=oM7Fr3V8L1xyD7SXdFjwcYSHVMEHIUb-yhM-D2V-b70,2084
30
- GeoAlchemy2-0.16.0.dist-info/WHEEL,sha256=a7TGlA-5DaHMRrarXjVbQagU3Man_dCnGIWMJr5kRWo,91
31
- GeoAlchemy2-0.16.0.dist-info/top_level.txt,sha256=3kGUTcfBeXd61zFpof6-qiuw1peNF_HuZabgHQgrdis,12
32
- GeoAlchemy2-0.16.0.dist-info/RECORD,,