[Git][debian-gis-team/pyshp][upstream] New upstream version 2.3.1

Bas Couwenberg (@sebastic) gitlab at salsa.debian.org
Thu Jul 28 04:32:20 BST 2022



Bas Couwenberg pushed to branch upstream at Debian GIS Project / pyshp


Commits:
fb4a6705 by Bas Couwenberg at 2022-07-28T05:16:48+02:00
New upstream version 2.3.1
- - - - -


25 changed files:

- + .github/ISSUE_TEMPLATE/bug.yml
- + .github/ISSUE_TEMPLATE/config.yml
- + .github/ISSUE_TEMPLATE/newfeature.yml
- + .github/ISSUE_TEMPLATE/question.yml
- + .github/ISSUE_TEMPLATE/unexpected.yml
- README.md
- changelog.txt
- shapefile.py
- shapefiles/test/balancing.dbf
- shapefiles/test/contextwriter.dbf
- shapefiles/test/corrupt_too_long.dbf
- shapefiles/test/dtype.dbf
- shapefiles/test/edit.dbf
- shapefiles/test/line.dbf
- shapefiles/test/linem.dbf
- shapefiles/test/linez.dbf
- shapefiles/test/merge.dbf
- shapefiles/test/multipatch.dbf
- shapefiles/test/multipoint.dbf
- shapefiles/test/onlydbf.dbf
- shapefiles/test/point.dbf
- shapefiles/test/polygon.dbf
- shapefiles/test/shapetype.dbf
- shapefiles/test/testfile.dbf
- test_shapefile.py


Changes:

=====================================
.github/ISSUE_TEMPLATE/bug.yml
=====================================
@@ -0,0 +1,47 @@
+name: Bug Report
+description: Something in PyShp crashed and raised an exception.
+title: "Title goes here..."
+labels: ["bug"]
+body:
+  - type: input
+    id: pyshp-version
+    attributes:
+      label: PyShp Version
+      description: Please input the version of PyShp you used. If unsure, call `shapefile.__version__`. 
+      placeholder: ...
+    validations:
+      required: true
+  - type: input
+    id: python-version
+    attributes:
+      label: Python Version
+      description: Please input the version of the Python executable. 
+      placeholder: ...
+    validations:
+      required: true
+  - type: textarea
+    id: your-code
+    attributes:
+      label: Your code
+      description: Please copy-paste the relevant parts of your code or script that triggered the error. 
+      placeholder: ...
+      render: shell
+    validations:
+      required: true
+  - type: textarea
+    id: stacktrace
+    attributes:
+      label: Full stacktrace
+      description: Please copy-paste the full stack trace of the exception that was raised.
+      placeholder: ...
+      render: shell
+    validations:
+      required: true
+  - type: textarea
+    id: notes
+    attributes:
+      label: Other notes
+      description: Please input any other notes that may be relevant, e.g. do you have any thoughts on what might be wrong? 
+      placeholder: ...
+    validations:
+      required: false
\ No newline at end of file


=====================================
.github/ISSUE_TEMPLATE/config.yml
=====================================
@@ -0,0 +1 @@
+blank_issues_enabled: true
\ No newline at end of file


=====================================
.github/ISSUE_TEMPLATE/newfeature.yml
=====================================
@@ -0,0 +1,21 @@
+name: Feature Request
+description: You would like to request a new feature.
+title: "Title goes here..."
+labels: ["enhancement"]
+body:
+  - type: textarea
+    id: functionality
+    attributes:
+      label: Describe the feature request
+      description: Please describe the functionality you would like added to PyShp.
+      placeholder: ...
+    validations:
+      required: true
+  - type: checkboxes
+    id: contribute
+    attributes:
+      label: Contributions
+      description: Would you be interested to contribute code that adds this functionality through a Pull Request? We gladly accept PRs - it's much faster and you'll be added a contributor. 
+      options:
+        - label: I am interested in implementing the described feature request and submit as a PR.
+          required: false
\ No newline at end of file


=====================================
.github/ISSUE_TEMPLATE/question.yml
=====================================
@@ -0,0 +1,13 @@
+name: Question
+description: You have a question about PyShp or how to use it.
+title: "Title goes here..."
+labels: ["question"]
+body:
+  - type: textarea
+    id: question
+    attributes:
+      label: What's your question? 
+      description: Please describe what you would like to know about PyShp, e.g. how to do something.
+      placeholder: ...
+    validations:
+      required: true
\ No newline at end of file


=====================================
.github/ISSUE_TEMPLATE/unexpected.yml
=====================================
@@ -0,0 +1,54 @@
+name: Unexpected Behavior
+description: You think PyShp might be doing something wrong.
+title: "Title goes here..."
+labels: ["bug"]
+body:
+  - type: input
+    id: pyshp-version
+    attributes:
+      label: PyShp Version
+      description: Please input the version of PyShp you used. If unsure, call `shapefile.__version__`. 
+      placeholder: ...
+    validations:
+      required: true
+  - type: input
+    id: python-version
+    attributes:
+      label: Python Version
+      description: Please input the version of the Python executable. 
+      placeholder: ...
+    validations:
+      required: true
+  - type: textarea
+    id: your-code
+    attributes:
+      label: Your code
+      description: Please copy-paste the relevant parts of your code or script that you tried to run. 
+      placeholder: ...
+      render: shell
+    validations:
+      required: true
+  - type: textarea
+    id: expected-results
+    attributes:
+      label: Expected results
+      description: Please describe what you expected to see in the output.
+      placeholder: ...
+    validations:
+      required: true
+  - type: textarea
+    id: actual-results
+    attributes:
+      label: Actual results
+      description: Please describe what you actually saw in the output.
+      placeholder: ...
+    validations:
+      required: true
+  - type: textarea
+    id: notes
+    attributes:
+      label: Other notes
+      description: Please input any other notes that may be relevant, e.g. do you have any thoughts on what might be wrong? 
+      placeholder: ...
+    validations:
+      required: false
\ No newline at end of file


=====================================
README.md
=====================================
@@ -95,6 +95,12 @@ part of your geospatial project.
 
 # Version Changes
 
+## 2.3.1
+
+### Bug fixes:
+
+- Fix recently introduced issue where Reader/Writer closes file-like objects provided by user (#244)
+
 ## 2.3.0
 
 ### New Features:
@@ -722,7 +728,18 @@ write to them:
 	>>> w.record()
 	>>> w.null()
 	>>> w.close()
+
 	>>> # To read back the files you could call the "StringIO.getvalue()" method later.
+	>>> assert shp.getvalue()
+	>>> assert shx.getvalue()
+	>>> assert dbf.getvalue()
+
+	>>> # In fact, you can read directly from them using the Reader
+	>>> r = shapefile.Reader(shp=shp, shx=shx, dbf=dbf)
+	>>> len(r)
+	1
+	
+	
 
 #### Writing Shapefiles Using the Context Manager
 
@@ -1391,14 +1408,25 @@ ESRI White Paper](http://downloads.esri.com/support/whitepapers/ao_/J9749_MultiP
 	
 # Testing
 
-The testing framework is doctest, which are located in this file README.md.
+The testing framework is pytest, and the tests are located in test_shapefile.py. 
+This includes an extensive set of unit tests of the various pyshp features, 
+and tests against various input data. Some of the tests that require 
+internet connectivity will be skipped in offline testing environments. 
+In the same folder as README.md and shapefile.py, from the command line run 
+```
+$ python -m pytest
+``` 
+
+Additionally, all the code and examples located in this file, README.md, 
+is tested and verified with the builtin doctest framework.
+A special routine for invoking the doctest is run when calling directly on shapefile.py.
 In the same folder as README.md and shapefile.py, from the command line run 
 ```
 $ python shapefile.py
 ``` 
 
 Linux/Mac and similar platforms will need to run `$ dos2unix README.md` in order
-correct line endings in README.md.
+to correct line endings in README.md.
 
 # Contributors
 


=====================================
changelog.txt
=====================================
@@ -1,4 +1,10 @@
 
+VERSION 2.3.1
+
+2022-07-28
+	Bug fixes:
+	* Fix recently introduced issue where Reader/Writer closes file-like objects provided by user (#244)
+
 VERSION 2.3.0
 
 2022-04-30


=====================================
shapefile.py
=====================================
@@ -6,7 +6,7 @@ maintainer: karim.bahgat.norway<at>gmail.com
 Compatible with Python versions 2.7-3.x
 """
 
-__version__ = "2.3.0"
+__version__ = "2.3.1"
 
 from struct import pack, unpack, calcsize, error, Struct
 import os
@@ -932,6 +932,7 @@ class Reader(object):
         self.shp = None
         self.shx = None
         self.dbf = None
+        self._files_to_close = []
         self.shapeName = "Not specified"
         self._offsets = []
         self.shpLength = None
@@ -998,6 +999,7 @@ class Reader(object):
                                 fileobj.write(member.read())
                                 fileobj.seek(0)
                                 setattr(self, ext, fileobj)
+                                self._files_to_close.append(fileobj)
                             except:
                                 pass
                     # Close and delete the temporary zipfile
@@ -1030,6 +1032,7 @@ class Reader(object):
                             fileobj.write(resp.read())
                             fileobj.seek(0)
                             setattr(self, ext, fileobj)
+                            self._files_to_close.append(fileobj)
                         except HTTPError:
                             pass
                     if (self.shp or self.dbf):
@@ -1202,9 +1205,11 @@ class Reader(object):
         shp_ext = 'shp'
         try:
             self.shp = open("%s.%s" % (shapefile_name, shp_ext), "rb")
+            self._files_to_close.append(self.shp)
         except IOError:
             try:
                 self.shp = open("%s.%s" % (shapefile_name, shp_ext.upper()), "rb")
+                self._files_to_close.append(self.shp)
             except IOError:
                 pass
 
@@ -1215,9 +1220,11 @@ class Reader(object):
         shx_ext = 'shx'
         try:
             self.shx = open("%s.%s" % (shapefile_name, shx_ext), "rb")
+            self._files_to_close.append(self.shx)
         except IOError:
             try:
                 self.shx = open("%s.%s" % (shapefile_name, shx_ext.upper()), "rb")
+                self._files_to_close.append(self.shx)
             except IOError:
                 pass
 
@@ -1228,9 +1235,11 @@ class Reader(object):
         dbf_ext = 'dbf'
         try:
             self.dbf = open("%s.%s" % (shapefile_name, dbf_ext), "rb")
+            self._files_to_close.append(self.dbf)
         except IOError:
             try:
                 self.dbf = open("%s.%s" % (shapefile_name, dbf_ext.upper()), "rb")
+                self._files_to_close.append(self.dbf)
             except IOError:
                 pass
 
@@ -1238,18 +1247,14 @@ class Reader(object):
         self.close()
 
     def close(self):
-        for attribute in ('shp','shx','dbf'):
-            try:
-                obj = getattr(self, attribute)
-            except AttributeError:
-                # deepcopies fail to copy these attributes and raises exception during
-                # garbage collection - https://github.com/mattijn/topojson/issues/120
-                obj = None
-            if obj and hasattr(obj, 'close'):
+        # Close any files that the reader opened (but not those given by user)
+        for attribute in self._files_to_close:
+            if hasattr(attribute, 'close'):
                 try:
-                    obj.close()
+                    attribute.close()
                 except IOError:
                     pass
+        self._files_to_close = []
 
     def __getFileObj(self, f):
         """Checks to see if the requested shapefile file object is
@@ -1786,6 +1791,7 @@ class Writer(object):
         self.fields = []
         self.shapeType = shapeType
         self.shp = self.shx = self.dbf = None
+        self._files_to_close = []
         if target:
             target = pathlike_obj(target)
             if not is_string(target):
@@ -1866,13 +1872,22 @@ class Writer(object):
         if self.dbf and dbf_open:
             self.__dbfHeader()
 
-        # Close files
+        # Flush files
         for attribute in (self.shp, self.shx, self.dbf):
+            if hasattr(attribute, 'flush') and not (hasattr(attribute, 'closed') and attribute.closed):
+                try:
+                    attribute.flush()
+                except IOError:
+                    pass
+
+        # Close any files that the writer opened (but not those given by user)
+        for attribute in self._files_to_close:
             if hasattr(attribute, 'close'):
                 try:
                     attribute.close()
                 except IOError:
                     pass
+        self._files_to_close = []
 
     def __getFileObj(self, f):
         """Safety handler to verify file-like objects"""
@@ -1884,7 +1899,9 @@ class Writer(object):
             pth = os.path.split(f)[0]
             if pth and not os.path.exists(pth):
                 os.makedirs(pth)
-            return open(f, "wb+")
+            fp = open(f, "wb+")
+            self._files_to_close.append(fp)
+            return fp
 
     def __shpFileLength(self):
         """Calculates the file length of the shp file."""


=====================================
shapefiles/test/balancing.dbf
=====================================
Binary files a/shapefiles/test/balancing.dbf and b/shapefiles/test/balancing.dbf differ


=====================================
shapefiles/test/contextwriter.dbf
=====================================
Binary files a/shapefiles/test/contextwriter.dbf and b/shapefiles/test/contextwriter.dbf differ


=====================================
shapefiles/test/corrupt_too_long.dbf
=====================================
Binary files a/shapefiles/test/corrupt_too_long.dbf and b/shapefiles/test/corrupt_too_long.dbf differ


=====================================
shapefiles/test/dtype.dbf
=====================================
Binary files a/shapefiles/test/dtype.dbf and b/shapefiles/test/dtype.dbf differ


=====================================
shapefiles/test/edit.dbf
=====================================
Binary files a/shapefiles/test/edit.dbf and b/shapefiles/test/edit.dbf differ


=====================================
shapefiles/test/line.dbf
=====================================
Binary files a/shapefiles/test/line.dbf and b/shapefiles/test/line.dbf differ


=====================================
shapefiles/test/linem.dbf
=====================================
Binary files a/shapefiles/test/linem.dbf and b/shapefiles/test/linem.dbf differ


=====================================
shapefiles/test/linez.dbf
=====================================
Binary files a/shapefiles/test/linez.dbf and b/shapefiles/test/linez.dbf differ


=====================================
shapefiles/test/merge.dbf
=====================================
Binary files a/shapefiles/test/merge.dbf and b/shapefiles/test/merge.dbf differ


=====================================
shapefiles/test/multipatch.dbf
=====================================
Binary files a/shapefiles/test/multipatch.dbf and b/shapefiles/test/multipatch.dbf differ


=====================================
shapefiles/test/multipoint.dbf
=====================================
Binary files a/shapefiles/test/multipoint.dbf and b/shapefiles/test/multipoint.dbf differ


=====================================
shapefiles/test/onlydbf.dbf
=====================================
Binary files a/shapefiles/test/onlydbf.dbf and b/shapefiles/test/onlydbf.dbf differ


=====================================
shapefiles/test/point.dbf
=====================================
Binary files a/shapefiles/test/point.dbf and b/shapefiles/test/point.dbf differ


=====================================
shapefiles/test/polygon.dbf
=====================================
Binary files a/shapefiles/test/polygon.dbf and b/shapefiles/test/polygon.dbf differ


=====================================
shapefiles/test/shapetype.dbf
=====================================
Binary files a/shapefiles/test/shapetype.dbf and b/shapefiles/test/shapetype.dbf differ


=====================================
shapefiles/test/testfile.dbf
=====================================
Binary files a/shapefiles/test/testfile.dbf and b/shapefiles/test/testfile.dbf differ


=====================================
test_shapefile.py
=====================================
@@ -248,22 +248,6 @@ def test_shaperecord_geo_interface():
             assert json.dumps(shaperec.__geo_interface__)
 
 
-def test_reader_context_manager():
-    """
-    Assert that the Reader context manager
-    closes the shp, shx, and dbf files
-    on exit.
-    """
-    # note uses an actual shapefile from
-    # the projects "shapefiles" directory
-    with shapefile.Reader("shapefiles/blockgroups") as sf:
-        pass
-
-    assert sf.shp.closed is True
-    assert sf.dbf.closed is True
-    assert sf.shx.closed is True
-
-
 @pytest.mark.network
 def test_reader_url():
     """
@@ -274,6 +258,7 @@ def test_reader_url():
     with shapefile.Reader(url) as sf:
         for recShape in sf.iterShapeRecords():
             pass
+    assert sf.shp.closed == sf.shx.closed == sf.dbf.closed == True
 
     # test without extension
     url = "https://github.com/nvkelso/natural-earth-vector/blob/master/110m_cultural/ne_110m_admin_0_tiny_countries?raw=true"
@@ -281,6 +266,7 @@ def test_reader_url():
         for recShape in sf.iterShapeRecords():
             pass
         assert len(sf) > 0
+    assert sf.shp.closed == sf.shx.closed == sf.dbf.closed == True
 
     # test no files found
     url = "https://raw.githubusercontent.com/nvkelso/natural-earth-vector/master/README.md"
@@ -294,6 +280,7 @@ def test_reader_url():
         for recShape in sf.iterShapeRecords():
             pass
         assert len(sf) > 0
+    assert sf.shp.closed == sf.shx.closed == sf.dbf.closed == True
 
 
 def test_reader_zip():
@@ -305,6 +292,7 @@ def test_reader_zip():
         for recShape in sf.iterShapeRecords():
             pass
         assert len(sf) > 0
+    assert sf.shp.closed == sf.shx.closed == sf.dbf.closed == True
     
     # test require specific path when reading multi-shapefile zipfile
     with pytest.raises(shapefile.ShapefileException):
@@ -316,12 +304,14 @@ def test_reader_zip():
         for recShape in sf.iterShapeRecords():
             pass
         assert len(sf) > 0
+    assert sf.shp.closed == sf.shx.closed == sf.dbf.closed == True
 
     # test specifying the path when reading multi-shapefile zipfile (without extension)
     with shapefile.Reader("shapefiles/blockgroups_multishapefile.zip/blockgroups2") as sf:
         for recShape in sf.iterShapeRecords():
             pass
         assert len(sf) > 0
+    assert sf.shp.closed == sf.shx.closed == sf.dbf.closed == True
 
     # test raising error when can't find shapefile inside zipfile
     with pytest.raises(shapefile.ShapefileException):
@@ -329,11 +319,54 @@ def test_reader_zip():
             pass
 
 
-def test_reader_close():
+def test_reader_close_path():
     """
     Assert that manually calling Reader.close()
     closes the shp, shx, and dbf files
-    on exit.
+    on exit, if given paths.
+    """
+    # note uses an actual shapefile from
+    # the projects "shapefiles" directory
+    sf = shapefile.Reader("shapefiles/blockgroups.shp")
+    sf.close()
+
+    assert sf.shp.closed is True
+    assert sf.dbf.closed is True
+    assert sf.shx.closed is True
+
+    # check that can read again
+    sf = shapefile.Reader("shapefiles/blockgroups.shp")
+    sf.close()
+
+
+def test_reader_close_filelike():
+    """
+    Assert that manually calling Reader.close()
+    leaves the shp, shx, and dbf files open
+    on exit, if given filelike objects.
+    """
+    # note uses an actual shapefile from
+    # the projects "shapefiles" directory
+    shp = open("shapefiles/blockgroups.shp", mode='rb')
+    shx = open("shapefiles/blockgroups.shx", mode='rb')
+    dbf = open("shapefiles/blockgroups.dbf", mode='rb')
+    sf = shapefile.Reader(shp=shp, shx=shx, dbf=dbf)
+    sf.close()
+
+    assert sf.shp.closed is False
+    assert sf.dbf.closed is False
+    assert sf.shx.closed is False
+
+    # check that can read again
+    sf = shapefile.Reader(shp=shp, shx=shx, dbf=dbf)
+    sf.close()
+
+
+def test_reader_context_path():
+    """
+    Assert that using the context manager
+    closes the shp, shx, and dbf files
+    on exit, if given paths.
     """
     # note uses an actual shapefile from
     # the projects "shapefiles" directory
@@ -344,6 +377,33 @@ def test_reader_close():
     assert sf.dbf.closed is True
     assert sf.shx.closed is True
 
+    # check that can read again
+    with shapefile.Reader("shapefiles/blockgroups") as sf:
+        pass
+
+
+def test_reader_context_filelike():
+    """
+    Assert that using the context manager
+    leaves the shp, shx, and dbf files open
+    on exit, if given filelike objects.
+    """
+    # note uses an actual shapefile from
+    # the projects "shapefiles" directory
+    shp = open("shapefiles/blockgroups.shp", mode='rb')
+    shx = open("shapefiles/blockgroups.shx", mode='rb')
+    dbf = open("shapefiles/blockgroups.dbf", mode='rb')
+    with shapefile.Reader(shp=shp, shx=shx, dbf=dbf) as sf:
+        pass
+
+    assert sf.shp.closed is False
+    assert sf.dbf.closed is False
+    assert sf.shx.closed is False
+
+    # check that can read again
+    with shapefile.Reader(shp=shp, shx=shx, dbf=dbf) as sf:
+        pass
+
 
 def test_reader_shapefile_type():
     """
@@ -1084,7 +1144,7 @@ def test_write_shp_only(tmpdir):
     creates just a shp file.
     """
     filename = tmpdir.join("test").strpath
-    with shapefile.Writer(shp=open(filename+'.shp','wb')) as writer:
+    with shapefile.Writer(shp=filename+'.shp') as writer:
         writer.point(1, 1)
     assert writer.shp and not writer.shx and not writer.dbf
     assert writer.shpNum == 1
@@ -1095,7 +1155,7 @@ def test_write_shp_only(tmpdir):
     assert os.path.exists(filename+'.shp')
     
     # test that can read shapes
-    with shapefile.Reader(shp=open(filename+'.shp','rb')) as reader:
+    with shapefile.Reader(shp=filename+'.shp') as reader:
         assert reader.shp and not reader.shx and not reader.dbf
         assert (reader.numRecords, reader.numShapes) == (None, None) # numShapes is unknown in the absence of shx file
         assert len(reader.shapes()) == 1
@@ -1114,7 +1174,7 @@ def test_write_shp_shx_only(tmpdir):
     creates just a shp and shx file.
     """
     filename = tmpdir.join("test").strpath
-    with shapefile.Writer(shp=open(filename+'.shp','wb'), shx=open(filename+'.shx','wb')) as writer:
+    with shapefile.Writer(shp=filename+'.shp', shx=filename+'.shx') as writer:
         writer.point(1, 1)
     assert writer.shp and writer.shx and not writer.dbf
     assert writer.shpNum == 1
@@ -1128,7 +1188,7 @@ def test_write_shp_shx_only(tmpdir):
     assert os.path.exists(filename+'.shx')
 
     # test that can read shapes and offsets
-    with shapefile.Reader(shp=open(filename+'.shp','rb'), shx=open(filename+'.shx','rb')) as reader:
+    with shapefile.Reader(shp=filename+'.shp', shx=filename+'.shx') as reader:
         assert reader.shp and reader.shx and not reader.dbf
         assert (reader.numRecords, reader.numShapes) == (None, 1)
         reader.shape(0) # trigger reading of shx offsets
@@ -1146,7 +1206,7 @@ def test_write_shp_dbf_only(tmpdir):
     creates just a shp and dbf file.
     """
     filename = tmpdir.join("test").strpath
-    with shapefile.Writer(shp=open(filename+'.shp','wb'), dbf=open(filename+'.dbf','wb')) as writer:
+    with shapefile.Writer(shp=filename+'.shp', dbf=filename+'.dbf') as writer:
         writer.field('field1', 'C') # required to create a valid dbf file
         writer.record('value')
         writer.point(1, 1)
@@ -1162,7 +1222,7 @@ def test_write_shp_dbf_only(tmpdir):
     assert os.path.exists(filename+'.dbf')
     
     # test that can read records and shapes
-    with shapefile.Reader(shp=open(filename+'.shp','rb'), dbf=open(filename+'.dbf','rb')) as reader:
+    with shapefile.Reader(shp=filename+'.shp', dbf=filename+'.dbf') as reader:
         assert reader.shp and not reader.shx and reader.dbf
         assert (reader.numRecords, reader.numShapes) == (1, None) # numShapes is unknown in the absence of shx file
         assert len(reader.records()) == 1
@@ -1179,7 +1239,7 @@ def test_write_dbf_only(tmpdir):
     creates just a dbf file.
     """
     filename = tmpdir.join("test").strpath
-    with shapefile.Writer(dbf=open(filename+'.dbf','wb')) as writer:
+    with shapefile.Writer(dbf=filename+'.dbf') as writer:
         writer.field('field1', 'C') # required to create a valid dbf file
         writer.record('value')
     assert not writer.shp and not writer.shx and writer.dbf
@@ -1191,7 +1251,7 @@ def test_write_dbf_only(tmpdir):
     assert os.path.exists(filename+'.dbf')
 
     # test that can read records
-    with shapefile.Reader(dbf=open(filename+'.dbf','rb')) as reader:
+    with shapefile.Reader(dbf=filename+'.dbf') as reader:
         assert not writer.shp and not writer.shx and writer.dbf
         assert (reader.numRecords, reader.numShapes) == (1, None)
         assert len(reader.records()) == 1
@@ -1212,6 +1272,8 @@ def test_write_default_shp_shx_dbf(tmpdir):
     filename = tmpdir.join("test").strpath
     with shapefile.Writer(filename) as writer:
         writer.field('field1', 'C') # required to create a valid dbf file
+        writer.record('value')
+        writer.null()
 
     # assert shp, shx, dbf files exist
     assert os.path.exists(filename + ".shp")
@@ -1228,11 +1290,123 @@ def test_write_pathlike(tmpdir):
     assert not isinstance(filename, str)
     with shapefile.Writer(filename) as writer:
         writer.field('field1', 'C')
+        writer.record('value')
+        writer.null()
     assert (filename + ".shp").ensure()
     assert (filename + ".shx").ensure()
     assert (filename + ".dbf").ensure()
 
 
+def test_write_filelike(tmpdir):
+    """
+    Assert that file-like objects are written correctly.
+    """
+    shp = open(tmpdir.join("test.shp").strpath, mode='wb+')
+    shx = open(tmpdir.join("test.shx").strpath, mode='wb+')
+    dbf = open(tmpdir.join("test.dbf").strpath, mode='wb+')
+    with shapefile.Writer(shx=shx, dbf=dbf, shp=shp) as writer:
+        writer.field('field1', 'C') # required to create a valid dbf file
+        writer.record('value')
+        writer.null()
+        
+    # test that filelike objects were written correctly
+    with shapefile.Reader(shp=shp, shx=shx, dbf=dbf) as reader:
+        assert len(reader) == 1
+        assert reader.shape(0).shapeType == shapefile.NULL
+
+
+def test_write_close_path(tmpdir):
+    """
+    Assert that the Writer close() method
+    closes the shp, shx, and dbf files
+    on exit, if given paths.
+    """
+    sf = shapefile.Writer(tmpdir.join('test'))
+    sf.field('field1', 'C') # required to create a valid dbf file
+    sf.record('value')
+    sf.null()
+    sf.close()
+
+    assert sf.shp.closed is True
+    assert sf.dbf.closed is True
+    assert sf.shx.closed is True
+
+    # test that opens and reads correctly after
+    with shapefile.Reader(tmpdir.join('test')) as reader:
+        assert len(reader) == 1
+        assert reader.shape(0).shapeType == shapefile.NULL
+
+
+def test_write_close_filelike(tmpdir):
+    """
+    Assert that the Writer close() method
+    leaves the shp, shx, and dbf files open
+    on exit, if given filelike objects.
+    """
+    shp = open(tmpdir.join("test.shp").strpath, mode='wb+')
+    shx = open(tmpdir.join("test.shx").strpath, mode='wb+')
+    dbf = open(tmpdir.join("test.dbf").strpath, mode='wb+')
+    sf = shapefile.Writer(shx=shx, dbf=dbf, shp=shp)
+    sf.field('field1', 'C') # required to create a valid dbf file
+    sf.record('value')
+    sf.null()
+    sf.close()
+
+    assert sf.shp.closed is False
+    assert sf.dbf.closed is False
+    assert sf.shx.closed is False
+
+    # test that opens and reads correctly after
+    with shapefile.Reader(shx=shx, dbf=dbf, shp=shp) as reader:
+        assert len(reader) == 1
+        assert reader.shape(0).shapeType == shapefile.NULL
+
+
+def test_write_context_path(tmpdir):
+    """
+    Assert that the Writer context manager
+    closes the shp, shx, and dbf files
+    on exit, if given paths.
+    """
+    with shapefile.Writer(tmpdir.join('test')) as sf:
+        sf.field('field1', 'C') # required to create a valid dbf file
+        sf.record('value')
+        sf.null()
+
+    assert sf.shp.closed is True
+    assert sf.dbf.closed is True
+    assert sf.shx.closed is True
+
+    # test that opens and reads correctly after
+    with shapefile.Reader(tmpdir.join('test')) as reader:
+        assert len(reader) == 1
+        assert reader.shape(0).shapeType == shapefile.NULL
+
+
+def test_write_context_filelike(tmpdir):
+    """
+    Assert that the Writer context manager
+    leaves the shp, shx, and dbf files open
+    on exit, if given filelike objects.
+    """
+    shp = open(tmpdir.join("test.shp").strpath, mode='wb+')
+    shx = open(tmpdir.join("test.shx").strpath, mode='wb+')
+    dbf = open(tmpdir.join("test.dbf").strpath, mode='wb+')
+    with shapefile.Writer(shx=shx, dbf=dbf, shp=shp) as sf:
+        sf.field('field1', 'C') # required to create a valid dbf file
+        sf.record('value')
+        sf.null()
+
+    assert sf.shp.closed is False
+    assert sf.dbf.closed is False
+    assert sf.shx.closed is False
+
+    # test that opens and reads correctly after
+    with shapefile.Reader(shx=shx, dbf=dbf, shp=shp) as reader:
+        assert len(reader) == 1
+        assert reader.shape(0).shapeType == shapefile.NULL
+
+
 def test_write_shapefile_extension_ignored(tmpdir):
     """
     Assert that the filename's extension is
@@ -1338,8 +1512,10 @@ def test_write_geojson(tmpdir):
         assert json.dumps(r.shapeRecords().__geo_interface__)
         assert json.dumps(r.__geo_interface__)
 
+
 shape_types = [k for k in shapefile.SHAPETYPE_LOOKUP.keys() if k != 31] # exclude multipatch
 
+
 @pytest.mark.parametrize("shape_type", shape_types)
 def test_write_empty_shapefile(tmpdir, shape_type):
     """



View it on GitLab: https://salsa.debian.org/debian-gis-team/pyshp/-/commit/fb4a6705bcc47bb5cca1e3fa7242f00a92b961bc

-- 
View it on GitLab: https://salsa.debian.org/debian-gis-team/pyshp/-/commit/fb4a6705bcc47bb5cca1e3fa7242f00a92b961bc
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20220728/f4f35484/attachment-0001.htm>


More information about the Pkg-grass-devel mailing list