You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
993 lines
44 KiB
993 lines
44 KiB
#!/usr/bin/env python
|
|
# encoding: utf-8
|
|
|
|
import sys
|
|
from Naked.settings import debug as DEBUG_FLAG
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [[ NakedObject class ]]
|
|
# A generic Python object
|
|
# Assigns object attributes by key name in the dictionary argument to the constructor
|
|
# The methods are inherited by other mutable Naked object extension types
|
|
# Attribute accessors: hasattr, getattr, setattr, delattr
|
|
#------------------------------------------------------------------------------
|
|
class NakedObject(object):
|
|
# initialize with an attributes dictionary {attribute_name: attribute_value}
|
|
def __init__(self, attributes={}, naked_type='NakedObject'):
|
|
if len(attributes) > 0:
|
|
for key in attributes:
|
|
setattr(self, key, attributes[key])
|
|
setattr(self, '_naked_type_', naked_type) # maintain an attribute to keep track of the extension type
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [ _getAttributeDict method ] (dictionary)
|
|
# returns a dictionary of the NakedObject instance attributes
|
|
#------------------------------------------------------------------------------
|
|
def _getAttributeDict(self):
|
|
return self.__dict__
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [ _equal_type method ] (boolean)
|
|
# returns boolean for type of instance == type of test parameter instance
|
|
#------------------------------------------------------------------------------
|
|
def _equal_type(self, other_obj):
|
|
return type(self) == type(other_obj)
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [ _equal_attributes metod ] (method)
|
|
# returns boolean for instance.__dict__ == test parameter .__dict__ (attribute comparison)
|
|
#------------------------------------------------------------------------------
|
|
def _equal_attributes(self, other_obj):
|
|
return self.__dict__ == other_obj.__dict__
|
|
|
|
#------------------------------------------------------------------------------
|
|
# == overload
|
|
#------------------------------------------------------------------------------
|
|
def __eq__(self, other_obj):
|
|
return self.equals(other_obj)
|
|
|
|
#------------------------------------------------------------------------------
|
|
# != overload
|
|
#------------------------------------------------------------------------------
|
|
def __ne__(self, other_obj):
|
|
result = self.equals(other_obj)
|
|
if result:
|
|
return False # reverse result of the equals method
|
|
else:
|
|
return True
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [ equals method ] (boolean)
|
|
# equality testing based on type and attributes
|
|
# **NEED TO OVERRIDE IN CLASSES THAT INHERIT
|
|
#------------------------------------------------------------------------------
|
|
def equals(self, other_obj):
|
|
return self._equal_type(other_obj) and self._equal_attributes(other_obj)
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [ type method ] (string)
|
|
# returns the Naked type extension string that is set in the constructor for each object type
|
|
#------------------------------------------------------------------------------
|
|
def type(self):
|
|
if hasattr(self, '_naked_type_'):
|
|
return self._naked_type_
|
|
else:
|
|
return None
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [[ XDict class ]]
|
|
# An inherited extension to the dictionary type
|
|
#------------------------------------------------------------------------------
|
|
class XDict(dict, NakedObject):
|
|
def __init__(self, dict_obj, attributes={}, naked_type='XDict'):
|
|
dict.__init__(self, dict_obj)
|
|
NakedObject.__init__(self, attributes, naked_type)
|
|
|
|
#------------------------------------------------------------------------------
|
|
# XDict Operator Overloads
|
|
#------------------------------------------------------------------------------
|
|
|
|
#------------------------------------------------------------------------------
|
|
# + overload
|
|
# overwrites existing keys with key:value pairs from new dictionaries if they are the same keys
|
|
# returns the updated XDict object
|
|
#------------------------------------------------------------------------------
|
|
def __add__(self, other_dict):
|
|
try:
|
|
self.update(other_dict)
|
|
if hasattr(other_dict, '_naked_type_') and (getattr(other_dict, '_naked_type_') == 'XDict'):
|
|
attr_dict = other_dict._getAttributeDict() # get the attributes from the parameter XDict and add to new XDict
|
|
if len(attr_dict) > 0:
|
|
for key in attr_dict:
|
|
setattr(self, key, attr_dict[key])
|
|
return self
|
|
except Exception as e:
|
|
if DEBUG_FLAG:
|
|
sys.stderr.write("Naked Framework Error: unable to combine XDict with parameter provided (Naked.toolshed.types.py)")
|
|
raise e
|
|
|
|
#------------------------------------------------------------------------------
|
|
# +- overload
|
|
# overwrites existing keys with another_dict (right sided argument) keys if they are the same keys
|
|
# returns the updated XDict object
|
|
#------------------------------------------------------------------------------
|
|
def __iadd__(self, other_dict):
|
|
try:
|
|
self.update(other_dict)
|
|
if hasattr(other_dict, '_naked_type_') and (getattr(other_dict, '_naked_type_') == 'XDict'):
|
|
attr_dict = other_dict._getAttributeDict() # get the attributes from the parameter XDict and add to new XDict
|
|
if len(attr_dict) > 0:
|
|
for key in attr_dict:
|
|
setattr(self, key, attr_dict[key])
|
|
return self
|
|
except Exception as e:
|
|
if DEBUG_FLAG:
|
|
sys.stderr.write("Naked Framework Error: unable to combine XDict with parameter provided (Naked.toolshed.types.py)")
|
|
raise e
|
|
|
|
#------------------------------------------------------------------------------
|
|
# == overload
|
|
#------------------------------------------------------------------------------
|
|
def __eq__(self, other_obj):
|
|
return self.equals(other_obj)
|
|
|
|
#------------------------------------------------------------------------------
|
|
# != overload
|
|
#------------------------------------------------------------------------------
|
|
def __ne__(self, other_obj):
|
|
result = self.equals(other_obj)
|
|
if result:
|
|
return False # reverse result of the equals method
|
|
else:
|
|
return True
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [ equals method ] (boolean)
|
|
# tests for equality of the XDict (type, attributes, dictionary equality)
|
|
#------------------------------------------------------------------------------
|
|
def equals(self, other_obj):
|
|
if self._equal_type(other_obj) and self._equal_attributes(other_obj):
|
|
if dict(self) == dict(other_obj):
|
|
return True
|
|
else:
|
|
return False
|
|
else:
|
|
return False
|
|
|
|
#------------------------------------------------------------------------------
|
|
# XDict Value Methods
|
|
#------------------------------------------------------------------------------
|
|
#------------------------------------------------------------------------------
|
|
# [ conditional_map_to_vals method ] (XDict)
|
|
# returns the original XDict with values that meet True condition in `conditional_function`
|
|
# modified as per the `mapped_function` with single value argument call
|
|
# Test: test_xdict_conditional_map
|
|
#------------------------------------------------------------------------------
|
|
def conditional_map_to_vals(self, conditional_function, mapped_function):
|
|
for key, value in self.xitems():
|
|
if conditional_function(key):
|
|
self[key] = mapped_function(value)
|
|
return self
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [ map_to_vals method ] (XDict)
|
|
# returns the original XDict with all values modified as per the `mapped_function`
|
|
# Test: test_xdict_map_to_vals
|
|
#------------------------------------------------------------------------------
|
|
def map_to_vals(self, mapped_function):
|
|
# return XDict( zip(self, map(mapped_function, self.values())), self._getAttributeDict() ) - slower in Py2
|
|
for key, value in self.xitems():
|
|
self[key] = mapped_function(value)
|
|
return self
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [ val_xlist method ] (XList)
|
|
# return an XList of the values in the XDict
|
|
# Test: test_xdict_val_xlist
|
|
#------------------------------------------------------------------------------
|
|
def val_xlist(self):
|
|
return XList(self.values(), self._getAttributeDict())
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [ max_val method ] (tuple of maximum value and associated key)
|
|
# Test: test_xdict_max_val, test_xdict_max_val_strings (strings are alphabetic if not numerals)
|
|
#------------------------------------------------------------------------------
|
|
def max_val(self):
|
|
return max(zip(self.values(), self.keys()))
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [ min_val method ] (tuple of minimum value and associated key)
|
|
#------------------------------------------------------------------------------
|
|
def min_val(self):
|
|
return min(zip(self.values(), self.keys()))
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [ sum_vals method ] (numeric return type dependent upon original value type)
|
|
# returns sum of all values in the dictionary
|
|
#------------------------------------------------------------------------------
|
|
def sum_vals(self):
|
|
return sum(self.values())
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [ val_count method ] (integer)
|
|
# returns an integer value for the total count of `value_name` in the dictionary values
|
|
# Case sensitive test if comparing strings
|
|
# Tests: test_xdict_val_count_string, test_xdict_val_count_integer
|
|
#------------------------------------------------------------------------------
|
|
def val_count(self, value_name):
|
|
count = 0
|
|
for test_value in self.values():
|
|
if value_name == test_value:
|
|
count += 1
|
|
return count
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [ value_count_ci method ] (integer)
|
|
# returns an integer value for the total count of case insensitive `value_name`
|
|
# strings/char in the dictionary values. Can include non-string types (ignores them)
|
|
# Test: test_xdict_val_count_ci
|
|
#------------------------------------------------------------------------------
|
|
def val_count_ci(self, value_name):
|
|
count = 0
|
|
for test_value in self.values():
|
|
try:
|
|
if value_name.lower() in test_value.lower():
|
|
count += 1
|
|
except AttributeError: # the test_value was not a string, catch exception and continue count attempt
|
|
continue
|
|
return count
|
|
|
|
|
|
#------------------------------------------------------------------------------
|
|
# XDict Key Methods
|
|
#------------------------------------------------------------------------------
|
|
#------------------------------------------------------------------------------
|
|
# [ difference method ] (difference set of keys)
|
|
# definition: keys that are included in self, but not in `another_dict`
|
|
# Tests: test_xdict_key_difference, test_xdict_key_difference_when_none_present
|
|
#------------------------------------------------------------------------------
|
|
def difference(self, another_dict):
|
|
return set(self.keys()) - set(another_dict.keys())
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [ intersection method ] (intersection set of keys)
|
|
# definition: keys that are included in both self and `another_dict`
|
|
# Tests: test_xdict_key_intersection, test_xdict_key_intersection_when_none_present
|
|
#------------------------------------------------------------------------------
|
|
def intersection(self, another_dict):
|
|
return set(self.keys()) & set(another_dict.keys())
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [ key_xlist method ] (XList)
|
|
# returns an XList of the keys in the XDict
|
|
# Test: test_xdict_key_xlist
|
|
#------------------------------------------------------------------------------
|
|
def key_xlist(self):
|
|
return XList(self.keys(), self._getAttributeDict())
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [ random method ] (dictionary)
|
|
# return new Python dictionary with single, random key:value pair
|
|
# Test: test_xdict_key_random
|
|
#------------------------------------------------------------------------------
|
|
def random(self):
|
|
import random
|
|
from Naked.toolshed.python import py_major_version
|
|
random_key_list = random.sample(self.keys(), 1)
|
|
the_key = random_key_list[0]
|
|
return {the_key: self[the_key]}
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [ random_sample method ] (dictionary)
|
|
# return new Python dictionary with `number_of_items` random key:value pairs
|
|
# Test: test_xdict_key_random_sample
|
|
#------------------------------------------------------------------------------
|
|
def random_sample(self, number_of_items):
|
|
import random
|
|
random_key_list = random.sample(self.keys(), number_of_items)
|
|
new_dict = {}
|
|
for item in random_key_list:
|
|
new_dict[item] = self[item]
|
|
return new_dict
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [ xitems method ] (tuple)
|
|
# Generator method that returns tuples of every key, value in dictionary
|
|
# uses appropriate method from Python 2 and 3 interpreters
|
|
# Test: test_xdict_xitems
|
|
#------------------------------------------------------------------------------
|
|
def xitems(self):
|
|
from Naked.toolshed.python import py_major_version
|
|
if py_major_version() > 2:
|
|
return self.items()
|
|
else:
|
|
return self.iteritems()
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [[ XList class ]]
|
|
# An inherited extension to the list object that permits attachment of attributes
|
|
#------------------------------------------------------------------------------
|
|
class XList(list, NakedObject):
|
|
def __init__(self, list_obj, attributes={}, naked_type='XList'):
|
|
list.__init__(self, list_obj)
|
|
NakedObject.__init__(self, attributes, naked_type)
|
|
|
|
#------------------------------------------------------------------------------
|
|
# XList Operator Overloads
|
|
#------------------------------------------------------------------------------
|
|
|
|
#------------------------------------------------------------------------------
|
|
# + operator overload
|
|
# extends XList with one or more other lists (`*other_lists`)
|
|
#------------------------------------------------------------------------------
|
|
def __add__(self, *other_lists):
|
|
try:
|
|
for the_list in other_lists:
|
|
# add attributes if it is an XList
|
|
if hasattr(the_list, '_naked_type_') and (getattr(the_list, '_naked_type_') == 'XList'):
|
|
attr_dict = the_list._getAttributeDict() # get XList attribute dictionary
|
|
if len(attr_dict) > 0:
|
|
for key in attr_dict:
|
|
setattr(self, key, attr_dict[key])
|
|
# extend the XList items
|
|
self.extend(the_list)
|
|
return self
|
|
except Exception as e:
|
|
if DEBUG_FLAG:
|
|
sys.stderr.write("Naked Framework Error: unable to combine XList with parameter provided (Naked.toolshed.types.py)")
|
|
raise e
|
|
|
|
#------------------------------------------------------------------------------
|
|
# += overload
|
|
# extends XList with one other list (`another_list`)
|
|
#------------------------------------------------------------------------------
|
|
def __iadd__(self, another_list):
|
|
try:
|
|
#add attributes if it is an XList
|
|
if hasattr(another_list, '_naked_type_') and (getattr(another_list, '_naked_type_') == 'XList'):
|
|
attr_dict = another_list._getAttributeDict() # get XList attribute dictionary
|
|
if len(attr_dict) > 0:
|
|
for key in attr_dict:
|
|
setattr(self, key, attr_dict[key])
|
|
# extend the XList items
|
|
self.extend(another_list)
|
|
return self
|
|
except Exception as e:
|
|
if DEBUG_FLAG:
|
|
sys.stderr.write("Naked Framework Error: unable to combine XList with parameter provided (Naked.toolshed.types.py)")
|
|
raise e
|
|
|
|
#------------------------------------------------------------------------------
|
|
# == overload
|
|
#------------------------------------------------------------------------------
|
|
def __eq__(self, other_obj):
|
|
return self.equals(other_obj)
|
|
|
|
#------------------------------------------------------------------------------
|
|
# != overload
|
|
#------------------------------------------------------------------------------
|
|
def __ne__(self, other_obj):
|
|
result = self.equals(other_obj)
|
|
if result:
|
|
return False # reverse result of the equals method
|
|
else:
|
|
return True
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [ equals method ] (boolean)
|
|
# tests for equality of the XList (type, attributes, list equality)
|
|
#------------------------------------------------------------------------------
|
|
def equals(self, other_obj):
|
|
if self._equal_type(other_obj) and self._equal_attributes(other_obj):
|
|
if list(self) == list(other_obj):
|
|
return True
|
|
else:
|
|
return False
|
|
else:
|
|
return False
|
|
|
|
#------------------------------------------------------------------------------
|
|
# XList Methods
|
|
#------------------------------------------------------------------------------
|
|
|
|
#------------------------------------------------------------------------------
|
|
# XList String Methods
|
|
#------------------------------------------------------------------------------
|
|
# [ join method ] (string)
|
|
# Concatenate strings in the list and return
|
|
# Default separator between string list values is an empty string
|
|
# Pass separator character(s) as an argument to the method
|
|
#------------------------------------------------------------------------------
|
|
def join(self, separator=""):
|
|
return separator.join(self)
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [ postfix method ] (list of strings)
|
|
# Append a string to each list item string
|
|
#------------------------------------------------------------------------------
|
|
def postfix(self, after):
|
|
return [ "".join([x, after]) for x in self ]
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [ prefix method ] (list of strings)
|
|
# Prepend a string to each list item string
|
|
#------------------------------------------------------------------------------
|
|
def prefix(self, before):
|
|
return [ "".join([before, x]) for x in self ]
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [ surround method ] (list of strings)
|
|
# Surround each list item string with a before and after string argument passed to the method
|
|
#------------------------------------------------------------------------------
|
|
def surround(self, before, after=""):
|
|
if after == "":
|
|
after = before
|
|
return [ "".join([before, x, after]) for x in self ]
|
|
|
|
#------------------------------------------------------------------------------
|
|
# XList Numeric Methods
|
|
#------------------------------------------------------------------------------
|
|
# [ max method ] (list dependent type, single value)
|
|
# return maximum value from the list items
|
|
#------------------------------------------------------------------------------
|
|
def max(self):
|
|
return max(self)
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [ min method ] (list dependent type, single value)
|
|
# return minimum value from the list items
|
|
#------------------------------------------------------------------------------
|
|
def min(self):
|
|
return min(self)
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [ sum method ] (list dependent type, single value)
|
|
# return the sum of all list items
|
|
#------------------------------------------------------------------------------
|
|
def sum(self):
|
|
return sum(self)
|
|
|
|
#------------------------------------------------------------------------------
|
|
# XList Data Management Methods
|
|
#------------------------------------------------------------------------------
|
|
#------------------------------------------------------------------------------
|
|
# [ count_duplicates method ] (integer)
|
|
# returns an integer count of number of duplicate values
|
|
#------------------------------------------------------------------------------
|
|
def count_duplicates(self):
|
|
return len(self) - len(set(self))
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [ remove_duplicates ] (XList)
|
|
# returns a new XList with duplicates removed
|
|
#------------------------------------------------------------------------------
|
|
def remove_duplicates(self):
|
|
return XList( set(self), self._getAttributeDict() )
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [ difference method ] (set)
|
|
# returns a set containing items in XList that are not contained in `another_list`
|
|
#------------------------------------------------------------------------------
|
|
def difference(self, another_list):
|
|
return set(self) - set(another_list)
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [ intersection method ] (set)
|
|
# returns a set containing items that are in both XList and `another_list`
|
|
#------------------------------------------------------------------------------
|
|
def intersection(self, another_list):
|
|
return set(self) & set(another_list)
|
|
|
|
#------------------------------------------------------------------------------
|
|
# XList Function Mapping Methods
|
|
#------------------------------------------------------------------------------
|
|
#------------------------------------------------------------------------------
|
|
# [ map_to_items method ] (XList)
|
|
# returns original XList with modification of each item based upon `mapped_function`
|
|
#------------------------------------------------------------------------------
|
|
def map_to_items(self, mapped_function):
|
|
# return XList( map(mapped_function, self), self._getAttributeDict() ) - slower
|
|
for index, item in enumerate(self):
|
|
self[index] = mapped_function(item)
|
|
return self
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [ conditional_map_to_items method ] (XList)
|
|
# returns original XList with modification of items that meet True condition in
|
|
# `conditional_function` with change performed as defined in `mapped_function`
|
|
#------------------------------------------------------------------------------
|
|
def conditional_map_to_items(self, conditional_function, mapped_function):
|
|
for index, item in enumerate(self):
|
|
if conditional_function(item):
|
|
self[index] = mapped_function(item)
|
|
return self
|
|
|
|
#------------------------------------------------------------------------------
|
|
# XList Descriptive Stats Methods
|
|
#------------------------------------------------------------------------------
|
|
#------------------------------------------------------------------------------
|
|
# [ count_ci method ] (integer)
|
|
# returns an integer count of the number of case-insensitive items that match `test_string`
|
|
#------------------------------------------------------------------------------
|
|
def count_ci(self, test_string):
|
|
count = 0
|
|
for item in self:
|
|
try:
|
|
if test_string.lower() in item.lower():
|
|
count += 1
|
|
except AttributeError: # the test_value was not a string, catch exception and continue count attempt
|
|
continue
|
|
return count
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [ random method ] (list)
|
|
# returns a single item list with a random element from the original XList
|
|
#------------------------------------------------------------------------------
|
|
def random(self):
|
|
import random
|
|
return random.choice(self)
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [ random_sample method ] (list)
|
|
# returns a list with one or more random items from the original XList
|
|
# number of items determined by the `number_of_items` argument
|
|
#------------------------------------------------------------------------------
|
|
def random_sample(self, number_of_items):
|
|
import random
|
|
return random.sample(self, number_of_items)
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [ shuffle method ] (XList)
|
|
# randomly shuffle the contents of the list
|
|
#------------------------------------------------------------------------------
|
|
def shuffle(self):
|
|
import random
|
|
random.shuffle(self)
|
|
return self
|
|
|
|
#------------------------------------------------------------------------------
|
|
# XList Match Methods
|
|
#------------------------------------------------------------------------------
|
|
#------------------------------------------------------------------------------
|
|
# [ wildcard_match method ] (list)
|
|
# returns a list of items that match the `wildcard` argument
|
|
#------------------------------------------------------------------------------
|
|
def wildcard_match(self, wildcard):
|
|
if hasattr(self, 'nkd_fnmatchcase'):
|
|
fnmatchcase = self.nkd_fnmatchcase
|
|
else:
|
|
from fnmatch import fnmatchcase
|
|
self.nkd_fnmatchcase = fnmatchcase
|
|
return [ x for x in self if fnmatchcase(x, wildcard) ]
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [ multi_wildcard_match method ] (list)
|
|
# returns a list of items that match one or more | separated wildcards passed as string
|
|
#------------------------------------------------------------------------------
|
|
def multi_wildcard_match(self, wildcards):
|
|
if hasattr(self, 'nkd_fnmatchcase'):
|
|
fnmatchcase = self.nkd_fnmatchcase
|
|
else:
|
|
from fnmatch import fnmatchcase
|
|
self.nkd_fnmatchcase = fnmatchcase
|
|
wc_list = wildcards.split('|')
|
|
return_list = []
|
|
for wc in wc_list:
|
|
temp_list = [ x for x in self if fnmatchcase(x, wc) ]
|
|
for result in temp_list:
|
|
return_list.append(result)
|
|
return return_list
|
|
|
|
#------------------------------------------------------------------------------
|
|
# XList Cast Methods
|
|
#------------------------------------------------------------------------------
|
|
#------------------------------------------------------------------------------
|
|
# [ xset method ] (XSet)
|
|
# return an XSet with unique XList item values and XList attributes
|
|
#------------------------------------------------------------------------------
|
|
def xset(self):
|
|
attr_dict = self._getAttributeDict()
|
|
return XSet(set(self), attr_dict)
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [ xfset method ] (XFSet)
|
|
# return an XFSet with unique XList item values and XList attributes
|
|
#------------------------------------------------------------------------------
|
|
def xfset(self):
|
|
attr_dict = self._getAttributeDict()
|
|
return XFSet(set(self), attr_dict)
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [ xtuple method ] (XTuple)
|
|
# returns an XTuple with XList item values and XList attributes
|
|
#------------------------------------------------------------------------------
|
|
def xtuple(self):
|
|
attr_dict = self._getAttributeDict()
|
|
return XTuple(tuple(self), attr_dict)
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [[ XMaxHeap class ]]
|
|
# max heap queue
|
|
#------------------------------------------------------------------------------
|
|
from heapq import heappush, heappop
|
|
class XMaxHeap(NakedObject):
|
|
def __init__(self, attributes={}, naked_type='XMaxHeap'):
|
|
NakedObject.__init__(self, attributes, naked_type)
|
|
self._queue = []
|
|
self._index = 0
|
|
|
|
# length of the queue
|
|
def __len__(self):
|
|
return len(self._queue)
|
|
|
|
# O(log n) complexity
|
|
def push(self, the_object, priority):
|
|
heappush(self._queue, (-priority, self._index, the_object))
|
|
self._index += 1
|
|
|
|
# O(log n) complexity
|
|
def pop(self):
|
|
if self._queue:
|
|
return heappop(self._queue)[-1]
|
|
else:
|
|
return None
|
|
|
|
# push new object and return the highest priority object
|
|
def pushpop(self, the_object, priority):
|
|
heappush(self._queue, (-priority, self._index, the_object))
|
|
self._index += 1
|
|
if self._queue:
|
|
return heappop(self._queue)[-1]
|
|
else:
|
|
return None # return None if the queue is empty
|
|
|
|
# the length of the queue
|
|
def length(self):
|
|
return len(self._queue)
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [[ XMinHeap class ]]
|
|
# min heap queue
|
|
#------------------------------------------------------------------------------
|
|
from heapq import heappush, heappop
|
|
class XMinHeap(NakedObject):
|
|
def __init__(self, attributes={}, naked_type='XMinHeap'):
|
|
NakedObject.__init__(self, attributes, naked_type)
|
|
self._queue = []
|
|
self._index = 0
|
|
|
|
|
|
# length of the queue
|
|
def __len__(self):
|
|
return len(self._queue)
|
|
|
|
# O(log n) complexity
|
|
def push(self, the_object, priority):
|
|
heappush(self._queue, (priority, self._index, the_object))
|
|
self._index += 1
|
|
|
|
# O(log n) complexity
|
|
def pop(self):
|
|
if self._queue:
|
|
return heappop(self._queue)[-1]
|
|
else:
|
|
return None # return None if the queue is empty
|
|
|
|
# push new object and return the lowest priority object
|
|
def pushpop(self, the_object, priority):
|
|
heappush(self._queue, (priority, self._index, the_object))
|
|
self._index += 1
|
|
if self._queue:
|
|
return heappop(self._queue)[-1]
|
|
else:
|
|
return None #return None if the queue is empty
|
|
|
|
# the length of the queue
|
|
def length(self):
|
|
return len(self._queue)
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [[ XQueue class ]]
|
|
#
|
|
#------------------------------------------------------------------------------
|
|
from collections import deque
|
|
class XQueue(deque, NakedObject):
|
|
def __init__(self, initial_iterable=[], attributes={}, max_length=10, naked_type='XQueue'):
|
|
deque.__init__(self, initial_iterable, max_length)
|
|
NakedObject.__init__(self, attributes, naked_type)
|
|
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [[ XSet class ]]
|
|
# An inherited extension to the mutable set object that permits attribute assignment
|
|
# Inherits from set and from NakedObject (see methods in NakedObject at top of this module
|
|
#------------------------------------------------------------------------------
|
|
class XSet(set, NakedObject):
|
|
def __init__(self, set_obj, attributes={}, naked_type='XSet'):
|
|
set.__init__(self, set_obj)
|
|
NakedObject.__init__(self, attributes, naked_type)
|
|
|
|
# += operator overload to extend the XSet with a second set
|
|
def __iadd__(self, another_set):
|
|
self.update(another_set)
|
|
return self
|
|
|
|
def xlist(self):
|
|
attr_dict = self._getAttributeDict()
|
|
return XList(list(self), attr_dict)
|
|
|
|
def xfset(self):
|
|
attr_dict = self._getAttributeDict()
|
|
return XFSet(self, attr_dict)
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [[ XFSet class ]]
|
|
# An inherited extension to the immutable frozenset object that permits attribute assignment
|
|
# Immutable so there is no setter method, attributes must be set in the constructor
|
|
#------------------------------------------------------------------------------
|
|
class XFSet(frozenset):
|
|
def __new__(cls, the_set, attributes={}, naked_type="XFSet"):
|
|
set_obj = frozenset.__new__(cls, the_set)
|
|
if len(attributes) > 0:
|
|
for key in attributes:
|
|
setattr(set_obj, key, attributes[key])
|
|
setattr(set_obj, '_naked_type_', naked_type) # set the naked extension type as an attribute (NakedObject does this for mutable classes)
|
|
return set_obj
|
|
|
|
def _getAttributeDict(self):
|
|
return self.__dict__
|
|
|
|
def xlist(self):
|
|
attr_dict = self._getAttributeDict()
|
|
return XList(list(self), attr_dict, naked_type="XList")
|
|
|
|
def xset(self):
|
|
attr_dict = self._getAttributeDict()
|
|
return XSet(self, attr_dict, naked_type="XSet")
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [ type method ] (string)
|
|
# returns the Naked type extension string that is set in the constructor for each object type
|
|
#------------------------------------------------------------------------------
|
|
def type(self):
|
|
if hasattr(self, '_naked_type_'):
|
|
return self._naked_type_
|
|
else:
|
|
return None
|
|
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [[ XString class ]]
|
|
# An inherited extension to the immutable string object that permits attribute assignment
|
|
# Immutable so there is no setter method, attributes must be set in the constructor
|
|
# Python 2: byte string by default, can cast to normalized UTF-8 with XString().unicode() method
|
|
# Python 3: string (that permits unicode) by default, can normalize with XString().unicode() method
|
|
#------------------------------------------------------------------------------
|
|
class XString(str):
|
|
def __new__(cls, string_text, attributes={}, naked_type='XString'):
|
|
str_obj = str.__new__(cls, string_text)
|
|
if len(attributes) > 0:
|
|
for key in attributes:
|
|
setattr(str_obj, key, attributes[key])
|
|
setattr(str_obj, '_naked_type_', naked_type)
|
|
return str_obj
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [ _getAttributeDict method ] (dictionary)
|
|
# returns a dictionary of the XString instance attributes
|
|
#------------------------------------------------------------------------------
|
|
def _getAttributeDict(self):
|
|
return self.__dict__
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [ type method ] (string)
|
|
# returns the Naked type extension string that is set in the constructor for each object type
|
|
#------------------------------------------------------------------------------
|
|
def type(self):
|
|
if hasattr(self, '_naked_type_'):
|
|
return self._naked_type_
|
|
else:
|
|
return None
|
|
|
|
## TODO: see where + vs. join breakpoint becomes important
|
|
def concat(self, *strings):
|
|
str_list = []
|
|
for x in strings:
|
|
str_list.append(x)
|
|
return "".join(str_list)
|
|
|
|
# fastest substring search truth test
|
|
def contains(self, substring):
|
|
return substring in self
|
|
|
|
# split the string on one or more delimiters, return list
|
|
# if up to two chars, then uses str.split(), if more chars then use re.split
|
|
def xsplit(self, split_delimiter):
|
|
length = len(split_delimiter)
|
|
if length > 2:
|
|
import re
|
|
split_delimiter = "".join([ '[', split_delimiter, ']' ])
|
|
return re.split(split_delimiter, self)
|
|
elif length > 1:
|
|
delim2 = split_delimiter[1]
|
|
first_list = self.split(split_delimiter[0])
|
|
result_list = []
|
|
for item in first_list:
|
|
for subitem in item.split(delim2):
|
|
result_list.append(subitem)
|
|
return result_list
|
|
else:
|
|
return self.split(split_delimiter)
|
|
|
|
# split the string on one or more characters and return items in set
|
|
def xsplit_set(self, split_delimiter):
|
|
return set(self.xsplit(split_delimiter))
|
|
|
|
# str begins with substring - faster than str.startswith()
|
|
def begins(self, begin_string):
|
|
return begin_string in self[0:len(begin_string)]
|
|
|
|
# str ends with substring - faster than str.endswith()
|
|
def ends(self, end_string):
|
|
return end_string in self[-len(end_string):]
|
|
|
|
# case sensitive wildcard match on the XString (boolean returned)
|
|
def wildcard_match(self, wildcard):
|
|
from fnmatch import fnmatchcase
|
|
return fnmatchcase(self, wildcard)
|
|
|
|
# convert string to normalized UTF-8 in Python 2 and 3 (##TODO: convert to XUnicode with attributes?)
|
|
def unicode(self):
|
|
from sys import version_info
|
|
from unicodedata import normalize
|
|
if version_info[0] == 2:
|
|
return normalize('NFKD', self.decode('UTF-8'))
|
|
else:
|
|
return normalize('NFKD', self)
|
|
|
|
|
|
# this version works
|
|
class XUnicode:
|
|
def __init__(self, string_text, attributes={}, naked_type='XUnicode'):
|
|
import sys
|
|
import unicodedata
|
|
norm_text = unicodedata.normalize('NFKD', string_text)
|
|
|
|
class XUnicode_2(unicode):
|
|
def __new__(cls, the_string_text, attributes={}, naked_type='XUnicode2'):
|
|
str_obj = unicode.__new__(cls, the_string_text)
|
|
if len(attributes) > 0:
|
|
for key in attributes:
|
|
setattr(str_obj, key, attributes[key])
|
|
setattr(str_obj, '_naked_type_', naked_type) # set the type to XUnicode2 for Py 2 strings
|
|
return str_obj
|
|
|
|
class XUnicode_3(str):
|
|
def __new__(cls, the_string_text, attributes={}, naked_type='XUnicode3'):
|
|
str_obj = str.__new__(cls, the_string_text)
|
|
if len(attributes) > 0:
|
|
for key in attributes:
|
|
setattr(str_obj, key, attributes[key])
|
|
setattr(str_obj, '_naked_type_', naked_type) # set the type to XUnicode3 for Py 3 strings
|
|
return str_obj
|
|
|
|
|
|
if sys.version_info[0] == 2:
|
|
self.obj = XUnicode_2(norm_text, attributes)
|
|
self.norm_unicode = norm_text
|
|
self.naked_u_string = self.obj.encode('utf-8') # utf-8 encoded byte string
|
|
elif sys.version_info[0] == 3:
|
|
self.naked_u_string = XUnicode_3(norm_text, attributes).encode('utf-8') # ?
|
|
|
|
def __str__(self):
|
|
# return self.naked_u_string
|
|
return self.obj
|
|
|
|
def __repr__(self):
|
|
return self.naked_u_string
|
|
|
|
def __getattr__(self, the_attribute):
|
|
return self.obj.__dict__[the_attribute]
|
|
|
|
def __cmp__(self, other_string):
|
|
return hash(self.naked_u_string) == hash(other_string)
|
|
# TODO: add check for same attributes
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [ _getAttributeDict method ] (dictionary)
|
|
# returns a dictionary of the NakedObject instance attributes
|
|
#------------------------------------------------------------------------------
|
|
def _getAttributeDict(self):
|
|
return self.__dict__
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [ type method ] (string)
|
|
# returns the Naked type extension string that is set in the constructor for each object type
|
|
#------------------------------------------------------------------------------
|
|
def type(self):
|
|
if hasattr(self, '_naked_type_'):
|
|
return self._naked_type_
|
|
else:
|
|
return None
|
|
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [[ XTuple class ]]
|
|
#
|
|
#------------------------------------------------------------------------------
|
|
class XTuple(tuple):
|
|
def __new__(cls, the_tuple, attributes={}, naked_type='XTuple'):
|
|
tup_obj = tuple.__new__(cls, the_tuple)
|
|
if len(attributes) > 0:
|
|
for key in attributes:
|
|
setattr(tup_obj, key, attributes[key])
|
|
setattr(tup_obj, '_naked_type_', naked_type)
|
|
return tup_obj
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [ _getAttributeDict method ] (dictionary)
|
|
# returns a dictionary of the NakedObject instance attributes
|
|
#------------------------------------------------------------------------------
|
|
def _getAttributeDict(self):
|
|
return self.__dict__
|
|
|
|
#------------------------------------------------------------------------------
|
|
# [ type method ] (string)
|
|
# returns the Naked type extension string that is set in the constructor for each object type
|
|
#------------------------------------------------------------------------------
|
|
def type(self):
|
|
if hasattr(self, '_naked_type_'):
|
|
return self._naked_type_
|
|
else:
|
|
return None
|
|
|
|
|
|
if __name__ == '__main__':
|
|
pass
|
|
# no = nobj({"version":"1.0.1", "test":"code"})
|
|
# print(no)
|
|
# print(no.version)
|
|
# print(no.test)
|
|
# nl = XList([1, 2, 3, 1, 2, 5], {"version":"1.0.1", "test":"code"})
|
|
# print(nl.count_duplicates())
|
|
# the_list = list(range(5000))
|
|
# nl = XList(the_list)
|
|
# nq = XPriorityQueue()
|
|
# nq.push('test', 5)
|
|
# nq.push('one', 3)
|
|
# nq.push('another', 4)
|
|
# print(nq.pop())
|
|
# print(nq.pop())
|
|
# print(nq.pop())
|
|
|
|
# nl = XList([2, 2, 2, 'another'], {'p': 'attribute'})
|
|
# print(nl)
|
|
# print(nl.count_item(2))
|
|
# nq = XQueue(nl, max_length=2)
|
|
# print(nq)
|
|
|
|
# xs = XSet({'test', 'true', 'false'}, {'bonus': 'candy', 'test': 'another'})
|
|
# xs += {'bogus', 'yep'}
|
|
# print(xs)
|
|
|
|
# xd = XDict({'test2': 0, 'is': 1}, {'a': '1', 'b': '2'})
|
|
# ad = {'test': 0, 'is': 2}
|
|
# ld = xd.intersection(ad)
|
|
# print(ld)
|
|
# xd = xd + ad + ld
|
|
# print(xd.map_to_vals(pr))
|
|
# print(xd.a)
|
|
# print(xd)
|
|
# print(xd.a)
|
|
# print(xd.min_val())
|
|
# print(xd.conditional_map_to_vals(matcher, resulter))
|
|
|
|
# nl = XList([ 'test.txt', 'bogus.txt', 'test.py', 'another.rb', 'est.doc', 'est.py' ])
|
|
# print(nl.multi_wildcard_match('*.py|*.txt|*.doc'))
|
|
|
|
# xstr = XString("Hey! Cœur It's Bengali ব য,\nand here is some more ২")
|
|
# ustr = xstr.unicode()
|
|
# print(isinstance(ustr, bytes))
|
|
# print(xstr)
|
|
|