sqlalchemy-postgres-point 0.1.0__tar.gz → 0.2.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.
@@ -1,15 +1,20 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: sqlalchemy-postgres-point
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Lightweight SQLAlchemy custom type for PostgreSQL POINT columns with tuple binding and an earth-distance comparator.
5
5
  Keywords: sqlalchemy,postgresql,point,geometry,geospatial,gis,database,types,earthdistance
6
6
  Author: Michael Bianco
7
7
  Author-email: Michael Bianco <mike@mikebian.co>
8
8
  Requires-Dist: sqlalchemy>=2.0
9
- Requires-Python: >=3.10
9
+ Requires-Python: >=3.12
10
10
  Project-URL: Repository, https://github.com/iloveitaly/sqlalchemy-postgres-point
11
11
  Description-Content-Type: text/markdown
12
12
 
13
+ [![Release Notes](https://img.shields.io/github/release/iloveitaly/sqlalchemy-postgres-point)](https://github.com/iloveitaly/sqlalchemy-postgres-point/releases)
14
+ [![Downloads](https://static.pepy.tech/badge/sqlalchemy-postgres-point/month)](https://pepy.tech/project/sqlalchemy-postgres-point)
15
+ ![GitHub CI Status](https://github.com/iloveitaly/sqlalchemy-postgres-point/actions/workflows/build_and_publish.yml/badge.svg)
16
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
17
+
13
18
  sqlalchemy-postgres-point
14
19
  =========================
15
20
 
@@ -72,6 +77,54 @@ stmt = select(Place.id, Place.location.earth_distance(origin).label("dist"))
72
77
 
73
78
  The comparator translates `Place.location.earth_distance(origin)` into SQL using the `<@>` operator (requires PostgreSQL with the `cube` / `earthdistance` extension for meaningful results; without extensions the operator may not exist—adapt as needed for your environment). This library only *emits* the operator; it does not manage PostgreSQL extensions.
74
79
 
80
+ ## PostgreSQL Extensions Setup
81
+
82
+ To use the earth distance functionality (`earth_distance()` comparator), you need to enable the `cube` and `earthdistance` PostgreSQL extensions. These extensions provide spatial operations for calculating distances between geographic points on Earth's surface using a spherical model.
83
+
84
+ ### Alembic Migration Example
85
+
86
+ If you're using Alembic for database migrations, you can create a migration to enable these extensions:
87
+
88
+ ```python
89
+ """Add PostgreSQL extensions for earth distance calculations
90
+
91
+ Revision ID: your_revision_id
92
+ Revises: your_previous_revision
93
+ Create Date: 2025-01-XX XX:XX:XX.XXXXXX
94
+
95
+ """
96
+ from typing import Sequence, Union
97
+
98
+ from alembic import op
99
+ import sqlalchemy as sa
100
+
101
+ # revision identifiers, used by Alembic.
102
+ revision: str = 'your_revision_id'
103
+ down_revision: Union[str, None] = 'your_previous_revision'
104
+ branch_labels: Union[str, Sequence[str], None] = None
105
+ depends_on: Union[str, Sequence[str], None] = None
106
+
107
+
108
+ def upgrade() -> None:
109
+ op.execute("""
110
+ CREATE EXTENSION IF NOT EXISTS cube;
111
+ CREATE EXTENSION IF NOT EXISTS earthdistance;
112
+ """)
113
+
114
+
115
+ def downgrade() -> None:
116
+ op.execute("""
117
+ DROP EXTENSION IF EXISTS earthdistance;
118
+ DROP EXTENSION IF EXISTS cube;
119
+ """)
120
+ ```
121
+
122
+ **Integration Steps:**
123
+
124
+ 1. Generate a new migration: `alembic revision -m "add_postgres_extensions"`
125
+ 2. Copy the `upgrade()` and `downgrade()` functions above into your new migration file
126
+ 3. Run the migration: `alembic upgrade head`
127
+
75
128
  Returned Python Values
76
129
  ----------------------
77
130
 
@@ -86,6 +139,33 @@ Run the test suite with:
86
139
  uv run pytest -q
87
140
  ```
88
141
 
142
+ ## Alembic Integration
143
+
144
+ When using `PointType` in your models, you can automatically include the necessary imports in migration files by updating your `alembic/env.py` file.
145
+
146
+ First, import the integration module:
147
+
148
+ ```python
149
+ import sqlalchemy_postgres_point.alembic_integration
150
+ ```
151
+
152
+ Then, ensure your `context.configure` call includes a `render_item` hook to handle the `PointType`:
153
+
154
+ ```python
155
+ def render_item(type_, obj, autogen_context):
156
+ if type_ == "type" and type(obj).__name__ == "PointType":
157
+ return sqlalchemy_postgres_point.alembic_integration.render_point_type(obj, obj, autogen_context)
158
+ return False
159
+
160
+ # ... in run_migrations_online and run_migrations_offline ...
161
+ context.configure(
162
+ # ... other options ...
163
+ render_item=render_item,
164
+ )
165
+ ```
166
+
167
+ Once added, `from sqlalchemy_postgres_point import PointType` will be automatically included in generated migration files whenever a `PointType` column is detected.
168
+
89
169
  Development
90
170
  -----------
91
171
 
@@ -114,3 +194,6 @@ License
114
194
 
115
195
  MIT (see your project's LICENSE file if added later). Contributions welcome.
116
196
 
197
+ ---
198
+
199
+ *This project was created from [iloveitaly/python-package-template](https://github.com/iloveitaly/python-package-template)*
@@ -0,0 +1,187 @@
1
+ [![Release Notes](https://img.shields.io/github/release/iloveitaly/sqlalchemy-postgres-point)](https://github.com/iloveitaly/sqlalchemy-postgres-point/releases)
2
+ [![Downloads](https://static.pepy.tech/badge/sqlalchemy-postgres-point/month)](https://pepy.tech/project/sqlalchemy-postgres-point)
3
+ ![GitHub CI Status](https://github.com/iloveitaly/sqlalchemy-postgres-point/actions/workflows/build_and_publish.yml/badge.svg)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ sqlalchemy-postgres-point
7
+ =========================
8
+
9
+ Lightweight, pure-Python SQLAlchemy custom type for PostgreSQL `POINT` columns.
10
+
11
+ Why
12
+ ----
13
+
14
+ PostgreSQL has a native `POINT` type (stored internally as a pair of float8 values). SQLAlchemy does not ship a dedicated high-level type wrapper for simple geometric primitives. This package provides a very small `PointType` you can use immediately without pulling in a full spatial stack (e.g. PostGIS + GeoAlchemy2) when all you need is storing and retrieving `(longitude, latitude)` pairs.
15
+
16
+ Features
17
+ --------
18
+
19
+ * Simple `(lng, lat)` tuple binding and result conversion.
20
+ * Safe NULL handling.
21
+ * Literal rendering for DDL / SQL emission.
22
+ * Custom comparator exposing the PostgreSQL earth-distance `<@>` operator (returns a `Float`).
23
+ * `cache_ok = True` for SQLAlchemy 2.x compilation caching.
24
+
25
+ Installation
26
+ ------------
27
+
28
+ Using `uv` (recommended):
29
+
30
+ ```bash
31
+ uv add sqlalchemy-postgres-point
32
+ ```
33
+
34
+ Or with pip:
35
+
36
+ ```bash
37
+ pip install sqlalchemy-postgres-point
38
+ ```
39
+
40
+ Usage
41
+ -----
42
+
43
+ ```python
44
+ from sqlalchemy import Column, Integer
45
+ from sqlalchemy.orm import DeclarativeBase
46
+ from sqlalchemy_postgres_point import PointType
47
+
48
+
49
+ class Base(DeclarativeBase):
50
+ pass
51
+
52
+
53
+ class Place(Base):
54
+ __tablename__ = "places"
55
+ id = Column(Integer, primary_key=True)
56
+ # Store as (longitude, latitude)
57
+ location = Column(PointType)
58
+
59
+ # Example query using the custom comparator
60
+ from sqlalchemy import select
61
+
62
+ origin = (0.0, 0.0)
63
+ stmt = select(Place.id, Place.location.earth_distance(origin).label("dist"))
64
+ ```
65
+
66
+ The comparator translates `Place.location.earth_distance(origin)` into SQL using the `<@>` operator (requires PostgreSQL with the `cube` / `earthdistance` extension for meaningful results; without extensions the operator may not exist—adapt as needed for your environment). This library only *emits* the operator; it does not manage PostgreSQL extensions.
67
+
68
+ ## PostgreSQL Extensions Setup
69
+
70
+ To use the earth distance functionality (`earth_distance()` comparator), you need to enable the `cube` and `earthdistance` PostgreSQL extensions. These extensions provide spatial operations for calculating distances between geographic points on Earth's surface using a spherical model.
71
+
72
+ ### Alembic Migration Example
73
+
74
+ If you're using Alembic for database migrations, you can create a migration to enable these extensions:
75
+
76
+ ```python
77
+ """Add PostgreSQL extensions for earth distance calculations
78
+
79
+ Revision ID: your_revision_id
80
+ Revises: your_previous_revision
81
+ Create Date: 2025-01-XX XX:XX:XX.XXXXXX
82
+
83
+ """
84
+ from typing import Sequence, Union
85
+
86
+ from alembic import op
87
+ import sqlalchemy as sa
88
+
89
+ # revision identifiers, used by Alembic.
90
+ revision: str = 'your_revision_id'
91
+ down_revision: Union[str, None] = 'your_previous_revision'
92
+ branch_labels: Union[str, Sequence[str], None] = None
93
+ depends_on: Union[str, Sequence[str], None] = None
94
+
95
+
96
+ def upgrade() -> None:
97
+ op.execute("""
98
+ CREATE EXTENSION IF NOT EXISTS cube;
99
+ CREATE EXTENSION IF NOT EXISTS earthdistance;
100
+ """)
101
+
102
+
103
+ def downgrade() -> None:
104
+ op.execute("""
105
+ DROP EXTENSION IF EXISTS earthdistance;
106
+ DROP EXTENSION IF EXISTS cube;
107
+ """)
108
+ ```
109
+
110
+ **Integration Steps:**
111
+
112
+ 1. Generate a new migration: `alembic revision -m "add_postgres_extensions"`
113
+ 2. Copy the `upgrade()` and `downgrade()` functions above into your new migration file
114
+ 3. Run the migration: `alembic upgrade head`
115
+
116
+ Returned Python Values
117
+ ----------------------
118
+
119
+ Values are loaded as a 2-tuple of floats `(lng, lat)` or `None` when NULL.
120
+
121
+ Testing
122
+ -------
123
+
124
+ Run the test suite with:
125
+
126
+ ```bash
127
+ uv run pytest -q
128
+ ```
129
+
130
+ ## Alembic Integration
131
+
132
+ When using `PointType` in your models, you can automatically include the necessary imports in migration files by updating your `alembic/env.py` file.
133
+
134
+ First, import the integration module:
135
+
136
+ ```python
137
+ import sqlalchemy_postgres_point.alembic_integration
138
+ ```
139
+
140
+ Then, ensure your `context.configure` call includes a `render_item` hook to handle the `PointType`:
141
+
142
+ ```python
143
+ def render_item(type_, obj, autogen_context):
144
+ if type_ == "type" and type(obj).__name__ == "PointType":
145
+ return sqlalchemy_postgres_point.alembic_integration.render_point_type(obj, obj, autogen_context)
146
+ return False
147
+
148
+ # ... in run_migrations_online and run_migrations_offline ...
149
+ context.configure(
150
+ # ... other options ...
151
+ render_item=render_item,
152
+ )
153
+ ```
154
+
155
+ Once added, `from sqlalchemy_postgres_point import PointType` will be automatically included in generated migration files whenever a `PointType` column is detected.
156
+
157
+ Development
158
+ -----------
159
+
160
+ After cloning:
161
+
162
+ ```bash
163
+ uv sync # installs runtime + dev deps
164
+ uv run pytest -q
165
+ ```
166
+
167
+ Project Structure
168
+ -----------------
169
+
170
+ * `sqlalchemy_postgres_point/point.py` – Implementation of `PointType`.
171
+ * `tests/test_point.py` – Unit tests for processors and comparator.
172
+
173
+ Limitations / Notes
174
+ -------------------
175
+
176
+ * No automatic validation of longitude/latitude ranges; add your own if required.
177
+ * Uses simple textual representation `(lng,lat)` accepted by PostgreSQL `POINT` input parser.
178
+ * If you need advanced spatial indexing / SRID support, look at GeoAlchemy2/PostGIS instead.
179
+
180
+ License
181
+ -------
182
+
183
+ MIT (see your project's LICENSE file if added later). Contributions welcome.
184
+
185
+ ---
186
+
187
+ *This project was created from [iloveitaly/python-package-template](https://github.com/iloveitaly/python-package-template)*
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sqlalchemy-postgres-point"
3
- version = "0.1.0"
3
+ version = "0.2.0"
4
4
  description = "Lightweight SQLAlchemy custom type for PostgreSQL POINT columns with tuple binding and an earth-distance comparator."
5
5
  keywords = [
6
6
  "sqlalchemy",
@@ -14,14 +14,14 @@ keywords = [
14
14
  "earthdistance",
15
15
  ]
16
16
  readme = "README.md"
17
- requires-python = ">=3.10"
17
+ requires-python = ">=3.12"
18
18
  dependencies = ["sqlalchemy>=2.0"]
19
19
  authors = [{ name = "Michael Bianco", email = "mike@mikebian.co" }]
20
20
  urls = { "Repository" = "https://github.com/iloveitaly/sqlalchemy-postgres-point" }
21
21
 
22
22
 
23
23
  [build-system]
24
- requires = ["uv_build>=0.8.11,<0.9.0"]
24
+ requires = ["uv_build>=0.10.0"]
25
25
  build-backend = "uv_build"
26
26
 
27
27
  [tool.uv.build-backend]
@@ -30,8 +30,30 @@ module-root = ""
30
30
 
31
31
  [dependency-groups]
32
32
  dev = [
33
- "pytest>=8.3.3",
34
- "sqlmodel>=0.0.21",
33
+ "pytest>=8.3.4",
34
+ "sqlmodel>=0.0.32",
35
35
  "psycopg[binary]>=3.2.0",
36
- "activemodel",
36
+ "activemodel>=0.15.0",
37
+ "pyright[nodejs]>=1.1.408",
38
+ "ruff>=0.15.0",
39
+ "coverage>=7.13.4",
40
+ "pytest-cov>=7.0.0",
41
+ "covdefaults>=2.3.0",
42
+ "alembic>=1.16.5",
37
43
  ]
44
+
45
+ [tool.pyright]
46
+ exclude = ["examples/", "playground/", "tmp/", ".venv/", "tests/"]
47
+
48
+ [tool.pytest.ini_options]
49
+ addopts = "--cov --cov-report=term-missing --cov-report=html:tmp/htmlcov"
50
+
51
+ [tool.coverage.run]
52
+ plugins = ["covdefaults"]
53
+ source = ["sqlalchemy_postgres_point"]
54
+
55
+ [tool.coverage.report]
56
+ fail_under = 50
57
+
58
+ [tool.ruff]
59
+ extend-exclude = ["playground.py", "playground/"]
@@ -0,0 +1,56 @@
1
+ """
2
+ Alembic integration to automatically add imports for PointType in migration files.
3
+
4
+ This module hooks into Alembic's autogenerate system to automatically include
5
+ the necessary import statement when PointType is detected in models.
6
+
7
+ Usage:
8
+ Add this import to your alembic/env.py file:
9
+
10
+ import sqlalchemy_postgres_point.alembic_integration
11
+
12
+ Once added, `from sqlalchemy_postgres_point import PointType` will be automatically
13
+ included in generated migration files.
14
+ """
15
+
16
+ import alembic.autogenerate
17
+ import alembic.operations.ops
18
+ from sqlalchemy.types import UserDefinedType
19
+
20
+ from .point import PointType
21
+
22
+
23
+ def render_point_type(type_, object_, autogen_context):
24
+ """Render PointType in migration files with proper import."""
25
+ # Add the import to the migration file
26
+ autogen_context.imports.add("from sqlalchemy_postgres_point import PointType")
27
+ return "PointType()"
28
+
29
+
30
+ # Register the renderer for PointType
31
+ alembic.autogenerate.render.renderers.dispatch_for(PointType, replace=True)(
32
+ render_point_type
33
+ )
34
+
35
+
36
+ # Also register for UserDefinedType as a fallback if the class doesn't match exactly
37
+ @alembic.autogenerate.render.renderers.dispatch_for(UserDefinedType, replace=True)
38
+ def render_user_defined_type(type_, object_, autogen_context):
39
+ if type(object_).__name__ == "PointType":
40
+ return render_point_type(type_, object_, autogen_context)
41
+ return False
42
+
43
+
44
+ @alembic.autogenerate.comparators.dispatch_for("schema")
45
+ def compare_point_types(
46
+ autogen_context: alembic.autogenerate.api.AutogenContext,
47
+ upgrade_ops: alembic.operations.ops.UpgradeOps,
48
+ schema_names,
49
+ ):
50
+ """
51
+ Hook into Alembic's schema comparison to detect PointType usage.
52
+
53
+ This ensures the import is added even if PointType is used in existing
54
+ columns that might not trigger the render hook directly.
55
+ """
56
+ pass
@@ -1,104 +0,0 @@
1
- sqlalchemy-postgres-point
2
- =========================
3
-
4
- Lightweight, pure-Python SQLAlchemy custom type for PostgreSQL `POINT` columns.
5
-
6
- Why
7
- ----
8
-
9
- PostgreSQL has a native `POINT` type (stored internally as a pair of float8 values). SQLAlchemy does not ship a dedicated high-level type wrapper for simple geometric primitives. This package provides a very small `PointType` you can use immediately without pulling in a full spatial stack (e.g. PostGIS + GeoAlchemy2) when all you need is storing and retrieving `(longitude, latitude)` pairs.
10
-
11
- Features
12
- --------
13
-
14
- * Simple `(lng, lat)` tuple binding and result conversion.
15
- * Safe NULL handling.
16
- * Literal rendering for DDL / SQL emission.
17
- * Custom comparator exposing the PostgreSQL earth-distance `<@>` operator (returns a `Float`).
18
- * `cache_ok = True` for SQLAlchemy 2.x compilation caching.
19
-
20
- Installation
21
- ------------
22
-
23
- Using `uv` (recommended):
24
-
25
- ```bash
26
- uv add sqlalchemy-postgres-point
27
- ```
28
-
29
- Or with pip:
30
-
31
- ```bash
32
- pip install sqlalchemy-postgres-point
33
- ```
34
-
35
- Usage
36
- -----
37
-
38
- ```python
39
- from sqlalchemy import Column, Integer
40
- from sqlalchemy.orm import DeclarativeBase
41
- from sqlalchemy_postgres_point import PointType
42
-
43
-
44
- class Base(DeclarativeBase):
45
- pass
46
-
47
-
48
- class Place(Base):
49
- __tablename__ = "places"
50
- id = Column(Integer, primary_key=True)
51
- # Store as (longitude, latitude)
52
- location = Column(PointType)
53
-
54
- # Example query using the custom comparator
55
- from sqlalchemy import select
56
-
57
- origin = (0.0, 0.0)
58
- stmt = select(Place.id, Place.location.earth_distance(origin).label("dist"))
59
- ```
60
-
61
- The comparator translates `Place.location.earth_distance(origin)` into SQL using the `<@>` operator (requires PostgreSQL with the `cube` / `earthdistance` extension for meaningful results; without extensions the operator may not exist—adapt as needed for your environment). This library only *emits* the operator; it does not manage PostgreSQL extensions.
62
-
63
- Returned Python Values
64
- ----------------------
65
-
66
- Values are loaded as a 2-tuple of floats `(lng, lat)` or `None` when NULL.
67
-
68
- Testing
69
- -------
70
-
71
- Run the test suite with:
72
-
73
- ```bash
74
- uv run pytest -q
75
- ```
76
-
77
- Development
78
- -----------
79
-
80
- After cloning:
81
-
82
- ```bash
83
- uv sync # installs runtime + dev deps
84
- uv run pytest -q
85
- ```
86
-
87
- Project Structure
88
- -----------------
89
-
90
- * `sqlalchemy_postgres_point/point.py` – Implementation of `PointType`.
91
- * `tests/test_point.py` – Unit tests for processors and comparator.
92
-
93
- Limitations / Notes
94
- -------------------
95
-
96
- * No automatic validation of longitude/latitude ranges; add your own if required.
97
- * Uses simple textual representation `(lng,lat)` accepted by PostgreSQL `POINT` input parser.
98
- * If you need advanced spatial indexing / SRID support, look at GeoAlchemy2/PostGIS instead.
99
-
100
- License
101
- -------
102
-
103
- MIT (see your project's LICENSE file if added later). Contributions welcome.
104
-