``hypothesis`` strategies for geometries.
Project description
hypothesis_geometry
In what follows python
is an alias for python3.6
or pypy3.6
or any later version (python3.7
, pypy3.7
and so on).
Installation
Install the latest pip
& setuptools
packages versions
python -m pip install --upgrade pip setuptools
User
Download and install the latest stable version from PyPI
repository
python -m pip install --upgrade hypothesis_geometry
Developer
Download the latest version from GitHub
repository
git clone https://github.com/lycantropos/hypothesis_geometry.git
cd hypothesis_geometry
Install dependencies
python -m pip install -r requirements.txt
Install
python setup.py install
Usage
With setup
>>> from ground.base import get_context >>> from hypothesis import strategies >>> from hypothesis_geometry import planar >>> context = get_context() >>> Contour = context.contour_cls >>> Empty = context.empty_cls >>> Mix = context.mix_cls >>> Multipoint = context.multipoint_cls >>> Multipolygon = context.multipolygon_cls >>> Multisegment = context.multisegment_cls >>> Point = context.point_cls >>> Polygon = context.polygon_cls >>> Segment = context.segment_cls >>> min_coordinate, max_coordinate = -100, 100 >>> coordinates_type = int >>> coordinates = strategies.integers(min_coordinate, max_coordinate) >>> import warnings >>> from hypothesis.errors import NonInteractiveExampleWarning >>> # ignore hypothesis warnings caused by `example` method call ... warnings.filterwarnings('ignore', category=NonInteractiveExampleWarning)
let's take a look at what can be generated and how.
Empty geometries
>>> empty_geometries = planar.empty_geometries() >>> empty = empty_geometries.example() >>> isinstance(empty, Empty) True
Points
>>> points = planar.points(coordinates) >>> point = points.example() >>> isinstance(point, Point) True >>> (isinstance(point.x, coordinates_type) ... and isinstance(point.y, coordinates_type)) True >>> (min_coordinate <= point.x <= max_coordinate ... and min_coordinate <= point.y <= max_coordinate) True
Multipoints
>>> min_size, max_size = 5, 10 >>> multipoints = planar.multipoints(coordinates, ... min_size=min_size, ... max_size=max_size) >>> multipoint = multipoints.example() >>> isinstance(multipoint, Multipoint) True >>> min_size <= len(multipoint.points) <= max_size True >>> all(isinstance(point.x, coordinates_type) ... and isinstance(point.y, coordinates_type) ... for point in multipoint.points) True >>> all(min_coordinate <= point.x <= max_coordinate ... and min_coordinate <= point.y <= max_coordinate ... for point in multipoint.points) True
Segments
>>> segments = planar.segments(coordinates) >>> segment = segments.example() >>> isinstance(segment, Segment) True >>> (isinstance(segment.start.x, coordinates_type) ... and isinstance(segment.start.y, coordinates_type) ... and isinstance(segment.end.x, coordinates_type) ... and isinstance(segment.end.y, coordinates_type)) True >>> (min_coordinate <= segment.start.x <= max_coordinate ... and min_coordinate <= segment.start.y <= max_coordinate ... and min_coordinate <= segment.end.x <= max_coordinate ... and min_coordinate <= segment.end.y <= max_coordinate) True
Multisegments
>>> min_size, max_size = 5, 10 >>> multisegments = planar.multisegments(coordinates, ... min_size=min_size, ... max_size=max_size) >>> multisegment = multisegments.example() >>> isinstance(multisegment, Multisegment) True >>> min_size <= len(multisegment.segments) <= max_size True >>> all(isinstance(segment.start.x, coordinates_type) ... and isinstance(segment.start.y, coordinates_type) ... and isinstance(segment.end.x, coordinates_type) ... and isinstance(segment.end.y, coordinates_type) ... for segment in multisegment.segments) True >>> all(min_coordinate <= segment.start.x <= max_coordinate ... and min_coordinate <= segment.start.y <= max_coordinate ... and min_coordinate <= segment.end.x <= max_coordinate ... and min_coordinate <= segment.end.y <= max_coordinate ... for segment in multisegment.segments) True
Contours
>>> min_size, max_size = 5, 10 >>> contours = planar.contours(coordinates, ... min_size=min_size, ... max_size=max_size) >>> contour = contours.example() >>> isinstance(contour, Contour) True >>> min_size <= len(contour.vertices) <= max_size True >>> all(isinstance(vertex.x, coordinates_type) ... and isinstance(vertex.y, coordinates_type) ... for vertex in contour.vertices) True >>> all(min_coordinate <= vertex.x <= max_coordinate ... and min_coordinate <= vertex.y <= max_coordinate ... for vertex in contour.vertices) True
also planar.concave_contours
& planar.convex_contours
options are available.
Multicontours
>>> min_size, max_size = 5, 10 >>> min_contour_size, max_contour_size = 4, 8 >>> multicontours = planar.multicontours(coordinates, ... min_size=min_size, ... max_size=max_size, ... min_contour_size=min_contour_size, ... max_contour_size=max_contour_size) >>> multicontour = multicontours.example() >>> isinstance(multicontour, list) True >>> all(isinstance(contour, Contour) for contour in multicontour) True >>> min_size <= len(multicontour) <= max_size True >>> all(min_contour_size <= len(contour.vertices) <= max_contour_size ... for contour in multicontour) True >>> all(isinstance(vertex.x, coordinates_type) ... and isinstance(vertex.y, coordinates_type) ... for contour in multicontour ... for vertex in contour.vertices) True >>> all(min_coordinate <= vertex.x <= max_coordinate ... and min_coordinate <= vertex.y <= max_coordinate ... for contour in multicontour ... for vertex in contour.vertices) True
Polygons
>>> min_size, max_size = 5, 10 >>> min_holes_size, max_holes_size = 1, 3 >>> min_hole_size, max_hole_size = 4, 8 >>> polygons = planar.polygons(coordinates, ... min_size=min_size, ... max_size=max_size, ... min_holes_size=min_holes_size, ... max_holes_size=max_holes_size, ... min_hole_size=min_hole_size, ... max_hole_size=max_hole_size) >>> polygon = polygons.example() >>> isinstance(polygon, Polygon) True >>> min_size <= len(polygon.border.vertices) <= max_size True >>> min_holes_size <= len(polygon.holes) <= max_holes_size True >>> all(min_hole_size <= len(hole.vertices) <= max_hole_size for hole in polygon.holes) True >>> polygon_contours = [polygon.border, *polygon.holes] >>> all(isinstance(vertex.x, coordinates_type) ... and isinstance(vertex.y, coordinates_type) ... for contour in polygon_contours ... for vertex in contour.vertices) True >>> all(min_coordinate <= vertex.x <= max_coordinate ... and min_coordinate <= vertex.y <= max_coordinate ... for contour in polygon_contours ... for vertex in contour.vertices) True
Multipolygons
>>> min_size, max_size = 2, 5 >>> min_border_size, max_border_size = 5, 10 >>> min_holes_size, max_holes_size = 1, 3 >>> min_hole_size, max_hole_size = 4, 8 >>> multipolygons = planar.multipolygons(coordinates, ... min_size=min_size, ... max_size=max_size, ... min_border_size=min_border_size, ... max_border_size=max_border_size, ... min_holes_size=min_holes_size, ... max_holes_size=max_holes_size, ... min_hole_size=min_hole_size, ... max_hole_size=max_hole_size) >>> multipolygon = multipolygons.example() >>> isinstance(multipolygon, Multipolygon) True >>> min_size <= len(multipolygon.polygons) <= max_size True >>> all(min_border_size <= len(polygon.border.vertices) <= max_border_size ... and min_holes_size <= len(polygon.holes) <= max_holes_size ... and all(min_hole_size <= len(hole.vertices) <= max_hole_size ... for hole in polygon.holes) ... for polygon in multipolygon.polygons) True >>> all(all(isinstance(vertex.x, coordinates_type) ... and isinstance(vertex.y, coordinates_type) ... for vertex in polygon.border.vertices) ... and all(isinstance(vertex.x, coordinates_type) ... and isinstance(vertex.y, coordinates_type) ... for hole in polygon.holes ... for vertex in hole.vertices) ... for polygon in multipolygon.polygons) True >>> all(all(min_coordinate <= vertex.x <= max_coordinate ... and min_coordinate <= vertex.y <= max_coordinate ... for vertex in polygon.border.vertices) ... and all(min_coordinate <= vertex.x <= max_coordinate ... and min_coordinate <= vertex.y <= max_coordinate ... for hole in polygon.holes ... for vertex in hole.vertices) ... for polygon in multipolygon.polygons) True
Mixes
>>> min_points_size, max_points_size = 2, 3 >>> min_segments_size, max_segments_size = 1, 4 >>> min_polygons_size, max_polygons_size = 0, 5 >>> min_polygon_border_size, max_polygon_border_size = 5, 10 >>> min_polygon_holes_size, max_polygon_holes_size = 1, 4 >>> min_polygon_hole_size, max_polygon_hole_size = 3, 5 >>> mixes = planar.mixes(coordinates, ... min_points_size=min_points_size, ... max_points_size=max_points_size, ... min_segments_size=min_segments_size, ... max_segments_size=max_segments_size, ... min_polygons_size=min_polygons_size, ... max_polygons_size=max_polygons_size, ... min_polygon_border_size=min_polygon_border_size, ... max_polygon_border_size=max_polygon_border_size, ... min_polygon_holes_size=min_polygon_holes_size, ... max_polygon_holes_size=max_polygon_holes_size, ... min_polygon_hole_size=min_polygon_hole_size, ... max_polygon_hole_size=max_polygon_hole_size) >>> mix = mixes.example() >>> isinstance(mix, Mix) True >>> isinstance(mix.discrete, (Empty, Multipoint)) True >>> points = [] if isinstance(mix.discrete, Empty) else mix.discrete.points >>> min_points_size <= len(points) <= max_points_size True >>> all(isinstance(point.x, coordinates_type) ... and isinstance(point.y, coordinates_type) ... for point in points) True >>> all(min_coordinate <= point.x <= max_coordinate ... and min_coordinate <= point.y <= max_coordinate ... for point in points) True >>> isinstance(mix.linear, (Empty, Segment, Contour, Multisegment)) True >>> segments = ([] ... if isinstance(mix.linear, Empty) ... else ([mix.linear] ... if isinstance(mix.linear, Segment) ... else (mix.linear.segments ... if isinstance(mix.linear, Multisegment) ... else context.contour_edges(mix.linear)))) >>> min_segments_size <= len(segments) <= max_segments_size True >>> all(isinstance(segment.start.x, coordinates_type) ... and isinstance(segment.start.y, coordinates_type) ... and isinstance(segment.end.x, coordinates_type) ... and isinstance(segment.end.y, coordinates_type) ... for segment in segments) True >>> all(min_coordinate <= segment.start.x <= max_coordinate ... and min_coordinate <= segment.start.y <= max_coordinate ... and min_coordinate <= segment.end.x <= max_coordinate ... and min_coordinate <= segment.end.y <= max_coordinate ... for segment in segments) True >>> isinstance(mix.shaped, (Empty, Polygon, Multipolygon)) True >>> polygons = ([] ... if isinstance(mix.shaped, Empty) ... else ([mix.shaped] ... if isinstance(mix.shaped, Polygon) ... else mix.shaped.polygons)) >>> min_polygons_size <= len(polygons) <= max_polygons_size True >>> all(min_polygon_border_size ... <= len(polygon.border.vertices) ... <= max_polygon_border_size ... and (min_polygon_holes_size ... <= len(polygon.holes) ... <= max_polygon_holes_size) ... and all(min_polygon_hole_size ... <= len(hole.vertices) ... <= max_polygon_hole_size ... for hole in polygon.holes) ... for polygon in polygons) True >>> all(all(isinstance(vertex.x, coordinates_type) ... and isinstance(vertex.y, coordinates_type) ... for vertex in polygon.border.vertices) ... and all(isinstance(vertex.x, coordinates_type) ... and isinstance(vertex.y, coordinates_type) ... for hole in polygon.holes ... for vertex in hole.vertices) ... for polygon in polygons) True >>> all(all(min_coordinate <= vertex.x <= max_coordinate ... and min_coordinate <= vertex.y <= max_coordinate ... for vertex in polygon.border.vertices) ... and all(min_coordinate <= vertex.x <= max_coordinate ... and min_coordinate <= vertex.y <= max_coordinate ... for hole in polygon.holes ... for vertex in hole.vertices) ... for polygon in polygons) True
Caveats
-
Strategies may be slow depending on domain, so it may be necessary to add
HealthCheck.filter_too_much
,HealthCheck.too_slow
insuppress_health_check
and setdeadline
toNone
. -
Unbounded floating point strategies for coordinates (like
hypothesis.strategies.floats
with unsetmin_value
/max_value
) do not play well with bounded sizes and may cause a lot of searching iterations with no success, so it is recommended to use bounded floating point coordinates with bounded sizes or unbounded coordinates with unbounded sizes. -
decimal.Decimal
coordinates are not supported, because they seem to be too hard to work with correctly (e.g. sometimes self-intersecting contours arise), so it is suggested to usefloat
orfractions.Fraction
instead.
Development
Bumping version
Preparation
Install bump2version.
Pre-release
Choose which version number category to bump following semver specification.
Test bumping version
bump2version --dry-run --verbose $CATEGORY
where $CATEGORY
is the target version number category name, possible
values are patch
/minor
/major
.
Bump version
bump2version --verbose $CATEGORY
This will set version to major.minor.patch-alpha
.
Release
Test bumping version
bump2version --dry-run --verbose release
Bump version
bump2version --verbose release
This will set version to major.minor.patch
.
Running tests
Install dependencies
python -m pip install -r requirements-tests.txt
Plain
pytest
Inside Docker
container:
- with
CPython
docker-compose --file docker-compose.cpython.yml up
- with
PyPy
docker-compose --file docker-compose.pypy.yml up
Bash
script:
-
with
CPython
./run-tests.sh
or
./run-tests.sh cpython
-
with
PyPy
./run-tests.sh pypy
PowerShell
script:
- with
CPython
.\run-tests.ps1
or.\run-tests.ps1 cpython
- with
PyPy
.\run-tests.ps1 pypy
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for hypothesis_geometry-7.3.0.tar.gz
Algorithm | Hash digest | |
---|---|---|
SHA256 | 652a143a79699ec832165b78a1110e286d2af178f8c73b7f86e46919001670a9 |
|
MD5 | 7e8e1cbd33386f9068c78cd17d24ae87 |
|
BLAKE2-256 | 9d7e81014200c69312559a310d541a6f5ed219fe0e54b715394f7fa291c57c57 |
Hashes for hypothesis_geometry-7.3.0-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | c7d0568665d74bb14331a405faa4487a5afa8b58ed85f8c412ee6c88d4571806 |
|
MD5 | d6f188c69af7e86da45452fa0641f69b |
|
BLAKE2-256 | 90e2d88d74d6e29b17fb19f71136581a10f3fcab88249f3b03c6572438f5b7cb |