GeoAlchemy2 0.15.2__tar.gz → 0.17.0__tar.gz

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.
Files changed (144) hide show
  1. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/.github/workflows/test_and_publish.yml +63 -26
  2. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/.pre-commit-config.yaml +3 -5
  3. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/CHANGES.txt +14 -0
  4. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0/GeoAlchemy2.egg-info}/PKG-INFO +14 -2
  5. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/GeoAlchemy2.egg-info/SOURCES.txt +13 -1
  6. geoalchemy2-0.17.0/GeoAlchemy2.egg-info/entry_points.txt +2 -0
  7. {geoalchemy2-0.15.2/GeoAlchemy2.egg-info → geoalchemy2-0.17.0}/PKG-INFO +14 -2
  8. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/doc/admin.rst +8 -3
  9. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/doc/core_tutorial.rst +14 -2
  10. geoalchemy2-0.17.0/doc/dialect_specific_features.rst +13 -0
  11. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/doc/index.rst +8 -3
  12. geoalchemy2-0.17.0/doc/mysql_mariadb_dialect.rst +43 -0
  13. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/doc/orm_tutorial.rst +15 -1
  14. geoalchemy2-0.17.0/doc/plugin.rst +9 -0
  15. geoalchemy2-0.15.2/doc/spatialite_tutorial.rst → geoalchemy2-0.17.0/doc/spatialite_dialect.rst +14 -106
  16. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/geoalchemy2/__init__.py +2 -0
  17. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/geoalchemy2/admin/__init__.py +4 -4
  18. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/geoalchemy2/admin/dialects/__init__.py +1 -0
  19. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/geoalchemy2/admin/dialects/common.py +5 -5
  20. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/geoalchemy2/admin/dialects/geopackage.py +7 -3
  21. geoalchemy2-0.17.0/geoalchemy2/admin/dialects/mariadb.py +134 -0
  22. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/geoalchemy2/admin/dialects/mysql.py +27 -11
  23. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/geoalchemy2/admin/dialects/sqlite.py +10 -5
  24. geoalchemy2-0.17.0/geoalchemy2/admin/plugin.py +98 -0
  25. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/geoalchemy2/shape.py +0 -1
  26. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/geoalchemy2/types/__init__.py +7 -7
  27. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/geoalchemy2/types/dialects/__init__.py +1 -0
  28. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/geoalchemy2/types/dialects/common.py +1 -1
  29. geoalchemy2-0.17.0/geoalchemy2/types/dialects/mariadb.py +47 -0
  30. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/pyproject.toml +3 -0
  31. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/requirements.txt +1 -1
  32. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/setup.py +5 -0
  33. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/test_container/Dockerfile +2 -2
  34. geoalchemy2-0.17.0/test_container/Dockerfile_mariadb +19 -0
  35. geoalchemy2-0.17.0/test_container/build_mariadb.sh +7 -0
  36. geoalchemy2-0.17.0/test_container/helpers/init_mariadb.sh +22 -0
  37. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/test_container/helpers/init_mysql.sh +2 -1
  38. geoalchemy2-0.17.0/test_container/run_mariadb.sh +12 -0
  39. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/tests/__init__.py +4 -1
  40. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/tests/conftest.py +85 -25
  41. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/tests/gallery/test_orm_mapped_v2.py +1 -1
  42. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/tests/gallery/test_summarystatsagg.py +1 -1
  43. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/tests/schema_fixtures.py +1 -1
  44. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/tests/test_alembic_migrations.py +1 -1
  45. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/tests/test_functional.py +85 -19
  46. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/tests/test_functional_mysql.py +19 -8
  47. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/tests/test_functional_postgresql.py +1 -1
  48. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/tests/test_pickle.py +1 -1
  49. geoalchemy2-0.17.0/tests/test_plugin.py +90 -0
  50. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/tests/test_shape.py +21 -0
  51. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/tox.ini +12 -7
  52. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/.codespellrc +0 -0
  53. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/.coveragerc +0 -0
  54. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/.flake8 +0 -0
  55. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/.github/ISSUE_TEMPLATE/bug_report.yaml +0 -0
  56. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  57. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/.github/ISSUE_TEMPLATE/feature_request.yaml +0 -0
  58. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/.github/ISSUE_TEMPLATE/how_to_use.yaml +0 -0
  59. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/.github/dependabot.yml +0 -0
  60. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/.github/pull_request_template.md +0 -0
  61. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/.gitignore +0 -0
  62. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/.readthedocs.yaml +0 -0
  63. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/COPYING.rst +0 -0
  64. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/GeoAlchemy2.egg-info/dependency_links.txt +0 -0
  65. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/GeoAlchemy2.egg-info/not-zip-safe +0 -0
  66. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/GeoAlchemy2.egg-info/requires.txt +0 -0
  67. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/GeoAlchemy2.egg-info/top_level.txt +0 -0
  68. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/GeoAlchemy2_dev.yml +0 -0
  69. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/MANIFEST.in +0 -0
  70. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/README.rst +0 -0
  71. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/RELEASE.rst +0 -0
  72. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/TEST.rst +0 -0
  73. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/doc/Makefile +0 -0
  74. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/doc/_static/geoalchemy.png +0 -0
  75. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/doc/_static/geoalchemy.svg +0 -0
  76. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/doc/_static/geoalchemy_small.png +0 -0
  77. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/doc/_static/geoalchemy_small.svg +0 -0
  78. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/doc/_templates/sidebar-about.html +0 -0
  79. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/doc/_templates/sidebar-links.html +0 -0
  80. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/doc/_templates/sidebar-logo.html +0 -0
  81. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/doc/_themes/LICENSE +0 -0
  82. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/doc/_themes/README +0 -0
  83. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/doc/_themes/flask/layout.html +0 -0
  84. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/doc/_themes/flask/relations.html +0 -0
  85. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/doc/_themes/flask/static/flasky.css_t +0 -0
  86. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/doc/_themes/flask/static/small_flask.css +0 -0
  87. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/doc/_themes/flask/theme.conf +0 -0
  88. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/doc/alembic.rst +0 -0
  89. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/doc/alembic_helpers.rst +0 -0
  90. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/doc/changelog.rst +0 -0
  91. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/doc/conf.py +0 -0
  92. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/doc/elements.rst +0 -0
  93. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/doc/make.bat +0 -0
  94. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/doc/migrate.rst +0 -0
  95. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/doc/shape.rst +0 -0
  96. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/doc/spatial_functions.rst +0 -0
  97. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/doc/spatial_operators.rst +0 -0
  98. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/doc/types.rst +0 -0
  99. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/generate_type_stubs.py +0 -0
  100. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/geoalchemy2/_functions.py +0 -0
  101. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/geoalchemy2/_functions_helpers.py +0 -0
  102. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/geoalchemy2/admin/dialects/postgresql.py +0 -0
  103. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/geoalchemy2/alembic_helpers.py +0 -0
  104. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/geoalchemy2/comparator.py +0 -0
  105. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/geoalchemy2/elements.py +0 -0
  106. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/geoalchemy2/exc.py +0 -0
  107. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/geoalchemy2/functions.py +0 -0
  108. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/geoalchemy2/functions.pyi +0 -0
  109. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/geoalchemy2/py.typed +0 -0
  110. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/geoalchemy2/types/dialects/geopackage.py +0 -0
  111. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/geoalchemy2/types/dialects/mysql.py +0 -0
  112. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/geoalchemy2/types/dialects/postgresql.py +0 -0
  113. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/geoalchemy2/types/dialects/sqlite.py +0 -0
  114. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/geoalchemy2/utils.py +0 -0
  115. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/requirements-doc.txt +0 -0
  116. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/requirements-mypy.txt +0 -0
  117. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/requirements-rtd.txt +0 -0
  118. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/setup.cfg +0 -0
  119. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/test_container/.dockerignore +0 -0
  120. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/test_container/.gitignore +0 -0
  121. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/test_container/build.sh +0 -0
  122. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/test_container/helpers/entrypoint.sh +0 -0
  123. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/test_container/helpers/init_postgres.sh +0 -0
  124. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/test_container/helpers/install_requirements.sh +0 -0
  125. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/test_container/run.sh +0 -0
  126. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/tests/alembic_config/alembic.ini +0 -0
  127. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/tests/data/spatialite_ge_4.sqlite +0 -0
  128. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/tests/data/spatialite_geopackage.gpkg +0 -0
  129. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/tests/data/spatialite_lt_4.sqlite +0 -0
  130. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/tests/gallery/README.rst +0 -0
  131. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/tests/gallery/__init__.py +0 -0
  132. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/tests/gallery/test_decipher_raster.py +0 -0
  133. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/tests/gallery/test_disable_wrapping.py +0 -0
  134. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/tests/gallery/test_insert_raster.py +0 -0
  135. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/tests/gallery/test_length_at_insert.py +0 -0
  136. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/tests/gallery/test_raster_transform.py +0 -0
  137. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/tests/gallery/test_specific_compilation.py +0 -0
  138. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/tests/gallery/test_type_decorator.py +0 -0
  139. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/tests/test_comparator.py +0 -0
  140. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/tests/test_elements.py +0 -0
  141. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/tests/test_functional_geopackage.py +0 -0
  142. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/tests/test_functional_sqlite.py +0 -0
  143. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/tests/test_functions.py +0 -0
  144. {geoalchemy2-0.15.2 → geoalchemy2-0.17.0}/tests/test_types.py +0 -0
@@ -33,7 +33,7 @@ jobs:
33
33
  {"pkg_name": "python==3.10.*", "flag": "3.10"},
34
34
  {"pkg_name": "python==3.11.*", "flag": "3.11"},
35
35
  {"pkg_name": "python==3.12.*", "flag": "3.12"},
36
- {"pkg_name": "pypy3.8", "flag": "pypy3.8"},
36
+ {"pkg_name": "pypy3.10", "flag": "pypy3.10"},
37
37
  ]
38
38
 
39
39
  # The type of runner that the job will run on
@@ -54,28 +54,54 @@ jobs:
54
54
  --health-interval 10s
55
55
  --health-timeout 5s
56
56
  --health-retries 5
57
+ mysql:
58
+ image: mysql:latest
59
+ ports:
60
+ - 3307:3306
61
+ env:
62
+ MYSQL_USER: gis
63
+ MYSQL_PASSWORD: gis
64
+ MYSQL_DATABASE: gis
65
+ MYSQL_ROOT_PASSWORD: gis
66
+ # Set health checks to wait until MySQL has started
67
+ options: >-
68
+ --health-cmd="mysqladmin ping"
69
+ --health-interval=10s
70
+ --health-timeout=5s
71
+ --health-retries=3
72
+ mariadb:
73
+ image: mariadb:latest
74
+ ports:
75
+ - 3308:3306
76
+ env:
77
+ MARIADB_USER: gis
78
+ MARIADB_PASSWORD: gis
79
+ MARIADB_DATABASE: gis
80
+ MARIADB_ROOT_PASSWORD: gis
81
+ # Set health checks to wait until MariaDB has started
82
+ options: >-
83
+ --health-cmd="healthcheck.sh --connect --innodb_initialized"
84
+ --health-interval=10s
85
+ --health-timeout=5s
86
+ --health-retries=3
87
+
57
88
 
58
89
  steps:
59
90
 
60
91
  # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
61
92
  - uses: actions/checkout@v4
62
93
 
63
- # Setup Conda for Python and Pypy
64
- - name: Install Conda environment with Micromamba
65
- uses: mamba-org/setup-micromamba@v1
94
+ # Setup Python
95
+ - name: Set up Python ${{ matrix.python-version }}
96
+ uses: actions/setup-python@v5
66
97
  with:
67
- environment-name: test_${{ matrix.python-version.flag }}
68
- cache-environment: true
69
- create-args: >-
70
- ${{ matrix.python-version.pkg_name }}
71
- libgdal
72
- libspatialite==5.0.1
73
- pyproj
74
- condarc: |
75
- channels:
76
- - conda-forge
77
- - defaults
78
- channel_priority: strict
98
+ python-version: ${{ matrix.python-version.flag }}
99
+
100
+ # Install MariaDB
101
+ - name: Install MariaDB and SpatiaLite
102
+ run: |
103
+ sudo apt-get update
104
+ sudo apt-get install -y mariadb-server mariadb-client libsqlite3-mod-spatialite libgdal-dev gdal-bin rasterio
79
105
 
80
106
  # Config PostgreSQL
81
107
  - name: Configure PostgreSQL
@@ -95,13 +121,17 @@ jobs:
95
121
  # Drop PostGIS Topology extension to "gis" database
96
122
  psql -h localhost -p 5432 -U gis -d gis -c 'DROP EXTENSION IF EXISTS postgis_topology;'
97
123
 
98
- # Setup MySQL
99
- - name: Set up MySQL
124
+ # Check MySQL
125
+ - name: Check MySQL
126
+ run: |
127
+ mysql --user=gis --password=gis --host=127.0.0.1 -P 3307 -e "SELECT VERSION();"
128
+ mysql --user=root --password=gis --host=127.0.0.1 -P 3307 -e "GRANT ALL PRIVILEGES ON *.* TO 'gis'@'%' WITH GRANT OPTION;"
129
+
130
+ # Check MariaDB
131
+ - name: Check MariaDB
100
132
  run: |
101
- sudo systemctl start mysql
102
- sudo mysql --user=root --password=root --host=127.0.0.1 -e "CREATE USER 'gis'@'%' IDENTIFIED BY 'gis';"
103
- sudo mysql --user=root --password=root --host=127.0.0.1 -e "GRANT ALL PRIVILEGES ON *.* TO 'gis'@'%' WITH GRANT OPTION;"
104
- mysql --user=gis --password=gis -e "CREATE DATABASE gis;"
133
+ mysql --user=gis --password=gis --host=127.0.0.1 -P 3308 -e "SELECT VERSION();"
134
+ mysql --user=root --password=gis --host=127.0.0.1 -P 3308 -e "GRANT ALL PRIVILEGES ON *.* TO 'gis'@'%' WITH GRANT OPTION;"
105
135
 
106
136
  # Check python version
107
137
  - name: Display Python version
@@ -114,16 +144,23 @@ jobs:
114
144
  - name: Install dependencies
115
145
  run: |
116
146
  python -m pip install --upgrade pip setuptools
117
- pip install tox-gh-actions
147
+ python -m pip install tox-gh-actions
118
148
 
119
149
  # Run the test suite
120
150
  - name: Run the tests
121
151
  env:
122
- SPATIALITE_LIBRARY_PATH: /home/runner/micromamba/envs/test_${{ matrix.python-version.flag }}/lib/mod_spatialite.so
123
- PROJ_LIB: /home/runner/micromamba/envs/test_${{ matrix.python-version.flag }}/share/proj
152
+ SPATIALITE_LIBRARY_PATH: /usr/lib/x86_64-linux-gnu/mod_spatialite.so
124
153
  COVERAGE_FILE: .coverage
125
- PYTEST_MYSQL_DB_URL: mysql://gis:gis@127.0.0.1/gis
154
+ PYTEST_MYSQL_DB_URL: mysql://gis:gis@127.0.0.1:3307/gis
155
+ PYTEST_MARIADB_DB_URL: mariadb://gis:gis@127.0.0.1:3308/gis
126
156
  run: |
157
+ if [[ ${{ matrix.python-version.flag }} == 'pypy3.10' ]]; then
158
+ export PYTEST_ADDOPTS=${PYTEST_ADDOPTS}' --ignore=tests/gallery/test_insert_raster.py'
159
+ export PYTEST_SPATIALITE3_DB_URL="FAILING URL"
160
+ export PYTEST_SPATIALITE4_DB_URL="FAILING URL"
161
+ else
162
+ export PYTEST_ADDOPTS='--require-all-dialects'
163
+ fi;
127
164
  # Run the unit test suite with SQLAlchemy=1.4.* and then with the latest version of SQLAlchemy
128
165
  tox -vv
129
166
 
@@ -1,8 +1,6 @@
1
- default_language_version:
2
- python: python3.8
3
1
  repos:
4
2
  - repo: https://github.com/pre-commit/pre-commit-hooks
5
- rev: v4.6.0
3
+ rev: v5.0.0
6
4
  hooks:
7
5
  - id: check-added-large-files
8
6
  - id: check-case-conflict
@@ -17,7 +15,7 @@ repos:
17
15
  hooks:
18
16
  - id: isort
19
17
  - repo: https://github.com/psf/black
20
- rev: 24.4.2
18
+ rev: 24.10.0
21
19
  hooks:
22
20
  - id: black
23
21
  - repo: https://github.com/codespell-project/codespell
@@ -31,7 +29,7 @@ repos:
31
29
  additional_dependencies: ["tomli"]
32
30
  exclude: "tests"
33
31
  - repo: https://github.com/PyCQA/flake8
34
- rev: 7.1.0
32
+ rev: 7.1.1
35
33
  hooks:
36
34
  - id: flake8
37
35
  ci:
@@ -1,6 +1,20 @@
1
1
  GeoAlchemy 2 Changelog
2
2
  ======================
3
3
 
4
+ 0.17.0
5
+ ------
6
+
7
+ * Perf: Enable cache for all types of GeoAlchemy2 @adrien-berchet (#525)
8
+ * Feat: Add new plugin to automatically attach events based on the engine dialect @adrien-berchet (#525)
9
+ * CI: Disable SQLite for Pypy job @adrien-berchet (#528)
10
+
11
+ 0.16.0
12
+ ------
13
+
14
+ * Test: Add test in test_functional.py for custom Geometry that uses WKT elements @adrien-berchet (#525)
15
+ * Add option to ensure all dialects are properly tested in CI @adrien-berchet (#526)
16
+ * Improve MariaDB support @adrien-berchet (#524)
17
+
4
18
  0.15.2
5
19
  ------
6
20
 
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: GeoAlchemy2
3
- Version: 0.15.2
3
+ Version: 0.17.0
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
@@ -29,6 +29,7 @@ tox.ini
29
29
  GeoAlchemy2.egg-info/PKG-INFO
30
30
  GeoAlchemy2.egg-info/SOURCES.txt
31
31
  GeoAlchemy2.egg-info/dependency_links.txt
32
+ GeoAlchemy2.egg-info/entry_points.txt
32
33
  GeoAlchemy2.egg-info/not-zip-safe
33
34
  GeoAlchemy2.egg-info/requires.txt
34
35
  GeoAlchemy2.egg-info/top_level.txt
@@ -39,15 +40,18 @@ doc/alembic_helpers.rst
39
40
  doc/changelog.rst
40
41
  doc/conf.py
41
42
  doc/core_tutorial.rst
43
+ doc/dialect_specific_features.rst
42
44
  doc/elements.rst
43
45
  doc/index.rst
44
46
  doc/make.bat
45
47
  doc/migrate.rst
48
+ doc/mysql_mariadb_dialect.rst
46
49
  doc/orm_tutorial.rst
50
+ doc/plugin.rst
47
51
  doc/shape.rst
48
52
  doc/spatial_functions.rst
49
53
  doc/spatial_operators.rst
50
- doc/spatialite_tutorial.rst
54
+ doc/spatialite_dialect.rst
51
55
  doc/types.rst
52
56
  doc/_static/geoalchemy.png
53
57
  doc/_static/geoalchemy.svg
@@ -76,9 +80,11 @@ geoalchemy2/py.typed
76
80
  geoalchemy2/shape.py
77
81
  geoalchemy2/utils.py
78
82
  geoalchemy2/admin/__init__.py
83
+ geoalchemy2/admin/plugin.py
79
84
  geoalchemy2/admin/dialects/__init__.py
80
85
  geoalchemy2/admin/dialects/common.py
81
86
  geoalchemy2/admin/dialects/geopackage.py
87
+ geoalchemy2/admin/dialects/mariadb.py
82
88
  geoalchemy2/admin/dialects/mysql.py
83
89
  geoalchemy2/admin/dialects/postgresql.py
84
90
  geoalchemy2/admin/dialects/sqlite.py
@@ -86,15 +92,20 @@ geoalchemy2/types/__init__.py
86
92
  geoalchemy2/types/dialects/__init__.py
87
93
  geoalchemy2/types/dialects/common.py
88
94
  geoalchemy2/types/dialects/geopackage.py
95
+ geoalchemy2/types/dialects/mariadb.py
89
96
  geoalchemy2/types/dialects/mysql.py
90
97
  geoalchemy2/types/dialects/postgresql.py
91
98
  geoalchemy2/types/dialects/sqlite.py
92
99
  test_container/.dockerignore
93
100
  test_container/.gitignore
94
101
  test_container/Dockerfile
102
+ test_container/Dockerfile_mariadb
95
103
  test_container/build.sh
104
+ test_container/build_mariadb.sh
96
105
  test_container/run.sh
106
+ test_container/run_mariadb.sh
97
107
  test_container/helpers/entrypoint.sh
108
+ test_container/helpers/init_mariadb.sh
98
109
  test_container/helpers/init_mysql.sh
99
110
  test_container/helpers/init_postgres.sh
100
111
  test_container/helpers/install_requirements.sh
@@ -111,6 +122,7 @@ tests/test_functional_postgresql.py
111
122
  tests/test_functional_sqlite.py
112
123
  tests/test_functions.py
113
124
  tests/test_pickle.py
125
+ tests/test_plugin.py
114
126
  tests/test_shape.py
115
127
  tests/test_types.py
116
128
  tests/alembic_config/alembic.ini
@@ -0,0 +1,2 @@
1
+ [sqlalchemy.plugins]
2
+ geoalchemy2 = geoalchemy2.admin.plugin:GeoEngine
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: GeoAlchemy2
3
- Version: 0.15.2
3
+ Version: 0.17.0
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
@@ -25,16 +25,21 @@ PostgreSQL-specific objects
25
25
  :private-members:
26
26
  :show-inheritance:
27
27
 
28
- MySQL-specific objects
29
- ---------------------------
28
+ MySQL/MariadDB-specific objects
29
+ -------------------------------
30
30
 
31
31
  .. automodule:: geoalchemy2.admin.dialects.mysql
32
32
  :members:
33
33
  :private-members:
34
34
  :show-inheritance:
35
35
 
36
+ .. automodule:: geoalchemy2.admin.dialects.mariadb
37
+ :members:
38
+ :private-members:
39
+ :show-inheritance:
40
+
36
41
  SQLite-specific objects
37
- ---------------------------
42
+ -----------------------
38
43
 
39
44
  .. automodule:: geoalchemy2.admin.dialects.sqlite
40
45
  :members:
@@ -21,7 +21,11 @@ For this tutorial we will use a PostGIS 2 database. To connect we use
21
21
  SQLAlchemy's ``create_engine()`` function::
22
22
 
23
23
  >>> from sqlalchemy import create_engine
24
- >>> engine = create_engine('postgresql://gis:gis@localhost/gis', echo=True)
24
+ >>> engine = create_engine(
25
+ ... 'postgresql://gis:gis@localhost/gis',
26
+ ... echo=True,
27
+ ... plugins=["geoalchemy2"],
28
+ ... )
25
29
 
26
30
  In this example the name of the database, the database user, and the database
27
31
  password, is ``gis``.
@@ -30,6 +34,11 @@ The ``echo`` flag is a shortcut to setting up SQLAlchemy logging, which is
30
34
  accomplished via Python's standard logging module. With it is enabled, we'll
31
35
  see all the generated SQL produced.
32
36
 
37
+ The ``plugins`` argument adds some event listeners to adapt the behavior of
38
+ ``GeoAlchemy2`` to the dialect. This is not mandatory but if the plugin is not
39
+ loaded, then the listeners will have to be added to the engine manually (see an
40
+ example in :ref:`spatialite_dialect`).
41
+
33
42
  The return value of ``create_engine`` is an ``Engine`` object, which
34
43
  represents the core interface to the database.
35
44
 
@@ -84,7 +93,10 @@ be registered into SQLAlchemy, even if it is not used explicitly.
84
93
 
85
94
  >>> from geoalchemy2 import Geometry # <= not used but must be imported
86
95
  >>> from sqlalchemy import create_engine, MetaData
87
- >>> engine = create_engine("postgresql://myuser:mypass@mydb.host.tld/mydbname")
96
+ >>> engine = create_engine(
97
+ ... "postgresql://myuser:mypass@mydb.host.tld/mydbname",
98
+ ... plugins=["geoalchemy2"]
99
+ ... )
88
100
  >>> meta = MetaData()
89
101
  >>> meta.reflect(bind=engine)
90
102
 
@@ -0,0 +1,13 @@
1
+ .. _dialect_specific_features:
2
+
3
+ Dialect specific features
4
+ =========================
5
+
6
+ Several dialects handle spatial data in different ways. ``GeoAlchemy2`` tries to hide these
7
+ differences but sometimes manual tweaks are needed.
8
+
9
+ .. toctree::
10
+ :maxdepth: 1
11
+
12
+ mysql_mariadb_dialect
13
+ spatialite_dialect
@@ -17,7 +17,7 @@ GeoAlchemy 2 also supports the following dialects:
17
17
  * `GeoPackage <http://www.geopackage.org/spec/>`_
18
18
 
19
19
  Note that using GeoAlchemy 2 with these dialects may require some specific configuration on the
20
- application side.
20
+ application side. It also may not be optimal for performance.
21
21
 
22
22
  GeoAlchemy 2 aims to be simpler than its predecessor, `GeoAlchemy
23
23
  <https://pypi.python.org/pypi/GeoAlchemy>`_. Simpler to use, and simpler
@@ -87,7 +87,7 @@ system. If you're new to GeoAlchemy 2 start with this.
87
87
 
88
88
  orm_tutorial
89
89
  core_tutorial
90
- spatialite_tutorial
90
+ dialect_specific_features
91
91
 
92
92
 
93
93
  Gallery
@@ -121,6 +121,7 @@ Reference Documentation
121
121
  :maxdepth: 1
122
122
 
123
123
  admin
124
+ plugin
124
125
  types
125
126
  elements
126
127
  spatial_functions
@@ -133,10 +134,14 @@ Development
133
134
 
134
135
  The code is available on GitHub: https://github.com/geoalchemy/geoalchemy2.
135
136
 
136
- Contributors:
137
+ Main authors:
137
138
 
138
139
  * Adrien Berchet (https://github.com/adrien-berchet)
139
140
  * Éric Lemoine (https://github.com/elemoine)
141
+
142
+ Other contributors:
143
+
144
+ * Caleb Johnson (https://github.com/calebj)
140
145
  * Dolf Andringa (https://github.com/dolfandringa)
141
146
  * Frédéric Junod, Camptocamp SA (https://github.com/fredj)
142
147
  * ijl (https://github.com/ijl)
@@ -0,0 +1,43 @@
1
+ .. _mysql_mariadb_dialect:
2
+
3
+ MySQL / MariaDB Tutorial
4
+ ========================
5
+
6
+ GeoAlchemy 2's main target is PostGIS. But GeoAlchemy 2 also supports MySQL and MariaDB.
7
+ This tutorial describes how to use GeoAlchemy 2 with these dialects.
8
+
9
+ .. _mysql_mariadb_connect:
10
+
11
+ Connect to the DB
12
+ -----------------
13
+
14
+ Just like when using PostGIS connecting to a MySQL or MariaDB database requires an ``Engine``.
15
+ An engine for these dialects can be created in two ways. Using the plugin provided by
16
+ ``GeoAlchemy2`` (see :ref:`plugin` for more details)::
17
+
18
+ >>> from sqlalchemy import create_engine
19
+ >>> engine = create_engine(
20
+ ... "mysql://user:password@host:port/dbname",
21
+ ... echo=True,
22
+ ... plugins=["geoalchemy2"]
23
+ ... )
24
+
25
+ The call to ``create_engine`` creates an engine bound to the database given in the URL. After that
26
+ a ``before_cursor_execute`` listener is registered on the engine (see
27
+ :func:`geoalchemy2.admin.dialects.mysql.before_cursor_execute` and
28
+ :func:`geoalchemy2.admin.dialects.mariadb.before_cursor_execute`). The listener is responsible for
29
+ converting the parameters passed to query in the proper format, which is often a necessary operation
30
+ for using these dialects, though it depends on the driver used. If the driver does not require such
31
+ conversion, it is possible to disable this feature with the URL parameter
32
+ ``geoalchemy2_before_cursor_execute_mysql_convert`` or
33
+ ``geoalchemy2_before_cursor_execute_mariadb_convert``, depending on the dialect used.
34
+
35
+
36
+ It is also possible to create a raw engine and attach the listener manually::
37
+
38
+ >>> from geoalchemy2.admin.dialects.mysql import before_cursor_execute
39
+ >>> from sqlalchemy import create_engine
40
+ >>> from sqlalchemy.event import listen
41
+ >>>
42
+ >>> engine = create_engine("mysql://user:password@host:port/dbname", echo=True)
43
+ >>> listen(engine, "before_cursor_execute", before_cursor_execute)
@@ -20,7 +20,11 @@ For this tutorial we will use a PostGIS 2 database. To connect we use
20
20
  SQLAlchemy's ``create_engine()`` function::
21
21
 
22
22
  >>> from sqlalchemy import create_engine
23
- >>> engine = create_engine('postgresql://gis:gis@localhost/gis', echo=True)
23
+ >>> engine = create_engine(
24
+ ... 'postgresql://gis:gis@localhost/gis',
25
+ ... echo=True,
26
+ ... plugins=["geoalchemy2"]
27
+ ... )
24
28
 
25
29
  In this example the name of the database, the database user, and the database
26
30
  password, is ``gis``.
@@ -29,6 +33,11 @@ The ``echo`` flag is a shortcut to setting up SQLAlchemy logging, which is
29
33
  accomplished via Python's standard logging module. With it is enabled, we'll
30
34
  see all the generated SQL produced.
31
35
 
36
+ The ``plugins`` argument adds some event listeners to adapt the behavior of
37
+ ``GeoAlchemy2`` to the dialect. This is not mandatory but if the plugin is not
38
+ loaded, then the listeners will have to be added to the engine manually (see an
39
+ example in :ref:`spatialite_dialect`).
40
+
32
41
  The return value of ``create_engine`` is an ``Engine`` object, which
33
42
  represents the core interface to the database.
34
43
 
@@ -128,7 +137,9 @@ Add New Objects
128
137
 
129
138
  To persist our ``Lake`` object, we ``add()`` it to the ``Session``::
130
139
 
140
+ >>> lake = Lake(name="Majeur", geom="POLYGON((0 0,1 0,1 1,0 1,0 0))")
131
141
  >>> session.add(lake)
142
+ >>> session.commit()
132
143
 
133
144
  At this point the ``lake`` object has been added to the ``Session``, but no SQL
134
145
  has been issued to the database. The object is in a *pending* state. To persist
@@ -237,6 +248,9 @@ We can also apply relationship functions to
237
248
  ``session.scalar`` allows executing a clause and returning a scalar
238
249
  value (a boolean value in this case).
239
250
 
251
+ The value ``True`` indicates that the lake "Garde" does intersects the ``LINESTRING(2 1,4 1)``
252
+ geometry. See the SpatiaLite SQL functions reference list for more information.
253
+
240
254
  The GeoAlchemy functions all start with ``ST_``. Operators are also called as
241
255
  functions, but the function names don't include the ``ST_`` prefix. As an
242
256
  example let's use PostGIS' ``&&`` operator, which allows testing
@@ -0,0 +1,9 @@
1
+ .. _plugin:
2
+
3
+ Plugin
4
+ ======
5
+
6
+ .. automodule:: geoalchemy2.admin.plugin
7
+ :members:
8
+ :private-members:
9
+ :show-inheritance:
@@ -1,4 +1,4 @@
1
- .. _spatialite_tutorial:
1
+ .. _spatialite_dialect:
2
2
 
3
3
  SpatiaLite Tutorial
4
4
  ===================
@@ -12,8 +12,18 @@ the :ref:`orm_tutorial`, which you may want to read first.
12
12
  Connect to the DB
13
13
  -----------------
14
14
 
15
- Just like when using PostGIS connecting to a SpatiaLite database requires an ``Engine``. This is how
16
- you create one for SpatiaLite::
15
+ Just like when using PostGIS connecting to a SpatiaLite database requires an ``Engine``. An engine
16
+ for the SpatiaLite dialect can be created in two ways. Using the plugin provided by
17
+ ``GeoAlchemy2`` (see :ref:`plugin` for more details)::
18
+
19
+ >>> from sqlalchemy import create_engine
20
+ >>> engine = create_engine(
21
+ ... "sqlite:///gis.db",
22
+ ... echo=True,
23
+ ... plugins=["geoalchemy2"]
24
+ ... )
25
+
26
+ Or by attaching the listeners manually::
17
27
 
18
28
  >>> from geoalchemy2 import load_spatialite
19
29
  >>> from sqlalchemy import create_engine
@@ -70,108 +80,6 @@ From the user point of view this works in the same way as with PostGIS. The diff
70
80
  internally the ``RecoverGeometryColumn`` and ``DiscardGeometryColumn`` management functions will be
71
81
  used for the creation and removal of the geometry column.
72
82
 
73
- Create the Table in the Database
74
- --------------------------------
75
-
76
- We can now create the ``lake`` table in the ``gis.db`` database::
77
-
78
- >>> Lake.__table__.create(engine)
79
-
80
- If we wanted to drop the table we'd use::
81
-
82
- >>> Lake.__table__.drop(engine)
83
-
84
- There's nothing specific to SpatiaLite here.
85
-
86
- Create a Session
87
- ----------------
88
-
89
- When using the SQLAlchemy ORM the ORM interacts with the database through a ``Session``.
90
-
91
- >>> from sqlalchemy.orm import sessionmaker
92
- >>> Session = sessionmaker(bind=engine)
93
- >>> session = Session()
94
-
95
- The session is associated with our SpatiaLite ``Engine``. Again, there's nothing
96
- specific to SpatiaLite here.
97
-
98
- Add New Objects
99
- ---------------
100
-
101
- We can now create and insert new ``Lake`` objects into the database, the same way we'd
102
- do it using GeoAlchemy 2 with PostGIS.
103
-
104
- ::
105
-
106
- >>> lake = Lake(name="Majeur", geom="POLYGON((0 0,1 0,1 1,0 1,0 0))")
107
- >>> session.add(lake)
108
- >>> session.commit()
109
-
110
- We can now query the database for ``Majeur``::
111
-
112
- >>> our_lake = session.query(Lake).filter_by(name="Majeur").first()
113
- >>> our_lake.name
114
- u"Majeur"
115
- >>> our_lake.geom
116
- <WKBElement at 0x9af594c; "0103000000010000000500000000000000000000000000000000000000000000000000f03f0000000000000000000000000000f03f000000000000f03f0000000000000000000000000000f03f00000000000000000000000000000000">
117
- >>> our_lake.id
118
- 1
119
-
120
- Let's add more lakes::
121
-
122
- >>> session.add_all([
123
- ... Lake(name="Garde", geom="POLYGON((1 0,3 0,3 2,1 2,1 0))"),
124
- ... Lake(name="Orta", geom="POLYGON((3 0,6 0,6 3,3 3,3 0))")
125
- ... ])
126
- >>> session.commit()
127
-
128
- Query
129
- -----
130
-
131
- Let's make a simple, non-spatial, query::
132
-
133
- >>> query = session.query(Lake).order_by(Lake.name)
134
- >>> for lake in query:
135
- ... print(lake.name)
136
- ...
137
- Garde
138
- Majeur
139
- Orta
140
-
141
- Now a spatial query::
142
-
143
- >>> from geolachemy2 import WKTElement
144
- >>> query = session.query(Lake).filter(
145
- ... func.ST_Contains(Lake.geom, WKTElement("POINT(4 1)")))
146
- ...
147
- >>> for lake in query:
148
- ... print(lake.name)
149
- ...
150
- Orta
151
-
152
- Here's another spatial query, using ``ST_Intersects`` this time::
153
-
154
- >>> query = session.query(Lake).filter(
155
- ... Lake.geom.ST_Intersects(WKTElement("LINESTRING(2 1,4 1)")))
156
- ...
157
- >>> for lake in query:
158
- ... print(lake.name)
159
- ...
160
- Garde
161
- Orta
162
-
163
- We can also apply relationship functions to :class:`geoalchemy2.elements.WKBElement`. For example::
164
-
165
- >>> lake = session.query(Lake).filter_by(name="Garde").one()
166
- >>> print(session.scalar(lake.geom.ST_Intersects(WKTElement("LINESTRING(2 1,4 1)"))))
167
- 1
168
-
169
- ``session.scalar`` allows executing a clause and returning a scalar value (an integer value in this
170
- case).
171
-
172
- The value ``1`` indicates that the lake "Garde" does intersects the ``LINESTRING(2 1,4 1)``
173
- geometry. See the SpatiaLite SQL functions reference list for more information.
174
-
175
83
  Function mapping
176
84
  ----------------
177
85
 
@@ -201,7 +109,7 @@ GeoPackage format
201
109
  Starting from the version ``4.2`` of Spatialite, it is possible to use GeoPackage files as DB
202
110
  containers. GeoAlchemy 2 is able to handle most of the GeoPackage features automatically if the
203
111
  GeoPackage dialect is used (i.e. the DB URL starts with ``gpkg:///``) and the SpatiaLite extension
204
- is loaded. Usually, this extension should be loaded using the ``load_spatialite_gpkg`` listener::
112
+ is loaded. Usually, this extension should be loaded using the the ``GeoAlchemy2`` plugin (see :ref:`connect <spatialite_connect>` section) or by attaching the ``load_spatialite_gpkg`` listener to the engine::
205
113
 
206
114
  >>> from geoalchemy2 import load_spatialite_gpkg
207
115
  >>> from sqlalchemy import create_engine