core/maxframe/lib/filesystem/base.py (76 lines of code) (raw):
# Copyright 1999-2025 Alibaba Group Holding Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
from abc import ABC, abstractmethod
from typing import BinaryIO, Dict, Iterator, List, TextIO, Tuple, Union
from urllib.parse import urlparse
from ...utils import stringify_path
path_type = Union[str, os.PathLike]
class FileSystem(ABC):
"""
Abstract filesystem interface
"""
@abstractmethod
def cat(self, path: path_type) -> bytes:
"""
Return contents of file as a bytes object
Parameters
----------
path : str or path-like
File path to read content from.
Returns
-------
contents : bytes
"""
@abstractmethod
def ls(self, path: path_type) -> List[path_type]:
"""
Return list of file paths
Returns
-------
paths : list
"""
@abstractmethod
def delete(self, path: path_type, recursive: bool = False):
"""
Delete the indicated file or directory
Parameters
----------
path : str
recursive : bool, default False
If True, also delete child paths for directories
"""
def disk_usage(self, path: path_type) -> int:
"""
Compute bytes used by all contents under indicated path in file tree
Parameters
----------
path : string
Can be a file path or directory
Returns
-------
usage : int
"""
path = stringify_path(path)
path_info = self.stat(path)
if path_info["type"] == "file":
return path_info["size"]
total = 0
for root, directories, files in self.walk(path):
for child_path in files:
abspath = self.path_join(root, child_path)
total += self.stat(abspath)["size"]
return total
def path_join(self, *args):
return self.pathsep.join(args)
def path_split(self, path):
"""
Split a pathname. Returns tuple "(head, tail)" where "tail" is everything after the final slash. Either part
may be empty.
Parameters
----------
path : string
Can be a file path or directory
Returns
-------
usage : int
"""
splits = path.rsplit(self.pathsep, 1)
if len(splits) == 1:
return "", splits[0]
else:
return splits
@abstractmethod
def stat(self, path: path_type) -> Dict:
"""
Information about a filesystem entry.
Returns
-------
stat : dict
"""
def rm(self, path: path_type, recursive: bool = False):
"""
Alias for FileSystem.delete
"""
return self.delete(path, recursive=recursive)
def mv(self, path, new_path):
"""
Alias for FileSystem.rename
"""
return self.rename(path, new_path)
@abstractmethod
def rename(self, path: path_type, new_path: path_type):
"""
Rename file, like UNIX mv command
Parameters
----------
path : string
Path to alter
new_path : string
Path to move to
"""
@abstractmethod
def mkdir(self, path: path_type, create_parents: bool = True):
"""
Create a directory.
Parameters
----------
path : str
Path to the directory.
create_parents : bool, default True
If the parent directories don't exists create them as well.
"""
@abstractmethod
def exists(self, path: path_type):
"""
Return True if path exists.
Parameters
----------
path : str
Path to check.
"""
@abstractmethod
def isdir(self, path: path_type) -> bool:
"""
Return True if path is a directory.
Parameters
----------
path : str
Path to check.
"""
@abstractmethod
def isfile(self, path: path_type) -> bool:
"""
Return True if path is a file.
Parameters
----------
path : str
Path to check.
"""
@abstractmethod
def _isfilestore(self) -> bool:
"""
Returns True if this FileSystem is a unix-style file store with
directories.
"""
@abstractmethod
def open(self, path: path_type, mode: str = "rb") -> Union[BinaryIO, TextIO]:
"""
Open file for reading or writing.
"""
@abstractmethod
def walk(self, path: path_type) -> Iterator[Tuple[str, List[str], List[str]]]:
"""
Directory tree generator.
Parameters
----------
path : str
Returns
-------
generator
"""
@abstractmethod
def glob(self, path: path_type, recursive: bool = False) -> List[path_type]:
"""
Return a list of paths matching a pathname pattern.
Parameters
----------
path : str
Pattern may contain simple shell-style wildcards
recursive : bool
If recursive is true, the pattern '**' will match any files and
zero or more directories and subdirectories.
Returns
-------
paths : List
"""
@property
def pathsep(self) -> str:
return "/"
@staticmethod
def parse_from_path(uri: str):
parsed_uri = urlparse(uri)
options = dict()
options["host"] = parsed_uri.netloc.rsplit("@", 1)[-1].rsplit(":", 1)[0]
if parsed_uri.port:
options["port"] = parsed_uri.port
if parsed_uri.username:
options["user"] = parsed_uri.username
if parsed_uri.password:
options["password"] = parsed_uri.password
return options
@classmethod
def get_storage_options(cls, storage_options: Dict, uri: str) -> Dict:
options = cls.parse_from_path(uri)
storage_options.update(options)
return storage_options