@@ -324,7 +324,7 @@ def func(*args, **kwds):
324324 log .debug ("Registering opener: raw_dataset_path=%r, opener=%r" , raw_dataset_path , opener )
325325 vsi_path_ctx = _opener_registration (raw_dataset_path , opener )
326326 registered_vsi_path = stack .enter_context (vsi_path_ctx )
327- log .debug ("Registered vsi path: registered_vsi_path%r" , registered_vsi_path )
327+ log .debug ("Registered vsi path: registered_vsi_path= %r" , registered_vsi_path )
328328 path = _UnparsedPath (registered_vsi_path )
329329 else :
330330 if vfs :
@@ -386,7 +386,7 @@ def func(*args, **kwds):
386386
387387
388388@ensure_env_with_credentials
389- def remove (path_or_collection , driver = None , layer = None ):
389+ def remove (path_or_collection , driver = None , layer = None , opener = None ):
390390 """Delete an OGR data source or one of its layers.
391391
392392 If no layer is specified, the entire dataset and all of its layers
@@ -396,6 +396,19 @@ def remove(path_or_collection, driver=None, layer=None):
396396 ----------
397397 path_or_collection : str, pathlib.Path, or Collection
398398 The target Collection or its path.
399+ opener : callable or obj, optional
400+ A custom dataset opener which can serve GDAL's virtual
401+ filesystem machinery via Python file-like objects. The
402+ underlying file-like object is obtained by calling *opener* with
403+ (*fp*, *mode*) or (*fp*, *mode* + "b") depending on the format
404+ driver's native mode. *opener* must return a Python file-like
405+ object that provides read, seek, tell, and close methods. Note:
406+ only one opener at a time per fp, mode pair is allowed.
407+
408+ Alternatively, opener may be a filesystem object from a package
409+ like fsspec that provides the following methods: isdir(),
410+ isfile(), ls(), mtime(), open(), and size(). The exact interface
411+ is defined in the fiona._vsiopener._AbstractOpener class.
399412 driver : str, optional
400413 The name of a driver to be used for deletion, optional. Can
401414 usually be detected.
@@ -414,21 +427,37 @@ def remove(path_or_collection, driver=None, layer=None):
414427 """
415428 if isinstance (path_or_collection , Collection ):
416429 collection = path_or_collection
417- path = collection .path
430+ raw_dataset_path = collection .path
418431 driver = collection .driver
419432 collection .close ()
420- elif isinstance (path_or_collection , Path ):
421- path = str (path_or_collection )
433+
422434 else :
423- path = path_or_collection
424- if layer is None :
425- _remove (path , driver )
435+ fp = path_or_collection
436+ if hasattr (fp , "path" ) and hasattr (fp , "fs" ):
437+ log .debug ("Detected fp is an OpenFile: fp=%r" , fp )
438+ raw_dataset_path = fp .path
439+ opener = fp .fs .open
440+ else :
441+ raw_dataset_path = os .fspath (fp )
442+
443+ if opener :
444+ log .debug ("Registering opener: raw_dataset_path=%r, opener=%r" , raw_dataset_path , opener )
445+ with _opener_registration (raw_dataset_path , opener ) as registered_vsi_path :
446+ log .debug ("Registered vsi path: registered_vsi_path=%r" , registered_vsi_path )
447+ if layer is None :
448+ _remove (registered_vsi_path , driver )
449+ else :
450+ _remove_layer (registered_vsi_path , layer , driver )
426451 else :
427- _remove_layer (path , layer , driver )
452+ pobj = _parse_path (raw_dataset_path )
453+ if layer is None :
454+ _remove (_vsi_path (pobj ), driver )
455+ else :
456+ _remove_layer (_vsi_path (pobj ), layer , driver )
428457
429458
430459@ensure_env_with_credentials
431- def listdir (fp ):
460+ def listdir (fp , opener = None ):
432461 """Lists the datasets in a directory or archive file.
433462
434463 Archive files must be prefixed like "zip://" or "tar://".
@@ -437,6 +466,19 @@ def listdir(fp):
437466 ----------
438467 fp : str or pathlib.Path
439468 Directory or archive path.
469+ opener : callable or obj, optional
470+ A custom dataset opener which can serve GDAL's virtual
471+ filesystem machinery via Python file-like objects. The
472+ underlying file-like object is obtained by calling *opener* with
473+ (*fp*, *mode*) or (*fp*, *mode* + "b") depending on the format
474+ driver's native mode. *opener* must return a Python file-like
475+ object that provides read, seek, tell, and close methods. Note:
476+ only one opener at a time per fp, mode pair is allowed.
477+
478+ Alternatively, opener may be a filesystem object from a package
479+ like fsspec that provides the following methods: isdir(),
480+ isfile(), ls(), mtime(), open(), and size(). The exact interface
481+ is defined in the fiona._vsiopener._AbstractOpener class.
440482
441483 Returns
442484 -------
@@ -449,18 +491,25 @@ def listdir(fp):
449491 If the input is not a str or Path.
450492
451493 """
452- if isinstance (fp , Path ):
453- fp = str (fp )
454-
455- if not isinstance (fp , str ):
456- raise TypeError ("invalid path: %r" % fp )
494+ if hasattr (fp , "path" ) and hasattr (fp , "fs" ):
495+ log .debug ("Detected fp is an OpenFile: fp=%r" , fp )
496+ raw_dataset_path = fp .path
497+ opener = fp .fs .open
498+ else :
499+ raw_dataset_path = os .fspath (fp )
457500
458- pobj = _parse_path (fp )
459- return _listdir (_vsi_path (pobj ))
501+ if opener :
502+ log .debug ("Registering opener: raw_dataset_path=%r, opener=%r" , raw_dataset_path , opener )
503+ with _opener_registration (raw_dataset_path , opener ) as registered_vsi_path :
504+ log .debug ("Registered vsi path: registered_vsi_path=%r" , registered_vsi_path )
505+ return _listdir (registered_vsi_path )
506+ else :
507+ pobj = _parse_path (raw_dataset_path )
508+ return _listdir (_vsi_path (pobj ))
460509
461510
462511@ensure_env_with_credentials
463- def listlayers (fp , vfs = None , ** kwargs ):
512+ def listlayers (fp , opener = None , vfs = None , ** kwargs ):
464513 """Lists the layers (collections) in a dataset.
465514
466515 Archive files must be prefixed like "zip://" or "tar://".
@@ -469,6 +518,19 @@ def listlayers(fp, vfs=None, **kwargs):
469518 ----------
470519 fp : str, pathlib.Path, or file-like object
471520 A dataset identifier or file object containing a dataset.
521+ opener : callable or obj, optional
522+ A custom dataset opener which can serve GDAL's virtual
523+ filesystem machinery via Python file-like objects. The
524+ underlying file-like object is obtained by calling *opener* with
525+ (*fp*, *mode*) or (*fp*, *mode* + "b") depending on the format
526+ driver's native mode. *opener* must return a Python file-like
527+ object that provides read, seek, tell, and close methods. Note:
528+ only one opener at a time per fp, mode pair is allowed.
529+
530+ Alternatively, opener may be a filesystem object from a package
531+ like fsspec that provides the following methods: isdir(),
532+ isfile(), ls(), mtime(), open(), and size(). The exact interface
533+ is defined in the fiona._vsiopener._AbstractOpener class.
472534 vfs : str
473535 This is a deprecated parameter. A URI scheme such as "zip://"
474536 should be used instead.
@@ -486,18 +548,26 @@ def listlayers(fp, vfs=None, **kwargs):
486548 If the input is not a str, Path, or file object.
487549
488550 """
551+ if vfs and not isinstance (vfs , str ):
552+ raise TypeError (f"invalid vfs: { vfs !r} " )
553+
489554 if hasattr (fp , 'read' ):
490555 with MemoryFile (fp .read ()) as memfile :
491556 return _listlayers (memfile .name , ** kwargs )
492- else :
493- if isinstance (fp , Path ):
494- fp = str (fp )
495557
496- if not isinstance (fp , str ):
497- raise TypeError (f"invalid path: { fp !r} " )
498- if vfs and not isinstance (vfs , str ):
499- raise TypeError (f"invalid vfs: { vfs !r} " )
558+ if hasattr (fp , "path" ) and hasattr (fp , "fs" ):
559+ log .debug ("Detected fp is an OpenFile: fp=%r" , fp )
560+ raw_dataset_path = fp .path
561+ opener = fp .fs .open
562+ else :
563+ raw_dataset_path = os .fspath (fp )
500564
565+ if opener :
566+ log .debug ("Registering opener: raw_dataset_path=%r, opener=%r" , raw_dataset_path , opener )
567+ with _opener_registration (raw_dataset_path , opener ) as registered_vsi_path :
568+ log .debug ("Registered vsi path: registered_vsi_path=%r" , registered_vsi_path )
569+ return _listlayers (registered_vsi_path , ** kwargs )
570+ else :
501571 if vfs :
502572 warnings .warn (
503573 "The vfs keyword argument is deprecated and will be removed in 2.0. "
@@ -506,10 +576,10 @@ def listlayers(fp, vfs=None, **kwargs):
506576 stacklevel = 2 ,
507577 )
508578 pobj_vfs = _parse_path (vfs )
509- pobj_path = _parse_path (fp )
579+ pobj_path = _parse_path (raw_dataset_path )
510580 pobj = _ParsedPath (pobj_path .path , pobj_vfs .path , pobj_vfs .scheme )
511581 else :
512- pobj = _parse_path (fp )
582+ pobj = _parse_path (raw_dataset_path )
513583
514584 return _listlayers (_vsi_path (pobj ), ** kwargs )
515585
0 commit comments