title: Use with statement to close GDAL datasets
body: <p>Consider this basic example for the use of a <code>with</code> statement from <a href="https://jeffknupp.com/blog/2016/03/07/python-with-context-managers/" rel="nofollow noreferrer">Jeff Knupp's blog</a>:</p>
  
  <pre><code>class File():
  
      def __init__(self, filename, mode):
          self.filename = filename
          self.mode = mode
  
      def __enter__(self):
          self.open_file = open(self.filename, self.mode)
          return self.open_file
  
      def __exit__(self, *args):
          self.open_file.close()
  </code></pre>
  
  <p>I have a <code>testfile</code> which contains two lines, 'ABC' and 'DEF'. Everything works as expected:</p>
  
  <pre><code>with File('/tmp/testfile','r') as f:
      txt = [x.strip() for x in f.readlines()]
      print(txt)
  
  # ['ABC', 'DEF']
  </code></pre>
  
  <p>Calling a class method outside the <code>with</code> block gives me the expected error:</p>
  
  <pre><code>f.readlines()
  </code></pre>
  
  <blockquote>
    <p>ValueError                                Traceback (most recent call last)
     in ()
    ----> 1 f.readlines()</p>
    
    <p>ValueError: I/O operation on closed file.</p>
  </blockquote>
  
  <hr>
  
  <p><strong>Now to my question:</strong></p>
  
  <p>How can I achieve the same behavior with a <code>gdal</code> object instead of a file?</p>
  
  <p>I have a class method, which should read data from disk and put it into a <code>gdal</code> raster for further processing. Once this is done, I would like to close the generated <code>gdal</code> raster appropriately. Normally this is done with setting it to <code>None</code> and with using <code>gdal.Unlink</code>.</p>
  
  <p>However, when I put everything into a context structure as in the previous example, I can still interact with the dataset outside of the <code>with</code> block.</p>
  
  <p>Here's a reproducible example:</p>
  
  <pre><code>class Raster:
      '''Raster class with sidelength s'''
      def __init__(self,s):
          self.sidelength = s
  
      def __enter__(self):
          # create raster in memory
          driver = gdal.GetDriverByName('GTiff')
          self.raster = driver.Create('/vsimem/inmem.tif', self.sidelength, self.sidelength, 1, gdal.GDT_Float32)
          self.raster.GetRasterBand(1).WriteArray(np.random.rand(self.sidelength,self.sidelength))
          return self.raster
  
      def __exit__(self, *args):
          # close file and unlink
          self.raster = None
          gdal.Unlink('/vsimem/inmem.tif')
  </code></pre>
  
  <p>The <code>with</code> block works as expected:</p>
  
  <pre><code>with Raster(5) as r:
      print(r)
  
  # &lt;osgeo.gdal.Dataset; proxy of &lt;Swig Object of type 'GDALDatasetShadow *' at 0x7f8078862ae0&gt; &gt;
  </code></pre>
  
  <p>But after the block the object is still there and I can still read the values:</p>
  
  <pre><code>print(r)
  
  #&lt;osgeo.gdal.Dataset; proxy of &lt;Swig Object of type 'GDALDatasetShadow *' at 0x7f8078044a20&gt; &gt;
  
  print(r.ReadAsArray())
  
  #[[0.2549882  0.80292517 0.23358545 0.6284887  0.7294142 ]
  # [0.9310723  0.21535267 0.9054575  0.60967094 0.9937953 ]
  # [0.69144976 0.01727938 0.16800325 0.61249655 0.1785022 ]
  # [0.16179436 0.43245795 0.7042811  0.4809799  0.85534436]
  # [0.67751276 0.7560658  0.9594516  0.6294476  0.3539126 ]]
  </code></pre>