GeospatialPython / pyshp

This library reads and writes ESRI Shapefiles in pure Python.
MIT License
1.09k stars 259 forks source link

PyShp's own Example from README.md fails under Doctest with Python 2.7. .__geo_interface__ = behaves differently in Python 2? #263

Closed JamesParrott closed 8 months ago

JamesParrott commented 8 months ago

PyShp Version

2.3.1

Python Version

2.7

Your code

Make local copies of shapefile.py, README.md & shapefiles/test/polygon.* from v3.2.1 and simply run , or py -2.7 shapefile.py on Windows (or python shapefile.py if the global python is python 2).

For convenience, an MRX with polygon.* in the same directory is:

README.md

        >>> import shapefile
    >>> r = shapefile.Reader('polygon')
    >>> w = shapefile.Writer('copy')
    >>> w.fields = r.fields[1:] # skip first deletion field
    >>> # adding existing Shape objects
    >>> for shaperec in r.iterShapeRecords():
    ...     w.record(*shaperec.record)
    ...     w.shape(shaperec.shape)
    >> # or GeoJSON dicts
    >>> for shaperec in r.iterShapeRecords():
    ...     w.record(*shaperec.record)
    ...     w.shape(shaperec.shape.__geo_interface__)
    >>> w.close()

Full stacktrace

py -2.7 shapefile.py
Running doctests...
**********************************************************************
File "README.md", line 6, in README
Failed example:
    for shaperec in r.iterShapeRecords():
        w.record(*shaperec.record)
        w.shape(shaperec.shape)
Expected:
    >> # or GeoJSON dicts
Got nothing
**********************************************************************
File "README.md", line 10, in README
Failed example:
    for shaperec in r.iterShapeRecords():
        w.record(*shaperec.record)
        w.shape(shaperec.shape.__geo_interface__)
Exception raised:
    Traceback (most recent call last):
      File "C:\Python27\lib\doctest.py", line 1315, in __run
        compileflags, 1) in test.globs
      File "<doctest README[5]>", line 3, in <module>
        w.shape(shaperec.shape.__geo_interface__)
      File "...\tmp\shapefile.py", line 2133, in shape
        "not: %r" % s)
    Exception: Can only write Shape objects, GeoJSON dictionaries, or objects with the __geo_interface__, not: {'type': 'MultiPolygon', 'coordinates': [[[(113.0, 24.0), (112.0, 32.0), (117.0, 36.0), (122.0, 37.0), (118.0, 20.0), (113.0, 24.0)], [(116.0, 29.0), (116.0, 26.0), (119.0, 29.0), (119.0, 32.0), (116.0, 29.0)]], [[(15.0, 2.0), (17.0, 6.0), (22.0, 7.0), (15.0, 2.0)]]]}
**********************************************************************
File "README.md", line 13, in README
Failed example:
    w.close()
Exception raised:
    Traceback (most recent call last):
      File "C:\Python27\lib\doctest.py", line 1315, in __run
        compileflags, 1) in test.globs
      File "<doctest README[6]>", line 1, in <module>
        w.close()
      File "...\tmp\shapefile.py", line 1888, in close
        "with the number of shapes (%s)" % (self.recNum, self.shpNum))
    ShapefileException: When saving both the dbf and shp file, the number of records (2) must correspond with the number of shapes (1)
**********************************************************************
1 items had failures:
   3 of   7 in README
***Test Failed*** 3 failures.
Exception shapefile.ShapefileException: ShapefileException('When saving both the dbf and shp file, the number of records (2) must correspond with the number of shapes (1)',) in <bound method Writer.__del__ of <shapefile.Writer object at 0x000000000376A1C8>> ignored

Other notes

Does .geo_interface behave differently in Python 2 to Python 3, or Writer.shape when called with a .geo_interface ?

Observed in a fixed, correctly functioning CI system, using the Docker python:2.7.18-slim image as a runner.

Reproduced in Windows 11 (CPython 2.7 as well as Iron Python 2) using above extract from the regular v3.2.1 README.md, local copy of polygon.shp (.dbf and .shx) and v3.2.1 shapefile.py (the code under test really was PyShp, but my fork IronPyshp also suffers from this bug in its v2.3.1 ...... ).

JamesParrott commented 8 months ago

Apologies. My own 'issue' cannot be reproduced, if the typo in the above 'extract' from README.md is corrected(*) (Doh!)

(*) >> # or GeoJSON dicts should be >>> # or GeoJSON dicts