#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
"""
this file provides the VariableVariablesManager class for manually handling
variable variables, i.e. variables with variable name.
The class:
- handles local and global variables
- can handle renaming of variable
- will provide basic constness
- can check for type equality, if desired
example:
x = VariableVariablesManager()
x['test'] = 25
x.defineConstVariable('myconst', 13)
x.renameVariable('myconst', 'myconstOther')
The code and the unit tests both work for python 2 and 3 (tested with 2.7 and 3.5)
See the respective unit test file for some examples.
"""
__author__ = "Dominik Rueß"
__copyright__ = "Copyright 2016, Dominik Rueß"
__credits__ = ["Dominik Rueß"]
__license__ = "GPLv3"
__version__ = "1.0.1"
__maintainer__ = "Dominik Rueß"
__email__ = "info@dominik-ruess.de"
__status__ = "Development"
def globalSingleton(cls):
"""
a quasi singleton pattern decorator which keeps one global instance and
returns new instances on every request, linking to the global one
"""
globalInstance = None
instances = {}
def getinstance(*args, **kwargs):
if cls not in instances:
if 'globalManager' in kwargs:
del kwargs['globalManager']
instances[cls] = cls(*args, **kwargs)
kwargs['globalManager'] = instances[cls]
out = cls(*args, **kwargs)
return out
return getinstance
def variableExists(func):
"""
a decorator for classes which asserts that in the dict class.variables
a variable exists
"""
def checkIfVariableExists(*args):
"""
test, if in VariableVariablesManager object's dict 'variables' a
key exists and if so, run decorated function
"""
manager = args[0]
variableName = args[1]
foundGlobally = manager.globalManager \
and variableName in manager.globalManager.variables
foundLocally = variableName in manager.variables
if not foundGlobally and not foundLocally:
raise NameError("variable variable '%s' not defined"
% variableName)
return func(*args)
return checkIfVariableExists
@globalSingleton
class VariableVariablesManager:
"""
VariableVariablesManager is a class, loosely based on the singleton and
object-pool pattern.
It will provide a centralized variable pool with variable names. You can:
- define variables as const
- enforce type equality on re-assignment
- define variables with local scope
"""
DEBUG_PREFIX = "Variable variables manager"
def __init__(self,
enforceSameTypeOnOverride = False,
debug = False,
globalManager = None):
"""
initialize the settings for the variables manager
(only once, due to singleton)
note: parameter 'globalManager' is for internal use, it will be
overridden
"""
self.enforceSameTypeOnOverride = enforceSameTypeOnOverride
self.printDebug = debug
self.variables = {}
self.constVariables = []
self.globalManager = globalManager
if not self.globalManager:
self.DEBUG_PREFIX += " (global variables manager)"
def _debug(self, message):
"""
if debug mode is on, print the message
"""
if self.printDebug:
print(self.DEBUG_PREFIX + ": " + message)
def _isGlobal(self, variableName):
"""
test if a given variable is already declared global
"""
# test this is not already the globalManager
return self.globalManager \
and variableName in self.globalManager.variables
@variableExists
def __getitem__(self, variableName):
"""
return the content of a named variable
"""
if self._isGlobal(variableName):
return self.globalManager.__getitem__(variableName)
else:
self._debug("getting var '%s'" % variableName)
return self.variables[variableName]
def __setitem__(self, variableName, value):
"""
assign content to a named variable.
- will check for constness, if a variable exists
- will check for type equality if a variable exists
"""
if self._isGlobal(variableName):
self.globalManager.__setitem__(variableName, value)
else:
# test for type equality (if desired)
if variableName in self.variables \
and self.enforceSameTypeOnOverride \
and type(value) != type(self.variables[variableName]):
raise TypeError("VariableVariablesManager was set up "
"to enforce type equality: tried to assign to "
"variable '%s' of type '%s' a value of type '%s'"
" with value: '%s')"
% (variableName,
type(self.variables[variableName]),
type(value),
str(value)))
# test for constness (if desired)
if variableName in self.constVariables:
raise AttributeError("tried assigning a value to"
"const variable '%s'" % variableName)
self._debug("assigning var '%s' with value '%s'"
% (variableName, str(value)))
self.variables[variableName] = value
@variableExists
def __delitem__(self, variableName):
"""
delete a variable variable, e.g.
del manager['myVariableName']
"""
if self._isGlobal(variableName):
self.globalManager.__delitem__(variableName)
else:
del self.variables[variableName]
# don't forget to remove possible appearance in const-references
#(and global references)
if variableName in self.constVariables:
self.constVariables.remove(variableName)
self._debug("deleted var '%s'" % variableName)
def defineConstVariable(self, variableName, value, setGlobal = False):
"""
mark the (new) variable variable as constant, such that the
value cannot be changed
"""
if self._isGlobal(variableName) or setGlobal:
self.globalManager.defineConstVariable(variableName, value)
else:
self._debug("assigning const variable '%s':" % variableName)
self.__setitem__(variableName, value)
self.constVariables.append(variableName)
def defineGlobalVariable(self, variableName, value):
"""
mark the (new) variable variable as global
(and possibly remove old, matching local content)
"""
if self.globalManager:
# we're in local scope
self.globalManager.__setitem__(variableName, value)
if variableName in self.variables:
# delete local variable with same name
del self.variables[variableName]
else:
# this is the global manager
variablePath = self._getCallStackStr(variableName)
self._debug("assigning const variable '%s':" % variableName)
self.__setitem__(variableName, value)
@variableExists
def renameVariable(self, currentName, newName):
"""
rename a variable variable to a different variable name. The
value will stay the same
"""
if self._isGlobal(currentName):
self.globalManager.renameVariable(currentName, newName)
else:
if newName in self.variables:
raise NameError("variable variable '%s' already defined"
" when renaming variable '%s'"
% (newName, currentName))
self.variables[newName] = self.variables[currentName]
del self.variables[currentName]
if currentName in self.constVariables:
self.constVariables.remove(currentName)
self.constVariables.append(newName)
self._debug("renamed var '%s' to var '%s'"
% (currentName, newName))