def from_image(cls, filename, components,
                   ignore=None,
                   col_offset=0.1,
                   row_offset=2):
        """
        A slightly easier way to make legends from images.

        Args:
            filename (str)
            components (list)
            ignore (list): Colours to ignore, e.g. "#FFFFFF" to ignore white.
            col_offset (Number): If < 1, interpreted as proportion of way
                across the image. If > 1, interpreted as pixels from left.
            row_offset (int): Number of pixels to skip at the top of each
                interval.
        """
        if ignore is None:
            ignore = []

        rgb = utils.loglike_from_image(filename, offset=col_offset)
        loglike = np.array([utils.rgb_to_hex(t) for t in rgb])

        # Get the pixels and colour values at 'tops' (i.e. changes).
        _, hexes = utils.tops_from_loglike(loglike, offset=row_offset)

        # Reduce to unique colours.
        hexes_reduced = []
        for h in hexes:
            if h not in hexes_reduced:
                if h not in ignore:
                    hexes_reduced.append(h)

        list_of_Decors = []
        for i, c in enumerate(components):
            d = Decor({'colour': hexes_reduced[i], 'component': c})
            list_of_Decors.append(d)

        return cls(list_of_Decors)

A slightly easier way to make legends from images .


def from_csv(cls, filename=None, text=None):
        """
        Read CSV text and generate a Legend.

        Args:
            string (str): The CSV string.

        In the first row, list the properties. Precede the properties of the
        component with 'comp ' or 'component '. For example:

        colour,  width, comp lithology, comp colour
        #FFFFFF, 0, ,
        #F7E9A6, 3, Sandstone, Grey
        #FF99CC, 2, Anhydrite,
        ... etc

        Note:
            To edit a legend, the easiest thing to do is probably this:

            - `legend.to_csv()`
            - Edit the legend, call it `new_legend`.
            - `legend = Legend.from_csv(text=new_legend)`
        """
        if (filename is None) and (text is None):
            raise LegendError("You must provide a filename or CSV text.")

        if (filename is not None):
            with open(filename, 'r') as f:
                text = f.read()

        try:
            f = StringIO(text)  # Python 3
        except TypeError:
            f = StringIO(unicode(text))  # Python 2

        r = csv.DictReader(f, skipinitialspace=True)
        list_of_Decors, components = [], []
        kind = 'component'
        for row in r:
            d, component = {}, {}
            for (k, v) in row.items():
                if (k in [None, '']):
                    continue
                if (v in [None, '']):
                    if k.lower() not in ['color', 'colour']:
                        continue
                if k[:4].lower() == 'comp':
                    prop = ' '.join(k.split()[1:])
                    if v.lower() == 'true':
                        component[prop] = True
                    elif v.lower() == 'false':
                        component[prop] = False
                    else:
                        try:
                            component[prop] = float(v)
                        except ValueError:
                            component[prop] = v.lower()

                elif k[:5].lower() == 'curve':
                    prop = ' '.join(k.split()[1:])
                    component[prop] = v.lower()
                    kind = 'curve'
                else:
                    try:
                        d[k] = float(v)
                    except ValueError:
                        d[k] = v.lower()

            this_component = Component(component)
            d[kind] = this_component

            # Check for duplicates and warn.
            if this_component in components:
                with warnings.catch_warnings():
                    warnings.simplefilter("always")
                    w = "This legend contains duplicate components."
                    warnings.warn(w)
            components.append(this_component)

            # Append to the master list and continue.
            list_of_Decors.append(Decor(d))

        return cls(list_of_Decors)


def to_csv(self):
        """
        Renders a legend as a CSV string.

        No arguments.

        Returns:
            str: The legend as a CSV.
        """
        # We can't delegate this to Decor because we need to know the superset
        # of all Decor properties. There may be lots of blanks.
        header = []
        component_header = []
        for row in self:
            for j in row.__dict__.keys():
                if j == '_colour':
                    j = 'colour'
                header.append(j)
            for k in row.component.__dict__.keys():
                component_header.append(k)
        header = set(header)
        component_header = set(component_header)
        header.remove('component')
        header_row = ''
        if 'colour' in header:
            header_row += 'colour,'
            header.remove('colour')
            has_colour = True
        for item in header:
            header_row += item + ','
        for item in component_header:
            header_row += 'component ' + item + ','

        # Now we have a header row! Phew.
        # Next we'll go back over the legend and collect everything.
        result = header_row.strip(',') + '\n'
        for row in self:
            if has_colour:
                result += row.__dict__.get('_colour', '') + ','
            for item in header:
                result += str(row.__dict__.get(item, '')) + ','
            for item in component_header:
                result += str(row.component.__dict__.get(item, '')) + ','
            result += '\n'

        return result


def max_width(self):
        """
        The maximum width of all the Decors in the Legend. This is needed
        to scale a Legend or Striplog when plotting with widths turned on.
        """
        try:
            maximum = max([row.width for row in self.__list if row.width is not None])
            return maximum
        except:
            return 0

def max_width ( self ) : try : maximum = max ( [ row . width for row in self . __list if row . width is not None ] ) return maximum except : return 0

The maximum width of all the Decors in the Legend . This is needed to scale a Legend or Striplog when plotting with widths turned on .


def get_decor(self, c, match_only=None):
        """
        Get the decor for a component.

        Args:
           c (component): The component to look up.
           match_only (list of str): The component attributes to include in the
               comparison. Default: All of them.

        Returns:
           Decor. The matching Decor from the Legend, or None if not found.
        """
        if isinstance(c, Component):
            if c:
                if match_only:
                    # Filter the component only those attributes
                    c = Component({k: getattr(c, k, None) for k in match_only})
                for decor in self.__list:
                    try:
                        if c == decor.component:
                            return decor
                    except AttributeError:
                        continue
        else:
            for decor in self.__list:
                try:
                    if getattr(c, 'mnemonic').lower() == decor.curve.mnemonic:
                        return decor
                except AttributeError:
                    continue
        return Decor({'colour': '#eeeeee', 'component': Component()})


def getattr(self, c, attr, default=None, match_only=None):
        """
        Get the attribute of a component.

        Args:
           c (component): The component to look up.
           attr (str): The attribute to get.
           default (str): What to return in the event of no match.
           match_only (list of str): The component attributes to include in the
               comparison. Default: All of them.

        Returns:
           obj. The specified attribute of the matching Decor in the Legend.
        """
        matching_decor = self.get_decor(c, match_only=match_only)

        try:
            return getattr(matching_decor, attr)
        except AttributeError:
            return default

def getattr ( self , c , attr , default = None , match_only = None ) : matching_decor = self . get_decor ( c , match_only = match_only ) try : return getattr ( matching_decor , attr ) except AttributeError : return default


def get_component(self, colour, tolerance=0, default=None):
        """
        Get the component corresponding to a display colour. This is for
        generating a Striplog object from a colour image of a striplog.

        Args:
           colour (str): The hex colour string to look up.
           tolerance (float): The colourspace distance within which to match.
           default (component or None): The component to return in the event
           of no match.

        Returns:
           component. The component best matching the provided colour.
        """
        if not (0 <= tolerance <= np.sqrt(195075)):
            raise LegendError('Tolerance must be between 0 and 441.67')

        for decor in self.__list:
            if colour.lower() == decor.colour:
                return decor.component

        # If we're here, we didn't find one yet.
        r1, g1, b1 = utils.hex_to_rgb(colour)

        # Start with a best match of black.
        best_match = '#000000'
        best_match_dist = np.sqrt(r1**2. + g1**2. + b1**2.)

        # Now compare to each colour in the legend.
        for decor in self.__list:
            r2, g2, b2 = decor.rgb
            distance = np.sqrt((r2-r1)**2. + (g2-g1)**2. + (b2-b1)**2.)
            if distance < best_match_dist:
                best_match = decor.component
                best_match_dist = distance
                best_match_colour = decor.colour

        if best_match_dist <= tolerance:
            return best_match
        else:
            with warnings.catch_warnings():
                warnings.simplefilter("always")
                w = "No match found for {0} ".format(colour.lower())
                w += "with tolerance of {0}. Best match is ".format(tolerance)
                w += "{0}, {1}".format(best_match.summary(), best_match_colour)
                w += ", d={0}".format(best_match_dist)
                warnings.warn(w)

            return default

Get the component corresponding to a display colour . This is for generating a Striplog object from a colour image of a striplog .


def plot(self, fmt=None):
        """
        Make a simple plot of the legend.

        Simply calls Decor.plot() on all of its members.

        TODO: Build a more attractive plot.
        """
        for d in self.__list:
            d.plot(fmt=fmt)

        return None

def plot ( self , fmt = None ) : for d in self . __list : d . plot ( fmt = fmt ) return None


def from_text(cls, text, lexicon, required=None, first_only=True):
        """
        Generate a Component from a text string, using a Lexicon.

        Args:
            text (str): The text string to parse.
            lexicon (Lexicon): The dictionary to use for the
                categories and lexemes.
            required (str): An attribute that we must have. If a required
                attribute is missing from the component, then None is returned.
            first_only (bool): Whether to only take the first
                match of a lexeme against the text string.

        Returns:
            Component: A Component object, or None if there was no
                must-have field.
        """
        component = lexicon.get_component(text, first_only=first_only)
        if required and (required not in component):
            return None
        else:
            return cls(component)

def from_text ( cls , text , lexicon , required = None , first_only = True ) : component = lexicon . get_component ( text , first_only = first_only ) if required and ( required not in component ) : return None else : return cls ( component )

Generate a Component from a text string using a Lexicon .


def summary(self, fmt=None, initial=True, default=''):
        """
        Given a format string, return a summary description of a component.

        Args:
            component (dict): A component dictionary.
            fmt (str): Describes the format with a string. If no format is
                given, you will just get a list of attributes. If you give the
                empty string (''), you'll get `default` back. By default this
                gives you the empty string, effectively suppressing the
                summary.
            initial (bool): Whether to capitialize the first letter. Default is
                True.
            default (str): What to give if there's no component defined.

        Returns:
            str: A summary string.

        Example:

            r = Component({'colour': 'Red',
                           'grainsize': 'VF-F',
                           'lithology': 'Sandstone'})

            r.summary()  -->  'Red, vf-f, sandstone'
        """
        if default and not self.__dict__:
            return default

        if fmt == '':
            return default

        keys = [k for k, v in self.__dict__.items() if v is not '']

        f = fmt or '{' + '}, {'.join(keys) + '}'

        try:
            summary = CustomFormatter().format(f, **self.__dict__)
        except KeyError as e:
            raise ComponentError("Error building summary, "+str(e))

        if summary and initial and not fmt:
            summary = summary[0].upper() + summary[1:]

        return summary

def
summary
(
self
,
fmt
=
,
initial
=
True
,
default
=
''
)
:
if
default
and
not
self
.
__dict__
:
return
default
if
fmt
==
''
:
return
default
keys
=
[
k
for
k
,
v
in
self
.
__dict__
.
items
(
)
if
v
is
not
''
]
f
=
fmt
or
'{'
+
'}, {'
.
join
(
keys
)
+
'}'
try
:
summary
=
CustomFormatter
(
)
.
format
(
f
,
*
*
self
.
__dict__
)
except
KeyError
as
e
:
raise
ComponentError
(
"Error building summary, "
+
str
(
e
)
)
if
summary
and
initial
and
not
fmt
:
summary
=
summary
[
0
]
.
upper
(
)
+
summary
[
1
:
]
return
summary

Given a format string return a summary description of a component .


def Rock(*args, **kwargs):
    """
    Graceful deprecation for old class name.
    """

    with warnings.catch_warnings():
        warnings.simplefilter("always")
        w = "The 'Rock' class was renamed 'Component'. "
        w += "Please update your code."
        warnings.warn(w, DeprecationWarning, stacklevel=2)

    return Component(*args, **kwargs)

Graceful deprecation for old class name .


def _process_row(text, columns):
    """
    Processes a single row from the file.
    """
    if not text:
        return

    # Construct the column dictionary that maps each field to
    # its start, its length, and its read and write functions.
    coldict = {k: {'start': s,
                   'len': l,
                   'read': r,
                   'write': w} for k, (s, l, r, w) in columns.items()}

    # Now collect the item
    item = {}
    for field in coldict:
        value = _get_field(text, coldict, field)
        if value is not None:
            item[field] = value

    return item


def parse_canstrat(text):
    """
    Read all the rows and return a dict of the results.
    """
    result = {}
    for row in text.split('\n'):
        if not row:
            continue

        if len(row) < 8:  # Not a real record.
            continue

        # Read the metadata for this row/
        row_header = _process_row(row, columns_) or {'card': None}
        card = row_header['card']

        # Now we know the card type for this row, we can process it.
        if card is not None:
            item = _process_row(row, columns[card])

        this_list = result.get(card, [])
        this_list.append(item)
        result[card] = this_list

    # Flatten if possible.
    for c, d in result.items():
        if len(d) == 1:
            result[c] = d[0]

    return result

Read all the rows and return a dict of the results .


def __strict(self):
        """
        Private method. Checks if striplog is monotonically increasing in
        depth.

        Returns:
            Bool.
        """
        def conc(a, b):
            return a + b

        # Check boundaries, b
        b = np.array(reduce(conc, [[i.top.z, i.base.z] for i in self]))

        return all(np.diff(b) >= 0)

def
__strict
(
self
)
:
def
conc
(
a
,
b
)
:
return
a
+
b
# Check boundaries, b
b
=
np
.
array
(
reduce
(
conc
,
[
[
i
.
top
.
z
,
i
.
base
.
z
]
for
i
in
self
]
)
)
return
all
(
np
.
diff
(
b
)
>=
0
)

Private method . Checks if striplog is monotonically increasing in depth .


def unique(self):
        """
        Property. Summarize a Striplog with some statistics.

        Returns:
            List. A list of (Component, total thickness thickness) tuples.
        """
        all_rx = set([iv.primary for iv in self])
        table = {r: 0 for r in all_rx}
        for iv in self:
            table[iv.primary] += iv.thickness

        return sorted(table.items(), key=operator.itemgetter(1), reverse=True)

def unique ( self ) : all_rx = set ( [ iv . primary for iv in self ] ) table = { r : 0 for r in all_rx } for iv in self : table [ iv . primary ] += iv . thickness return sorted ( table . items ( ) , key = operator . itemgetter ( 1 ) , reverse = True )

Property . Summarize a Striplog with some statistics .


def __intervals_from_tops(self,
                              tops,
                              values,
                              basis,
                              components,
                              field=None,
                              ignore_nan=True):
        """
        Private method. Take a sequence of tops in an arbitrary dimension,
        and provide a list of intervals from which a striplog can be made.

        This is only intended to be used by ``from_image()``.

        Args:
            tops (iterable). A list of floats.
            values (iterable). A list of values to look up.
            basis (iterable). A list of components.
            components (iterable). A list of Components.

        Returns:
            List. A list of Intervals.
        """
        # Scale tops to actual depths.
        length = float(basis.size)
        start, stop = basis[0], basis[-1]
        tops = [start + (p/(length-1)) * (stop-start) for p in tops]
        bases = tops[1:] + [stop]

        list_of_Intervals = []
        for i, t in enumerate(tops):

            v, c, d = values[i], [], {}

            if ignore_nan and np.isnan(v):
                continue

            if (field is not None):
                d = {field: v}

            if components is not None:
                try:
                    c = [deepcopy(components[int(v)])]
                except IndexError:
                    c = []

                if c and (c[0] is None):
                    c = []

            interval = Interval(t, bases[i], data=d, components=c)
            list_of_Intervals.append(interval)

        return list_of_Intervals

def
__intervals_from_tops
(
self
,
tops
,
values
,
basis
,
components
,
field
=
,
ignore_nan
=
True
)
:
# Scale tops to actual depths.
length
=
float
(
basis
.
size
)
start
,
stop
=
basis
[
0
]
,
basis
[
1
]
tops
=
[
start
+
(
p
/
(
length
1
)
)
*
(
stop
start
)
for
p
in
tops
]
bases
=
tops
[
1
:
]
+
[
stop
]
list_of_Intervals
=
[
]
for
i
,
t
in
enumerate
(
tops
)
:
v
,
c
,
d
=
values
[
i
]
,
[
]
,
{
}
if
ignore_nan
and
np
.
isnan
(
v
)
:
continue
if
(
field
is
not
)
:
d
=
{
field
:
v
}
if
components
is
not
:
try
:
c
=
[
deepcopy
(
components
[
int
(
v
)
]
)
]
except
IndexError
:
c
=
[
]
if
c
and
(
c
[
0
]
is
)
:
c
=
[
]
interval
=
Interval
(
t
,
bases
[
i
]
,
data
=
d
,
components
=
c
)
list_of_Intervals
.
append
(
interval
)
return
list_of_Intervals

Private method . Take a sequence of tops in an arbitrary dimension and provide a list of intervals from which a striplog can be made .


def _clean_longitudinal_data(cls, data, null=None):
        """
        Private function. Make sure we have what we need to make a striplog.
        """

        # Rename 'depth' or 'MD'
        if ('top' not in data.keys()):
            data['top'] = data.pop('depth', data.pop('MD', None))

        # Sort everything
        idx = list(data.keys()).index('top')
        values = sorted(zip(*data.values()), key=lambda x: x[idx])
        data = {k: list(v) for k, v in zip(data.keys(), zip(*values))}

        if data['top'] is None:
            raise StriplogError('Could not get tops.')

        # Get rid of null-like values if specified.
        if null is not None:
            for k, v in data.items():
                data[k] = [i if i != null else None for i in v]

        return data

Private function . Make sure we have what we need to make a striplog .


def from_petrel(cls, filename,
                    stop=None,
                    points=False,
                    null=None,
                    function=None,
                    include=None,
                    exclude=None,
                    remap=None,
                    ignore=None):

        """
        Makes a striplog from a Petrel text file.

        Returns:
            striplog.
        """
        result = utils.read_petrel(filename,
                                   function=function,
                                   remap=remap,
                                   )

        data = cls._clean_longitudinal_data(result,
                                            null=null
                                            )

        list_of_Intervals = cls._build_list_of_Intervals(data,
                                                         stop=stop,
                                                         points=points,
                                                         include=include,
                                                         exclude=exclude,
                                                         ignore=ignore
                                                         )
        if list_of_Intervals:
            return cls(list_of_Intervals)
        return None

def from_petrel ( cls , filename , stop = None , points = False , null = None , function = None , include = None , exclude = None , remap = None , ignore = None ) : result = utils . read_petrel ( filename , function = function , remap = remap , ) data = cls . _clean_longitudinal_data ( result , null = null ) list_of_Intervals = cls . _build_list_of_Intervals ( data , stop = stop , points = points , include = include , exclude = exclude , ignore = ignore ) if list_of_Intervals : return cls ( list_of_Intervals ) return None

Makes a striplog from a Petrel text file .


def _build_list_of_Intervals(cls,
                                 data_dict,
                                 stop=None,
                                 points=False,
                                 include=None,
                                 exclude=None,
                                 ignore=None,
                                 lexicon=None):
        """
        Private function. Takes a data dictionary and reconstructs a list
        of Intervals from it.

        Args:
            data_dict (dict)
            stop (float): Where to end the last interval.
            points (bool)
            include (dict)
            exclude (dict)
            ignore (list)
            lexicon (Lexicon)

        Returns:
            list.
        """

        include = include or {}
        exclude = exclude or {}
        ignore = ignore or []

        # Reassemble as list of dicts
        all_data = []
        for data in zip(*data_dict.values()):
            all_data.append({k: v for k, v in zip(data_dict.keys(), data)})

        # Sort
        all_data = sorted(all_data, key=lambda x: x['top'])

        # Filter down:
        wanted_data = []
        for dictionary in all_data:
            keep = True
            delete = []
            for k, v in dictionary.items():
                incl = include.get(k, utils.null_default(True))
                excl = exclude.get(k, utils.null_default(False))
                if k in ignore:
                    delete.append(k)
                if not incl(v):
                    keep = False
                if excl(v):
                    keep = False
            if delete:
                for key in delete:
                    _ = dictionary.pop(key, None)
            if keep:
                wanted_data.append(dictionary)

        # Fill in
        if not points:
            for i, iv in enumerate(wanted_data):
                if iv.get('base', None) is None:
                    try:  # To set from next interval
                        iv['base'] = wanted_data[i+1]['top']
                    except (IndexError, KeyError):
                        # It's the last interval
                        if stop is not None:
                            thick = stop - iv['top']
                        else:
                            thick = 1
                        iv['base'] = iv['top'] + thick

        # Build the list of intervals to pass to __init__()
        list_of_Intervals = []
        for iv in wanted_data:
            top = iv.pop('top')
            base = iv.pop('base', None)
            descr = iv.pop('description', '')
            if iv:
                c, d = {}, {}
                for k, v in iv.items():
                    if (k[:5].lower() == 'comp ') or (k[:9].lower() == 'component'):
                        k = re.sub(r'comp(?:onent)? ', '', k, flags=re.I)
                        c[k] = v  # It's a component
                    else:
                        if v is not None:
                            d[k] = v  # It's data
                comp = [Component(c)] if c else None
                this = Interval(**{'top': top,
                                   'base': base,
                                   'description': descr,
                                   'data': d,
                                   'components': comp})
            else:
                this = Interval(**{'top': top,
                                   'base': base,
                                   'description': descr,
                                   'lexicon': lexicon})
            list_of_Intervals.append(this)

        return list_of_Intervals

Private function . Takes a data dictionary and reconstructs a list of Intervals from it .


def from_csv(cls, filename=None,
                 text=None,
                 dlm=',',
                 lexicon=None,
                 points=False,
                 include=None,
                 exclude=None,
                 remap=None,
                 function=None,
                 null=None,
                 ignore=None,
                 source=None,
                 stop=None,
                 fieldnames=None):
        """
        Load from a CSV file or text.
        """
        if (filename is None) and (text is None):
            raise StriplogError("You must provide a filename or CSV text.")

        if (filename is not None):
            if source is None:
                source = filename
            with open(filename, 'r') as f:
                text = f.read()

        source = source or 'CSV'

        # Deal with multiple spaces in space delimited file.
        if dlm == ' ':
            text = re.sub(r'[ \t]+', ' ', text)

        if fieldnames is not None:
            text = dlm.join(fieldnames) + '\n' + text

        try:
            f = StringIO(text)  # Python 3
        except TypeError:
            f = StringIO(unicode(text))  # Python 2

        reader = csv.DictReader(f, delimiter=dlm)

        # Reorganize the data to make fixing it easier.
        reorg = {k.strip().lower(): [] for k in reader.fieldnames if k is not None}
        t = f.tell()
        for key in reorg:
            f.seek(t)
            for r in reader:
                s = {k.strip().lower(): v.strip() for k, v in r.items()}
                try:
                    reorg[key].append(float(s[key]))
                except ValueError:
                    reorg[key].append(s[key])

        f.close()

        remap = remap or {}
        for k, v in remap.items():
            reorg[v] = reorg.pop(k)

        data = cls._clean_longitudinal_data(reorg, null=null)

        list_of_Intervals = cls._build_list_of_Intervals(data,
                                                         points=points,
                                                         lexicon=lexicon,
                                                         include=include,
                                                         exclude=exclude,
                                                         ignore=ignore,
                                                         stop=stop)

        return cls(list_of_Intervals, source=source)


def from_image(cls, filename, start, stop, legend,
                   source="Image",
                   col_offset=0.1,
                   row_offset=2,
                   tolerance=0):
        """
        Read an image and generate Striplog.

        Args:
            filename (str): An image file, preferably high-res PNG.
            start (float or int): The depth at the top of the image.
            stop (float or int): The depth at the bottom of the image.
            legend (Legend): A legend to look up the components in.
            source (str): A source for the data. Default: 'Image'.
            col_offset (Number): The proportion of the way across the image
                from which to extract the pixel column. Default: 0.1 (ie 10%).
            row_offset (int): The number of pixels to skip at the top of
                each change in colour. Default: 2.
            tolerance (float): The Euclidean distance between hex colours,
                which has a maximum (black to white) of 441.67 in base 10.
                Default: 0.

        Returns:
            Striplog: The ``striplog`` object.
        """
        rgb = utils.loglike_from_image(filename, col_offset)
        loglike = np.array([utils.rgb_to_hex(t) for t in rgb])

        # Get the pixels and colour values at 'tops' (i.e. changes).
        tops, hexes = utils.tops_from_loglike(loglike, offset=row_offset)

        # If there are consecutive tops, we assume it's because there is a
        # single-pixel row that we don't want. So take the second one only.
        # We used to do this reduction in ``utils.tops_from_loglike()`` but
        # it was prventing us from making intervals only one sample thick.
        nonconsecutive = np.append(np.diff(tops), 2)
        tops = tops[nonconsecutive > 1]
        hexes = hexes[nonconsecutive > 1]

        # Get the set of unique colours.
        hexes_reduced = list(set(hexes))

        # Get the components corresponding to the colours.
        components = [legend.get_component(h, tolerance=tolerance)
                      for h in hexes_reduced]

        # Turn them into integers.
        values = [hexes_reduced.index(i) for i in hexes]

        basis = np.linspace(start, stop, loglike.size)

        list_of_Intervals = cls.__intervals_from_tops(tops,
                                                      values,
                                                      basis,
                                                      components)

        return cls(list_of_Intervals, source="Image")


def from_log(cls, log,
                 cutoff=None,
                 components=None,
                 legend=None,
                 legend_field=None,
                 field=None,
                 right=False,
                 basis=None,
                 source='Log'):
        """
        Turn a 1D array into a striplog, given a cutoff.

        Args:
            log (array-like): A 1D array or a list of integers.
            cutoff (number or array-like): The log value(s) at which to bin
                the log. Optional.
            components (array-like): A list of components. Use this or
                ``legend``.
            legend (``Legend``): A legend object. Use this or ``components``.
            legend_field ('str'): If you're not trying to match against
                components, then you can match the log values to this field in
                the Decors.
            field (str): The field in the Interval's ``data`` to store the log
                values as.
            right (bool): Which side of the cutoff to send things that are
                equal to, i.e. right on, the cutoff.
            basis (array-like): A depth basis for the log, so striplog knows
                where to put the boundaries.
            source (str): The source of the data. Default 'Log'.

        Returns:
            Striplog: The ``striplog`` object.
        """
        if (components is None) and (legend is None) and (field is None):
            m = 'You must provide a list of components, and legend, or a field.'
            raise StriplogError(m)

        if (legend is not None) and (legend_field is None):
            try:  # To treat it like a legend.
                components = [deepcopy(decor.component) for decor in legend]
            except AttributeError:  # It's just a list of components.
                pass

        if legend_field is not None:
            field_values = [getattr(d, legend_field, 0) for d in legend]
            components = [Component() for i in range(int(max(field_values)+1))]
            for i, decor in enumerate(legend):
                components[i] = deepcopy(decor.component)

        if cutoff is not None:

            # First make sure we have enough components.
            try:
                n = len(cutoff)
            except TypeError:
                n = 1
            if len(components) < n+1:
                m = 'For n cutoffs, you need to provide at least'
                m += 'n+1 components.'
                raise StriplogError(m)

            # Digitize.
            try:  # To use cutoff as a list.
                a = np.digitize(log, cutoff, right)
            except ValueError:  # It's just a number.
                a = np.digitize(log, [cutoff], right)

        else:
            a = np.copy(log)

        tops, values = utils.tops_from_loglike(a)

        if basis is None:
            m = 'You must provide a depth or elevation basis.'
            raise StriplogError(m)

        list_of_Intervals = cls.__intervals_from_tops(tops,
                                                      values,
                                                      basis,
                                                      components,
                                                      field=field
                                                      )

        return cls(list_of_Intervals, source=source)

Turn a 1D array into a striplog given a cutoff .


def from_las3(cls, string, lexicon=None,
                  source="LAS",
                  dlm=',',
                  abbreviations=False):
        """
        Turn LAS3 'lithology' section into a Striplog.

        Args:
            string (str): A section from an LAS3 file.
            lexicon (Lexicon): The language for conversion to components.
            source (str): A source for the data.
            dlm (str): The delimiter.
            abbreviations (bool): Whether to expand abbreviations.

        Returns:
            Striplog: The ``striplog`` object.

        Note:
            Handles multiple 'Data' sections. It would be smarter for it
            to handle one at a time, and to deal with parsing the multiple
            sections in the Well object.

            Does not read an actual LAS file. Use the Well object for that.
        """
        f = re.DOTALL | re.IGNORECASE
        regex = r'\~\w+?_Data.+?\n(.+?)(?:\n\n+|\n*\~|\n*$)'
        pattern = re.compile(regex, flags=f)
        text = pattern.search(string).group(1)

        s = re.search(r'\.(.+?)\: ?.+?source', string)
        if s:
            source = s.group(1).strip()

        return cls.from_descriptions(text, lexicon,
                                     source=source,
                                     dlm=dlm,
                                     abbreviations=abbreviations)

Turn LAS3 lithology section into a Striplog .


def from_canstrat(cls, filename, source='canstrat'):
        """
        Eat a Canstrat DAT file and make a striplog.
        """
        with open(filename) as f:
            dat = f.read()

        data = parse_canstrat(dat)

        list_of_Intervals = []
        for d in data[7]:  # 7 is the 'card type' for lithology info.
            if d.pop('skip'):
                continue
            top = d.pop('top')
            base = d.pop('base')
            comps = [Component({'lithology': d['rtc'],
                                'colour': d['colour_name']
                                })]
            iv = Interval(top=top, base=base, components=comps, data=d)
            list_of_Intervals.append(iv)

        return cls(list_of_Intervals, source=source)

Eat a Canstrat DAT file and make a striplog .


def copy(self):
        """Returns a shallow copy."""
        return Striplog([i.copy() for i in self],
                        order=self.order,
                        source=self.source)

def copy ( self ) : return Striplog ( [ i . copy ( ) for i in self ] , order = self . order , source = self . source )


def to_csv(self,
               filename=None,
               as_text=True,
               use_descriptions=False,
               dlm=",",
               header=True):
        """
        Returns a CSV string built from the summaries of the Intervals.

        Args:
            use_descriptions (bool): Whether to use descriptions instead
                of summaries, if available.
            dlm (str): The delimiter.
            header (bool): Whether to form a header row.

        Returns:
            str: A string of comma-separated values.
        """
        if (filename is None):
            if (not as_text):
                raise StriplogError("You must provide a filename or set as_text to True.")
        else:
            as_text = False

        if as_text:
            output = StringIO()
        else:
            output = open(filename, 'w')

        fieldnames = ['Top', 'Base', 'Component']
        writer = csv.DictWriter(output,
                                delimiter=dlm,
                                fieldnames=fieldnames,
                                quoting=csv.QUOTE_MINIMAL)

        if header:
            writer.writeheader()

        for i in self.__list:
            if use_descriptions and i.description:
                text = i.description
            elif i.primary:
                text = i.primary.summary()
            else:
                text = ''
            data = {j: k for j, k in zip(fieldnames, [i.top.z, i.base.z, text])}
            writer.writerow(data)

        if as_text:
            return output.getvalue()
            #return output
        else:
            output.close
            return None

Returns a CSV string built from the summaries of the Intervals .


def to_las3(self, use_descriptions=False, dlm=",", source="Striplog"):
        """
        Returns an LAS 3.0 section string.

        Args:
            use_descriptions (bool): Whether to use descriptions instead
                of summaries, if available.
            dlm (str): The delimiter.
            source (str): The sourse of the data.

        Returns:
            str: A string forming Lithology section of an LAS3 file.
        """
        data = self.to_csv(use_descriptions=use_descriptions,
                           dlm=dlm,
                           header=False)

        return templates.section.format(name='Lithology',
                                        short="LITH",
                                        source=source,
                                        data=data)

def to_las3 ( self , use_descriptions = False , dlm = "," , source = "Striplog" ) : data = self . to_csv ( use_descriptions = use_descriptions , dlm = dlm , header = False ) return templates . section . format ( name = 'Lithology' , short = "LITH" , source = source , data = data )


def plot_axis(self,
                  ax,
                  legend,
                  ladder=False,
                  default_width=1,
                  match_only=None,
                  colour=None,
                  colour_function=None,
                  cmap=None,
                  default=None,
                  width_field=None,
                  **kwargs
                  ):
        """
        Plotting, but only the Rectangles. You have to set up the figure.
        Returns a matplotlib axis object.

        Args:
            ax (axis): The matplotlib axis to plot into.
            legend (Legend): The Legend to use for colours, etc.
            ladder (bool): Whether to use widths or not. Default False.
            default_width (int): A width for the plot if not using widths.
                Default 1.
            match_only (list): A list of strings matching the attributes you
                want to compare when plotting.
            colour (str): Which data field to use for colours.
            cmap (cmap): Matplotlib colourmap. Default ``viridis``.
            default (float): The default (null) value.
            width_field (str): The field to use for the width of the patches.
            **kwargs are passed through to matplotlib's ``patches.Rectangle``.

        Returns:
            axis: The matplotlib.pyplot axis.
        """
        default_c = None
        patches = []
        for iv in self.__list:
            origin = (0, iv.top.z)
            d = legend.get_decor(iv.primary, match_only=match_only)
            thick = iv.base.z - iv.top.z

            if ladder:
                if width_field is not None:
                    w = iv.data.get(width_field, 1)
                    w = default_width * w/self.max_field(width_field)
                    default_c = 'gray'
                elif legend is not None:
                    w = d.width or default_width
                    try:
                        w = default_width * w/legend.max_width
                    except:
                        w = default_width
            else:
                w = default_width

            # Allow override of lw
            this_patch_kwargs = kwargs.copy()
            lw = this_patch_kwargs.pop('lw', 0)
            ec = this_patch_kwargs.pop('ec', 'k')
            fc = this_patch_kwargs.pop('fc', None) or default_c or d.colour

            if colour is None:
                rect = mpl.patches.Rectangle(origin,
                                             w,
                                             thick,
                                             fc=fc,
                                             lw=lw,
                                             hatch=d.hatch,
                                             ec=ec,  # edgecolour for hatching
                                             **this_patch_kwargs)
                ax.add_patch(rect)
            else:
                rect = mpl.patches.Rectangle(origin,
                                             w,
                                             thick,
                                             lw=lw,
                                             ec=ec,  # edgecolour for hatching
                                             **this_patch_kwargs)
                patches.append(rect)

        if colour is not None:
            cmap = cmap or 'viridis'
            p = mpl.collections.PatchCollection(patches, cmap=cmap, lw=lw)
            p.set_array(self.get_data(colour, colour_function, default=default))
            ax.add_collection(p)
            cb = plt.colorbar(p)  #  orientation='horizontal' only really works with ticks=[0, 0.1, 0.2] say
            cb.outline.set_linewidth(0)

        return ax

Plotting but only the Rectangles . You have to set up the figure . Returns a matplotlib axis object .


def get_data(self, field, function=None, default=None):
        """
        Get data from the striplog.
        """
        f = function or utils.null
        data = []
        for iv in self:
            d = iv.data.get(field)
            if d is None:
                if default is not None:
                    d = default
                else:
                    d = np.nan
            data.append(f(d))

        return np.array(data)

def get_data ( self , field , function = None , default = None ) : f = function or utils . null data = [ ] for iv in self : d = iv . data . get ( field ) if d is None : if default is not None : d = default else : d = np . nan data . append ( f ( d ) ) return np . array ( data )


def extract(self, log, basis, name, function=None):
        """
        'Extract' a log into the components of a striplog.

        Args:
            log (array_like). A log or other 1D data.
            basis (array_like). The depths or elevations of the log samples.
            name (str). The name of the attribute to store in the components.
            function (function). A function that takes an array as the only
                input, and returns whatever you want to store in the 'name'
                attribute of the primary component.
        Returns:
            None. The function works on the striplog in place.
        """
        # Build a dict of {index: [log values]} to keep track.
        intervals = {}
        previous_ix = -1
        for i, z in enumerate(basis):
            ix = self.read_at(z, index=True)
            if ix is None:
                continue
            if ix == previous_ix:
                intervals[ix].append(log[i])
            else:
                intervals[ix] = [log[i]]
            previous_ix = ix

        # Set the requested attribute in the primary comp of each interval.
        for ix, data in intervals.items():
            f = function or utils.null
            d = f(np.array(data))
            self[ix].data[name] = d

        return None

Extract a log into the components of a striplog .


def find(self, search_term, index=False):
        """
        Look for a regex expression in the descriptions of the striplog.
        If there's no description, it looks in the summaries.

        If you pass a Component, then it will search the components, not the
        descriptions or summaries.

        Case insensitive.

        Args:
            search_term (string or Component): The thing you want to search
                for. Strings are treated as regular expressions.
            index (bool): Whether to return the index instead of the interval.
        Returns:
            Striplog: A striplog that contains only the 'hit' Intervals.
                However, if ``index`` was ``True``, then that's what you get.
        """
        hits = []
        for i, iv in enumerate(self):
            try:
                search_text = iv.description or iv.primary.summary()
                pattern = re.compile(search_term, flags=re.IGNORECASE)
                if pattern.search(search_text):
                    hits.append(i)
            except TypeError:
                if search_term in iv.components:
                    hits.append(i)
        if hits and index:
            return hits
        elif hits:
            return self[hits]
        else:
            return

def find ( self , search_term , index = False ) : hits = [ ] for i , iv in enumerate ( self ) : try : search_text = iv . description or iv . primary . summary ( ) pattern = re . compile ( search_term , flags = re . IGNORECASE ) if pattern . search ( search_text ) : hits . append ( i ) except TypeError : if search_term in iv . components : hits . append ( i ) if hits and index : return hits elif hits : return self [ hits ] else : return

Look for a regex expression in the descriptions of the striplog . If there s no description it looks in the summaries .


def find_overlaps(self, index=False):
        """
        Find overlaps in a striplog.

        Args:
            index (bool): If True, returns indices of intervals with
            gaps after them.

        Returns:
            Striplog: A striplog of all the overlaps as intervals.
        """
        return self.__find_incongruities(op=operator.gt, index=index)

def find_overlaps ( self , index = False ) : return self . __find_incongruities ( op = operator . gt , index = index )


def find_gaps(self, index=False):
        """
        Finds gaps in a striplog.

        Args:
            index (bool): If True, returns indices of intervals with
            gaps after them.

        Returns:
            Striplog: A striplog of all the gaps. A sort of anti-striplog.
        """
        return self.__find_incongruities(op=operator.lt, index=index)

def find_gaps ( self , index = False ) : return self . __find_incongruities ( op = operator . lt , index = index )


def prune(self, limit=None, n=None, percentile=None, keep_ends=False):
        """
        Remove intervals below a certain limit thickness. In place.

        Args:
            limit (float): Anything thinner than this will be pruned.
            n (int): The n thinnest beds will be pruned.
            percentile (float): The thinnest specified percentile will be
                pruned.
            keep_ends (bool): Whether to keep the first and last, regardless
                of whether they meet the pruning criteria.
        """
        strip = self.copy()

        if not (limit or n or percentile):
            m = "You must provide a limit or n or percentile for pruning."
            raise StriplogError(m)
        if limit:
            prune = [i for i, iv in enumerate(strip) if iv.thickness < limit]
        if n:
            prune = strip.thinnest(n=n, index=True)
        if percentile:
            n = np.floor(len(strip)*percentile/100)
            prune = strip.thinnest(n=n, index=True)

        if keep_ends:
            first, last = 0, len(strip) - 1
            if first in prune:
                prune.remove(first)
            if last in prune:
                prune.remove(last)

        del strip[prune]

        return strip

Remove intervals below a certain limit thickness . In place .


def anneal(self):
        """
        Fill in empty intervals by growing from top and base.

        Note that this operation happens in-place and destroys any information
        about the ``Position`` (e.g. metadata associated with the top or base).
        See GitHub issue #54.
        """
        strip = self.copy()

        gaps = strip.find_gaps(index=True)

        if not gaps:
            return

        for gap in gaps:
            before = strip[gap]
            after = strip[gap + 1]

            if strip.order == 'depth':
                t = (after.top.z-before.base.z)/2
                before.base = before.base.z + t
                after.top = after.top.z - t
            else:
                t = (after.base-before.top)/2
                before.top = before.top.z + t
                after.base = after.base.z - t

        return strip

def anneal ( self ) : strip = self . copy ( ) gaps = strip . find_gaps ( index = True ) if not gaps : return for gap in gaps : before = strip [ gap ] after = strip [ gap + 1 ] if strip . order == 'depth' : t = ( after . top . z - before . base . z ) / 2 before . base = before . base . z + t after . top = after . top . z - t else : t = ( after . base - before . top ) / 2 before . top = before . top . z + t after . base = after . base . z - t return strip

Fill in empty intervals by growing from top and base .


def fill(self, component=None):
        """
        Fill gaps with the component provided.

        Example
            t = s.fill(Component({'lithology': 'cheese'}))
        """
        c = [component] if component is not None else []

        # Make the intervals to go in the gaps.
        gaps = self.find_gaps()
        if not gaps:
            return self
        for iv in gaps:
            iv.components = c

        return deepcopy(self) + gaps

def
fill
(
self
,
component
=
)
:
c
=
[
component
]
if
component
is
not
else
[
]
# Make the intervals to go in the gaps.
gaps
=
self
.
find_gaps
(
)
if
not
gaps
:
return
self
for
iv
in
gaps
:
iv
.
components
=
c
return
deepcopy
(
self
)
+
gaps


def union(self, other):
        """
        Makes a striplog of all unions.

        Args:
            Striplog. The striplog instance to union with.

        Returns:
            Striplog. The result of the union.
        """
        if not isinstance(other, self.__class__):
            m = "You can only union striplogs with each other."
            raise StriplogError(m)

        result = []
        for iv in deepcopy(self):
            for jv in other:
                if iv.any_overlaps(jv):
                    iv = iv.union(jv)
            result.append(iv)
        return Striplog(result)


def intersect(self, other):
        """
        Makes a striplog of all intersections.

        Args:
            Striplog. The striplog instance to intersect with.

        Returns:
            Striplog. The result of the intersection.
        """
        if not isinstance(other, self.__class__):
            m = "You can only intersect striplogs with each other."
            raise StriplogError(m)

        result = []
        for iv in self:
            for jv in other:
                try:
                    result.append(iv.intersect(jv))
                except IntervalError:
                    # The intervals don't overlap
                    pass
        return Striplog(result)


def merge_overlaps(self):
        """
        Merges overlaps by merging overlapping Intervals.

        The function takes no arguments and returns ``None``. It operates on
        the striplog 'in place'

        TODO: This function will not work if any interval overlaps more than
            one other intervals at either its base or top.
        """
        overlaps = np.array(self.find_overlaps(index=True))

        if not overlaps.any():
            return

        for overlap in overlaps:
            before = self[overlap].copy()
            after = self[overlap + 1].copy()

            # Get rid of the before and after pieces.
            del self[overlap]
            del self[overlap]

            # Make the new piece.
            new_segment = before.merge(after)

            # Insert it.
            self.__insert(overlap, new_segment)

            overlaps += 1

        return

Merges overlaps by merging overlapping Intervals .


def hist(self,
             lumping=None,
             summary=False,
             sort=True,
             plot=True,
             legend=None,
             ax=None
             ):
        """
        Plots a histogram and returns the data for it.

        Args:
            lumping (str): If given, the bins will be lumped based on this
                attribute of the primary components of the intervals
                encountered.
            summary (bool): If True, the summaries of the components are
                returned as the bins. Otherwise, the default behaviour is to
                return the Components themselves.
            sort (bool): If True (default), the histogram is sorted by value,
                starting with the largest.
            plot (bool): If True (default), produce a bar plot.
            legend (Legend): The legend with which to colour the bars.
            ax (axis): An axis object, which will be returned if provided.
                If you don't provide one, it will be created but not returned.

        Returns:
            Tuple: A tuple of tuples of entities and counts.

        TODO:
            Deal with numeric properties, so I can histogram 'Vp' values, say.
        """
        # This seems like overkill, but collecting all this stuff gives
        # the user some choice about what they get back.
        comps = []
        labels = []
        entries = defaultdict(int)
        for i in self:
            if lumping:
                k = i.primary[lumping]
            else:
                if summary:
                    k = i.primary.summary()
                else:
                    k = i.primary
            comps.append(i.primary)
            labels.append(i.primary.summary())
            entries[k] += i.thickness

        if sort:
            allitems = sorted(entries.items(), key=lambda i: i[1], reverse=True)
            ents, counts = zip(*allitems)
        else:
            ents, counts = tuple(entries.keys()), tuple(entries.values())

        # Make plot.
        if plot:
            if ax is None:
                fig, ax = plt.subplots()
                return_ax = False
            else:
                return_ax = True
            ind = np.arange(len(ents))
            bars = ax.bar(ind, counts, align='center')
            ax.set_xticks(ind)
            ax.set_xticklabels(labels)
            if legend:
                colours = [legend.get_colour(c) for c in comps]
                for b, c in zip(bars, colours):
                    b.set_color(c)
            ax.set_ylabel('Thickness [m]')
        else:
            bars = []

        if plot and return_ax:
            return counts, ents, ax

        return counts, ents, bars

Plots a histogram and returns the data for it .


def invert(self, copy=False):
        """
        Inverts the striplog, changing its order and the order of its contents.

        Operates in place by default.

        Args:
            copy (bool): Whether to operate in place or make a copy.

        Returns:
            None if operating in-place, or an inverted copy of the striplog
                if not.
        """
        if copy:
            return Striplog([i.invert(copy=True) for i in self])
        else:
            for i in self:
                i.invert()
            self.__sort()
            o = self.order
            self.order = {'depth': 'elevation', 'elevation': 'depth'}[o]
            return

def invert ( self , copy = False ) : if copy : return Striplog ( [ i . invert ( copy = True ) for i in self ] ) else : for i in self : i . invert ( ) self . __sort ( ) o = self . order self . order = { 'depth' : 'elevation' , 'elevation' : 'depth' } [ o ] return

Inverts the striplog changing its order and the order of its contents .


def crop(self, extent, copy=False):
        """
        Crop to a new depth range.

        Args:
            extent (tuple): The new start and stop depth. Must be 'inside'
                existing striplog.
            copy (bool): Whether to operate in place or make a copy.

        Returns:
            Operates in place by deault; if copy is True, returns a striplog.
        """
        try:
            if extent[0] is None:
                extent = (self.start.z, extent[1])
            if extent[1] is None:
                extent = (extent[0], self.stop.z)
        except:
            m = "You must provide a 2-tuple for the new extents. Use None for"
            m += " the existing start or stop."
            raise StriplogError(m)

        first_ix = self.read_at(extent[0], index=True)
        last_ix = self.read_at(extent[1], index=True)

        first = self[first_ix].split_at(extent[0])[1]
        last = self[last_ix].split_at(extent[1])[0]

        new_list = self.__list[first_ix:last_ix+1].copy()
        new_list[0] = first
        new_list[-1] = last

        if copy:
            return Striplog(new_list)
        else:
            self.__list = new_list
            return


def quality(self, tests, alias=None):
        """
        Run a series of tests and return the corresponding results.

        Based on curve testing for ``welly``.

        Args:
            tests (list): a list of functions.

        Returns:
            list. The results. Stick to booleans (True = pass) or ints.
        """
        # This is hacky... striplog should probably merge with welly...

        # Ignore aliases
        alias = alias or {}
        alias = alias.get('striplog', alias.get('Striplog', []))

        # Gather the tests.
        # First, anything called 'all', 'All', or 'ALL'.
        # Second, anything with the name of the curve we're in now.
        # Third, anything that the alias list has for this curve.
        # (This requires a reverse look-up so it's a bit messy.)
        this_tests =\
            tests.get('all', [])+tests.get('All', [])+tests.get('ALL', [])\
            + tests.get('striplog', tests.get('Striplog', []))\
            + utils.flatten_list([tests.get(a) for a in alias])
        this_tests = filter(None, this_tests)

        # If we explicitly set zero tests for a particular key, then this
        # overrides the 'all' tests.
        if not tests.get('striplog', tests.get('Striplog', 1)):
            this_tests = []

        return {test.__name__: test(self) for test in this_tests}

Run a series of tests and return the corresponding results .


def hex_to_name(hexx):
    """
    Convert hex to a color name, using matplotlib's colour names.

    Args:
        hexx (str): A hexadecimal colour, starting with '#'.

    Returns:
        str: The name of the colour, or None if not found.
    """
    for n, h in defaults.COLOURS.items():
        if (len(n) > 1) and (h == hexx.upper()):
            return n.lower()
    return None

def hex_to_name ( hexx ) : for n , h in defaults . COLOURS . items ( ) : if ( len ( n ) > 1 ) and ( h == hexx . upper ( ) ) : return n . lower ( ) return None

Convert hex to a color name using matplotlib s colour names .


def loglike_from_image(filename, offset):
    """
    Get a log-like stream of RGB values from an image.

    Args:
        filename (str): The filename of a PNG image.
        offset (Number): If < 1, interpreted as proportion of way across
            the image. If > 1, interpreted as pixels from left.

    Returns:
        ndarray: A 2d array (a column of RGB triples) at the specified
        offset.

    TODO:
        Generalize this to extract 'logs' from images in other ways, such
        as giving the mean of a range of pixel columns, or an array of
        columns. See also a similar routine in pythonanywhere/freqbot.
    """
    im = plt.imread(filename)
    if offset < 1:
        col = int(im.shape[1] * offset)
    else:
        col = offset
    return im[:, col, :3]

def loglike_from_image ( filename , offset ) : im = plt . imread ( filename ) if offset < 1 : col = int ( im . shape [ 1 ] * offset ) else : col = offset return im [ : , col , : 3 ]

Get a log - like stream of RGB values from an image .


def get_field(self, field_name, args, kwargs):
        """
        Return an underscore if the attribute is absent.
        Not all components have the same attributes.
        """
        try:
            s = super(CustomFormatter, self)
            return s.get_field(field_name, args, kwargs)
        except KeyError:    # Key is missing
            return ("_", field_name)
        except IndexError:  # Value is missing
            return ("_", field_name)

def
get_field
(
self
,
field_name
,
args
,
kwargs
)
:
try
:
s
=
super
(
CustomFormatter
,
self
)
return
s
.
get_field
(
field_name
,
args
,
kwargs
)
except
KeyError
:
# Key is missing
return
(
"_"
,
field_name
)
except
IndexError
:
# Value is missing
return
(
"_"
,
field_name
)

Return an underscore if the attribute is absent . Not all components have the same attributes .


def get_jobs(self, prefix=None):
        """ Lists all the jobs registered with Nomad.

           https://www.nomadproject.io/docs/http/jobs.html
            arguments:
              - prefix :(str) optional, specifies a string to filter jobs on based on an prefix.
                        This is specified as a querystring parameter.
            returns: list
            raises:
              - nomad.api.exceptions.BaseNomadException
              - nomad.api.exceptions.URLNotFoundNomadException
        """
        params = {"prefix": prefix}
        return self.request(method="get", params=params).json()

def get_jobs ( self , prefix = None ) : params = { "prefix" : prefix } return self . request ( method = "get" , params = params ) . json ( )

Lists all the jobs registered with Nomad .


def parse(self, hcl, canonicalize=False):
        """ Parse a HCL Job file. Returns a dict with the JSON formatted job.
            This API endpoint is only supported from Nomad version 0.8.3.

            https://www.nomadproject.io/api/jobs.html#parse-job

            returns: dict
            raises:
              - nomad.api.exceptions.BaseNomadException
              - nomad.api.exceptions.URLNotFoundNomadException
        """
        return self.request("parse", json={"JobHCL": hcl, "Canonicalize": canonicalize}, method="post", allow_redirects=True).json()

def parse ( self , hcl , canonicalize = False ) : return self . request ( "parse" , json = { "JobHCL" : hcl , "Canonicalize" : canonicalize } , method = "post" , allow_redirects = True ) . json ( )

Parse a HCL Job file . Returns a dict with the JSON formatted job . This API endpoint is only supported from Nomad version 0 . 8 . 3 .


def update_token(self, id, token):
        """ Update token.

            https://www.nomadproject.io/api/acl-tokens.html

            arguments:
                - AccdesorID
                - token
            returns: dict

            raises:
              - nomad.api.exceptions.BaseNomadException
              - nomad.api.exceptions.URLNotFoundNomadException
        """
        return self.request("token", id, json=token, method="post").json()

def update_token ( self , id , token ) : return self . request ( "token" , id , json = token , method = "post" ) . json ( )


def get_allocations(self, prefix=None):
        """ Lists all the allocations.

           https://www.nomadproject.io/docs/http/allocs.html
            arguments:
              - prefix :(str) optional, specifies a string to filter allocations on based on an prefix.
                        This is specified as a querystring parameter.
            returns: list
            raises:
              - nomad.api.exceptions.BaseNomadException
              - nomad.api.exceptions.URLNotFoundNomadException
        """
        params = {"prefix": prefix}
        return self.request(method="get", params=params).json()

def get_allocations ( self , prefix = None ) : params = { "prefix" : prefix } return self . request ( method = "get" , params = params ) . json ( )


def fail_deployment(self, id):
        """ This endpoint is used to mark a deployment as failed. This should be done to force the scheduler to stop
            creating allocations as part of the deployment or to cause a rollback to a previous job version.

           https://www.nomadproject.io/docs/http/deployments.html

            arguments:
              - id
            returns: dict
            raises:
              - nomad.api.exceptions.BaseNomadException
              - nomad.api.exceptions.URLNotFoundNomadException
        """
        fail_json = {"DeploymentID": id}
        return self.request("fail", id, json=fail_json, method="post").json()

def fail_deployment ( self , id ) : fail_json = { "DeploymentID" : id } return self . request ( "fail" , id , json = fail_json , method = "post" ) . json ( )

This endpoint is used to mark a deployment as failed . This should be done to force the scheduler to stop creating allocations as part of the deployment or to cause a rollback to a previous job version .


def pause_deployment(self, id, pause):
        """ This endpoint is used to pause or unpause a deployment.
            This is done to pause a rolling upgrade or resume it.

           https://www.nomadproject.io/docs/http/deployments.html

            arguments:
              - id
              - pause, Specifies whether to pause or resume the deployment.
            returns: dict
            raises:
              - nomad.api.exceptions.BaseNomadException
              - nomad.api.exceptions.URLNotFoundNomadException
        """
        pause_json = {"Pause": pause,
                      "DeploymentID": id}
        return self.request("pause", id, json=pause_json, method="post").json()

def pause_deployment ( self , id , pause ) : pause_json = { "Pause" : pause , "DeploymentID" : id } return self . request ( "pause" , id , json = pause_json , method = "post" ) . json ( )

This endpoint is used to pause or unpause a deployment . This is done to pause a rolling upgrade or resume it .


def deployment_allocation_health(self, id, healthy_allocations=list(), unhealthy_allocations=list()):
        """ This endpoint is used to set the health of an allocation that is in the deployment manually. In some use
            cases, automatic detection of allocation health may not be desired. As such those task groups can be marked
            with an upgrade policy that uses health_check = "manual". Those allocations must have their health marked
            manually using this endpoint. Marking an allocation as healthy will allow the rolling upgrade to proceed.
            Marking it as failed will cause the deployment to fail.

           https://www.nomadproject.io/docs/http/deployments.html

            arguments:
              - id
              - healthy_allocations, Specifies the set of allocation that should be marked as healthy.
              - unhealthy_allocations,  Specifies the set of allocation that should be marked as unhealthy.
            returns: dict
            raises:
              - nomad.api.exceptions.BaseNomadException
              - nomad.api.exceptions.URLNotFoundNomadException
        """
        allocations = {"HealthyAllocationIDs": healthy_allocations,
                       "UnHealthyAllocationIDs": unhealthy_allocations,
                       "DeploymentID": id}
        return self.request("allocation-health", id, json=allocations, method="post").json()

def deployment_allocation_health ( self , id , healthy_allocations = list ( ) , unhealthy_allocations = list ( ) ) : allocations = { "HealthyAllocationIDs" : healthy_allocations , "UnHealthyAllocationIDs" : unhealthy_allocations , "DeploymentID" : id } return self . request ( "allocation-health" , id , json = allocations , method = "post" ) . json ( )

This endpoint is used to set the health of an allocation that is in the deployment manually . In some use cases automatic detection of allocation health may not be desired . As such those task groups can be marked with an upgrade policy that uses health_check = manual . Those allocations must have their health marked manually using this endpoint . Marking an allocation as healthy will allow the rolling upgrade to proceed . Marking it as failed will cause the deployment to fail .


def drain_node(self, id, enable=False):
        """ Toggle the drain mode of the node.
            When enabled, no further allocations will be
            assigned and existing allocations will be migrated.

           https://www.nomadproject.io/docs/http/node.html

            arguments:
              - id (str uuid): node id
              - enable (bool): enable node drain or not to enable node drain
            returns: dict
            raises:
              - nomad.api.exceptions.BaseNomadException
              - nomad.api.exceptions.URLNotFoundNomadException
        """

        return self.request(id, "drain", params={"enable": enable}, method="post").json()

def drain_node ( self , id , enable = False ) : return self . request ( id , "drain" , params = { "enable" : enable } , method = "post" ) . json ( )

Toggle the drain mode of the node . When enabled no further allocations will be assigned and existing allocations will be migrated .


def drain_node_with_spec(self, id, drain_spec, mark_eligible=None):
        """ This endpoint toggles the drain mode of the node. When draining is enabled,
            no further allocations will be assigned to this node, and existing allocations
            will be migrated to new nodes.

            If an empty dictionary is given as drain_spec this will disable/toggle the drain.

            https://www.nomadproject.io/docs/http/node.html

            arguments:
              - id (str uuid): node id
              - drain_spec (dict): https://www.nomadproject.io/api/nodes.html#drainspec
              - mark_eligible (bool): https://www.nomadproject.io/api/nodes.html#markeligible
            returns: dict
            raises:
              - nomad.api.exceptions.BaseNomadException
              - nomad.api.exceptions.URLNotFoundNomadException
        """
        payload = {}

        if drain_spec and mark_eligible is not None:
            payload = {
                "NodeID": id,
                "DrainSpec": drain_spec,
                "MarkEligible": mark_eligible
            }
        elif drain_spec and mark_eligible is None:
            payload = {
                "NodeID": id,
                "DrainSpec": drain_spec
            }
        elif not drain_spec and mark_eligible is not None:
            payload = {
                "NodeID": id,
                "DrainSpec": None,
                "MarkEligible": mark_eligible
            }
        elif not drain_spec and mark_eligible is None:
            payload = {
                "NodeID": id,
                "DrainSpec": None,
            }

        return self.request(id, "drain", json=payload, method="post").json()

def drain_node_with_spec ( self , id , drain_spec , mark_eligible = None ) : payload = { } if drain_spec and mark_eligible is not None : payload = { "NodeID" : id , "DrainSpec" : drain_spec , "MarkEligible" : mark_eligible } elif drain_spec and mark_eligible is None : payload = { "NodeID" : id , "DrainSpec" : drain_spec } elif not drain_spec and mark_eligible is not None : payload = { "NodeID" : id , "DrainSpec" : None , "MarkEligible" : mark_eligible } elif not drain_spec and mark_eligible is None : payload = { "NodeID" : id , "DrainSpec" : None , } return self . request ( id , "drain" , json = payload , method = "post" ) . json ( )

This endpoint toggles the drain mode of the node . When draining is enabled no further allocations will be assigned to this node and existing allocations will be migrated to new nodes .


def eligible_node(self, id, eligible=None, ineligible=None):
        """ Toggle the eligibility of the node.

           https://www.nomadproject.io/docs/http/node.html

            arguments:
              - id (str uuid): node id
              - eligible (bool): Set to True to mark node eligible
              - ineligible (bool): Set to True to mark node ineligible
            returns: dict
            raises:
              - nomad.api.exceptions.BaseNomadException
              - nomad.api.exceptions.URLNotFoundNomadException
        """
        payload = {}

        if eligible is not None and ineligible is not None:
            raise nomad.api.exceptions.InvalidParameters
        if eligible is None and ineligible is None:
            raise nomad.api.exceptions.InvalidParameters

        if eligible is not None and eligible:
            payload = {"Eligibility": "eligible", "NodeID": id}
        elif eligible is not None and not eligible:
            payload = {"Eligibility": "ineligible", "NodeID": id}
        elif ineligible is not None:
            payload = {"Eligibility": "ineligible", "NodeID": id}
        elif ineligible is not None and not ineligible:
            payload = {"Eligibility": "eligible", "NodeID": id}

        return self.request(id, "eligibility", json=payload, method="post").json()

def eligible_node ( self , id , eligible = None , ineligible = None ) : payload = { } if eligible is not None and ineligible is not None : raise nomad . api . exceptions . InvalidParameters if eligible is None and ineligible is None : raise nomad . api . exceptions . InvalidParameters if eligible is not None and eligible : payload = { "Eligibility" : "eligible" , "NodeID" : id } elif eligible is not None and not eligible : payload = { "Eligibility" : "ineligible" , "NodeID" : id } elif ineligible is not None : payload = { "Eligibility" : "ineligible" , "NodeID" : id } elif ineligible is not None and not ineligible : payload = { "Eligibility" : "eligible" , "NodeID" : id } return self . request ( id , "eligibility" , json = payload , method = "post" ) . json ( )


def list_files(self, id=None, path="/"):
        """ List files in an allocation directory.

           https://www.nomadproject.io/docs/http/client-fs-ls.html

            arguments:
              - id
              - path
            returns: list
            raises:
              - nomad.api.exceptions.BaseNomadException
              - nomad.api.exceptions.URLNotFoundNomadException
        """
        if id:
            return self.request(id, params={"path": path}, method="get").json()
        else:
            return self.request(params={"path": path}, method="get").json()

def list_files ( self , id = None , path = "/" ) : if id : return self . request ( id , params = { "path" : path } , method = "get" ) . json ( ) else : return self . request ( params = { "path" : path } , method = "get" ) . json ( )


def stream(self, id, offset, origin, path="/"):
        """ This endpoint streams the contents of a file in an allocation directory.

            https://www.nomadproject.io/api/client.html#stream-file

            arguments:
              - id: (str) allocation_id required
              - offset: (int) required
              - origin: (str) either start|end
              - path: (str) optional
            returns: (str) text
            raises:
              - nomad.api.exceptions.BaseNomadException
              - nomad.api.exceptions.BadRequestNomadException
        """
        params = {
            "path": path,
            "offset": offset,
            "origin": origin
        }
        return self.request(id, params=params, method="get").text

def stream ( self , id , offset , origin , path = "/" ) : params = { "path" : path , "offset" : offset , "origin" : origin } return self . request ( id , params = params , method = "get" ) . text

This endpoint streams the contents of a file in an allocation directory .


def stat_file(self, id=None, path="/"):
        """ Stat a file in an allocation directory.

           https://www.nomadproject.io/docs/http/client-fs-stat.html

            arguments:
              - id
              - path
            returns: dict
            raises:
              - nomad.api.exceptions.BaseNomadException
              - nomad.api.exceptions.URLNotFoundNomadException
        """
        if id:
            return self.request(id, params={"path": path}, method="get").json()
        else:
            return self.request(params={"path": path}, method="get").json()

def stat_file ( self , id = None , path = "/" ) : if id : return self . request ( id , params = { "path" : path } , method = "get" ) . json ( ) else : return self . request ( params = { "path" : path } , method = "get" ) . json ( )

Stat a file in an allocation directory .


def join_agent(self, addresses):
        """Initiate a join between the agent and target peers.

           https://www.nomadproject.io/docs/http/agent-join.html

            returns: dict
            raises:
              - nomad.api.exceptions.BaseNomadException
              - nomad.api.exceptions.URLNotFoundNomadException
        """
        params = {"address": addresses}
        return self.request("join", params=params, method="post").json()

def join_agent ( self , addresses ) : params = { "address" : addresses } return self . request ( "join" , params = params , method = "post" ) . json ( )

Initiate a join between the agent and target peers .


def update_servers(self, addresses):
        """Updates the list of known servers to the provided list.
           Replaces all previous server addresses with the new list.

           https://www.nomadproject.io/docs/http/agent-servers.html

            returns: 200 status code
            raises:
              - nomad.api.exceptions.BaseNomadException
              - nomad.api.exceptions.URLNotFoundNomadException
        """
        params = {"address": addresses}
        return self.request("servers", params=params, method="post").status_code

def update_servers ( self , addresses ) : params = { "address" : addresses } return self . request ( "servers" , params = params , method = "post" ) . status_code

Updates the list of known servers to the provided list . Replaces all previous server addresses with the new list .


def force_leave(self, node):
        """Force a failed gossip member into the left state.

            https://www.nomadproject.io/docs/http/agent-force-leave.html

            returns: 200 status code
            raises:
              - nomad.api.exceptions.BaseNomadException
              - nomad.api.exceptions.URLNotFoundNomadException
        """
        params = {"node": node}
        return self.request("force-leave", params=params, method="post").status_code

def force_leave ( self , node ) : params = { "node" : node } return self . request ( "force-leave" , params = params , method = "post" ) . status_code

Force a failed gossip member into the left state .


def get_nodes(self, prefix=None):
        """ Lists all the client nodes registered with Nomad.

           https://www.nomadproject.io/docs/http/nodes.html
            arguments:
              - prefix :(str) optional, specifies a string to filter nodes on based on an prefix.
                        This is specified as a querystring parameter.
            returns: list
            raises:
              - nomad.api.exceptions.BaseNomadException
              - nomad.api.exceptions.URLNotFoundNomadException
        """
        params = {"prefix": prefix}
        return self.request(method="get", params=params).json()

def get_nodes ( self , prefix = None ) : params = { "prefix" : prefix } return self . request ( method = "get" , params = params ) . json ( )

Lists all the client nodes registered with Nomad .


def get_evaluations(self, prefix=None):
        """ Lists all the evaluations.

           https://www.nomadproject.io/docs/http/evals.html
            arguments:
              - prefix :(str) optional, specifies a string to filter evaluations on based on an prefix.
                        This is specified as a querystring parameter.
            returns: list
            raises:
              - nomad.api.exceptions.BaseNomadException
              - nomad.api.exceptions.URLNotFoundNomadException
        """
        params = {"prefix": prefix}
        return self.request(method="get", params=params).json()

def get_evaluations ( self , prefix = None ) : params = { "prefix" : prefix } return self . request ( method = "get" , params = params ) . json ( )


def get_namespaces(self, prefix=None):
        """ Lists all the namespaces registered with Nomad.

           https://www.nomadproject.io/docs/enterprise/namespaces/index.html
            arguments:
              - prefix :(str) optional, specifies a string to filter namespaces on based on an prefix.
                        This is specified as a querystring parameter.
            returns: list
            raises:
              - nomad.api.exceptions.BaseNomadException
              - nomad.api.exceptions.URLNotFoundNomadException
        """
        params = {"prefix": prefix}
        return self.request(method="get", params=params).json()

def get_namespaces ( self , prefix = None ) : params = { "prefix" : prefix } return self . request ( method = "get" , params = params ) . json ( )

Lists all the namespaces registered with Nomad .


def register_job(self, id, job):
        """ Registers a new job or updates an existing job

           https://www.nomadproject.io/docs/http/job.html

            arguments:
              - id
            returns: dict
            raises:
              - nomad.api.exceptions.BaseNomadException
              - nomad.api.exceptions.URLNotFoundNomadException
        """
        return self.request(id, json=job, method="post").json()

def register_job ( self , id , job ) : return self . request ( id , json = job , method = "post" ) . json ( )


def plan_job(self, id, job, diff=False, policy_override=False):
        """ Invoke a dry-run of the scheduler for the job.

           https://www.nomadproject.io/docs/http/job.html

            arguments:
              - id
              - job, dict
              - diff, boolean
              - policy_override, boolean
            returns: dict
            raises:
              - nomad.api.exceptions.BaseNomadException
              - nomad.api.exceptions.URLNotFoundNomadException
        """
        json_dict = {}
        json_dict.update(job)
        json_dict.setdefault('Diff', diff)
        json_dict.setdefault('PolicyOverride', policy_override)
        return self.request(id, "plan", json=json_dict, method="post").json()

def plan_job ( self , id , job , diff = False , policy_override = False ) : json_dict = { } json_dict . update ( job ) json_dict . setdefault ( 'Diff' , diff ) json_dict . setdefault ( 'PolicyOverride' , policy_override ) return self . request ( id , "plan" , json = json_dict , method = "post" ) . json ( )

Invoke a dry - run of the scheduler for the job .


def dispatch_job(self, id, payload=None, meta=None):
        """ Dispatches a new instance of a parameterized job.

           https://www.nomadproject.io/docs/http/job.html

            arguments:
              - id
              - payload
              - meta
            returns: dict
            raises:
              - nomad.api.exceptions.BaseNomadException
              - nomad.api.exceptions.URLNotFoundNomadException
        """
        dispatch_json = {"Meta": meta, "Payload": payload}
        return self.request(id, "dispatch", json=dispatch_json, method="post").json()

def dispatch_job ( self , id , payload = None , meta = None ) : dispatch_json = { "Meta" : meta , "Payload" : payload } return self . request ( id , "dispatch" , json = dispatch_json , method = "post" ) . json ( )

Dispatches a new instance of a parameterized job .


def revert_job(self, id, version, enforce_prior_version=None):
        """ This endpoint reverts the job to an older version.

           https://www.nomadproject.io/docs/http/job.html

            arguments:
              - id
              - version, Specifies the job version to revert to.
            optional_arguments:
              - enforce_prior_version, Optional value specifying the current job's version.
                                       This is checked and acts as a check-and-set value before reverting to the
                                       specified job.
            returns: dict
            raises:
              - nomad.api.exceptions.BaseNomadException
              - nomad.api.exceptions.URLNotFoundNomadException
        """
        revert_json = {"JobID": id,
                       "JobVersion": version,
                       "EnforcePriorVersion": enforce_prior_version}
        return self.request(id, "revert", json=revert_json, method="post").json()

def revert_job ( self , id , version , enforce_prior_version = None ) : revert_json = { "JobID" : id , "JobVersion" : version , "EnforcePriorVersion" : enforce_prior_version } return self . request ( id , "revert" , json = revert_json , method = "post" ) . json ( )

This endpoint reverts the job to an older version .


def stable_job(self, id, version, stable):
        """ This endpoint sets the job's stability.

           https://www.nomadproject.io/docs/http/job.html

            arguments:
              - id
              - version, Specifies the job version to revert to.
              - stable, Specifies whether the job should be marked as stable or not.
            returns: dict
            raises:
              - nomad.api.exceptions.BaseNomadException
              - nomad.api.exceptions.URLNotFoundNomadException
        """
        revert_json = {"JobID": id,
                       "JobVersion": version,
                       "Stable": stable}
        return self.request(id, "stable", json=revert_json, method="post").json()

def stable_job ( self , id , version , stable ) : revert_json = { "JobID" : id , "JobVersion" : version , "Stable" : stable } return self . request ( id , "stable" , json = revert_json , method = "post" ) . json ( )

This endpoint sets the job s stability .


def deregister_job(self, id, purge=None):
        """ Deregisters a job, and stops all allocations part of it.

           https://www.nomadproject.io/docs/http/job.html

            arguments:
              - id
              - purge (bool), optionally specifies whether the job should be
                stopped and purged immediately (`purge=True`) or deferred to the
                Nomad garbage collector (`purge=False`).

            returns: dict
            raises:
              - nomad.api.exceptions.BaseNomadException
              - nomad.api.exceptions.URLNotFoundNomadException
              - nomad.api.exceptions.InvalidParameters
        """
        params = None
        if purge is not None:
            if not isinstance(purge, bool):
                raise nomad.api.exceptions.InvalidParameters("purge is invalid "
                        "(expected type %s but got %s)"%(type(bool()), type(purge)))
            params = {"purge": purge}
        return self.request(id, params=params, method="delete").json()

def
deregister_job
(
self
,
id
,
purge
=
)
:
params
=
if
purge
is
not
:
if
not
isinstance
(
purge
,
bool
)
:
raise
nomad
.
api
.
exceptions
.
InvalidParameters
(
"purge is invalid "
"(expected type %s but got %s)"
%
(
type
(
bool
(
)
)
,
type
(
purge
)
)
)
params
=
{
"purge"
:
purge
}
return
self
.
request
(
id
,
params
=
params
,
method
=
"delete"
)
.
json
(
)

Deregisters a job and stops all allocations part of it .


def get_configuration(self, stale=False):
        """ Query the status of a client node registered with Nomad.

            https://www.nomadproject.io/docs/http/operator.html

            returns: dict
            optional arguments:
              - stale, (defaults to False), Specifies if the cluster should respond without an active leader.
                                            This is specified as a querystring parameter.
            raises:
              - nomad.api.exceptions.BaseNomadException
              - nomad.api.exceptions.URLNotFoundNomadException
        """

        params = {"stale": stale}
        return self.request("raft", "configuration", params=params, method="get").json()

def get_configuration ( self , stale = False ) : params = { "stale" : stale } return self . request ( "raft" , "configuration" , params = params , method = "get" ) . json ( )

Query the status of a client node registered with Nomad .


def delete_peer(self, peer_address, stale=False):
        """ Remove the Nomad server with given address from the Raft configuration.
            The return code signifies success or failure.

            https://www.nomadproject.io/docs/http/operator.html

            arguments:
              - peer_address, The address specifies the server to remove and is given as an IP:port
            optional arguments:
              - stale, (defaults to False), Specifies if the cluster should respond without an active leader.
                                            This is specified as a querystring parameter.
            returns: Boolean
            raises:
              - nomad.api.exceptions.BaseNomadException
              - nomad.api.exceptions.URLNotFoundNomadException
        """

        params = {"address": peer_address, "stale": stale}
        return self.request("raft", "peer", params=params, method="delete").ok

def delete_peer ( self , peer_address , stale = False ) : params = { "address" : peer_address , "stale" : stale } return self . request ( "raft" , "peer" , params = params , method = "delete" ) . ok

Remove the Nomad server with given address from the Raft configuration . The return code signifies success or failure .


def get_deployments(self, prefix=""):
        """ This endpoint lists all deployments.

           https://www.nomadproject.io/docs/http/deployments.html

            optional_arguments:
              - prefix, (default "") Specifies a string to filter deployments on based on an index prefix.
                        This is specified as a querystring parameter.

            returns: list of dicts
            raises:
              - nomad.api.exceptions.BaseNomadException
              - nomad.api.exceptions.URLNotFoundNomadException
        """
        params = {"prefix": prefix}
        return self.request(params=params, method="get").json()

def get_deployments ( self , prefix = "" ) : params = { "prefix" : prefix } return self . request ( params = params , method = "get" ) . json ( )


def _get_random(self, obj_type):
        """
        Get a random mutator from a list of mutators
        """
        return self.mutator[obj_type][random.randint(0, self.config.level)]

def _get_random ( self , obj_type ) : return self . mutator [ obj_type ] [ random . randint ( 0 , self . config . level ) ]


def get_mutator(self, obj, obj_type):
        """
        Get a random mutator for the given type
        """
        if obj_type == unicode:
            obj_type = str
            obj = str(obj)
        return self._get_random(obj_type)(obj)

def get_mutator ( self , obj , obj_type ) : if obj_type == unicode : obj_type = str obj = str ( obj ) return self . _get_random ( obj_type ) ( obj )


def get_string_polyglot_attack(self, obj):
        """
        Return a polyglot attack containing the original object
        """
        return self.polyglot_attacks[random.choice(self.config.techniques)] % obj

def get_string_polyglot_attack ( self , obj ) : return self . polyglot_attacks [ random . choice ( self . config . techniques ) ] % obj


def fuzz(self, obj):
        """
        Perform the fuzzing
        """
        buf = list(obj)
        FuzzFactor = random.randrange(1, len(buf))
        numwrites=random.randrange(math.ceil((float(len(buf)) / FuzzFactor)))+1
        for j in range(numwrites):
            self.random_action(buf)
        return self.safe_unicode(buf)

def fuzz ( self , obj ) : buf = list ( obj ) FuzzFactor = random . randrange ( 1 , len ( buf ) ) numwrites = random . randrange ( math . ceil ( ( float ( len ( buf ) ) / FuzzFactor ) ) ) + 1 for j in range ( numwrites ) : self . random_action ( buf ) return self . safe_unicode ( buf )


def safe_unicode(self, buf):
        """
        Safely return an unicode encoded string
        """
        tmp = ""
        buf = "".join(b for b in buf)
        for character in buf:
            tmp += character
        return tmp

def safe_unicode ( self , buf ) : tmp = "" buf = "" . join ( b for b in buf ) for character in buf : tmp += character return tmp


def run(self):
        """
        Start the servers
        """
        route("/")(self.serve)
        if self.config.html:
            route("/<filepath:path>")(self.custom_html)
        if self.config.fuzz_web:
            self.request_checker.start()
        self.httpd.start()
        self.httpsd.start()

def run ( self ) : route ( "/" ) ( self . serve ) if self . config . html : route ( "/<filepath:path>" ) ( self . custom_html ) if self . config . fuzz_web : self . request_checker . start ( ) self . httpd . start ( ) self . httpsd . start ( )


def stop(self):
        """
        Kill the servers
        """
        os.kill(self.httpd.pid, signal.SIGKILL)
        os.kill(self.httpsd.pid, signal.SIGKILL)
        self.client_queue.put((0,0))
        if self.config.fuzz_web:
            self.request_checker.join()
        self.logger.debug("[{0}] - PJFServer successfully completed".format(time.strftime("%H:%M:%S")))


def custom_html(self, filepath):
        """
        Serve custom HTML page
        """
        try:
            response.headers.append("Access-Control-Allow-Origin", "*")
            response.headers.append("Accept-Encoding", "identity")
            response.headers.append("Content-Type", "text/html")
            return static_file(filepath, root=self.config.html)
        except Exception as e:
            raise PJFBaseException(e.message if hasattr(e, "message") else str(e))

def custom_html ( self , filepath ) : try : response . headers . append ( "Access-Control-Allow-Origin" , "*" ) response . headers . append ( "Accept-Encoding" , "identity" ) response . headers . append ( "Content-Type" , "text/html" ) return static_file ( filepath , root = self . config . html ) except Exception as e : raise PJFBaseException ( e . message if hasattr ( e , "message" ) else str ( e ) )


def serve(self):
        """
        Serve fuzzed JSON object
        """
        try:
            fuzzed = self.json.fuzzed
            if self.config.fuzz_web:
                self.client_queue.put((request.environ.get('REMOTE_ADDR'), fuzzed))
            response.headers.append("Access-Control-Allow-Origin", "*")
            response.headers.append("Accept-Encoding", "identity")
            response.headers.append("Content-Type", self.config.content_type)
            if self.config.notify:
                PJFTestcaseServer.send_testcase(fuzzed, '127.0.0.1', self.config.ports["servers"]["TCASE_PORT"])
            yield fuzzed
        except Exception as e:
            raise PJFBaseException(e.message if hasattr(e, "message") else str(e))

def serve ( self ) : try : fuzzed = self . json . fuzzed if self . config . fuzz_web : self . client_queue . put ( ( request . environ . get ( 'REMOTE_ADDR' ) , fuzzed ) ) response . headers . append ( "Access-Control-Allow-Origin" , "*" ) response . headers . append ( "Accept-Encoding" , "identity" ) response . headers . append ( "Content-Type" , self . config . content_type ) if self . config . notify : PJFTestcaseServer . send_testcase ( fuzzed , '127.0.0.1' , self . config . ports [ "servers" ] [ "TCASE_PORT" ] ) yield fuzzed except Exception as e : raise PJFBaseException ( e . message if hasattr ( e , "message" ) else str ( e ) )


def fuzz(self, obj):
        """
        Generic fuzz mutator, use a decorator for the given type
        """
        decorators = self.decorators

        @decorators.mutate_object_decorate
        def mutate():
            return obj
        return mutate()

def fuzz ( self , obj ) : decorators = self . decorators @ decorators . mutate_object_decorate def mutate ( ) : return obj return mutate ( )

Generic fuzz mutator use a decorator for the given type