Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pickling #276

Merged
merged 2 commits into from
Mar 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
- useless conversions
- const functions
- pyo3 v0.24.x
- frozen py structs

---

Expand Down
1 change: 0 additions & 1 deletion crates/utiles/src/mbt/stream_writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,6 @@ impl MbtStreamWriterSync {
pub async fn write(&mut self) -> UtilesResult<()> {
let db_type = self.mbt.query_mbt_type()?;
self.preflight()?;
// let db_type = MbtType::Flat;
match db_type {
MbtType::Flat => self.write_flat().await,
MbtType::Hash => self.write_hash().await,
Expand Down
12 changes: 6 additions & 6 deletions utiles-pyo3/src/pyutiles/pybbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use pyo3::types::PyType;
use pyo3::{exceptions, pyclass, pymethods, PyAny, PyErr, PyRef, PyResult, Python};
use utiles::BBox;

#[pyclass(name = "Bbox", module = "utiles._utiles")]
#[pyclass(name = "Bbox", module = "utiles._utiles", frozen)]
#[derive(Clone)]
pub struct PyBbox {
pub bbox: BBox,
Expand All @@ -15,7 +15,7 @@ pub struct PyBbox {
#[pymethods]
impl PyBbox {
#[new]
pub fn new(left: f64, bottom: f64, right: f64, top: f64) -> Self {
pub fn py_new(left: f64, bottom: f64, right: f64, top: f64) -> Self {
PyBbox {
bbox: BBox {
west: left,
Expand All @@ -30,10 +30,10 @@ impl PyBbox {
pub fn from_tile(_cls: &Bound<'_, PyType>, tile: &PyTile) -> Self {
let ul = utiles::ul(tile.xyz.x, tile.xyz.y, tile.xyz.z);
let lr = utiles::lr(tile.xyz.x, tile.xyz.y, tile.xyz.z);
Self::new(ul.lng(), lr.lat(), lr.lng(), ul.lat())
Self::py_new(ul.lng(), lr.lat(), lr.lng(), ul.lat())
}

pub fn __str__(&self) -> String {
pub fn __repr__(&self) -> String {
format!(
"Bbox(left={}, bottom={}, right={}, top={})",
self.bbox.left(),
Expand All @@ -43,8 +43,8 @@ impl PyBbox {
)
}

pub fn __repr__(&self) -> String {
self.__str__()
pub fn __str__(&self) -> String {
self.__repr__()
}

#[getter]
Expand Down
14 changes: 7 additions & 7 deletions utiles-pyo3/src/pyutiles/pyfns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use crate::pyutiles::zoom::PyZoomOrZooms;

#[pyfunction]
pub fn xyz(x: u32, y: u32, z: u8) -> PyTile {
PyTile::new(x, y, z)
PyTile::py_new(x, y, z)
}

#[pyfunction]
Expand Down Expand Up @@ -57,7 +57,7 @@ pub fn _xy(lng: f64, lat: f64, truncate: Option<bool>) -> PyResult<(f64, f64)> {
#[pyo3(signature = (x, y, truncate = None))]
pub fn lnglat(x: f64, y: f64, truncate: Option<bool>) -> PyLngLat {
let lnglat = utiles::lnglat(x, y, truncate);
PyLngLat::new(lnglat.lng(), lnglat.lat())
PyLngLat::py_new(lnglat.lng(), lnglat.lat())
}

#[pyfunction]
Expand Down Expand Up @@ -100,7 +100,7 @@ pub fn qk2xyz(quadkey: &str) -> PyResult<PyTile> {

#[pyfunction]
pub fn from_tuple(tile: TileTuple) -> PyTile {
PyTile::new(tile.0, tile.1, tile.2)
PyTile::py_new(tile.0, tile.1, tile.2)
}
#[pyfunction]
#[pyo3(signature = (tile, fid = None, props = None, projected = None, buffer = None, precision = None)
Expand Down Expand Up @@ -138,12 +138,12 @@ pub fn _extract(arg: &Bound<'_, PyAny>) -> PyResult<Vec<PyTile>> {
} else if let Ok(seq) = arg.extract::<Vec<(u32, u32, u32)>>() {
return Ok(seq
.iter()
.map(|xyz| PyTile::new(xyz.0, xyz.1, xyz.2 as u8))
.map(|xyz| PyTile::py_new(xyz.0, xyz.1, xyz.2 as u8))
.collect());
} else if let Ok(seq) = arg.extract::<Vec<Vec<u32>>>() {
return Ok(seq
.iter()
.map(|xyz| PyTile::new(xyz[0], xyz[1], xyz[2] as u8))
.map(|xyz| PyTile::py_new(xyz[0], xyz[1], xyz[2] as u8))
.collect());
}
Err(PyErr::new::<PyValueError, _>(
Expand All @@ -156,7 +156,7 @@ pub fn _extract(arg: &Bound<'_, PyAny>) -> PyResult<Vec<PyTile>> {
pub fn xy_bounds(args: &Bound<'_, PyTuple>) -> PyResult<PyBbox> {
let tile = pyparsing::parse_tile_arg(args)?;
let web_bbox = utiles::xyz2bbox(tile.xyz.x, tile.xyz.y, tile.xyz.z);
Ok(PyBbox::new(
Ok(PyBbox::py_new(
web_bbox.left(),
web_bbox.bottom(),
web_bbox.right(),
Expand Down Expand Up @@ -390,5 +390,5 @@ pub fn geojson_bounds(obj: &Bound<'_, PyAny>) -> PyResult<PyLngLatBbox> {
bbox.3.max(lat),
);
}
Ok(PyLngLatBbox::new(bbox.0, bbox.1, bbox.2, bbox.3))
Ok(PyLngLatBbox::py_new(bbox.0, bbox.1, bbox.2, bbox.3))
}
6 changes: 3 additions & 3 deletions utiles-pyo3/src/pyutiles/pylnglat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ use pyo3::exceptions::{self};
use pyo3::prelude::*;
use pyo3::types::PyType;

#[pyclass(name = "LngLat", module = "utiles._utiles")]
#[pyclass(name = "LngLat", module = "utiles._utiles", frozen)]
pub struct PyLngLat {
lnglat: utiles::LngLat,
}

#[pymethods]
impl PyLngLat {
#[new]
pub fn new(lng: f64, lat: f64) -> Self {
pub fn py_new(lng: f64, lat: f64) -> Self {
Self {
lnglat: utiles::LngLat::new(lng, lat),
}
Expand All @@ -21,7 +21,7 @@ impl PyLngLat {
#[classmethod]
pub fn from_tile(_cls: &Bound<'_, PyType>, tile: &PyTile) -> Self {
let ll = utiles::ul(tile.xyz.x, tile.xyz.y, tile.xyz.z);
Self::new(ll.lng(), ll.lat())
Self::py_new(ll.lng(), ll.lat())
}

pub fn __repr__(&self) -> String {
Expand Down
6 changes: 3 additions & 3 deletions utiles-pyo3/src/pyutiles/pylnglatbbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use pyo3::types::PyType;
use pyo3::{exceptions, pyclass, pymethods, Py, PyAny, PyErr, PyRef, PyResult, Python};
use utiles::bbox::BBox;

#[pyclass(name = "LngLatBbox", module = "utiles._utiles")]
#[pyclass(name = "LngLatBbox", module = "utiles._utiles", frozen)]
#[derive(Clone)]
pub struct PyLngLatBbox {
pub bbox: BBox,
Expand All @@ -22,7 +22,7 @@ impl From<PyLngLatBbox> for BBox {
#[pymethods]
impl PyLngLatBbox {
#[new]
pub fn new(west: f64, south: f64, east: f64, north: f64) -> Self {
pub fn py_new(west: f64, south: f64, east: f64, north: f64) -> Self {
PyLngLatBbox {
bbox: BBox {
west,
Expand Down Expand Up @@ -66,7 +66,7 @@ impl PyLngLatBbox {
pub fn from_tile(_cls: &Bound<'_, PyType>, tile: &PyTile) -> Self {
let ul = utiles::ul(tile.xyz.x, tile.xyz.y, tile.xyz.z);
let lr = utiles::lr(tile.xyz.x, tile.xyz.y, tile.xyz.z);
Self::new(ul.lng(), lr.lat(), lr.lng(), ul.lat())
Self::py_new(ul.lng(), lr.lat(), lr.lng(), ul.lat())
}

#[getter]
Expand Down
16 changes: 8 additions & 8 deletions utiles-pyo3/src/pyutiles/pyparsing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ pub fn parse_tile_arg(args: &Bound<'_, PyTuple>) -> PyResult<PyTile> {
if let Ok(tile) = arg.extract::<PyTile>() {
return Ok(tile);
} else if let Ok(seq) = arg.extract::<(u32, u32, u8)>() {
return Ok(PyTile::new(seq.0, seq.1, seq.2));
return Ok(PyTile::py_new(seq.0, seq.1, seq.2));
} else if let Ok(seq) = arg.extract::<Vec<u32>>() {
return Ok(PyTile::new(seq[0], seq[1], seq[2] as u8));
return Ok(PyTile::py_new(seq[0], seq[1], seq[2] as u8));
}
} else if args.len() == 3 {
let x = args.get_item(0)?.extract()?;
let y = args.get_item(1)?.extract()?;
let z = args.get_item(2)?.extract()?;
return Ok(PyTile::new(x, y, z));
return Ok(PyTile::py_new(x, y, z));
}

Err(PyErr::new::<PyValueError, _>(
Expand All @@ -38,9 +38,9 @@ pub fn parse_bbox(args: &Bound<'_, PyTuple>) -> PyResult<PyLngLatBbox> {
1 => {
let arg = args.get_item(0)?;
if let Ok(bbox) = arg.extract::<(f64, f64, f64, f64)>() {
return Ok(PyLngLatBbox::new(bbox.0, bbox.1, bbox.2, bbox.3));
return Ok(PyLngLatBbox::py_new(bbox.0, bbox.1, bbox.2, bbox.3));
} else if let Ok(seq) = arg.extract::<Vec<f64>>() {
return Ok(PyLngLatBbox::new(seq[0], seq[1], seq[2], seq[3]));
return Ok(PyLngLatBbox::py_new(seq[0], seq[1], seq[2], seq[3]));
}
// raise ValueError("the bbox argument may have 1 or 4 values")
Err(PyErr::new::<PyValueError, _>(
Expand All @@ -50,14 +50,14 @@ pub fn parse_bbox(args: &Bound<'_, PyTuple>) -> PyResult<PyLngLatBbox> {
2 => {
let x = args.get_item(0)?.extract()?;
let y = args.get_item(1)?.extract()?;
Ok(PyLngLatBbox::new(x, y, x, y))
Ok(PyLngLatBbox::py_new(x, y, x, y))
}
4 => {
let x1 = args.get_item(0)?.extract()?;
let y1 = args.get_item(1)?.extract()?;
let x2 = args.get_item(2)?.extract()?;
let y2 = args.get_item(3)?.extract()?;
Ok(PyLngLatBbox::new(x1, y1, x2, y2))
Ok(PyLngLatBbox::py_new(x1, y1, x2, y2))
}
_ => Err(PyErr::new::<PyValueError, _>(
"the bbox argument may have 1, 2 or 4 values",
Expand All @@ -80,7 +80,7 @@ pub fn parse_tiles(args: &Bound<'_, PyTuple>) -> PyResult<Vec<PyTile>> {
if let Ok(x) = args.get_item(0)?.extract::<u32>() {
let y = args.get_item(1)?.extract()?;
let z = args.get_item(2)?.extract()?;
return Ok(vec![PyTile::new(x, y, z)]);
return Ok(vec![PyTile::py_new(x, y, z)]);
}
}

Expand Down
46 changes: 16 additions & 30 deletions utiles-pyo3/src/pyutiles/pytile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ use pyo3::exceptions::PyValueError;
use pyo3::prelude::*;
use pyo3::types::{PyDict, PyNotImplemented, PyTuple, PyType};
use pyo3::{
exceptions, intern, pyclass, pymethods, Py, PyAny, PyErr, PyRef, PyResult, Python,
exceptions, intern, pyclass, pymethods, IntoPyObjectExt, Py, PyAny, PyErr, PyRef,
PyResult, Python,
};
use serde::Serialize;
use utiles::bbox::BBox;
Expand All @@ -33,27 +34,25 @@ macro_rules! pytile {
};
}

#[pyclass(name = "Tile", module = "utiles._utiles", sequence)]
#[pyclass(name = "Tile", module = "utiles._utiles", sequence, frozen)]
#[derive(Clone, Debug, PartialEq, Serialize, Eq, Hash, Copy)]
pub struct PyTile {
pub xyz: Tile,
}

// #[derive(FromPyObject)]
// pub enum PyTileOrTuple {
// Tile(PyTile),
// Tuple(TileTuple),
// }

#[pymethods]
impl PyTile {
#[new]
pub fn new(x: u32, y: u32, z: u8) -> Self {
pub fn py_new(x: u32, y: u32, z: u8) -> Self {
Self {
xyz: Tile::new(x, y, z),
}
}

fn __getnewargs__<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyTuple>> {
PyTuple::new(py, [self.x(), self.y(), u32::from(self.z())])
}

pub fn valid(&self) -> bool {
self.xyz.valid()
}
Expand All @@ -64,8 +63,7 @@ impl PyTile {
}

pub fn json_arr(&self) -> String {
let s = format!("[{}, {}, {}]", self.xyz.x, self.xyz.y, self.xyz.z);
s
format!("[{}, {}, {}]", self.xyz.x, self.xyz.y, self.xyz.z)
}

#[pyo3(signature = (obj = true))]
Expand Down Expand Up @@ -224,10 +222,7 @@ impl PyTile {
&self,
idx: tuple_slice::SliceOrInt,
py: Python<'py>,
) -> PyResult<
// tuple_slice::TupleSliceResult<u32>
Bound<'py, PyAny>,
> {
) -> PyResult<Bound<'py, PyAny>> {
match idx {
tuple_slice::SliceOrInt::Slice(slice) => {
let psi = slice.indices(3)?;
Expand All @@ -237,27 +232,18 @@ impl PyTile {
.step_by(step as usize)
.copied()
.collect();
let tuple =
PyTuple::new(py, m)
.map(pyo3::Bound::into_any)
.map_err(|e| {
PyErr::new::<PyValueError, _>(format!("Error: {e}"))
})?;
let tuple = PyTuple::new(py, m).map(Bound::into_any).map_err(|e| {
PyErr::new::<PyValueError, _>(format!("Error: {e}"))
})?;
Ok(tuple)
}
tuple_slice::SliceOrInt::Int(idx) => match idx {
0 | -3 => {
let r = self.xyz.x.into_pyobject(py).map(pyo3::Bound::into_any)?;
Ok(r)
}
1 | -2 => {
let r = self.xyz.y.into_pyobject(py).map(pyo3::Bound::into_any)?;
Ok(r)
}
0 | -3 => self.xyz.x().into_bound_py_any(py),
1 | -2 => self.xyz.y().into_bound_py_any(py),
2 | -1 => {
let r = u32::from(self.xyz.z)
.into_pyobject(py)
.map(pyo3::Bound::into_any)?;
.map(Bound::into_any)?;
Ok(r)
}
3 => Err(PyErr::new::<exceptions::PyStopIteration, _>("")),
Expand Down
4 changes: 2 additions & 2 deletions utiles-pyo3/src/pyutiles/pytile_fmts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ use utiles::TileStringFormatter;
use crate::pyutiles::pyparsing::parse_tile_arg;

#[derive(Clone, Debug, PartialEq, Hash)]
#[pyclass(name = "TileFmts", module = "utiles._utiles")]
#[pyclass(name = "TileFmts", module = "utiles._utiles", frozen)]
pub struct PyTileFmts {
pub tformatter: TileStringFormatter,
}

#[pymethods]
impl PyTileFmts {
#[new]
pub fn new(fmtstr: &str) -> Self {
pub fn py_new(fmtstr: &str) -> Self {
PyTileFmts {
tformatter: TileStringFormatter::new(fmtstr),
}
Expand Down
11 changes: 2 additions & 9 deletions utiles-pyo3/src/pyutiles/pytile_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ impl FromPyObject<'_> for PyTileEncoding {
}
}

pub struct PyTileFormat(tile_type::TileFormat);
pub struct PyTileFormat(TileFormat);

const TILE_FORMAT_STRINGS: &str = "png, webp, pbf, mvt, gif, jpg, jpeg, json, geojson";
impl FromPyObject<'_> for PyTileFormat {
Expand Down Expand Up @@ -107,14 +107,7 @@ impl PyTileType {

#[getter]
fn compression<'py>(&self, py: Python<'py>) -> &Bound<'py, PyString> {
match self.0.encoding {
TileEncoding::Uncompressed => intern!(py, "uncompressed"),
TileEncoding::Internal => intern!(py, "internal"),
TileEncoding::Zlib => intern!(py, "zlib"),
TileEncoding::Gzip => intern!(py, "gzip"),
TileEncoding::Brotli => intern!(py, "brotli"),
TileEncoding::Zstd => intern!(py, "zstd"),
}
self.encoding(py)
}

#[getter]
Expand Down
8 changes: 8 additions & 0 deletions utiles-pyo3/tests/test_tile.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from __future__ import annotations

import pickle

import utiles as ut


Expand Down Expand Up @@ -27,6 +29,12 @@ def test_tile_children() -> None:
assert children_2 == children_1


def test_pickling() -> None:
pickled = pickle.dumps(ut.Tile(0, 0, 0))
loaded = pickle.loads(pickled)
assert loaded == ut.Tile(0, 0, 0)


def test_tile_children_zorder() -> None:
children = ut.Tile(0, 0, 0).children(zorder=True)
children_1 = [
Expand Down
Loading