Skip to content

Commit d3b46b3

Browse files
authored
fix: circular import (#186)
Closes #184
1 parent fc82961 commit d3b46b3

File tree

6 files changed

+112
-60
lines changed

6 files changed

+112
-60
lines changed

.github/dependabot.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
version: 2
2+
updates:
3+
# Cargo dependencies
4+
- package-ecosystem: "cargo"
5+
directory: "/"
6+
schedule:
7+
interval: "weekly"
8+
groups:
9+
cargo-dependencies:
10+
patterns:
11+
- "*"
12+
13+
# Python pip dependencies
14+
- package-ecosystem: "pip"
15+
directory: "/"
16+
schedule:
17+
interval: "weekly"
18+
groups:
19+
pip-dependencies:
20+
patterns:
21+
- "*"
22+
23+
# GitHub Actions
24+
- package-ecosystem: "github-actions"
25+
directory: "/"
26+
schedule:
27+
interval: "weekly"
28+
groups:
29+
github-actions:
30+
patterns:
31+
- "*"

.github/workflows/ci.yaml

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,23 +31,27 @@ jobs:
3131
os:
3232
- runner: ubuntu-latest
3333
duckdb-slug: linux-amd64
34-
- runner: macos-latest
35-
duckdb-slug: osx-universal
34+
# Installing GDAL is just too slow, for now. We should cache eventually.
35+
# - runner: macos-latest
36+
# duckdb-slug: osx-universal
3637
# https://github.com/stac-utils/rustac-py/issues/1
3738
# - windows-latest
3839
steps:
3940
- uses: actions/checkout@v4
4041
with:
4142
submodules: true
42-
- uses: astral-sh/setup-uv@v3
43+
- uses: astral-sh/setup-uv@v7
44+
with:
45+
python-version: ${{ matrix.python-version }}
4346
- uses: Swatinem/rust-cache@v2
44-
- name: Install Python version
45-
run: uv python install ${{ matrix.python-version }}
4647
- name: Install libduckdb
4748
run: |
4849
wget https://github.com/duckdb/duckdb/releases/download/v${{ env.duckdb-version }}/libduckdb-${{ matrix.os.duckdb-slug }}.zip
4950
mkdir -p ${{ github.workspace }}/opt/duckdb
5051
unzip libduckdb-${{ matrix.os.duckdb-slug }}.zip -d ${{ github.workspace }}/opt/duckdb
52+
- name: Install GDAL
53+
if: runner.os == 'macOS'
54+
run: brew install gdal
5155
- name: Sync
5256
run: uv sync --all-extras
5357
- name: Lint
@@ -62,3 +66,22 @@ jobs:
6266
# not worth it to install cairo on macos
6367
if: runner.os == 'ubuntu-latest'
6468
run: uv run mkdocs build --strict
69+
import:
70+
name: Import
71+
runs-on: ubuntu-latest
72+
env:
73+
DUCKDB_LIB_DIR: ${{ github.workspace }}/opt/duckdb
74+
LD_LIBRARY_PATH: ${{ github.workspace }}/opt/duckdb
75+
steps:
76+
- uses: actions/checkout@v4
77+
with:
78+
submodules: true
79+
- uses: astral-sh/setup-uv@v7
80+
- uses: Swatinem/rust-cache@v2
81+
- name: Install libduckdb
82+
run: |
83+
wget https://github.com/duckdb/duckdb/releases/download/v${{ env.duckdb-version }}/libduckdb-linux-amd64.zip
84+
mkdir -p ${{ github.workspace }}/opt/duckdb
85+
unzip libduckdb-linux-amd64.zip -d ${{ github.workspace }}/opt/duckdb
86+
- name: Import
87+
run: uv run --no-default-groups python -c "import rustac"

pyproject.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ exclude = [
5252
"_obstore",
5353
]
5454

55+
[tool.ruff.lint.per-file-ignores]
56+
"python/rustac/__init__.py" = ["F403", "F405"]
57+
58+
5559
[dependency-groups]
5660
dev = [
5761
"geopandas>=1.0.1",

python/rustac/__init__.py

Lines changed: 4 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,7 @@
11
from __future__ import annotations
22

3-
from collections.abc import AsyncGenerator
4-
from contextlib import asynccontextmanager
5-
from typing import Any
3+
from .geoparquet import geoparquet_writer
4+
from .rustac import *
65

7-
from obstore.store import ObjectStore
8-
9-
from . import store
10-
from .rustac import * # noqa: F403
11-
12-
13-
@asynccontextmanager
14-
async def geoparquet_writer(
15-
items: list[dict[str, Any]],
16-
path: str,
17-
drop_invalid_attributes: bool = True,
18-
store: store.Store | ObjectStore | None = None,
19-
) -> AsyncGenerator[GeoparquetWriter]: # noqa: F405
20-
"""Open a geoparquet writer in a context manager.
21-
22-
The items provided to the initial call will be used to build the geoparquet
23-
schema. All subsequent items must have the same schema.
24-
25-
The underlying parquet writer will group batches of items into row groups
26-
based upon it's default configuration; the row groups are _not_ determined
27-
by the size of the item lists passed to the writer.
28-
29-
Args:
30-
items: The STAC items
31-
path: The path for the stac-geoparquet file
32-
drop_invalid_attributes: If true, invalid attributes (e.g. an `id` in
33-
the `properties` field) will be dropped. If false, raise an error if
34-
an invalid attribute is encountered.
35-
store: The optional object store to use for writing the geoparquet file.
36-
37-
Examples:
38-
39-
>>> async with geoparquet_writer(item_batches[0], "out.parquet") as w:
40-
... for items in item_batches[1:]:
41-
... await w.write(items)
42-
...
43-
>>>
44-
"""
45-
writer = await GeoparquetWriter.open(items, path, drop_invalid_attributes, store) # noqa: F405
46-
yield writer
47-
await writer.finish()
48-
49-
50-
__doc__ = rustac.__doc__ # noqa: F405
51-
if hasattr(rustac, "__all__"): # noqa: F405
52-
__all__ = rustac.__all__ # noqa: F405
53-
else:
54-
__all__ = []
55-
56-
__all__.append("store")
6+
__doc__ = rustac.__doc__
7+
__all__ = rustac.__all__ + ["geoparquet_writer"] # pyright: ignore[reportUnsupportedDunderAll, reportAttributeAccessIssue]

python/rustac/geoparquet.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
from collections.abc import AsyncGenerator
2+
from contextlib import asynccontextmanager
3+
from typing import Any
4+
5+
from .rustac import GeoparquetWriter
6+
7+
8+
@asynccontextmanager
9+
async def geoparquet_writer(
10+
items: list[dict[str, Any]],
11+
path: str,
12+
drop_invalid_attributes: bool = True,
13+
store=None, # We don't type because the context manager is a PITA
14+
) -> AsyncGenerator[GeoparquetWriter]:
15+
"""Open a geoparquet writer in a context manager.
16+
17+
The items provided to the initial call will be used to build the geoparquet
18+
schema. All subsequent items must have the same schema.
19+
20+
The underlying parquet writer will group batches of items into row groups
21+
based upon it's default configuration; the row groups are _not_ determined
22+
by the size of the item lists passed to the writer.
23+
24+
Args:
25+
items: The STAC items
26+
path: The path for the stac-geoparquet file
27+
drop_invalid_attributes: If true, invalid attributes (e.g. an `id` in
28+
the `properties` field) will be dropped. If false, raise an error if
29+
an invalid attribute is encountered.
30+
store: The optional object store to use for writing the geoparquet file.
31+
32+
Examples:
33+
34+
>>> async with geoparquet_writer(item_batches[0], "out.parquet") as w:
35+
... for items in item_batches[1:]:
36+
... await w.write(items)
37+
...
38+
>>>
39+
"""
40+
writer = await GeoparquetWriter.open(items, path, drop_invalid_attributes, store)
41+
yield writer
42+
await writer.finish()

python/rustac/rustac.pyi

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,13 @@ AnyObjectStore = ObjectStore | ObstoreObjectStore
1414
class GeoparquetWriter:
1515
"""A helper class to write geoparquet from batches of items."""
1616

17+
@staticmethod
1718
async def open(
1819
items: list[dict[str, Any]],
1920
path: str,
2021
drop_invalid_attributes: bool = True,
21-
store: AnyObjectStore = None,
22-
) -> None:
22+
store: AnyObjectStore | None = None,
23+
) -> GeoparquetWriter:
2324
"""Creates a new writer for the provided items and the path.
2425
2526
Args:

0 commit comments

Comments
 (0)