Skip to content

Commit

Permalink
Add better documentation to writer base class
Browse files Browse the repository at this point in the history
  • Loading branch information
djhoese committed Oct 29, 2018
1 parent fd2f28b commit 6947c3d
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 23 deletions.
3 changes: 3 additions & 0 deletions doc/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ def __getattr__(cls, name):
sys.modules[mod_name] = Mock()

autodoc_mock_imports = ['h5netcdf', 'pyninjotiff', 'pygac', 'cf', 'glymur', 'pyhdf', 'osgeo', 'mipp']
autoclass_content = 'both' # append class __init__ docstring to the class docstring

# -- General configuration -----------------------------------------------------

Expand Down Expand Up @@ -243,4 +244,6 @@ def __getattr__(cls, name):
'xarray': ('https://xarray.pydata.org/en/stable', None),
'dask': ('https://dask.pydata.org/en/latest', None),
'pyresample': ('https://pyresample.readthedocs.io/en/stable', None),
'trollsift': ('https://trollsift.readthedocs.io/en/stable', None),
'trollimage': ('https://trollimage.readthedocs.io/en/stable', None),
}
34 changes: 18 additions & 16 deletions satpy/plugin_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,38 +23,39 @@
"""

import logging
import os
import yaml

from satpy.config import config_search_paths, get_environ_config_dir, recursive_dict_update

try:
import configparser
except ImportError:
from six.moves import configparser

LOG = logging.getLogger(__name__)


class Plugin(object):
"""Base plugin class for all dynamically loaded and configured objects."""

def __init__(self, ppp_config_dir=None, default_config_filename=None, config_files=None, **kwargs):
"""Load configuration files related to this plugin.
This initializes a `self.config` dictionary that can be used to customize the subclass.
"""The base plugin class. It is not to be used as is, it has to be
inherited by other classes.
"""
Args:
ppp_config_dir (str): Base "etc" directory for all configuration
files.
default_config_filename (str): Configuration filename to use if
no other files have been specified with `config_files`.
config_files (list or str): Configuration files to load instead
of those automatically found in `ppp_config_dir` and other
default configuration locations.
kwargs (dict): Unused keyword arguments.
def __init__(self,
ppp_config_dir=None,
default_config_filename=None,
config_files=None,
**kwargs):
"""
self.ppp_config_dir = ppp_config_dir or get_environ_config_dir()

self.default_config_filename = default_config_filename
self.config_files = config_files
if self.config_files is None and self.default_config_filename is not None:
# Specify a default
self.config_files = config_search_paths(
self.default_config_filename, self.ppp_config_dir)
self.config_files = config_search_paths(self.default_config_filename, self.ppp_config_dir)
if not isinstance(self.config_files, (list, tuple)):
self.config_files = [self.config_files]

Expand All @@ -64,5 +65,6 @@ def __init__(self,
self.load_yaml_config(config_file)

def load_yaml_config(self, conf):
"""Load a YAML configuration file and recursively update the overall configuration."""
with open(conf) as fd:
self.config = recursive_dict_update(self.config, yaml.load(fd))
58 changes: 51 additions & 7 deletions satpy/writers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -432,12 +432,36 @@ def compute_writer_results(results):


class Writer(Plugin):
"""Base Writer class for all other writers.
"""Writer plugins. They must implement the *save_image* method. This is an
abstract class to be inherited.
A minimal writer subclass should implement the `save_dataset` method.
"""

def __init__(self, name=None, filename=None, base_dir=None, **kwargs):
"""Initialize the writer object.
Args:
name (str): A name for this writer for log and error messages.
If this writer is configured in a YAML file its name should
match the name of the YAML file. Writer names may also appear
in output file attributes.
filename (str): Filename to save data to. This filename can and
should specify certain python string formatting fields to
differentiate between data written to the files. Any
attributes provided by the ``.attrs`` of a DataArray object
may be included. Format and conversion specifiers provided by
the :class:`trollsift <trollsift.parser.StringFormatter>`
package may also be used. Any directories in the provided
pattern will be created if they do not exist. Example::
{platform_name}_{sensor}_{name}_{start_time:%Y%m%d_%H%M%S.tif
base_dir (str):
Base destination directories for all created files.
kwargs (dict): Additional keyword arguments to pass to the
:class:`~satpy.plugin_base.Plugin` class.
"""
# Load the config
Plugin.__init__(self, **kwargs)
self.info = self.config['writer']
Expand All @@ -452,10 +476,8 @@ def __init__(self, name=None, filename=None, base_dir=None, **kwargs):
filename = kwargs.pop('file_pattern')

# Use options from the config file if they weren't passed as arguments
self.name = self.info.get("name",
None) if name is None else name
self.file_pattern = self.info.get(
"filename", None) if filename is None else filename
self.name = self.info.get("name", None) if name is None else name
self.file_pattern = self.info.get("filename", None) if filename is None else filename

if self.name is None:
raise ValueError("Writer 'name' not provided")
Expand All @@ -464,6 +486,20 @@ def __init__(self, name=None, filename=None, base_dir=None, **kwargs):

@classmethod
def separate_init_kwargs(cls, kwargs):
"""Helper class method to separate arguments between init and save methods.
Currently the :class:`~satpy.scene.Scene` is passed one set of
arguments to represent the Writer creation and saving steps. This is
not preferred for Writer structure, but provides a simpler interface
to users. This method splits the provided keyword arguments between
those needed for initialization and those needed for the ``save_dataset``
and ``save_datasets`` method calls.
Writer subclasses should try to prefer keyword arguments only for the
save methods only and leave the init keyword arguments to the base
classes when possible.
"""
# FUTURE: Don't pass Scene.save_datasets kwargs to init and here
init_kwargs = {}
kwargs = kwargs.copy()
Expand All @@ -473,6 +509,7 @@ def separate_init_kwargs(cls, kwargs):
return init_kwargs, kwargs

def create_filename_parser(self, base_dir):
"""Create a :class:`trollsift.parser.Parser` object for later use."""
# just in case a writer needs more complex file patterns
# Set a way to create filenames if we were given a pattern
if base_dir and self.file_pattern:
Expand All @@ -482,11 +519,18 @@ def create_filename_parser(self, base_dir):
return parser.Parser(file_pattern) if file_pattern else None

def get_filename(self, **kwargs):
"""Create a filename where output data will be saved.
Args:
kwargs (dict): Attributes and other metadata to use for formatting
the previously provided `filename`.
"""
if self.filename_parser is None:
raise RuntimeError("No filename pattern or specific filename provided")
output_filename = self.filename_parser.compose(kwargs)
dirname = os.path.dirname(output_filename)
if not os.path.isdir(dirname):
if dirname and not os.path.isdir(dirname):
LOG.info("Creating output directory: {}".format(dirname))
os.makedirs(dirname)
return output_filename
Expand Down
2 changes: 2 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@
'mitiff': ['libtiff'],
# MultiScene:
'animations': ['imageio'],
# Documentation:
'doc': ['sphinx'],
}
all_extras = []
for extra_deps in extras_require.values():
Expand Down

0 comments on commit 6947c3d

Please sign in to comment.