Source code for satpy.node

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (c) 2016-2019 Satpy developers
#
# This file is part of satpy.
#
# satpy is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# satpy is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# satpy.  If not, see <http://www.gnu.org/licenses/>.
"""Nodes to build trees."""

from satpy.utils import get_logger

LOG = get_logger(__name__)
# Empty leaf used for marking composites with no prerequisites
EMPTY_LEAF_NAME = "__EMPTY_LEAF_SENTINEL__"


[docs] class MissingDependencies(RuntimeError): """Exception when dependencies are missing.""" def __init__(self, missing_dependencies, *args, **kwargs): """Set up the exception.""" super().__init__(*args, **kwargs) self.missing_dependencies = missing_dependencies def __str__(self): """Return the string representation of the exception.""" prefix = super().__str__() unknown_str = ", ".join(map(str, self.missing_dependencies)) return "{} {}".format(prefix, unknown_str)
[docs] class Node: """A node object.""" def __init__(self, name, data=None): """Init the node object.""" self.name = name self.data = data self.children = [] self.parents = []
[docs] def update_name(self, new_name): """Update 'name' property.""" self.name = new_name
@property def is_leaf(self): """Check if the node is a leaf.""" return not self.children
[docs] def flatten(self, d=None): """Flatten tree structure to a one level dictionary. Args: d (dict, optional): output dictionary to update Returns: dict: Node.name -> Node. The returned dictionary includes the current Node and all its children. """ if d is None: d = {} if self.name is not None: d[self.name] = self for child in self.children: child.flatten(d=d) return d
[docs] def copy(self, node_cache=None): """Make a copy of the node.""" if node_cache and self.name in node_cache: return node_cache[self.name] if self.name is EMPTY_LEAF_NAME: return self s = self._copy_name_and_data(node_cache) for c in self.children: c = c.copy(node_cache=node_cache) s.add_child(c) if node_cache is not None: node_cache[s.name] = s return s
[docs] def _copy_name_and_data(self, node_cache=None): return Node(self.name, self.data)
[docs] def add_child(self, obj): """Add a child to the node.""" self.children.append(obj) obj.parents.append(self)
def __str__(self): """Display the node.""" return self.display() def __repr__(self): """Generate a representation of the node.""" return "<{} ({})>".format(self.__class__.__name__, repr(self.name)) def __eq__(self, other): """Check equality.""" return self.name == other.name def __hash__(self): """Generate the hash of the node.""" return hash(self.name)
[docs] def display(self, previous=0, include_data=False): """Display the node.""" no_data = " (No Data)" if self.data is None else "" return ( (" +" * previous) + str(self.name) + no_data + "\n" + "".join([child.display(previous + 1) for child in self.children]))
[docs] def leaves(self, unique=True): """Get the leaves of the tree starting at this root.""" if self.name is EMPTY_LEAF_NAME: return [] elif not self.children: return [self] res = list() for child in self.children: for sub_child in child.leaves(unique=unique): if not unique or sub_child not in res: res.append(sub_child) return res
[docs] def trunk(self, unique=True, limit_children_to=None): """Get the trunk of the tree starting at this root.""" # FIXME: uniqueness is not correct in `trunk` yet unique = False res = [] if self.children and self.name is not EMPTY_LEAF_NAME: if self.name is not None: res.append(self) if limit_children_to is not None and self.name in limit_children_to: return res for child in self.children: for sub_child in child.trunk(unique=unique, limit_children_to=limit_children_to): if not unique or sub_child not in res: res.append(sub_child) return res
[docs] class CompositorNode(Node): """Implementation of a compositor-specific node.""" def __init__(self, compositor): """Set up the node.""" super().__init__(compositor.id, data=(compositor, [], []))
[docs] def add_required_nodes(self, children): """Add nodes to the required field.""" self.data[1].extend(children)
@property def required_nodes(self): """Get the required nodes.""" return self.data[1]
[docs] def add_optional_nodes(self, children): """Add nodes to the optional field.""" self.data[2].extend(children)
@property def optional_nodes(self): """Get the optional nodes.""" return self.data[2] @property def compositor(self): """Get the compositor.""" return self.data[0]
[docs] def _copy_name_and_data(self, node_cache=None): new_node = CompositorNode(self.compositor) new_required_nodes = [node.copy(node_cache) for node in self.required_nodes] new_node.add_required_nodes(new_required_nodes) new_optional_nodes = [node.copy(node_cache) for node in self.optional_nodes] new_node.add_optional_nodes(new_optional_nodes) # `comp.id` uses the compositor's attributes to compute itself # however, this node may have been updated by creation of the # composite. In order to not modify the compositor's attrs, we # overwrite the name here instead. new_node.name = self.name return new_node
[docs] class ReaderNode(Node): """Implementation of a storage-based node.""" def __init__(self, unique_id, reader_name): """Set up the node.""" super().__init__(unique_id, data={"reader_name": reader_name})
[docs] def _copy_name_and_data(self, node_cache): return ReaderNode(self.name, self.data["reader_name"])
@property def reader_name(self): """Get the name of the reader.""" return self.data["reader_name"]