Skip to content

Commit

Permalink
Safe-guard/isolate legacy GDAL filenames
Browse files Browse the repository at this point in the history
  • Loading branch information
Sean C. Gillies committed May 18, 2018
1 parent 7f2caad commit 6490d86
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 16 deletions.
23 changes: 23 additions & 0 deletions docs/topics/datasets.rst
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,26 @@ Datasets on AWS S3 may be identified using "s3" scheme identifiers.
``'s3://landsat-pds/L8/139/045/LC81390452014295LGN00/LC81390452014295LGN00_B1.TIF'``

Resources in other cloud storage systems will be similarly supported.

Legacy GDAL filenames
---------------------

Legacy GDAL filenames like

* ``'/vsis3/landsat-pds/L8/139/045/LC81390452014295LGN00/LC81390452014295LGN00_B1.TIF'``
* ``'PG:"host=localhost port:80 dbname=foo user=bar password=xxxxxx"'``

may be used by wrapping them in an instance of ``GDALFilename``.

.. code-block:: python
import rasterio
filename = rasterio.GDALFilename(
'PG:host=localhost port:80 dbname=foo user=bar password=xxx')
with rasterio.open(filename) as src:
print(src.profile)
Without ``GDALFilename`` Rasterio cannot guarantee that the legacy filenames
are handled as you would expect.
15 changes: 12 additions & 3 deletions rasterio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def emit(self, record):
DatasetReader, get_writer_for_path, get_writer_for_driver, MemoryFile)
from rasterio.profiles import default_gtiff_profile
from rasterio.transform import Affine, guard_transform
from rasterio.vfs import parse_path
from rasterio.vfs import parse_path, GDALFilename

# These modules are imported from the Cython extensions, but are also import
# here to help tools like cx_Freeze find them automatically
Expand Down Expand Up @@ -146,7 +146,8 @@ def open(fp, mode='r', driver=None, width=None, height=None, count=None,
"""

if not isinstance(fp, string_types):
if not (hasattr(fp, 'read') or hasattr(fp, 'write') or isinstance(fp, Path)):
if not (hasattr(fp, 'read') or hasattr(fp, 'write') or
isinstance(fp, Path) or isinstance(fp, GDALFilename)):
raise TypeError("invalid path or file: {0!r}".format(fp))
if mode and not isinstance(mode, string_types):
raise TypeError("invalid mode: {0!r}".format(mode))
Expand Down Expand Up @@ -199,12 +200,20 @@ def fp_writer(fp):
return fp_writer(fp)

else:

scheme = None

# If a pathlib.Path instance is given, convert it to a string path.
if isinstance(fp, Path):
fp = str(fp)

# If a GDALFilename, do not parse it.
elif isinstance(fp, GDALFilename):
pass

# The 'normal' filename or URL path.
_, _, scheme = parse_path(fp)
else:
_, _, scheme = parse_path(fp)

with Env() as env:
if scheme == 's3':
Expand Down
15 changes: 12 additions & 3 deletions rasterio/_base.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ from rasterio.errors import (
RasterBlockError, BandOverviewError)
from rasterio.profiles import Profile
from rasterio.transform import Affine, guard_transform, tastes_like_gdal
from rasterio.vfs import parse_path, vsi_path
from rasterio.vfs import parse_path, vsi_path, GDALFilename
from rasterio import windows

include "gdal.pxi"
Expand Down Expand Up @@ -59,7 +59,11 @@ def get_dataset_driver(path):
cdef GDALDatasetH dataset = NULL
cdef GDALDriverH driver = NULL

path = vsi_path(*parse_path(path))
if isinstance(path, GDALFilename):
path = path.filename
else:
path = vsi_path(*parse_path(path))

path = path.encode('utf-8')

try:
Expand Down Expand Up @@ -118,7 +122,12 @@ cdef class DatasetBase(object):
self._hds = NULL

if path is not None:
filename = vsi_path(*parse_path(path))

if isinstance(path, GDALFilename):
filename = path.filename

else:
filename = vsi_path(*parse_path(path))

# driver may be a string or list of strings. If the
# former, we put it into a list.
Expand Down
27 changes: 18 additions & 9 deletions rasterio/_io.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ from rasterio.errors import (
)
from rasterio.sample import sample_gen
from rasterio.transform import Affine
from rasterio.vfs import parse_path, vsi_path
from rasterio.vfs import parse_path, vsi_path, GDALFilename
from rasterio.vrt import WarpedVRT
from rasterio.windows import Window, intersection

Expand Down Expand Up @@ -940,10 +940,15 @@ cdef class DatasetWriterBase(DatasetReaderBase):

# Make and store a GDAL dataset handle.

# Parse the path to determine if there is scheme-specific
# configuration to be done.
path, archive, scheme = parse_path(path)
path = vsi_path(path, archive, scheme)
if isinstance(path, GDALFilename):
path = path.filename
archive = scheme = None

else:
# Parse the path to determine if there is scheme-specific
# configuration to be done.
path, archive, scheme = parse_path(path)
path = vsi_path(path, archive, scheme)

if scheme and scheme != 'file':
raise TypeError(
Expand Down Expand Up @@ -1802,11 +1807,15 @@ cdef class BufferedDatasetWriterBase(DatasetWriterBase):

# Make and store a GDAL dataset handle.

# Parse the path to determine if there is scheme-specific
# configuration to be done.
path = vsi_path(*parse_path(path))
name_b = path.encode('utf-8')
if isinstance(path, GDALFilename):
path = path.filename

else:
# Parse the path to determine if there is scheme-specific
# configuration to be done.
path = vsi_path(*parse_path(path))

name_b = path.encode('utf-8')
memdrv = GDALGetDriverByName("MEM")

if self.mode == 'w':
Expand Down
18 changes: 18 additions & 0 deletions rasterio/vfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,21 @@ def vsi_path(path, archive=None, scheme=None):
else:
result = path
return result


class GDALFilename(object):
"""A GDAL filename object
All legacy GDAL filenames must be wrapped using this class.
Attributes
----------
filename : str
A GDAL filename such as "/vsicurl/https://example.com/data.tif".
"""

def __init__(self, filename):
self.filename = filename

def __repr__(self):
return "<GDALFilename filename={}>".format(self.filename)
11 changes: 10 additions & 1 deletion tests/test_vfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import rasterio
from rasterio.profiles import default_gtiff_profile
from rasterio.vfs import parse_path, vsi_path
from rasterio.vfs import parse_path, vsi_path, GDALFilename


logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
Expand Down Expand Up @@ -98,3 +98,12 @@ def test_parse_path_accept_get_params():
# See https://github.com/mapbox/rasterio/issues/1121
assert parse_path('http://example.com/index?a=1') == (
'example.com/index?a=1', None, 'http')


def test_gdal_filename():
assert GDALFilename("foo.tif").filename == "foo.tif"


def test_gdal_filename_open():
with rasterio.open(GDALFilename("tests/data/RGB.byte.tif")) as src:
assert src.count == 3

0 comments on commit 6490d86

Please sign in to comment.