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

Modernize packaging #172

Merged
merged 11 commits into from
Oct 24, 2023
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
2 changes: 1 addition & 1 deletion .github/workflows/run-all-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
# run tests and lint with a variety of Python versions
---
name: Tests
on: [push, pull_request]

Check warning on line 5 in .github/workflows/run-all-tests.yml

View workflow job for this annotation

GitHub Actions / static-tests (3.10)

5:1 [truthy] truthy value should be one of [false, true]

Check warning on line 5 in .github/workflows/run-all-tests.yml

View workflow job for this annotation

GitHub Actions / static-tests (3.10)

5:1 [truthy] truthy value should be one of [false, true]

jobs:
cpython:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12']
python-version: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12', '3.13-dev']

steps:
- uses: actions/checkout@v4
Expand Down
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@ venv/
.venv/
coverage.xml
.mutmut-cache
.watchmanconfig
html/
.dccache
.pytest_cache
.pyre/
.mypy_cache/
.pytype/
.ruff_cache/
.venv/
venv/
__pycache__/
*.stderr*
24 changes: 12 additions & 12 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,6 @@ repos:
rev: 5.12.0
hooks:
- id: isort
# - repo: https://github.com/dhruvmanila/remove-print-statements
# rev: 'v0.5.0'
# hooks:
# - id: remove-print-statements
# args: ['--verbose'] # Show all the print statements to be removed
- repo: https://github.com/psf/black
rev: 23.10.0
hooks:
Expand All @@ -53,6 +48,7 @@ repos:
rev: 'v0.1.1'
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
- repo: https://github.com/PyCQA/flake8
rev: 6.1.0
hooks:
Expand Down Expand Up @@ -97,18 +93,22 @@ repos:
# rev: v1.0.1
# hooks:
# - id: rst-linter
- repo: https://github.com/mgedmin/check-manifest
rev: "0.49"
- repo: https://github.com/abravalheri/validate-pyproject
rev: v0.15
hooks:
- id: validate-pyproject
- repo: https://github.com/kieran-ryan/pyprojectsort
rev: v0.3.0
hooks:
- id: check-manifest
- id: pyprojectsort
- repo: https://github.com/python-jsonschema/check-jsonschema
rev: "0.27.0"
hooks:
- id: check-github-workflows
- id: check-github-actions
- id: check-readthedocs
# - repo: https://github.com/regebro/pyroma
# rev: "4.1"
# hooks:
# - id: pyroma
- repo: https://github.com/regebro/pyroma
rev: "4.2"
hooks:
- id: pyroma
...
7 changes: 0 additions & 7 deletions MANIFEST.in

This file was deleted.

14 changes: 9 additions & 5 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,21 @@ implement this protocol:
* `Shapely <https://github.com/Toblerity/Shapely>`_
* `pyshp <https://pypi.python.org/pypi/pyshp>`_

So when you want to write your own geospatial library with support
When you want to write your own geospatial library with support
for this protocol you may use pygeoif as a starting point and build
your functionality on top of it
your functionality on top of it. It has no requirements outside the
Python standard library and is therefore easy to integrate into your
project. It is tested on `CPython <https://python.org>`_ and
`PyPy <https://www.pypy.org/>`_, but it should work on alternative
Python implementations (that implement the language specification *>=3.8*) as well.

You may think of pygeoif as a 'shapely ultralight' which lets you
construct geometries and perform **very** basic operations like
reading and writing geometries from/to WKT, constructing line strings
out of points, polygons from linear rings, multi polygons from
polygons, etc. It was inspired by shapely and implements the
geometries in a way that when you are familiar with shapely
you feel right at home with pygeoif.
geometries in a way that when you are familiar with pygeoif,
you will feel right at home with shapely or the other way round.

It was written to provide clean and python only geometries for fastkml_

Expand All @@ -35,7 +39,7 @@ It was written to provide clean and python only geometries for fastkml_
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
:target: https://github.com/psf/black

.. image:: https://img.shields.io/badge/type%20checker-mypy-blue
.. image:: https://img.shields.io/badge/type-checker-mypy-blue
:target: http://mypy-lang.org/

.. image:: https://www.openhub.net/p/pygeoif/widgets/project_thin_badge.gif
Expand Down
5 changes: 5 additions & 0 deletions docs/HISTORY.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
Changelog
=========

1.1.1 (unreleased)
------------------

- Use pyproject.toml instead of setup.py

1.1 (2023/10/13)
-----------------

Expand Down
2 changes: 1 addition & 1 deletion docs/LICENSE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Pygeoif is a basic implementation of the __geo_interface__ in
pure Python

Copyright (C) 2012 - 2022 Christian Ledermann
Copyright (C) 2012 - 2023 Christian Ledermann

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
Expand Down
9 changes: 7 additions & 2 deletions mutmut_config.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""Mutmut configuration."""
from typing import Any
from typing import Protocol

files_to_mutate = [
"pygeoif/geometry.py",
Expand All @@ -9,7 +9,12 @@
]


def pre_mutation(context: Any) -> None:
class Context(Protocol):
filename: str
skip: bool


def pre_mutation(context: Context) -> None:
"""Only include the files specified above."""
if context.filename not in files_to_mutate:
context.skip = True
Expand Down
3 changes: 2 additions & 1 deletion pygeoif/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# Copyright (C) 2012 - 2022 Christian Ledermann
# Copyright (C) 2012 - 2023 Christian Ledermann
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
Expand All @@ -16,6 +16,7 @@
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
"""PyGeoIf provides a GeoJSON-like protocol for geo-spatial (GIS) vector data."""
from pygeoif.about import __version__ # noqa: F401
from pygeoif.factories import from_wkt
from pygeoif.factories import mapping
from pygeoif.factories import orient
Expand Down
6 changes: 6 additions & 0 deletions pygeoif/about.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
"""
About pygeoif.

The only purpose of this module is to provide a version number for the package.
"""
__version__ = "1.1.1"
2 changes: 1 addition & 1 deletion pygeoif/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class WKTParserError(AttributeError):


class InvalidGeometryError(ValueError):
"""This geometry is not valid."""
"""Geometry is not valid."""


__all__ = [
Expand Down
22 changes: 14 additions & 8 deletions pygeoif/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
mpre: Pattern[str] = re.compile(r"\(\((.+?)\)\)")


def orient(polygon: Polygon, ccw: bool = True) -> Polygon:
def orient(polygon: Polygon, ccw: bool = True) -> Polygon: # noqa: FBT001, FBT002
"""
Return a polygon with exteriors and interiors in the right orientation.

Expand All @@ -69,12 +69,12 @@ def orient(polygon: Polygon, ccw: bool = True) -> Polygon:
s = 1.0 if ccw else -1.0
rings = []
ring = polygon.exterior
if signed_area(ring.coords) / s >= 0.0:
if signed_area(ring.coords) / s >= 0:
rings.append(ring.coords)
else:
rings.append(list(ring.coords)[::-1])
for ring in polygon.interiors:
if signed_area(ring.coords) / s <= 0.0:
if signed_area(ring.coords) / s <= 0:
rings.append(ring.coords)
else:
rings.append(list(ring.coords)[::-1])
Expand All @@ -86,7 +86,7 @@ def box(
miny: float,
maxx: float,
maxy: float,
ccw: bool = True,
ccw: bool = True, # noqa: FBT001, FBT002
) -> Polygon:
"""Return a rectangular polygon with configurable normal vector."""
coords = [(maxx, miny), (maxx, maxy), (minx, maxy), (minx, miny)]
Expand Down Expand Up @@ -143,7 +143,10 @@ def shape(
}
geometry = context if isinstance(context, dict) else mapping(context)
if not geometry:
raise TypeError("Object does not implement __geo_interface__")
msg = ( # type: ignore [unreachable]
"Object does not implement __geo_interface__"
)
raise TypeError(msg)

constructor = type_map.get(geometry["type"])
if constructor:
Expand All @@ -155,7 +158,8 @@ def shape(
shape(fi) for fi in geometry["geometries"] # type: ignore [typeddict-item]
]
return GeometryCollection(geometries)
raise NotImplementedError(f"[{geometry['type']} is not implemented")
msg = f"[{geometry['type']} is not implemented"
raise NotImplementedError(msg)


def num(number: str) -> float:
Expand Down Expand Up @@ -296,12 +300,14 @@ def from_wkt(geo_str: str) -> Optional[Union[Geometry, GeometryCollection]]:
outerstr = outer.search(wkt)
coordinates = outerstr.group(1) # type: ignore [union-attr]
except AttributeError as exc:
raise WKTParserError(f"Cannot parse {wkt}") from exc
msg = f"Cannot parse {wkt}"
raise WKTParserError(msg) from exc
constructor = type_map[geometry_type]
try:
return constructor(coordinates) # type: ignore [return-value]
except TypeError as exc:
raise WKTParserError(f"Cannot parse {wkt}") from exc
msg = f"Cannot parse {wkt}"
raise WKTParserError(msg) from exc


def mapping(
Expand Down
25 changes: 12 additions & 13 deletions pygeoif/feature.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from typing import Any
from typing import Dict
from typing import Generator
from typing import Iterable
from typing import Iterator
from typing import Optional
from typing import Sequence
from typing import Union
Expand Down Expand Up @@ -56,17 +56,16 @@ class Feature:
"""
Aggregates a geometry instance with associated user-defined properties.

Attributes
~~~~~~~~~~~
Attributes:
----------
geometry : object
A geometry instance
properties : dict
A dictionary linking field keys with values
associated with geometry instance

Example
~~~~~~~~

Example:
-------
>>> p = Point(1.0, -1.0)
>>> props = {'Name': 'Sample Point', 'Other': 'Other Data'}
>>> a = Feature(p, props)
Expand Down Expand Up @@ -109,7 +108,7 @@ def __repr__(self) -> str:
)

@property
def id(self) -> Optional[Union[str, int]]:
def id(self) -> Optional[Union[str, int]]: # noqa: A003
"""Return the id of the feature."""
return self._feature_id

Expand Down Expand Up @@ -139,17 +138,17 @@ def __geo_interface__(self) -> GeoFeatureInterface:


class FeatureCollection:
"""A heterogenous collection of Features.
"""
A heterogenous collection of Features.

Attributes
Attributes:
----------
features : sequence
A sequence of feature instances


Example
Example:
-------

>>> from pygeoif import geometry
>>> p = geometry.Point(1.0, -1.0)
>>> props = {'Name': 'Sample Point', 'Other': 'Other Data'}
Expand Down Expand Up @@ -189,12 +188,12 @@ def __len__(self) -> int:
"""Return the umber of features in this collection."""
return len(self._features)

def __iter__(self) -> Iterable[Feature]:
def __iter__(self) -> Iterator[Feature]:
"""Iterate over the features of the collection."""
return iter(self._features)

def __repr__(self) -> str:
"""Retrun the representation."""
"""Return the representation."""
return f"{self.__class__.__name__}({self._features!r})"

@property
Expand Down
9 changes: 5 additions & 4 deletions pygeoif/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@


def signed_area(coords: LineType) -> float:
"""Return the signed area enclosed by a ring.
"""
Return the signed area enclosed by a ring.

Linear time algorithm: http://www.cgafaq.info/wiki/Polygon_Area.
A value >= 0 indicates a counter-clockwise oriented ring.
Expand Down Expand Up @@ -88,7 +89,7 @@ def _cross(o: Point2D, a: Point2D, b: Point2D) -> float:
def _build_hull(points: Iterable[Point2D]) -> List[Point2D]:
hull: List[Point2D] = []
for p in points:
while len(hull) >= 2 and _cross(hull[-2], hull[-1], p) <= 0:
while len(hull) >= 2 and _cross(hull[-2], hull[-1], p) <= 0: # noqa: PLR2004
hull.pop()
hull.append(p)
return hull
Expand Down Expand Up @@ -119,13 +120,13 @@ def convex_hull(points: Iterable[Point2D]) -> LineType:

# Boring case: no points, a single point or a line between two points,
# possibly repeated multiple times.
if len(points) <= 2:
if len(points) <= 2: # noqa: PLR2004
return points

lower = _build_hull(points)
upper = _build_hull(reversed(points))

if len(lower) == len(upper) == 2 and set(lower) == set(upper):
if len(lower) == len(upper) == 2 and set(lower) == set(upper): # noqa: PLR2004
# all points are in a straight line
return lower
# Concatenation of the lower and upper hulls gives the convex hull.
Expand Down
Loading
Loading