From a957e1dbaaa0fb904af852a6fd7ae0fd08c3e999 Mon Sep 17 00:00:00 2001
From: Philipp Rudiger
Date: Tue, 14 Feb 2017 16:03:39 +0000
Subject: [PATCH] Factored Layout/Overlay path finding code out into utilities
---
holoviews/core/layout.py | 52 ++++++++++++++--------------------------
holoviews/core/util.py | 37 +++++++++++++++++++++++++++-
2 files changed, 54 insertions(+), 35 deletions(-)
diff --git a/holoviews/core/layout.py b/holoviews/core/layout.py
index d88efcdc2a..568f3774f8 100644
--- a/holoviews/core/layout.py
+++ b/holoviews/core/layout.py
@@ -16,8 +16,7 @@
from .dimension import Dimension, Dimensioned, ViewableElement
from .ndmapping import OrderedDict, NdMapping, UniformNdMapping
from .tree import AttrTree
-from .util import (int_to_roman, sanitize_identifier, group_sanitizer,
- label_sanitizer, unique_array)
+from .util import (unique_array, get_path, make_path_unique)
from . import traversal
@@ -330,31 +329,7 @@ def collate(cls, data, kdims=None, key_dimensions=None):
@classmethod
- def _get_path(cls, item):
- sanitizers = [group_sanitizer, label_sanitizer]
- capitalize = lambda x: x[0].upper() + x[1:]
- if isinstance(item, tuple):
- path, item = item
- new_path = cls._get_path(item)
- path = path[:2] if item.label and path[1] == new_path[1] else path[:1]
- else:
- path = (item.group, item.label) if item.label else (item.group,)
- return tuple(capitalize(fn(p)) for (p, fn) in zip(path, sanitizers))
-
-
- @classmethod
- def new_path(cls, path, paths, counts):
- while path in paths:
- count = counts[path]
- counts[path] += 1
- path = path + (int_to_roman(count),)
- if len(path) == 1:
- path = path + (int_to_roman(counts.get(path, 1)),)
- return path
-
-
- @classmethod
- def _unpack_paths(cls, objs, paths, items, counts):
+ def _unpack_paths(cls, objs, items, counts):
"""
Recursively unpacks lists and Layout-like objects, accumulating
into the supplied list of items.
@@ -364,16 +339,19 @@ def _unpack_paths(cls, objs, paths, items, counts):
for item in objs:
path, obj = item if isinstance(item, tuple) else (None, item)
if type(obj) is cls:
- cls._unpack_paths(obj, paths, items, counts)
+ cls._unpack_paths(obj, items, counts)
continue
- path = cls._get_path(item)
- new_path = cls.new_path(path, paths, counts)
- paths.append(new_path)
+ path = get_path(item)
+ new_path = make_path_unique(path, counts)
items.append((new_path, obj))
@classmethod
def _initial_paths(cls, items, paths=None):
+ """
+ Recurses the passed items finding paths for each. Useful for
+ determining which paths are not unique and have to be resolved.
+ """
if paths is None:
paths = []
for item in items:
@@ -381,21 +359,27 @@ def _initial_paths(cls, items, paths=None):
if type(item) is cls:
cls._initial_paths(item.items(), paths)
continue
- paths.append(cls._get_path(item))
+ paths.append(get_path(item))
return paths
@classmethod
def _process_items(cls, vals):
+ """
+ Processes a list of Labelled types unpacking any objects of
+ the same type (e.g. a Layout) and finding unique paths for
+ all the items in the list.
+ """
if type(vals) is cls:
return vals.data
elif not isinstance(vals, (list, tuple)):
vals = [vals]
paths = cls._initial_paths(vals)
path_counter = Counter(paths)
- paths, items = [k for k, v in path_counter.items() if v > 1], []
+ items = []
counts = defaultdict(lambda: 1)
- cls._unpack_paths(vals, paths, items, counts)
+ counts.update({k: 1 for k, v in path_counter.items() if v > 1})
+ cls._unpack_paths(vals, items, counts)
return items
diff --git a/holoviews/core/util.py b/holoviews/core/util.py
index 1800d45811..5e83b0f8ae 100644
--- a/holoviews/core/util.py
+++ b/holoviews/core/util.py
@@ -4,7 +4,7 @@
import string, fnmatch
import unicodedata
import datetime as dt
-from collections import defaultdict
+from collections import defaultdict, Counter
import numpy as np
import param
@@ -1002,6 +1002,41 @@ def unpack_group(group, getter):
yield (wrap_tuple(key), obj)
+def capitalize(string):
+ """
+ Capitalizes the first letter of a string.
+ """
+ return string[0].upper() + string[1:]
+
+
+def get_path(item):
+ """
+ Gets a path from an Labelled object or from a tuple of an existing
+ path and a labelled object. The path strings are sanitized and
+ capitalized.
+ """
+ sanitizers = [group_sanitizer, label_sanitizer]
+ if isinstance(item, tuple):
+ path, item = item
+ new_path = get_path(item)
+ path = path[:2] if item.label and path[1] == new_path[1] else path[:1]
+ else:
+ path = (item.group, item.label) if item.label else (item.group,)
+ return tuple(capitalize(fn(p)) for (p, fn) in zip(path, sanitizers))
+
+
+def make_path_unique(path, counts):
+ """
+ Given a path, a list of existing paths and counts for each of the
+ existing paths.
+ """
+ while path in counts:
+ count = counts[path]
+ counts[path] += 1
+ path = path + (int_to_roman(count),)
+ if len(path) == 1:
+ path = path + (int_to_roman(counts.get(path, 1)),)
+ return path
class ndmapping_groupby(param.ParameterizedFunction):