We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Example PickleWriterDriver does not support URIs
### Expected behaviour
Using the example `pickles` driver should allow to successfully run, e.g., `integration_tests/test_end_to_end`. This is assuming the example dcio driver has been installed first.

### Actual behaviour
The test fails because a URI gets passed to `PickleWriterDriver.write_dataset_to_storage()` which still expects a filepath string. Also, this URI may contain intermediate directories that need creating.

### Steps to reproduce the behaviour
- Set `pickles` as driver name in, e.g., `datacube-core/docs/config_samples/ingester/ls5_nbar_albers.yaml(106)`
- Run `pytest integration_tests/test_end_to_end.py`
- First error: `AttributeError: 'PickleWriterDriver' object has no attribute 'mk_uri'` but subsequent errors happen due to missing intermediate directories.

### Environment information
- `Open Data Cube core, version 1.8.4.dev52+g07bc51a5.d20210222`
- Docker image: `opendatacube/datacube-tests:latest`

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `examples/io_plugin/dcio_example/pickles.py`
Content:
```
1 # This file is part of the Open Data Cube, see https://opendatacube.org for more information
2 #
3 # Copyright (c) 2015-2020 ODC Contributors
4 # SPDX-License-Identifier: Apache-2.0
5 """ Example reader plugin
6 """
7 from contextlib import contextmanager
8 import pickle
9 
10 
11 PROTOCOL = 'file'
12 FORMAT = 'pickle'
13 
14 
15 def uri_split(uri):
16     loc = uri.find('://')
17     if loc < 0:
18         return uri, PROTOCOL
19     return uri[loc+3:], uri[:loc]
20 
21 
22 class PickleDataSource(object):
23     class BandDataSource(object):
24         def __init__(self, da):
25             self._da = da
26             self.nodata = da.nodata
27 
28         @property
29         def crs(self):
30             return self._da.crs
31 
32         @property
33         def transform(self):
34             return self._da.affine
35 
36         @property
37         def dtype(self):
38             return self._da.dtype
39 
40         @property
41         def shape(self):
42             return self._da.shape
43 
44         def read(self, window=None, out_shape=None):
45             if window is None:
46                 data = self._da.values
47             else:
48                 rows, cols = [slice(*w) for w in window]
49                 data = self._da.values[rows, cols]
50 
51             if out_shape is None or out_shape == data.shape:
52                 return data
53 
54             raise NotImplementedError('Native reading not supported for this data source')
55 
56     def __init__(self, band):
57         self._band = band
58         uri = band.uri
59         self._filename, protocol = uri_split(uri)
60 
61         if protocol not in [PROTOCOL, 'pickle']:
62             raise ValueError('Expected file:// or pickle:// url')
63 
64     @contextmanager
65     def open(self):
66         with open(self._filename, 'rb') as f:
67             ds = pickle.load(f)
68 
69         yield PickleDataSource.BandDataSource(ds[self._band.name].isel(time=0))
70 
71 
72 class PickleReaderDriver(object):
73     def __init__(self):
74         self.name = 'PickleReader'
75         self.protocols = [PROTOCOL, 'pickle']
76         self.formats = [FORMAT]
77 
78     def supports(self, protocol, fmt):
79         return (protocol in self.protocols and
80                 fmt in self.formats)
81 
82     def new_datasource(self, band):
83         return PickleDataSource(band)
84 
85 
86 def rdr_driver_init():
87     return PickleReaderDriver()
88 
89 
90 class PickleWriterDriver(object):
91     def __init__(self):
92         pass
93 
94     @property
95     def aliases(self):
96         return ['pickles']
97 
98     @property
99     def format(self):
100         return FORMAT
101 
102     @property
103     def uri_scheme(self):
104         return PROTOCOL
105 
106     def write_dataset_to_storage(self, dataset, filename,
107                                  global_attributes=None,
108                                  variable_params=None,
109                                  storage_config=None,
110                                  **kwargs):
111         with open(filename, 'wb') as f:
112             pickle.dump(dataset, f)
113         return {}
114 
115 
116 def writer_driver_init():
117     return PickleWriterDriver()
118
```
--- 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/examples/io_plugin/dcio_example/pickles.py b/examples/io_plugin/dcio_example/pickles.py
--- a/examples/io_plugin/dcio_example/pickles.py
+++ b/examples/io_plugin/dcio_example/pickles.py
@@ -4,9 +4,12 @@
 # SPDX-License-Identifier: Apache-2.0
 """ Example reader plugin
 """
-from contextlib import contextmanager
 import pickle
+from contextlib import contextmanager
+from pathlib import Path
+from urllib.parse import urlsplit
 
+from datacube.utils.uris import normalise_path
 
 PROTOCOL = 'file'
 FORMAT = 'pickle'
@@ -103,12 +106,33 @@
     def uri_scheme(self):
         return PROTOCOL
 
-    def write_dataset_to_storage(self, dataset, filename,
+    def mk_uri(self, file_path, storage_config):
+        """
+        Constructs a URI from the file_path and storage config.
+
+        A typical implementation should return f'{scheme}://{file_path}'
+
+        Example:
+            file_path = '/path/to/my_file.pickled'
+            storage_config = {'driver': 'pickles'}
+
+            mk_uri(file_path, storage_config) should return 'file:///path/to/my_file.pickled'
+
+        :param Path file_path: The file path of the file to be converted into a URI.
+        :param dict storage_config: The dict holding the storage config found in the ingest definition.
+        :return: file_path as a URI that the Driver understands.
+        :rtype: str
+        """
+        return normalise_path(file_path).as_uri()
+
+    def write_dataset_to_storage(self, dataset, file_uri,
                                  global_attributes=None,
                                  variable_params=None,
                                  storage_config=None,
                                  **kwargs):
-        with open(filename, 'wb') as f:
+        filepath = Path(urlsplit(file_uri).path)
+        filepath.parent.mkdir(parents=True, exist_ok=True)
+        with filepath.open('wb') as f:
             pickle.dump(dataset, f)
         return {}

{"golden_diff": "diff --git a/examples/io_plugin/dcio_example/pickles.py b/examples/io_plugin/dcio_example/pickles.py\n--- a/examples/io_plugin/dcio_example/pickles.py\n+++ b/examples/io_plugin/dcio_example/pickles.py\n@@ -4,9 +4,12 @@\n # SPDX-License-Identifier: Apache-2.0\n \"\"\" Example reader plugin\n \"\"\"\n-from contextlib import contextmanager\n import pickle\n+from contextlib import contextmanager\n+from pathlib import Path\n+from urllib.parse import urlsplit\n \n+from datacube.utils.uris import normalise_path\n \n PROTOCOL = 'file'\n FORMAT = 'pickle'\n@@ -103,12 +106,33 @@\n     def uri_scheme(self):\n         return PROTOCOL\n \n-    def write_dataset_to_storage(self, dataset, filename,\n+    def mk_uri(self, file_path, storage_config):\n+        \"\"\"\n+        Constructs a URI from the file_path and storage config.\n+\n+        A typical implementation should return f'{scheme}://{file_path}'\n+\n+        Example:\n+            file_path = '/path/to/my_file.pickled'\n+            storage_config = {'driver': 'pickles'}\n+\n+            mk_uri(file_path, storage_config) should return 'file:///path/to/my_file.pickled'\n+\n+        :param Path file_path: The file path of the file to be converted into a URI.\n+        :param dict storage_config: The dict holding the storage config found in the ingest definition.\n+        :return: file_path as a URI that the Driver understands.\n+        :rtype: str\n+        \"\"\"\n+        return normalise_path(file_path).as_uri()\n+\n+    def write_dataset_to_storage(self, dataset, file_uri,\n                                  global_attributes=None,\n                                  variable_params=None,\n                                  storage_config=None,\n                                  **kwargs):\n-        with open(filename, 'wb') as f:\n+        filepath = Path(urlsplit(file_uri).path)\n+        filepath.parent.mkdir(parents=True, exist_ok=True)\n+        with filepath.open('wb') as f:\n             pickle.dump(dataset, f)\n         return {}\n", "issue": "Example PickleWriterDriver does not support URIs\n### Expected behaviour\r\nUsing the example `pickles` driver should allow to successfully run, e.g., `integration_tests/test_end_to_end`. This is assuming the example dcio driver has been installed first.\r\n\r\n### Actual behaviour\r\nThe test fails because a URI gets passed to `PickleWriterDriver.write_dataset_to_storage()` which still expects a filepath string. Also, this URI may contain intermediate directories that need creating.\r\n\r\n### Steps to reproduce the behaviour\r\n- Set `pickles` as driver name in, e.g., `datacube-core/docs/config_samples/ingester/ls5_nbar_albers.yaml(106)`\r\n- Run `pytest integration_tests/test_end_to_end.py`\r\n- First error: `AttributeError: 'PickleWriterDriver' object has no attribute 'mk_uri'` but subsequent errors happen due to missing intermediate directories.\r\n\r\n### Environment information\r\n- `Open Data Cube core, version 1.8.4.dev52+g07bc51a5.d20210222`\r\n- Docker image: `opendatacube/datacube-tests:latest`\n", "before_files": [{"content": "# This file is part of the Open Data Cube, see https://opendatacube.org for more information\n#\n# Copyright (c) 2015-2020 ODC Contributors\n# SPDX-License-Identifier: Apache-2.0\n\"\"\" Example reader plugin\n\"\"\"\nfrom contextlib import contextmanager\nimport pickle\n\n\nPROTOCOL = 'file'\nFORMAT = 'pickle'\n\n\ndef uri_split(uri):\n    loc = uri.find('://')\n    if loc < 0:\n        return uri, PROTOCOL\n    return uri[loc+3:], uri[:loc]\n\n\nclass PickleDataSource(object):\n    class BandDataSource(object):\n        def __init__(self, da):\n            self._da = da\n            self.nodata = da.nodata\n\n        @property\n        def crs(self):\n            return self._da.crs\n\n        @property\n        def transform(self):\n            return self._da.affine\n\n        @property\n        def dtype(self):\n            return self._da.dtype\n\n        @property\n        def shape(self):\n            return self._da.shape\n\n        def read(self, window=None, out_shape=None):\n            if window is None:\n                data = self._da.values\n            else:\n                rows, cols = [slice(*w) for w in window]\n                data = self._da.values[rows, cols]\n\n            if out_shape is None or out_shape == data.shape:\n                return data\n\n            raise NotImplementedError('Native reading not supported for this data source')\n\n    def __init__(self, band):\n        self._band = band\n        uri = band.uri\n        self._filename, protocol = uri_split(uri)\n\n        if protocol not in [PROTOCOL, 'pickle']:\n            raise ValueError('Expected file:// or pickle:// url')\n\n    @contextmanager\n    def open(self):\n        with open(self._filename, 'rb') as f:\n            ds = pickle.load(f)\n\n        yield PickleDataSource.BandDataSource(ds[self._band.name].isel(time=0))\n\n\nclass PickleReaderDriver(object):\n    def __init__(self):\n        self.name = 'PickleReader'\n        self.protocols = [PROTOCOL, 'pickle']\n        self.formats = [FORMAT]\n\n    def supports(self, protocol, fmt):\n        return (protocol in self.protocols and\n                fmt in self.formats)\n\n    def new_datasource(self, band):\n        return PickleDataSource(band)\n\n\ndef rdr_driver_init():\n    return PickleReaderDriver()\n\n\nclass PickleWriterDriver(object):\n    def __init__(self):\n        pass\n\n    @property\n    def aliases(self):\n        return ['pickles']\n\n    @property\n    def format(self):\n        return FORMAT\n\n    @property\n    def uri_scheme(self):\n        return PROTOCOL\n\n    def write_dataset_to_storage(self, dataset, filename,\n                                 global_attributes=None,\n                                 variable_params=None,\n                                 storage_config=None,\n                                 **kwargs):\n        with open(filename, 'wb') as f:\n            pickle.dump(dataset, f)\n        return {}\n\n\ndef writer_driver_init():\n    return PickleWriterDriver()\n", "path": "examples/io_plugin/dcio_example/pickles.py"}], "after_files": [{"content": "# This file is part of the Open Data Cube, see https://opendatacube.org for more information\n#\n# Copyright (c) 2015-2020 ODC Contributors\n# SPDX-License-Identifier: Apache-2.0\n\"\"\" Example reader plugin\n\"\"\"\nimport pickle\nfrom contextlib import contextmanager\nfrom pathlib import Path\nfrom urllib.parse import urlsplit\n\nfrom datacube.utils.uris import normalise_path\n\nPROTOCOL = 'file'\nFORMAT = 'pickle'\n\n\ndef uri_split(uri):\n    loc = uri.find('://')\n    if loc < 0:\n        return uri, PROTOCOL\n    return uri[loc+3:], uri[:loc]\n\n\nclass PickleDataSource(object):\n    class BandDataSource(object):\n        def __init__(self, da):\n            self._da = da\n            self.nodata = da.nodata\n\n        @property\n        def crs(self):\n            return self._da.crs\n\n        @property\n        def transform(self):\n            return self._da.affine\n\n        @property\n        def dtype(self):\n            return self._da.dtype\n\n        @property\n        def shape(self):\n            return self._da.shape\n\n        def read(self, window=None, out_shape=None):\n            if window is None:\n                data = self._da.values\n            else:\n                rows, cols = [slice(*w) for w in window]\n                data = self._da.values[rows, cols]\n\n            if out_shape is None or out_shape == data.shape:\n                return data\n\n            raise NotImplementedError('Native reading not supported for this data source')\n\n    def __init__(self, band):\n        self._band = band\n        uri = band.uri\n        self._filename, protocol = uri_split(uri)\n\n        if protocol not in [PROTOCOL, 'pickle']:\n            raise ValueError('Expected file:// or pickle:// url')\n\n    @contextmanager\n    def open(self):\n        with open(self._filename, 'rb') as f:\n            ds = pickle.load(f)\n\n        yield PickleDataSource.BandDataSource(ds[self._band.name].isel(time=0))\n\n\nclass PickleReaderDriver(object):\n    def __init__(self):\n        self.name = 'PickleReader'\n        self.protocols = [PROTOCOL, 'pickle']\n        self.formats = [FORMAT]\n\n    def supports(self, protocol, fmt):\n        return (protocol in self.protocols and\n                fmt in self.formats)\n\n    def new_datasource(self, band):\n        return PickleDataSource(band)\n\n\ndef rdr_driver_init():\n    return PickleReaderDriver()\n\n\nclass PickleWriterDriver(object):\n    def __init__(self):\n        pass\n\n    @property\n    def aliases(self):\n        return ['pickles']\n\n    @property\n    def format(self):\n        return FORMAT\n\n    @property\n    def uri_scheme(self):\n        return PROTOCOL\n\n    def mk_uri(self, file_path, storage_config):\n        \"\"\"\n        Constructs a URI from the file_path and storage config.\n\n        A typical implementation should return f'{scheme}://{file_path}'\n\n        Example:\n            file_path = '/path/to/my_file.pickled'\n            storage_config = {'driver': 'pickles'}\n\n            mk_uri(file_path, storage_config) should return 'file:///path/to/my_file.pickled'\n\n        :param Path file_path: The file path of the file to be converted into a URI.\n        :param dict storage_config: The dict holding the storage config found in the ingest definition.\n        :return: file_path as a URI that the Driver understands.\n        :rtype: str\n        \"\"\"\n        return normalise_path(file_path).as_uri()\n\n    def write_dataset_to_storage(self, dataset, file_uri,\n                                 global_attributes=None,\n                                 variable_params=None,\n                                 storage_config=None,\n                                 **kwargs):\n        filepath = Path(urlsplit(file_uri).path)\n        filepath.parent.mkdir(parents=True, exist_ok=True)\n        with filepath.open('wb') as f:\n            pickle.dump(dataset, f)\n        return {}\n\n\ndef writer_driver_init():\n    return PickleWriterDriver()\n", "path": "examples/io_plugin/dcio_example/pickles.py"}]}