Skip to content

Commit

Permalink
Improved docstrings and imports for PR #7 (issue #6).
Browse files Browse the repository at this point in the history
  • Loading branch information
johannesbulin committed Jan 10, 2025
1 parent 77b77d3 commit 65dedfc
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 160 deletions.
8 changes: 4 additions & 4 deletions ifsbench/data/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# granted to it by virtue of its status as an intergovernmental organisation
# nor does it submit to any jurisdiction.

from .datahandler import * # noqa
from .extracthandler import * # noqa
from .namelisthandler import * # noqa
from .renamehandler import * # noqa
from ifsbench.data.datahandler import * # noqa
from ifsbench.data.extracthandler import * # noqa
from ifsbench.data.namelisthandler import * # noqa
from ifsbench.data.renamehandler import * # noqa
25 changes: 5 additions & 20 deletions ifsbench/data/datahandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@

class DataHandler(ABC):
"""
Base class for data pipeline steps. Each DataHandler object describes one
step in the data pipeline. Multiple DataHandler objects can be executed
sequentially to perform specific data setup tasks.
Base class for data pipeline steps.
Each DataHandler object describes one step in the data pipeline. Multiple
DataHandler objects can be executed sequentially to perform specific data
setup tasks.
"""


Expand All @@ -30,20 +32,3 @@ def execute(self, wdir, **kwargs):
unless absolute paths are given.
"""
return NotImplemented

# @abstractmethod
# def to_dict(self):
# """
# Convert this object to a dictionary. It's class name and module name
# should be given as the "class" and "module" entry, respectively.
# """
# return NotImplemented

# @classmethod
# @abstractmethod
# def from_dict(cls, data):
# """
# Convert a dictionary to an object of this type. This is the inverse
# function to "to_dict".
# """
# return NotImplemented
31 changes: 14 additions & 17 deletions ifsbench/data/extracthandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,33 +8,30 @@
import pathlib
import shutil

from .datahandler import DataHandler
from ..logging import debug
from ifsbench.data.datahandler import DataHandler
from ifsbench.logging import debug

__all__ = ['ExtractHandler']


class ExtractHandler(DataHandler):
"""
DataHandler that extracts a given archive to a specific directory.
Parameters
----------
archive_path: str or :any:`pathlib.Path`
The path to the archive that will be extracted. If a relative path
is given, this will be relative to the ``wdir`` argument in
:meth:`execute`.
target_dir: str, :any:`pathlib.Path` or None
The directory where the archive will be unpacked. If a relative path
is given, this will be relative to the ``wdir`` argument in
:meth:`execute`.
"""

def __init__(self, archive_path, target_dir=None):
"""
Initialise the handler.
Parameters
----------
archive_path: str or `pathlib.Path`
The path to the archive that will be extracted. If a relative path
is given, this will be relative to the `wdir` argument in `execute`.
target_dir: str, `pathlib.Path` or `None`
The directory to where the archive will be unpacked. If a relative path
is given, this will be relative to the `wdir` argument in `execute`.
If None is given, this will re extracted to `wdir`.
"""

self._archive_path = pathlib.Path(archive_path)
if target_dir is None:
self._target_dir = None
Expand Down
91 changes: 44 additions & 47 deletions ifsbench/data/namelisthandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,41 +10,40 @@

import f90nml

from .datahandler import DataHandler
from ..logging import debug, info
from ifsbench.data.datahandler import DataHandler
from ifsbench.logging import debug, info


__all__ = ['NamelistOverride', 'NamelistHandler']
__all__ = ['NamelistOverride', 'NamelistHandler', 'NamelistOperation']

class NamelistOperation(Enum):
SET = auto()
APPEND = auto()
DELETE = auto()

class NamelistOverride:
"""
Specify changes that will be applied to a namelist.
Parameters
----------
key: str or iterable of str
The namelist entry that will be modified. Can be either a string
where '/' separates the namelist name and the entry key or an iterable
of strings of length two.
mode: NamelistOperation
What kind of operation is specified. Can be
* Set a certain entry.
* Append to an array entry.
* Delete an entry.
value: str or None
The value that is set (SET operation) or appended (APPEND).
"""
class NamelistOperation(Enum):
SET = auto()
APPEND = auto()
DELETE = auto()

def __init__(self, key, mode, value=None):
"""
Parameters
----------
key: str or iterable of str
The namelist entry that will be modified. Can be either a string
where '/' separates the namelist name and the entry key or an iterable
of strings of length two.
mode: NamelistOverride.NamelistOperation
What kind of operation is specified. Can be
* Set a certain entry.
* Append to an array entry.
* Delete an entry.
value:
The value that is set (SET operation) or appended (APPEND).
"""

def __init__(self, key, mode, value=None):
if isinstance(key, str):
self._keys = key.split('/')
else:
Expand All @@ -57,7 +56,7 @@ def __init__(self, key, mode, value=None):
self._value = value

if self._value is None:
if self._mode in (self.NamelistOperation.SET, self.NamelistOperation.APPEND):
if self._mode in (NamelistOperation.SET, NamelistOperation.APPEND):
raise ValueError("The new value must not be None!")

def apply(self, namelist):
Expand All @@ -66,23 +65,23 @@ def apply(self, namelist):
Parameters
----------
namelist: f90nml.Namelist
namelist: :any:`f90nml.Namelist`
The namelist to which the changes are applied.
"""

if self._keys[0] not in namelist:
if self._mode == self.NamelistOperation.DELETE:
if self._mode == NamelistOperation.DELETE:
return

namelist[self._keys[0]] = {}

namelist = namelist[self._keys[0]]
key = self._keys[-1]

if self._mode == self.NamelistOperation.SET:
if self._mode == NamelistOperation.SET:
debug(f"Set namelist entry {str(self._keys)} = {str(self._value)}.")
namelist[key] = self._value
elif self._mode == self.NamelistOperation.APPEND:
elif self._mode == NamelistOperation.APPEND:
if key not in namelist:
namelist[key] = []

Expand All @@ -105,34 +104,32 @@ def apply(self, namelist):

namelist[key].append(self._value)

elif self._mode == self.NamelistOperation.DELETE:
elif self._mode == NamelistOperation.DELETE:
if key in namelist:
debug(f"Delete namelist entry {str(self._keys)}.")
del namelist[key]

class NamelistHandler(DataHandler):
"""
DataHandler specialisation that can modify Fortran namelists.
"""
def __init__(self, input_path, output_path, overrides):
"""
Initialise the handler.
Parameters
----------
input_path: str or :any:`pathlib.Path`
The path to the namelist that will be modified. If a relative path
is given, this will be relative to the ``wdir`` argument in
:meth:`execute`.
Parameters
----------
input_path: str or `pathlib.Path`
The path to the namelist that will be modified. If a relative path
is given, this will be relative to the `wdir` argument in `execute`.
output_path: str or :any:`pathlib.Path`
The path to which the updated namelist will be written. If a relative
path is given, this will be relative to the ``wdir`` argument in
:meth:`execute`.
output_path: str, `pathlib.Path` or `None`
The path to which the updated namelist will be written. If a relative path
is given, this will be relative to the `wdir` argument in `execute`.
If None is given, this will re extracted to `wdir`.
overrides: iterable of :class:`NamelistOverride`
The NamelistOverrides that will be applied.
"""

overrides: iterable of NamelistOverride
The NamelistOverrides that will be applied.
"""
def __init__(self, input_path, output_path, overrides):

self._input_path = pathlib.Path(input_path)
self._output_path = pathlib.Path(output_path)
Expand Down
80 changes: 38 additions & 42 deletions ifsbench/data/renamehandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,54 +10,50 @@
import re
import shutil

from .datahandler import DataHandler
from ..logging import debug
from ifsbench.data.datahandler import DataHandler
from ifsbench.logging import debug

__all__ = ['RenameHandler']
__all__ = ['RenameHandler', 'RenameMode']

class RenameHandler(DataHandler):

class RenameMode(Enum):
"""
DataHandler specialisation that can move/rename files by using regular
expressions (as in re.sub).
Enumeration of available rename operations.
Attributes
----------
COPY :
Copy the file from its current place to the new location.
SYMLINK :
Create a symlink in the new location, pointing to its current
location.
MOVE :
Move the file from its current place to the new location.
"""
COPY = auto()
SYMLINK = auto()
MOVE = auto()

class RenameMode(Enum):
"""
Enumeration of available rename operations.
Attributes
----------
COPY :
Copy the file from its current place to the new location.
SYMLINK :
Create a symlink in the new location, pointing to its current
location.
MOVE :
Move the file from its current place to the new location.
"""
COPY = auto()
SYMLINK = auto()
MOVE = auto()

def __init__(self, pattern, repl, mode=RenameMode.SYMLINK):
"""
Initialise the handler.
class RenameHandler(DataHandler):
"""
DataHandler specialisation that can move/rename files by using regular
expressions (as in re.sub).
Parameters
----------
pattern: str
The pattern that will be replaced. Corresponds to `pattern` in
`re.sub`.
Parameters
----------
pattern: str
The pattern that will be replaced. Corresponds to ``pattern`` in
:any:`re.sub`.
repl: str
The replacement pattern. Corresponds to `repl` in `re.sub`.
repl: str
The replacement pattern. Corresponds to ``repl`` in :any:`re.sub`.
mode: `RenameHandler.RenameMode`
Specifies how the renaming is done (copy, move, symlink).
mode: :class:`RenameMode`
Specifies how the renaming is done (copy, move, symlink).
"""

mode: `RenameHandler.RenameMode`
Specifies how the renaming is done (copy, move, symlink).
"""
def __init__(self, pattern, repl, mode=RenameMode.SYMLINK):
self._pattern = str(pattern)
self._repl = str(repl)
self._mode = mode
Expand All @@ -68,7 +64,7 @@ def execute(self, wdir, **kwargs):
# modified.
path_mapping = {}

for f in list(wdir.rglob('*')):
for f in wdir.rglob('*'):
if f.is_dir():
continue

Expand Down Expand Up @@ -100,15 +96,15 @@ def execute(self, wdir, **kwargs):

dest.parent.mkdir(parents=True, exist_ok=True)

if self._mode == self.RenameMode.COPY:
if self._mode == RenameMode.COPY:
debug(f"Copy {source} to {dest}.")

shutil.copy(source, dest)
elif self._mode == self.RenameMode.SYMLINK:
elif self._mode == RenameMode.SYMLINK:
debug(f"Symlink {source} to {dest}.")

dest.symlink_to(source)
elif self._mode == self.RenameMode.MOVE:
elif self._mode == RenameMode.MOVE:
debug(f"Move {source} to {dest}.")

source.rename(dest)
Loading

0 comments on commit 65dedfc

Please sign in to comment.