Source code for objconfig.config
r"""
This is a port of zend-config to Python
Some idioms of PHP are still employed, but where possible I have Pythonized it
IGNORE:
Author: Asher Wolfstein Copyright 2017
Blog: http://wunk.me/
E-Mail: asherwunk@gmail.com
Twitter: https://twitter.com/asherwolfstein Send Me Some Love!
Package Homepage: http://wunk.me/programming-projects/objconfig-python/
GitHub: http://github.com/asherwunk/objconfig for the source repository
DevPost: https://devpost.com/software/objconfig
Buy Me A Coffee: https://ko-fi.com/A18224XC
Support Me On Patreon: https://www.patreon.com/asherwolfstein
IGNORE
Following is the header as given in zend-config::
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the
* canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc.
* (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
"""
from objconfig.util import ArrayAccess
from objconfig.util import Countable
from objconfig.util import Iterator
from objconfig.exception import InvalidArgumentException
from objconfig.exception import RuntimeException
import inspect
import copy
[docs]class Config(Countable, Iterator, ArrayAccess):
r"""
Following is the class documentation as given in zend-config::
/**
* Provides a property based interface to an array.
* The data are read-only unless $allowModifications is set to true
* on construction.
*
* Implements Countable, Iterator and ArrayAccess
* to facilitate easy access to the data.
*/
"""
def __len__(self):
r"""
Notes:
__len__ is inherited from Countable
"""
return len(self.__data)
def __iter__(self):
r"""
Notes:
__iter__ is inherited from Iterator
"""
def ConfigIterator(self):
for key, value in self.__data.items():
yield key, value
return ConfigIterator(self)
def __getitem__(self, key):
r"""
Notes:
__getitem__ is inherited by ArrayAccess
Following is the header as given in zend-config::
/**
* offsetGet(): defined by ArrayAccess interface.
*
* @see ArrayAccess::offsetGet()
* @param mixed $offset
* @return mixed
*/
"""
return self.__data[key]
def __setitem__(self, key, value):
r"""
Notes:
__setitem_ is inherited by ArrayAccess
Following is the header as given in zend-config::
/**
* offsetSet(): defined by ArrayAccess interface.
*
* @see ArrayAccess::offsetSet()
* @param mixed $offset
* @param mixed $value
* @return void
*/
"""
self.__setattr__(key, value)
def __delitem__(self, key):
r"""
Notes:
__delitem__ is inherited by ArrayAccess
Following is the header as given in zend-config::
/**
* offsetUnset(): defined by ArrayAccess interface.
*
* @see ArrayAccess::offsetUnset()
* @param mixed $offset
* @return void
*/
"""
self.__delattr__(key)
def __init__(self, array, allowModifications=False):
r"""
Notes:
array is expected to be instance of dict
OR implement toArray() method
Following is the header as given in zend-config::
/**
* Constructor.
*
* Data is read-only unless $allowModifications is set to true
* on construction.
*
* @param array $array
* @param bool $allowModifications
*/
"""
r"""
Following is the header as given in zend-config::
/**
* Whether modifications to configuration data are allowed.
*
* @var bool
*/
"""
self.__allowModifications = allowModifications
r"""
protected $skipNextIteration;
Notes:
Not necessary, items() is an up-to-date view
Following is the header as given in zend-config::
/**
* Used when unsetting values during iteration to ensure we do not skip
* the next element.
*
* @var bool
*/
""" # -- unnecessary
r"""
Following is the header as given in zend-config::
/**
* Data within the configuration.
*
* @var array
*/
"""
self.__data = {}
try:
for key, value in array.items():
if 'items' in dir(value):
self.__data[key] = Config(value, allowModifications=self.__allowModifications)
elif 'toArray' in dir(value) and inspect.ismethod(value.toArray):
self.__data[key] = Config(value.toArray(), allowModifications=self.__allowModifications)
else:
self.__data[key] = value
except AttributeError:
raise InvalidArgumentException("Config: __init__ passed array doesn't implement key, value : items()")
[docs] def get(self, name, default=None):
r"""
Following is the header as given in zend-config::
/**
* Retrieve a value and return $default if there is no element set.
*
* @param string $name
* @param mixed $default
* @return mixed
*/
"""
if name in self.__data:
return self.__data[name]
return default
def __getattr__(self, attribute):
r"""
Following is the header as given in zend-config::
/**
* Magic function so that $obj->value will work.
*
* @param string $name
* @return mixed
*/
"""
return self.get(attribute)
def __setattr__(self, attribute, value):
r"""
Notes:
Makes a new Config object if value implements key, value : items()
Following is the header as given in zend-config::
/**
* Set a value in the config.
*
* Only allow setting of a property if $allowModifications was set to true
* on construction. Otherwise, throw an exception.
*
* @param string $name
* @param mixed $value
* @return void
* @throws Exception\RuntimeException
*/
"""
if attribute == '_Config__allowModifications':
self.__dict__['_Config__allowModifications'] = value
return
elif attribute == '_Config__data':
self.__dict__['_Config__data'] = value
return
if self.__allowModifications:
if isinstance(value, dict):
self.__data[attribute] = Config(value, allowModifications=True)
elif attribute is None:
raise RuntimeException("Config: __setattr__ attribute must have name")
else:
self.__data[attribute] = value
else:
raise RuntimeException("Config: __setattr__ config is read only")
def __deepcopy__(self, memo):
r"""
Following is the header as given in zend-config::
/**
* Deep clone of this instance to ensure that nested Zend\Configs are also
* cloned.
*
* @return void
*/
"""
copyconf = Config({}, allowModifications=True)
for key, value in self:
setattr(copyconf, key, copy.deepcopy(value))
if not self.__allowModifications:
copyconf.isReadOnly()
return copyconf
def _clone(self):
return copy.deepcopy(self)
[docs] def copy(self):
return self._clone()
[docs] def toArray(self):
r"""
Notes:
Returns a copy of the data dictionar(ies)
Following is the header as given in zend-config::
/**
* Return an associative array of the stored data.
*
* @return array
*/
"""
array = {}
for key, value in self.__data.items():
if 'toArray' in dir(value) and inspect.ismethod(value.toArray):
array[key] = value.toArray()
else:
array[key] = value
return array
def _isset(self, attribute):
r"""
Following is the header as given in zend-config::
/**
* isset() overloading
*
* @param string $name
* @return bool
*/
"""
return attribute in self.__data
def __delattr__(self, attribute):
r"""
Following is the header as given in zend-config::
/**
* unset() overloading
*
* @param string $name
* @return void
* @throws Exception\InvalidArgumentException
*/
"""
if self.__allowModifications:
del self.__data[attribute]
else:
raise InvalidArgumentException("Config: __delattr__ config is read only")
[docs] def merge(self, merge):
r"""
Notes:
Checks for existence of merge and toArray method
Following is the header as given in zend-config::
/**
* Merge another Config with this one.
*
* For duplicate keys, the following will be performed:
* - Nested Configs will be recursively merged.
* - Items in $merge with INTEGER keys will be appended.
* - Items in $merge with STRING keys will overwrite current values.
*
* @param Config $merge
* @return Config
*/
"""
for key, value in merge:
if key in self.__data:
if (('toArray' in dir(value) and inspect.ismethod(value.toArray))
and ('merge' in dir(self) and inspect.ismethod(self.__data[key].merge))):
self.__data[key].merge(value)
elif 'toArray' in dir(value) and inspect.ismethod(value.toArray):
self.__data[key] = Config(value.toArray(), allowModifications=self.__allowModifications)
else:
self.__data[key] = value
else:
if 'toArray' in dir(value) and inspect.ismethod(value.toArray):
self.__data[key] = Config(value.toArray(), allowModifications=self.__allowModifications)
else:
self.__data[key] = value
return self
[docs] def setReadOnly(self):
r"""
Note:
Tests for setReadOnly method
Following is the header as given in zend-config::
/**
* Prevent any more modifications being made to this instance.
*
* Useful after merge() has been used to merge multiple Config objects
* into one object which should then not be modified again.
*
* @return void
*/
"""
self.__allowModifications = False
for value in self.__data.values():
if 'setReadOnly' in dir(value) and inspect.ismethod(value.setReadOnly):
value.setReadOnly()
[docs] def isReadOnly(self):
r"""
Following is the header as given in zend-config::
/**
* Returns whether this Config object is read only or not.
*
* @return bool
*/
"""
return not self.__allowModifications