We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Allow type annotation support for rasterio data types
Recently I've been adding PEP 484 type annotations to my projects and rasterio has proved quite ugly to use as is, the main annoyance being that `rasterio.open` may return objects belonging to three classes (`DatasetReader`, `DatasetWriter` and `BufferedDatasetWriter`) with a non-public base `DatasetBase`, so right now the only option to annotate a generic dataset is:
```
from typing import Union
from rasterio import io

def myfunc(
        ds: Union[io.DatasetReader, io.DatasetWriter, io.BufferedDatasetWriter]
) -> None:
    pass
```

Something like exposing the DatasetBase class inside rasterio would make the above much more readable:
```
import rasterio

def myfunc(ds: rasterio.DatasetBase) -> None:
    pass
```

Would you consider restructuring a bit the dataset classes so that they are easier to use in type annotations? I guess I'm the first one having such a need :)

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `rasterio/io.py`
Content:
```
1 """Classes capable of reading and writing datasets
2 
3 Instances of these classes are called dataset objects.
4 """
5 
6 import logging
7 import math
8 import uuid
9 import warnings
10 
11 from rasterio._base import (
12     get_dataset_driver, driver_can_create, driver_can_create_copy)
13 from rasterio._io import (
14     DatasetReaderBase, DatasetWriterBase, BufferedDatasetWriterBase,
15     MemoryFileBase)
16 from rasterio import enums, windows
17 from rasterio.env import Env
18 from rasterio.transform import guard_transform, xy, rowcol
19 
20 
21 log = logging.getLogger(__name__)
22 
23 
24 class TransformMethodsMixin(object):
25     """Mixin providing methods for calculations related
26     to transforming between rows and columns of the raster
27     array and the coordinates.
28 
29     These methods are wrappers for the functionality in
30     `rasterio.transform` module.
31 
32     A subclass with this mixin MUST provide a `transform`
33     property.
34     """
35 
36     def xy(self, row, col, offset="center"):
37         """Returns the coordinates ``(x, y)`` of a pixel at `row` and `col`.
38         The pixel's center is returned by default, but a corner can be returned
39         by setting `offset` to one of `ul, ur, ll, lr`.
40 
41         Parameters
42         ----------
43         row : int
44             Pixel row.
45         col : int
46             Pixel column.
47         offset : str, optional
48             Determines if the returned coordinates are for the center of the
49             pixel or for a corner.
50 
51         Returns
52         -------
53         tuple
54             ``(x, y)``
55         """
56         return xy(self.transform, row, col, offset=offset)
57 
58     def ul(self, row, col):
59         """Returns the coordinates (x, y) of the upper left corner of a
60         pixel at `row` and `col` in the units of the dataset's
61         coordinate reference system.
62 
63         Deprecated; Use `xy(row, col, offset='ul')` instead.
64         """
65         warnings.warn("ul method is deprecated. Use xy(row, col, offset='ul')",
66                       DeprecationWarning)
67         return xy(self.transform, row, col, offset='ul')
68 
69     def index(self, x, y, op=math.floor, precision=6):
70         """
71         Returns the (row, col) index of the pixel containing (x, y) given a
72         coordinate reference system.
73 
74         Use an epsilon, magnitude determined by the precision parameter
75         and sign determined by the op function:
76             positive for floor, negative for ceil.
77 
78         Parameters
79         ----------
80         x : float
81             x value in coordinate reference system
82         y : float
83             y value in coordinate reference system
84         op : function, optional (default: math.floor)
85             Function to convert fractional pixels to whole numbers (floor,
86             ceiling, round)
87         precision : int, optional (default: 6)
88             Decimal places of precision in indexing, as in `round()`.
89 
90         Returns
91         -------
92         tuple
93             (row index, col index)
94         """
95         return rowcol(self.transform, x, y, op=op, precision=precision)
96 
97 
98 class WindowMethodsMixin(object):
99     """Mixin providing methods for window-related calculations.
100     These methods are wrappers for the functionality in
101     `rasterio.windows` module.
102 
103     A subclass with this mixin MUST provide the following
104     properties: `transform`, `height` and `width`
105     """
106 
107     def window(self, left, bottom, right, top, boundless=False):
108         """Get the window corresponding to the bounding coordinates.
109 
110         Parameters
111         ----------
112         left : float
113             Left (west) bounding coordinate
114         bottom : float
115             Bottom (south) bounding coordinate
116         right : float
117             Right (east) bounding coordinate
118         top : float
119             Top (north) bounding coordinate
120         boundless: boolean, optional
121             If boundless is False, window is limited
122             to extent of this dataset.
123 
124         Returns
125         -------
126         window: tuple
127             ((row_start, row_stop), (col_start, col_stop))
128             corresponding to the bounding coordinates
129 
130         """
131 
132         transform = guard_transform(self.transform)
133         return windows.from_bounds(
134             left, bottom, right, top, transform=transform,
135             height=self.height, width=self.width, boundless=boundless)
136 
137     def window_transform(self, window):
138         """Get the affine transform for a dataset window.
139 
140         Parameters
141         ----------
142         window: tuple
143             Dataset window tuple
144 
145         Returns
146         -------
147         transform: Affine
148             The affine transform matrix for the given window
149         """
150 
151         transform = guard_transform(self.transform)
152         return windows.transform(window, transform)
153 
154     def window_bounds(self, window):
155         """Get the bounds of a window
156 
157         Parameters
158         ----------
159         window: tuple
160             Dataset window tuple
161 
162         Returns
163         -------
164         bounds : tuple
165             x_min, y_min, x_max, y_max for the given window
166         """
167 
168         transform = guard_transform(self.transform)
169         return windows.bounds(window, transform)
170 
171 
172 class DatasetReader(DatasetReaderBase, WindowMethodsMixin,
173                     TransformMethodsMixin):
174     """An unbuffered data and metadata reader"""
175 
176     def __repr__(self):
177         return "<{} DatasetReader name='{}' mode='{}'>".format(
178             self.closed and 'closed' or 'open', self.name, self.mode)
179 
180 
181 class DatasetWriter(DatasetWriterBase, WindowMethodsMixin,
182                     TransformMethodsMixin):
183     """An unbuffered data and metadata writer. Its methods write data
184     directly to disk.
185     """
186 
187     def __repr__(self):
188         return "<{} DatasetWriter name='{}' mode='{}'>".format(
189             self.closed and 'closed' or 'open', self.name, self.mode)
190 
191 
192 class BufferedDatasetWriter(BufferedDatasetWriterBase, WindowMethodsMixin,
193                             TransformMethodsMixin):
194     """Maintains data and metadata in a buffer, writing to disk or
195     network only when `close()` is called.
196 
197     This allows incremental updates to datasets using formats that don't
198     otherwise support updates, such as JPEG.
199     """
200 
201     def __repr__(self):
202         return "<{} BufferedDatasetWriter name='{}' mode='{}'>".format(
203             self.closed and 'closed' or 'open', self.name, self.mode)
204 
205 
206 class MemoryFile(MemoryFileBase):
207     """A BytesIO-like object, backed by an in-memory file.
208 
209     This allows formatted files to be read and written without I/O.
210 
211     A MemoryFile created with initial bytes becomes immutable. A
212     MemoryFile created without initial bytes may be written to using
213     either file-like or dataset interfaces.
214 
215     Examples
216     --------
217 
218     A GeoTIFF can be loaded in memory and accessed using the GeoTIFF
219     format driver
220 
221     >>> with open('tests/data/RGB.byte.tif', 'rb') as f, \
222     ...         MemoryFile(f.read()) as memfile:
223     ...     with memfile.open() as src:
224     ...         pprint.pprint(src.profile)
225     ...
226     {'count': 3,
227      'crs': CRS({'init': 'epsg:32618'}),
228      'driver': 'GTiff',
229      'dtype': 'uint8',
230      'height': 718,
231      'interleave': 'pixel',
232      'nodata': 0.0,
233      'tiled': False,
234      'transform': Affine(300.0379266750948, 0.0, 101985.0,
235            0.0, -300.041782729805, 2826915.0),
236      'width': 791}
237 
238     """
239 
240     def open(self, driver=None, width=None, height=None,
241              count=None, crs=None, transform=None, dtype=None, nodata=None,
242              **kwargs):
243         """Open the file and return a Rasterio dataset object.
244 
245         If data has already been written, the file is opened in 'r+'
246         mode. Otherwise, the file is opened in 'w' mode.
247         """
248         with Env():
249             if self.closed:
250                 raise IOError("I/O operation on closed file.")
251             if self.exists():
252                 s = get_writer_for_path(self.name)(self.name, 'r+')
253             else:
254                 s = get_writer_for_driver(
255                     driver)(self.name, 'w', driver=driver, width=width,
256                             height=height, count=count, crs=crs,
257                             transform=transform, dtype=dtype, nodata=nodata,
258                             **kwargs)
259             s.start()
260             return s
261 
262     def __enter__(self):
263         return self
264 
265     def __exit__(self, *args, **kwargs):
266         self.close()
267 
268 
269 def get_writer_for_driver(driver):
270     """Return the writer class appropriate for the specified driver."""
271     cls = None
272     if driver_can_create(driver):
273         cls = DatasetWriter
274     elif driver_can_create_copy(driver):  # pragma: no branch
275         cls = BufferedDatasetWriter
276     return cls
277 
278 
279 def get_writer_for_path(path):
280     """Return the writer class appropriate for the existing dataset."""
281     driver = get_dataset_driver(path)
282     return get_writer_for_driver(driver)
283
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/rasterio/io.py b/rasterio/io.py
--- a/rasterio/io.py
+++ b/rasterio/io.py
@@ -9,7 +9,7 @@
 import warnings
 
 from rasterio._base import (
-    get_dataset_driver, driver_can_create, driver_can_create_copy)
+    DatasetBase, get_dataset_driver, driver_can_create, driver_can_create_copy)
 from rasterio._io import (
     DatasetReaderBase, DatasetWriterBase, BufferedDatasetWriterBase,
     MemoryFileBase)

{"golden_diff": "diff --git a/rasterio/io.py b/rasterio/io.py\n--- a/rasterio/io.py\n+++ b/rasterio/io.py\n@@ -9,7 +9,7 @@\n import warnings\n \n from rasterio._base import (\n-    get_dataset_driver, driver_can_create, driver_can_create_copy)\n+    DatasetBase, get_dataset_driver, driver_can_create, driver_can_create_copy)\n from rasterio._io import (\n     DatasetReaderBase, DatasetWriterBase, BufferedDatasetWriterBase,\n     MemoryFileBase)\n", "issue": "Allow type annotation support for rasterio data types\nRecently I've been adding PEP 484 type annotations to my projects and rasterio has proved quite ugly to use as is, the main annoyance being that `rasterio.open` may return objects belonging to three classes (`DatasetReader`, `DatasetWriter` and `BufferedDatasetWriter`) with a non-public base `DatasetBase`, so right now the only option to annotate a generic dataset is:\r\n```\r\nfrom typing import Union\r\nfrom rasterio import io\r\n\r\ndef myfunc(\r\n        ds: Union[io.DatasetReader, io.DatasetWriter, io.BufferedDatasetWriter]\r\n) -> None:\r\n    pass\r\n```\r\n\r\nSomething like exposing the DatasetBase class inside rasterio would make the above much more readable:\r\n```\r\nimport rasterio\r\n\r\ndef myfunc(ds: rasterio.DatasetBase) -> None:\r\n    pass\r\n```\r\n\r\nWould you consider restructuring a bit the dataset classes so that they are easier to use in type annotations? I guess I'm the first one having such a need :)\n", "before_files": [{"content": "\"\"\"Classes capable of reading and writing datasets\n\nInstances of these classes are called dataset objects.\n\"\"\"\n\nimport logging\nimport math\nimport uuid\nimport warnings\n\nfrom rasterio._base import (\n    get_dataset_driver, driver_can_create, driver_can_create_copy)\nfrom rasterio._io import (\n    DatasetReaderBase, DatasetWriterBase, BufferedDatasetWriterBase,\n    MemoryFileBase)\nfrom rasterio import enums, windows\nfrom rasterio.env import Env\nfrom rasterio.transform import guard_transform, xy, rowcol\n\n\nlog = logging.getLogger(__name__)\n\n\nclass TransformMethodsMixin(object):\n    \"\"\"Mixin providing methods for calculations related\n    to transforming between rows and columns of the raster\n    array and the coordinates.\n\n    These methods are wrappers for the functionality in\n    `rasterio.transform` module.\n\n    A subclass with this mixin MUST provide a `transform`\n    property.\n    \"\"\"\n\n    def xy(self, row, col, offset=\"center\"):\n        \"\"\"Returns the coordinates ``(x, y)`` of a pixel at `row` and `col`.\n        The pixel's center is returned by default, but a corner can be returned\n        by setting `offset` to one of `ul, ur, ll, lr`.\n\n        Parameters\n        ----------\n        row : int\n            Pixel row.\n        col : int\n            Pixel column.\n        offset : str, optional\n            Determines if the returned coordinates are for the center of the\n            pixel or for a corner.\n\n        Returns\n        -------\n        tuple\n            ``(x, y)``\n        \"\"\"\n        return xy(self.transform, row, col, offset=offset)\n\n    def ul(self, row, col):\n        \"\"\"Returns the coordinates (x, y) of the upper left corner of a\n        pixel at `row` and `col` in the units of the dataset's\n        coordinate reference system.\n\n        Deprecated; Use `xy(row, col, offset='ul')` instead.\n        \"\"\"\n        warnings.warn(\"ul method is deprecated. Use xy(row, col, offset='ul')\",\n                      DeprecationWarning)\n        return xy(self.transform, row, col, offset='ul')\n\n    def index(self, x, y, op=math.floor, precision=6):\n        \"\"\"\n        Returns the (row, col) index of the pixel containing (x, y) given a\n        coordinate reference system.\n\n        Use an epsilon, magnitude determined by the precision parameter\n        and sign determined by the op function:\n            positive for floor, negative for ceil.\n\n        Parameters\n        ----------\n        x : float\n            x value in coordinate reference system\n        y : float\n            y value in coordinate reference system\n        op : function, optional (default: math.floor)\n            Function to convert fractional pixels to whole numbers (floor,\n            ceiling, round)\n        precision : int, optional (default: 6)\n            Decimal places of precision in indexing, as in `round()`.\n\n        Returns\n        -------\n        tuple\n            (row index, col index)\n        \"\"\"\n        return rowcol(self.transform, x, y, op=op, precision=precision)\n\n\nclass WindowMethodsMixin(object):\n    \"\"\"Mixin providing methods for window-related calculations.\n    These methods are wrappers for the functionality in\n    `rasterio.windows` module.\n\n    A subclass with this mixin MUST provide the following\n    properties: `transform`, `height` and `width`\n    \"\"\"\n\n    def window(self, left, bottom, right, top, boundless=False):\n        \"\"\"Get the window corresponding to the bounding coordinates.\n\n        Parameters\n        ----------\n        left : float\n            Left (west) bounding coordinate\n        bottom : float\n            Bottom (south) bounding coordinate\n        right : float\n            Right (east) bounding coordinate\n        top : float\n            Top (north) bounding coordinate\n        boundless: boolean, optional\n            If boundless is False, window is limited\n            to extent of this dataset.\n\n        Returns\n        -------\n        window: tuple\n            ((row_start, row_stop), (col_start, col_stop))\n            corresponding to the bounding coordinates\n\n        \"\"\"\n\n        transform = guard_transform(self.transform)\n        return windows.from_bounds(\n            left, bottom, right, top, transform=transform,\n            height=self.height, width=self.width, boundless=boundless)\n\n    def window_transform(self, window):\n        \"\"\"Get the affine transform for a dataset window.\n\n        Parameters\n        ----------\n        window: tuple\n            Dataset window tuple\n\n        Returns\n        -------\n        transform: Affine\n            The affine transform matrix for the given window\n        \"\"\"\n\n        transform = guard_transform(self.transform)\n        return windows.transform(window, transform)\n\n    def window_bounds(self, window):\n        \"\"\"Get the bounds of a window\n\n        Parameters\n        ----------\n        window: tuple\n            Dataset window tuple\n\n        Returns\n        -------\n        bounds : tuple\n            x_min, y_min, x_max, y_max for the given window\n        \"\"\"\n\n        transform = guard_transform(self.transform)\n        return windows.bounds(window, transform)\n\n\nclass DatasetReader(DatasetReaderBase, WindowMethodsMixin,\n                    TransformMethodsMixin):\n    \"\"\"An unbuffered data and metadata reader\"\"\"\n\n    def __repr__(self):\n        return \"<{} DatasetReader name='{}' mode='{}'>\".format(\n            self.closed and 'closed' or 'open', self.name, self.mode)\n\n\nclass DatasetWriter(DatasetWriterBase, WindowMethodsMixin,\n                    TransformMethodsMixin):\n    \"\"\"An unbuffered data and metadata writer. Its methods write data\n    directly to disk.\n    \"\"\"\n\n    def __repr__(self):\n        return \"<{} DatasetWriter name='{}' mode='{}'>\".format(\n            self.closed and 'closed' or 'open', self.name, self.mode)\n\n\nclass BufferedDatasetWriter(BufferedDatasetWriterBase, WindowMethodsMixin,\n                            TransformMethodsMixin):\n    \"\"\"Maintains data and metadata in a buffer, writing to disk or\n    network only when `close()` is called.\n\n    This allows incremental updates to datasets using formats that don't\n    otherwise support updates, such as JPEG.\n    \"\"\"\n\n    def __repr__(self):\n        return \"<{} BufferedDatasetWriter name='{}' mode='{}'>\".format(\n            self.closed and 'closed' or 'open', self.name, self.mode)\n\n\nclass MemoryFile(MemoryFileBase):\n    \"\"\"A BytesIO-like object, backed by an in-memory file.\n\n    This allows formatted files to be read and written without I/O.\n\n    A MemoryFile created with initial bytes becomes immutable. A\n    MemoryFile created without initial bytes may be written to using\n    either file-like or dataset interfaces.\n\n    Examples\n    --------\n\n    A GeoTIFF can be loaded in memory and accessed using the GeoTIFF\n    format driver\n\n    >>> with open('tests/data/RGB.byte.tif', 'rb') as f, \\\n    ...         MemoryFile(f.read()) as memfile:\n    ...     with memfile.open() as src:\n    ...         pprint.pprint(src.profile)\n    ...\n    {'count': 3,\n     'crs': CRS({'init': 'epsg:32618'}),\n     'driver': 'GTiff',\n     'dtype': 'uint8',\n     'height': 718,\n     'interleave': 'pixel',\n     'nodata': 0.0,\n     'tiled': False,\n     'transform': Affine(300.0379266750948, 0.0, 101985.0,\n           0.0, -300.041782729805, 2826915.0),\n     'width': 791}\n\n    \"\"\"\n\n    def open(self, driver=None, width=None, height=None,\n             count=None, crs=None, transform=None, dtype=None, nodata=None,\n             **kwargs):\n        \"\"\"Open the file and return a Rasterio dataset object.\n\n        If data has already been written, the file is opened in 'r+'\n        mode. Otherwise, the file is opened in 'w' mode.\n        \"\"\"\n        with Env():\n            if self.closed:\n                raise IOError(\"I/O operation on closed file.\")\n            if self.exists():\n                s = get_writer_for_path(self.name)(self.name, 'r+')\n            else:\n                s = get_writer_for_driver(\n                    driver)(self.name, 'w', driver=driver, width=width,\n                            height=height, count=count, crs=crs,\n                            transform=transform, dtype=dtype, nodata=nodata,\n                            **kwargs)\n            s.start()\n            return s\n\n    def __enter__(self):\n        return self\n\n    def __exit__(self, *args, **kwargs):\n        self.close()\n\n\ndef get_writer_for_driver(driver):\n    \"\"\"Return the writer class appropriate for the specified driver.\"\"\"\n    cls = None\n    if driver_can_create(driver):\n        cls = DatasetWriter\n    elif driver_can_create_copy(driver):  # pragma: no branch\n        cls = BufferedDatasetWriter\n    return cls\n\n\ndef get_writer_for_path(path):\n    \"\"\"Return the writer class appropriate for the existing dataset.\"\"\"\n    driver = get_dataset_driver(path)\n    return get_writer_for_driver(driver)\n", "path": "rasterio/io.py"}], "after_files": [{"content": "\"\"\"Classes capable of reading and writing datasets\n\nInstances of these classes are called dataset objects.\n\"\"\"\n\nimport logging\nimport math\nimport uuid\nimport warnings\n\nfrom rasterio._base import (\n    DatasetBase, get_dataset_driver, driver_can_create, driver_can_create_copy)\nfrom rasterio._io import (\n    DatasetReaderBase, DatasetWriterBase, BufferedDatasetWriterBase,\n    MemoryFileBase)\nfrom rasterio import enums, windows\nfrom rasterio.env import Env\nfrom rasterio.transform import guard_transform, xy, rowcol\n\n\nlog = logging.getLogger(__name__)\n\n\nclass TransformMethodsMixin(object):\n    \"\"\"Mixin providing methods for calculations related\n    to transforming between rows and columns of the raster\n    array and the coordinates.\n\n    These methods are wrappers for the functionality in\n    `rasterio.transform` module.\n\n    A subclass with this mixin MUST provide a `transform`\n    property.\n    \"\"\"\n\n    def xy(self, row, col, offset=\"center\"):\n        \"\"\"Returns the coordinates ``(x, y)`` of a pixel at `row` and `col`.\n        The pixel's center is returned by default, but a corner can be returned\n        by setting `offset` to one of `ul, ur, ll, lr`.\n\n        Parameters\n        ----------\n        row : int\n            Pixel row.\n        col : int\n            Pixel column.\n        offset : str, optional\n            Determines if the returned coordinates are for the center of the\n            pixel or for a corner.\n\n        Returns\n        -------\n        tuple\n            ``(x, y)``\n        \"\"\"\n        return xy(self.transform, row, col, offset=offset)\n\n    def ul(self, row, col):\n        \"\"\"Returns the coordinates (x, y) of the upper left corner of a\n        pixel at `row` and `col` in the units of the dataset's\n        coordinate reference system.\n\n        Deprecated; Use `xy(row, col, offset='ul')` instead.\n        \"\"\"\n        warnings.warn(\"ul method is deprecated. Use xy(row, col, offset='ul')\",\n                      DeprecationWarning)\n        return xy(self.transform, row, col, offset='ul')\n\n    def index(self, x, y, op=math.floor, precision=6):\n        \"\"\"\n        Returns the (row, col) index of the pixel containing (x, y) given a\n        coordinate reference system.\n\n        Use an epsilon, magnitude determined by the precision parameter\n        and sign determined by the op function:\n            positive for floor, negative for ceil.\n\n        Parameters\n        ----------\n        x : float\n            x value in coordinate reference system\n        y : float\n            y value in coordinate reference system\n        op : function, optional (default: math.floor)\n            Function to convert fractional pixels to whole numbers (floor,\n            ceiling, round)\n        precision : int, optional (default: 6)\n            Decimal places of precision in indexing, as in `round()`.\n\n        Returns\n        -------\n        tuple\n            (row index, col index)\n        \"\"\"\n        return rowcol(self.transform, x, y, op=op, precision=precision)\n\n\nclass WindowMethodsMixin(object):\n    \"\"\"Mixin providing methods for window-related calculations.\n    These methods are wrappers for the functionality in\n    `rasterio.windows` module.\n\n    A subclass with this mixin MUST provide the following\n    properties: `transform`, `height` and `width`\n    \"\"\"\n\n    def window(self, left, bottom, right, top, boundless=False):\n        \"\"\"Get the window corresponding to the bounding coordinates.\n\n        Parameters\n        ----------\n        left : float\n            Left (west) bounding coordinate\n        bottom : float\n            Bottom (south) bounding coordinate\n        right : float\n            Right (east) bounding coordinate\n        top : float\n            Top (north) bounding coordinate\n        boundless: boolean, optional\n            If boundless is False, window is limited\n            to extent of this dataset.\n\n        Returns\n        -------\n        window: tuple\n            ((row_start, row_stop), (col_start, col_stop))\n            corresponding to the bounding coordinates\n\n        \"\"\"\n\n        transform = guard_transform(self.transform)\n        return windows.from_bounds(\n            left, bottom, right, top, transform=transform,\n            height=self.height, width=self.width, boundless=boundless)\n\n    def window_transform(self, window):\n        \"\"\"Get the affine transform for a dataset window.\n\n        Parameters\n        ----------\n        window: tuple\n            Dataset window tuple\n\n        Returns\n        -------\n        transform: Affine\n            The affine transform matrix for the given window\n        \"\"\"\n\n        transform = guard_transform(self.transform)\n        return windows.transform(window, transform)\n\n    def window_bounds(self, window):\n        \"\"\"Get the bounds of a window\n\n        Parameters\n        ----------\n        window: tuple\n            Dataset window tuple\n\n        Returns\n        -------\n        bounds : tuple\n            x_min, y_min, x_max, y_max for the given window\n        \"\"\"\n\n        transform = guard_transform(self.transform)\n        return windows.bounds(window, transform)\n\n\nclass DatasetReader(DatasetReaderBase, WindowMethodsMixin,\n                    TransformMethodsMixin):\n    \"\"\"An unbuffered data and metadata reader\"\"\"\n\n    def __repr__(self):\n        return \"<{} DatasetReader name='{}' mode='{}'>\".format(\n            self.closed and 'closed' or 'open', self.name, self.mode)\n\n\nclass DatasetWriter(DatasetWriterBase, WindowMethodsMixin,\n                    TransformMethodsMixin):\n    \"\"\"An unbuffered data and metadata writer. Its methods write data\n    directly to disk.\n    \"\"\"\n\n    def __repr__(self):\n        return \"<{} DatasetWriter name='{}' mode='{}'>\".format(\n            self.closed and 'closed' or 'open', self.name, self.mode)\n\n\nclass BufferedDatasetWriter(BufferedDatasetWriterBase, WindowMethodsMixin,\n                            TransformMethodsMixin):\n    \"\"\"Maintains data and metadata in a buffer, writing to disk or\n    network only when `close()` is called.\n\n    This allows incremental updates to datasets using formats that don't\n    otherwise support updates, such as JPEG.\n    \"\"\"\n\n    def __repr__(self):\n        return \"<{} BufferedDatasetWriter name='{}' mode='{}'>\".format(\n            self.closed and 'closed' or 'open', self.name, self.mode)\n\n\nclass MemoryFile(MemoryFileBase):\n    \"\"\"A BytesIO-like object, backed by an in-memory file.\n\n    This allows formatted files to be read and written without I/O.\n\n    A MemoryFile created with initial bytes becomes immutable. A\n    MemoryFile created without initial bytes may be written to using\n    either file-like or dataset interfaces.\n\n    Examples\n    --------\n\n    A GeoTIFF can be loaded in memory and accessed using the GeoTIFF\n    format driver\n\n    >>> with open('tests/data/RGB.byte.tif', 'rb') as f, \\\n    ...         MemoryFile(f.read()) as memfile:\n    ...     with memfile.open() as src:\n    ...         pprint.pprint(src.profile)\n    ...\n    {'count': 3,\n     'crs': CRS({'init': 'epsg:32618'}),\n     'driver': 'GTiff',\n     'dtype': 'uint8',\n     'height': 718,\n     'interleave': 'pixel',\n     'nodata': 0.0,\n     'tiled': False,\n     'transform': Affine(300.0379266750948, 0.0, 101985.0,\n           0.0, -300.041782729805, 2826915.0),\n     'width': 791}\n\n    \"\"\"\n\n    def open(self, driver=None, width=None, height=None,\n             count=None, crs=None, transform=None, dtype=None, nodata=None,\n             **kwargs):\n        \"\"\"Open the file and return a Rasterio dataset object.\n\n        If data has already been written, the file is opened in 'r+'\n        mode. Otherwise, the file is opened in 'w' mode.\n        \"\"\"\n        with Env():\n            if self.closed:\n                raise IOError(\"I/O operation on closed file.\")\n            if self.exists():\n                s = get_writer_for_path(self.name)(self.name, 'r+')\n            else:\n                s = get_writer_for_driver(\n                    driver)(self.name, 'w', driver=driver, width=width,\n                            height=height, count=count, crs=crs,\n                            transform=transform, dtype=dtype, nodata=nodata,\n                            **kwargs)\n            s.start()\n            return s\n\n    def __enter__(self):\n        return self\n\n    def __exit__(self, *args, **kwargs):\n        self.close()\n\n\ndef get_writer_for_driver(driver):\n    \"\"\"Return the writer class appropriate for the specified driver.\"\"\"\n    cls = None\n    if driver_can_create(driver):\n        cls = DatasetWriter\n    elif driver_can_create_copy(driver):  # pragma: no branch\n        cls = BufferedDatasetWriter\n    return cls\n\n\ndef get_writer_for_path(path):\n    \"\"\"Return the writer class appropriate for the existing dataset.\"\"\"\n    driver = get_dataset_driver(path)\n    return get_writer_for_driver(driver)\n", "path": "rasterio/io.py"}]}