def testUniq(self): in_lst = ["B", "A", "B", "a", "1", "b", "/", "", "2"] expected = ["B", "A", "a", "1", "b", "/", "", "2"] result = Array.uniq(in_lst) self.assertEqual(result, expected) in_lst = { "B": 0, "A": 1, "B": 0, "a": 2, "1": 3, "b": 4, "/": 5, "": "d", "2": 6 } expected = { "B": 0, "A": 1, "a": 2, "1": 3, "b": 4, "/": 5, "": "d", "2": 6 } result = Array.uniq(in_lst) self.assertEqual(result, expected)
def _mergeValues(self, leftSide, rightSide): """Merges values together. @param leftSide: mixed The left side to merge. @param rightSide: mixed The right side to merge. @return mixed The merged values @raise InvalidConfigurationException: @raise RuntimeException: """ if rightSide is False: # if this is still false after the last config has been merged the # finalization pass will take care of removing this key entirely return False; if not leftSide or not self._performDeepMerging: return rightSide; if isinstance(leftSide, list): leftSide = Array.toDict(leftSide); if isinstance(rightSide, list): rightSide = Array.toDict(rightSide); i = -1; for k, v in rightSide.items(): i += 1; # prototype, and key is irrelevant, so simply append the element if self._keyAttribute is None: # dict: append index = 0; while index in leftSide: index += 1; leftSide[index] = v; continue; # no conflict if k not in leftSide: if not self._allowNewKeys: ex = InvalidConfigurationException( 'You are not allowed to define new elements for path ' '"{0}". Please define all elements for this path in ' 'one config file. If you are trying to overwrite an ' 'element, make sure you redefine it with the same ' 'name.'.format(self.getPath()) ); ex.setPath(self.getPath()); raise ex; leftSide[k] = v; continue; self._prototype.setName(k); leftSide[k] = self._prototype.merge(leftSide[k], v); return leftSide;
def _mergeValues(self, leftSide, rightSide): """Merges values together. @param leftSide: mixed The left side to merge. @param rightSide: mixed The right side to merge. @return mixed The merged values @raise InvalidConfigurationException: @raise RuntimeException: """ if rightSide is False: # if this is still false after the last config has been merged the # finalization pass will take care of removing this key entirely return False if not leftSide or not self._performDeepMerging: return rightSide if isinstance(leftSide, list): leftSide = Array.toDict(leftSide) if isinstance(rightSide, list): rightSide = Array.toDict(rightSide) i = -1 for k, v in rightSide.items(): i += 1 # prototype, and key is irrelevant, so simply append the element if self._keyAttribute is None: # dict: append index = 0 while index in leftSide: index += 1 leftSide[index] = v continue # no conflict if k not in leftSide: if not self._allowNewKeys: ex = InvalidConfigurationException( 'You are not allowed to define new elements for path ' '"{0}". Please define all elements for this path in ' 'one config file. If you are trying to overwrite an ' 'element, make sure you redefine it with the same ' 'name.'.format(self.getPath())) ex.setPath(self.getPath()) raise ex leftSide[k] = v continue self._prototype.setName(k) leftSide[k] = self._prototype.merge(leftSide[k], v) return leftSide
def testUniq(self): in_lst = ["B", "A", "B", "a", "1", "b", "/", "", "2"]; expected = ["B", "A", "a", "1", "b", "/", "", "2"]; result = Array.uniq(in_lst); self.assertEqual(result, expected); in_lst = {"B":0, "A":1, "B":0, "a":2, "1":3, "b":4, "/":5, "":"d", "2":6}; expected = {"B":0, "A":1, "a":2, "1":3, "b":4, "/":5, "":"d", "2":6}; result = Array.uniq(in_lst); self.assertEqual(result, expected);
def __init__(self, definition=None): if definition is None: definition = InputDefinition() assert isinstance(definition, InputDefinition) self.__routes = Array() # @var: Route[] self.__resources = list() # @var: list self.__definition = definition
def __writeArray(self, array, depth): assert isinstance(array, (list, dict)) isIndexed = False if isinstance(array, dict): for key in array.keys(): if not isinstance(key, int): isIndexed = False break else: isIndexed = True array = Array.toDict(array) for key, value in array.items(): if isinstance(value, (list, dict)): val = '' else: val = value if (isIndexed): self.__writeLine('- ' + val, depth * 4) else: self.__writeLine('{0:20} {1}'.format(key + ':', val), depth * 4) if isinstance(value, (list, dict)): self.__writeArray(value, depth + 1)
def __writeArray(self, array, depth): assert isinstance(array, (list, dict)); isIndexed = False; if isinstance(array, dict): for key in array.keys(): if not isinstance(key, int): isIndexed = False; break; else: isIndexed = True; array = Array.toDict(array); for key, value in array.items(): if isinstance(value, (list, dict)) : val = ''; else : val = value; if (isIndexed) : self.__writeLine('- '+val, depth * 4); else : self.__writeLine('{0:20} {1}'.format( key+':', val), depth * 4); if isinstance(value, (list, dict)) : self.__writeArray(value, depth + 1);
def __isInlineableDefinition(self, container, identifier, definition): """Checks if the definition is inlineable.: @param ContainerBuilder container @param string identifier @param Definition definition @return Boolean If the definition is inlineable """ assert isinstance(definition, Definition); assert isinstance(container, ContainerBuilder); if (ContainerInterface.SCOPE_PROTOTYPE == definition.getScope()) : return True; if (definition.isPublic()) : return False; if ( not self.__graph.hasNode(identifier)) : return True; ids = list(); for edge in self.__graph.getNode(identifier).getInEdges(): ids.append(edge.getSourceNode().getId()); if (len(Array.uniq(ids)) > 1) : return False; return container.getDefinition(ids[0]).getScope() == definition.getScope();
def __isInlineableDefinition(self, container, identifier, definition): """Checks if the definition is inlineable.: @param ContainerBuilder container @param string identifier @param Definition definition @return Boolean If the definition is inlineable """ assert isinstance(definition, Definition) assert isinstance(container, ContainerBuilder) if (ContainerInterface.SCOPE_PROTOTYPE == definition.getScope()): return True if (definition.isPublic()): return False if (not self.__graph.hasNode(identifier)): return True ids = list() for edge in self.__graph.getNode(identifier).getInEdges(): ids.append(edge.getSourceNode().getId()) if (len(Array.uniq(ids)) > 1): return False return container.getDefinition( ids[0]).getScope() == definition.getScope()
def getResources(self): """Returns an array of resources loaded to build this collection. @return: ResourceInterface[] An array of resources """ return Array.uniq(self.__resources, str)
def ifNotInArray(self, target): """Tests if the value is not in an array @param target: dict @return: ExprBuilder """ self.ifPart = lambda v: v not in Array.toDict(target).values(); return self;
def getServiceIds(self): """Gets all service ids. @return: list A list of all defined service ids """ return Array.uniq( list(self.getDefinitions().keys()) +\ list(self.getAliases().keys()) +\ Container.getServiceIds(self) )
def values(self, values): assert isinstance(values, list); values = Array.uniq(values); if (len(values) <= 1) : raise InvalidArgumentException('.values() must be called with at least two distinct values.'); self.__values = values; return self;
def process(self, container): """Processes the ContainerBuilder to remove unused definitions. @param ContainerBuilder container """ assert isinstance(container, ContainerBuilder); compiler = container.getCompiler(); formatter = compiler.getLoggingFormatter(); graph = compiler.getServiceReferenceGraph(); hasChanged = False; definitions = container.getDefinitions().copy(); for identifier, definition in definitions.items(): if (definition.isPublic()) : continue; if (graph.hasNode(identifier)) : edges = graph.getNode(identifier).getInEdges(); referencingAliases = list(); sourceIds = list(); for edge in edges: node = edge.getSourceNode(); sourceIds.append(node.getId()); if (node.isAlias()) : referencingAliases.append(node.getValue()); isReferenced = (len(Array.uniq(sourceIds)) - len(referencingAliases)) > 0; else : referencingAliases = list(); isReferenced = False; if (1 == len(referencingAliases) and False is isReferenced) : container.setDefinition(str(referencingAliases[0]), definition); definition.setPublic(True); container.removeDefinition(identifier); compiler.addLogMessage(formatter.formatRemoveService(self, identifier, 'replaces alias '+referencingAliases[0])); elif (0 == len(referencingAliases) and False is isReferenced) : container.removeDefinition(identifier); compiler.addLogMessage(formatter.formatRemoveService(self, identifier, 'unused')); hasChanged = True; if (hasChanged) : self.__repeatedPass.setRepeat();
def process(self, container): """Processes the ContainerBuilder to remove unused definitions. @param ContainerBuilder container """ assert isinstance(container, ContainerBuilder) compiler = container.getCompiler() formatter = compiler.getLoggingFormatter() graph = compiler.getServiceReferenceGraph() hasChanged = False definitions = container.getDefinitions().copy() for identifier, definition in definitions.items(): if (definition.isPublic()): continue if (graph.hasNode(identifier)): edges = graph.getNode(identifier).getInEdges() referencingAliases = list() sourceIds = list() for edge in edges: node = edge.getSourceNode() sourceIds.append(node.getId()) if (node.isAlias()): referencingAliases.append(node.getValue()) isReferenced = (len(Array.uniq(sourceIds)) - len(referencingAliases)) > 0 else: referencingAliases = list() isReferenced = False if (1 == len(referencingAliases) and False is isReferenced): container.setDefinition(str(referencingAliases[0]), definition) definition.setPublic(True) container.removeDefinition(identifier) compiler.addLogMessage( formatter.formatRemoveService( self, identifier, 'replaces alias ' + referencingAliases[0])) elif (0 == len(referencingAliases) and False is isReferenced): container.removeDefinition(identifier) compiler.addLogMessage( formatter.formatRemoveService(self, identifier, 'unused')) hasChanged = True if (hasChanged): self.__repeatedPass.setRepeat()
def getServiceIds(self): """Gets all service ids. @return: list An array of all defined service ids """ ids = list() r = ReflectionObject(self) for method in r.getMethods(): match = re.search('^get(.+)Service$', method.getName()) if match: ids.append(self.underscore(match.group(1))) return Array.uniq(ids + list(self._services.keys()))
def setDefaultValue(self, value): """Sets the default value of this node. @param value: dict @raise InvalidArgumentException: if the default value is not an array """ if isinstance(value, list): value = Array.toDict(value) if not isinstance(value, dict): raise InvalidArgumentException( '{0}: the default value of an array node has to be an array.' ''.format(self.getPath())) self._defaultValue = value
def _mergeValues(self, leftSide, rightSide): """Merges values together. @param leftSide: mixed The left side to merge. @param rightSide: mixed The right side to merge. @return: mixed The merged values @rasie InvalidConfigurationException: @rasie RuntimeException: """ if rightSide is False: # if this is still false after the last config has been merged the # finalization pass will take care of removing this key entirely return False; if not leftSide or not self._performDeepMerging: return rightSide; if isinstance(rightSide, list): rightSide = Array.toDict(rightSide); for k, v in rightSide.items(): # no conflict if k not in leftSide: if not self._allowNewKeys: ex = InvalidConfigurationException( 'You are not allowed to define new elements for path ' '"{0}". Please define all elements for this path in ' 'one config file. If you are trying to overwrite an ' 'element, make sure you redefine it with the same ' 'name.'.format(self.getPath()) ); ex.setPath(self.getPath()); raise ex; leftSide[k] = v; continue; if k not in self._children: raise RuntimeException( 'merge() expects a normalized config array.' ); leftSide[k] = self._children[k].merge(leftSide[k], v); return leftSide;
def __init__(self, name, parent=None, values=None): if values is None: values = list() assert isinstance(values, list) if parent: assert isinstance(parent, NodeInterface) self.__values = None values = Array.uniq(values) if (len(values) <= 1): raise InvalidArgumentException( 'values must contain at least two distinct elements.') ScalarNode.__init__(self, name, parent) self.__values = values
def __init__(self, name, parent = None, values = None): if values is None: values = list(); assert isinstance(values, list); if parent: assert isinstance(parent, NodeInterface); self.__values = None; values = Array.uniq(values); if (len(values) <= 1) : raise InvalidArgumentException('values must contain at least two distinct elements.'); ScalarNode.__init__(self, name, parent); self.__values = values;
def setDefaultValue(self, value): """Sets the default value of this node. @param value: dict @raise InvalidArgumentException: if the default value is not an array """ if isinstance(value, list): value = Array.toDict(value); if not isinstance(value, dict): raise InvalidArgumentException( '{0}: the default value of an array node has to be an array.' ''.format(self.getPath()) ); self._defaultValue = value;
def enterScope(self, name): """This is called when you enter a scope @param: string name @raise RuntimeException When the parent scope is inactive @raise InvalidArgumentException When the scope does not exist @api """ if name not in self._scopes: raise InvalidArgumentException( 'The scope "{0}" does not exist.'.format(name)) if self.SCOPE_CONTAINER != self._scopes[name] and self._scopes[ name] not in self._scopedServices: raise RuntimeException( 'The parent scope "{0}" must be active when entering this ' 'scope.'.format(self._scopes[name])) # check if a scope of this name is already active, if so we need to # remove all services of this scope, and those of any of its child # scopes from the global services map if name in self._scopedServices: services = OrderedDict() services[0] = self._services services[name] = self._scopedServices[name] self._scopedServices.pop(name, None) for child in self._scopeChildren[name]: if child in self._scopedServices: services[child] = self._scopedServices[child] self._scopedServices.pop(child, None) # update global map self._services = Array.diffKey(*services.values()) services.pop(0) # add stack entry for this scope so we can restore the removed services later if name not in self._scopeStacks: self._scopeStacks[name] = list() self._scopeStacks[name].append(services) self._scopedServices[name] = dict()
def testDiffKeyBasic(self): array1 = { 'blue': 1, 'red': 2, 'green': 3, 'purple': 4 } array2 = { 'green': 5, 'blue': 6, 'yellow': 7, 'cyan': 8 } self.assertEqual({ "red": 2, "purple": 4 }, Array.diffKey(array1, array2))
def testDiffKeyName(self): input_array = { 0: '0', 1: '1', -10: '-10', 'True': 1, 'False': 0 } boolean_indx_array = { True: 'boolt', False: 'boolf', True: 'boolT', False: 'boolF' } expected = {-10: "-10", "True": 1, "False": 0} self.assertEqual(expected, Array.diffKey(input_array, boolean_indx_array))
def _mergeValues(self, leftSide, rightSide): """Merges values together. @param leftSide: mixed The left side to merge. @param rightSide: mixed The right side to merge. @return: mixed The merged values @rasie InvalidConfigurationException: @rasie RuntimeException: """ if rightSide is False: # if this is still false after the last config has been merged the # finalization pass will take care of removing this key entirely return False if not leftSide or not self._performDeepMerging: return rightSide if isinstance(rightSide, list): rightSide = Array.toDict(rightSide) for k, v in rightSide.items(): # no conflict if k not in leftSide: if not self._allowNewKeys: ex = InvalidConfigurationException( 'You are not allowed to define new elements for path ' '"{0}". Please define all elements for this path in ' 'one config file. If you are trying to overwrite an ' 'element, make sure you redefine it with the same ' 'name.'.format(self.getPath())) ex.setPath(self.getPath()) raise ex leftSide[k] = v continue if k not in self._children: raise RuntimeException( 'merge() expects a normalized config array.') leftSide[k] = self._children[k].merge(leftSide[k], v) return leftSide
def setAddChildrenIfNoneSet(self, children=None): """Adds default children when none are set. @param children: integer|string|dict|null The number of children|The child name|The children names to be added """ if children is None: children = ['defaults']; elif isinstance(children, int) and children > 0: children = list(range(1, children+1)); elif isinstance(children, String): children = [children]; if isinstance(children, list): children = Array.toDict(children); assert isinstance(children, dict); self._defaultChildren = children;
def setAddChildrenIfNoneSet(self, children=None): """Adds default children when none are set. @param children: integer|string|dict|null The number of children|The child name|The children names to be added """ if children is None: children = ['defaults'] elif isinstance(children, int) and children > 0: children = list(range(1, children + 1)) elif isinstance(children, String): children = [children] if isinstance(children, list): children = Array.toDict(children) assert isinstance(children, dict) self._defaultChildren = children
def getServiceConditionals(cls, value): """Returns the Service Conditionals. @param: mixed value An array of conditionals to return. @return array An array of Service conditionals """ services = list() if isinstance(value, list): for v in value: iterable = cls.getServiceConditionals(v) services.extend(iterable) services = Array.uniq(services) elif isinstance(value, Reference): services.append(str(value)) return services
def locate(self, name, currentPath = None, first = True): """Returns a full path for a given file name. @param name: mixed The file name to locate @param currentPath: string The current path @param first: boolean Whether to return the first occurrence or an array of filenames @return: string|list The full path to the file|A list of file paths @raise InvalidArgumentException: When file is not found """ if self.__isAbsolutePath(name): if not os.path.exists(name): raise InvalidArgumentException( 'The file "{0}" does not exist.'.format(name) ); return name; filepaths = list(); paths = []; if currentPath: paths.append(currentPath); paths.extend(self._paths); for path in paths: filename = os.path.join(path, name); if os.path.exists(filename): if first: return filename; filepaths.append(filename); if not filepaths: raise InvalidArgumentException( 'The file "{0}" does not exist (in: {1}).' ''.format(name, ", ".join(paths)) ); return Array.uniq(filepaths);
def _normalizeValue(self, value): """Normalizes the value. @param value: mixed The value to normalize @return: mixed The normalized value @raise InvalidConfigurationException: """ if value is False: return value; if isinstance(value, list): value = Array.toDict(value); assert isinstance(value, dict); value = self._remapXml(value); normalized = dict(); valueCopy = value.copy(); for name, child in self._children.items(): assert isinstance(child, NodeInterface) if name in valueCopy: normalized[name] = child.normalize(value[name]); valueCopy.pop(name); # if extra fields are present, throw exception if valueCopy and not self._ignoreExtraKeys: ex = InvalidConfigurationException( 'Unrecognized options "{0}" under "{1}"' ''.format(", ".join(value.keys()), self.getPath()) ); ex.setPath(self.getPath()); raise ex; return normalized;
def locate(self, name, currentPath=None, first=True): """Returns a full path for a given file name. @param name: mixed The file name to locate @param currentPath: string The current path @param first: boolean Whether to return the first occurrence or an array of filenames @return: string|list The full path to the file|A list of file paths @raise InvalidArgumentException: When file is not found """ if self.__isAbsolutePath(name): if not os.path.exists(name): raise InvalidArgumentException( 'The file "{0}" does not exist.'.format(name)) return name filepaths = list() paths = [] if currentPath: paths.append(currentPath) paths.extend(self._paths) for path in paths: filename = os.path.join(path, name) if os.path.exists(filename): if first: return filename filepaths.append(filename) if not filepaths: raise InvalidArgumentException( 'The file "{0}" does not exist (in: {1}).' ''.format(name, ", ".join(paths))) return Array.uniq(filepaths)
def _normalizeValue(self, value): """Normalizes the value. @param value: mixed The value to normalize @return: mixed The normalized value @raise InvalidConfigurationException: """ if value is False: return value if isinstance(value, list): value = Array.toDict(value) assert isinstance(value, dict) value = self._remapXml(value) normalized = dict() valueCopy = value.copy() for name, child in self._children.items(): assert isinstance(child, NodeInterface) if name in valueCopy: normalized[name] = child.normalize(value[name]) valueCopy.pop(name) # if extra fields are present, throw exception if valueCopy and not self._ignoreExtraKeys: ex = InvalidConfigurationException( 'Unrecognized options "{0}" under "{1}"' ''.format(", ".join(value.keys()), self.getPath())) ex.setPath(self.getPath()) raise ex return normalized
def leaveScope(self, name): """This is called to leave the current scope, and move back to the parent scope. @param: string name The name of the scope to leave @raise InvalidArgumentException if the scope is not active: @api """ if name not in self._scopedServices: raise InvalidArgumentException( 'The scope "{0}" is not active.'.format(name)) # remove all services of this scope, or any of its child scopes from # the global service map services = [self._services, self._scopedServices[name]] self._scopedServices.pop(name, None) for child in self._scopeChildren[name]: if child not in self._scopedServices: continue services.append(self._scopedServices[child]) self._scopedServices.pop(child, None) self._services = Array.diffKey(*services) # check if we need to restore services of a previous scope of this type: if name in self._scopeStacks and self._scopeStacks[name]: services = self._scopeStacks[name].pop() self._scopedServices.update(services) for scopeServices in services.values(): self._services.update(scopeServices)
def _initializeBundles(self): """Initializes the data structures related to the bundle management. - the bundles property maps a bundle name to the bundle instance, - the bundleMap property maps a bundle name to the bundle inheritance hierarchy (most derived bundle first). @raise LogicException: if two bundles share a common name @raise LogicException: if a bundle tries to extend a non-registered bundle @raise LogicException: if a bundle tries to extend itself @raise LogicException: if two bundles extend the same ancestor """ # init bundle self._bundles = dict(); topMostBundles = dict(); directChildren = dict(); for bundle in self.registerBundles(): assert isinstance(bundle, BundleInterface); name = bundle.getName(); if name in self._bundles.keys(): raise LogicException( 'Trying to register two bundles with the same name "{0}"' ''.format(name) ); self._bundles[name] = bundle; parentName = bundle.getParent(); if parentName: if parentName in directChildren.keys(): raise LogicException( 'Bundle "{0}" is directly extended by two bundles ' '"{1}" and "{2}".' ''.format(parentName, name, directChildren[parentName]) ); if parentName == name: raise LogicException( 'Bundle "{0}" can not extend itself.'.format(name) ); directChildren[parentName] = name; else: topMostBundles[name] = bundle; # look for orphans diff = Array.diff( list(directChildren.keys()), list(self._bundles.keys()), ); if diff: raise LogicException( 'Bundle "{0}" extends bundle "{1}", which is not registered.' ''.format(directChildren[diff[0]], diff[0]) ); # inheritance self._bundleMap = dict(); for name, bundle in topMostBundles.items(): bundleMap = [bundle]; hierarchy = [name]; while name in directChildren.keys(): name = directChildren[name]; bundleMap.insert(0, self._bundles[name]); hierarchy.append(name); for name in hierarchy: self._bundleMap[name] = list(bundleMap); bundleMap.pop();
def _validate(self, config, name, path): """Validates the route configuration. @param: dict config A resource config @param string name The config key @param string path The loaded file path @raise InvalidArgumentException If one of the provided config keys is not supported, something is missing or the combination is nonsense """ if not isinstance(config, dict) : raise InvalidArgumentException( 'The definition of "{0}" in "{1}" must be a Json object.' ''.format(name, path) ); extraKeys = Array.diff(config.keys(), self.__availableKeys); if (extraKeys) : raise InvalidArgumentException( 'The routing file "{0}" contains unsupported keys for "{1}": ' '"{2}". Expected one of: "{3}".'.format( path, name, '", "'.join(extraKeys), '", "'.join(self.__availableKeys), )); if 'resource' in config and 'path' in config : raise InvalidArgumentException( 'The routing file "{0}" must not specify both the "resource" ' 'key and the "path" key for "{1}". Choose between an import ' 'and a route definition.'.format( path, name )); if 'resource' in config and 'description' in config : raise InvalidArgumentException( 'The routing file "{0}" must not specify both the "resource" ' 'key and the "description" key for "{1}". Choose between an ' 'import and a route definition.'.format( path, name )); if 'resource' not in config and 'type' in config : raise InvalidArgumentException( 'The "type" key for the route definition "{0}" in "{1}" is ' 'unsupported. It is only available for imports in combination ' 'with the "resource" key.'.format( name, path )); if 'resource' not in config and 'path' not in config : raise InvalidArgumentException( 'You must define a "path" for the route "{0}" in file "{1}".' ''.format(name, path) ); if 'path' in config and not config['path']: raise InvalidArgumentException( 'The "path" for the route "{0}" in file "{1}" cannot be empty.' ''.format(name, path) ); if 'definition' in config: if 'arguments' in config['definition']: if not isinstance(config['definition']['arguments'], OrderedDict): raise InvalidArgumentException( 'The definition.arguments key should be a JSON object ' 'in route "{0}" in file "{1}".' ''.format(name, path) ); if 'options' in config['definition']: if not isinstance(config['definition']['options'], dict): raise InvalidArgumentException( 'The definition.options key should be a JSON object ' 'in route "{0}" in file "{1}".' ''.format(name, path) );
class RouteCollection(IteratorAggregateInterface, CountableInterface): """A RouteCollection represents a set of Route instances. When adding a route at the end of the collection, an existing route with the same name is removed first. So there can only be one route with a given name. @author: Fabien Potencier <*****@*****.**> @author Tobias Schultze <http://tobion.de> @api """ def __init__(self, definition=None): if definition is None: definition = InputDefinition() assert isinstance(definition, InputDefinition) self.__routes = Array() # @var: Route[] self.__resources = list() # @var: list self.__definition = definition # @var: InputDefinition def __clone__(self): for name, route in self.__routes.items(): self.__routes[name] = clone(route) def __iter__(self): """Gets the current RouteCollection as an Iterator that includes all routes. It implements IteratorAggregate. @see: all() @return ArrayIterator An ArrayIterator object for iterating over routes """ return self.__routes.__iter__() def __len__(self): """Gets the number of Routes in this collection. @return: int The number of routes """ return len(self.__routes) def add(self, name, route): """Adds a route. @param: string name The route name @param Route route A Route instance @return: RouteCollection The current instance @api """ assert isinstance(route, Route) self.__routes.pop(name, None) # Force the creation of the synopsis before the merge with # the collection definition route.getSynopsis() self.__mergeDefinition(route, self.__definition) self.__routes[name] = route return self def all(self): """Returns all routes in this collection. @return: Route[] An array of routes """ return self.__routes def get(self, name): """Gets a route by name. @param: string name The route name @return Route|None A Route instance or None when not found """ return self.__routes[name] if name in self.__routes else None def remove(self, name): """Removes a route or an array of routes by name from the collection @param: string|list name The route name or an array of route names @return: RouteCollection The current instance """ if not isinstance(name, list): name = [name] for n in name: self.__routes.pop(n, None) return self def addCollection(self, collection): """Adds a route collection at the end of the current set by appending all routes of the added collection. @param: RouteCollection collection A RouteCollection instance @return: RouteCollection The current instance @api """ assert isinstance(collection, RouteCollection) for name, route in collection.all().items(): self.add(name, route) self.__resources = self.__resources + collection.getResources() return self def getResources(self): """Returns an array of resources loaded to build this collection. @return: ResourceInterface[] An array of resources """ return Array.uniq(self.__resources, str) def addResource(self, resource): """Adds a resource for this collection. @param: ResourceInterface resource A resource instance @return: RouteCollection The current instance """ assert isinstance(resource, ResourceInterface) self.__resources.append(resource) return self def addObjectResource(self, objectResource): """Adds the object class hierarchy as resources. @param: object object An object instance @return RouteCollection The current instance """ assert isinstance(objectResource, Object) parent = ReflectionObject(objectResource) while parent: self.addResource(FileResource(parent.getFileName())) parent = parent.getParentClass() return self def getDefinition(self): """Returns the definition of this collection. @return: InputDefinition The InputDefinition of this collection """ return self.__definition def setDefinition(self, definition): """Sets the definition of this collection. @param: InputDefinition definition A resource instance @return: RouteCollection The current instance """ assert isinstance(definition, InputDefinition) self.__definition = definition return self def prependDefinition(self, definition): """Prepends a definition to the definition of all child routes. @param: InputDefinition definition A definition to prepend @return: RouteCollection The current instance """ assert isinstance(definition, InputDefinition) self.__mergeDefinition(self.__definition, definition) for route in self.__routes.values(): self.__mergeDefinition(route, definition) return self def addPrefix(self, prefix): """Adds a prefix to the path of all child routes. @param: string prefix An optional prefix to add before each pattern of the route collection @return: RouteCollection The current instance @api """ prefix = prefix.strip().strip(':') if not prefix: return for route in self.__routes.values(): route.setPath(prefix + ':' + route.getPath()) return self def addDefaults(self, defaults): """Adds defaults to all routes. An existing default value under the same name in a route will be overridden. @param: dict defaults An array of default values @return: RouteCollection The current instance """ assert isinstance(defaults, dict) if defaults: for route in self.__routes.values(): route.addDefaults(defaults) return self def addRequirements(self, requirements): """Adds requirements to all routes. An existing requirement under the same name in a route will be overridden. @param: dict requirements An array of requirements @return: RouteCollection The current instance """ assert isinstance(requirements, dict) if requirements: for route in self.__routes.values(): route.addRequirements(requirements) return self def __mergeDefinition(self, definition, parentDefinition): """Merges the definition with the parentDefinition. @param: InputDefinition definition Initial definition to merge @param: InputDefinition parentDefinition A definition from merge @return: RouteCollection The current instance """ assert isinstance(definition, InputDefinition) assert isinstance(parentDefinition, InputDefinition) currentArguments = definition.getArguments() definition.setArguments(parentDefinition.getArguments()) definition.addArguments(currentArguments) definition.addOptions(parentDefinition.getOptions()) return self
def _normalizeValue(self, value): """Normalizes the value. @param value: mixed The value to normalize @return mixed The normalized value @raise InvalidConfigurationException: @raise DuplicateKeyException: """ if value is False: return value if isinstance(value, list): value = Array.toDict(value) assert isinstance(value, dict) value = self._remapXml(value) isAssoc = list(value.keys()) != list(range(len(value))) normalized = dict() i = -1 for k, v in value.items(): i += 1 if self._keyAttribute is not None and isinstance(v, (dict, list)): if isinstance(v, list): v = Array.toDict(v) if self._keyAttribute not in v \ and isinstance(k, int) \ and not isAssoc: ex = InvalidConfigurationException( 'The attribute "{0}" must be set for path "{1}".' ''.format(self._keyAttribute, self.getPath())) ex.setPath(self.getPath()) raise ex elif self._keyAttribute in v: k = v[self._keyAttribute] # remove the key attribute when required if self._removeKeyAttribute: del v[self._keyAttribute] # if only "value" is left if 1 == len(v) and 'value' in v: v = v['value'] if k in normalized: ex = DuplicateKeyException( 'Duplicate key "{0}" for path "{1}".' ''.format(k, self.getPath())) ex.setPath(self.getPath()) raise ex self._prototype.setName(k) if not self._keyAttribute is None or isAssoc: normalized[k] = self._prototype.normalize(v) else: normalized[i] = self._prototype.normalize(v) return normalized
def _normalizeValue(self, value): """Normalizes the value. @param value: mixed The value to normalize @return mixed The normalized value @raise InvalidConfigurationException: @raise DuplicateKeyException: """ if value is False: return value; if isinstance(value, list): value = Array.toDict(value); assert isinstance(value, dict); value = self._remapXml(value); isAssoc = list(value.keys()) != list(range(len(value))); normalized = dict(); i = -1; for k, v in value.items(): i += 1; if self._keyAttribute is not None and isinstance(v, (dict, list)): if isinstance(v, list): v = Array.toDict(v); if self._keyAttribute not in v \ and isinstance(k, int) \ and not isAssoc: ex = InvalidConfigurationException( 'The attribute "{0}" must be set for path "{1}".' ''.format(self._keyAttribute, self.getPath()) ); ex.setPath(self.getPath()); raise ex; elif self._keyAttribute in v: k = v[self._keyAttribute]; # remove the key attribute when required if self._removeKeyAttribute: del v[self._keyAttribute]; # if only "value" is left if 1 == len(v) and 'value' in v: v = v['value']; if k in normalized: ex = DuplicateKeyException( 'Duplicate key "{0}" for path "{1}".' ''.format(k, self.getPath()) ); ex.setPath(self.getPath()); raise ex; self._prototype.setName(k); if not self._keyAttribute is None or isAssoc: normalized[k] = self._prototype.normalize(v); else: normalized[i] = self._prototype.normalize(v); return normalized;
def _validate(self, config, name, path): """Validates the route configuration. @param: dict config A resource config @param string name The config key @param string path The loaded file path @raise InvalidArgumentException If one of the provided config keys is not supported, something is missing or the combination is nonsense """ if not isinstance(config, dict): raise InvalidArgumentException( 'The definition of "{0}" in "{1}" must be a Json object.' ''.format(name, path)) extraKeys = Array.diff(config.keys(), self.__availableKeys) if (extraKeys): raise InvalidArgumentException( 'The routing file "{0}" contains unsupported keys for "{1}": ' '"{2}". Expected one of: "{3}".'.format( path, name, '", "'.join(extraKeys), '", "'.join(self.__availableKeys), )) if 'resource' in config and 'path' in config: raise InvalidArgumentException( 'The routing file "{0}" must not specify both the "resource" ' 'key and the "path" key for "{1}". Choose between an import ' 'and a route definition.'.format(path, name)) if 'resource' in config and 'description' in config: raise InvalidArgumentException( 'The routing file "{0}" must not specify both the "resource" ' 'key and the "description" key for "{1}". Choose between an ' 'import and a route definition.'.format(path, name)) if 'resource' not in config and 'type' in config: raise InvalidArgumentException( 'The "type" key for the route definition "{0}" in "{1}" is ' 'unsupported. It is only available for imports in combination ' 'with the "resource" key.'.format(name, path)) if 'resource' not in config and 'path' not in config: raise InvalidArgumentException( 'You must define a "path" for the route "{0}" in file "{1}".' ''.format(name, path)) if 'path' in config and not config['path']: raise InvalidArgumentException( 'The "path" for the route "{0}" in file "{1}" cannot be empty.' ''.format(name, path)) if 'definition' in config: if 'arguments' in config['definition']: if not isinstance(config['definition']['arguments'], OrderedDict): raise InvalidArgumentException( 'The definition.arguments key should be a JSON object ' 'in route "{0}" in file "{1}".' ''.format(name, path)) if 'options' in config['definition']: if not isinstance(config['definition']['options'], dict): raise InvalidArgumentException( 'The definition.options key should be a JSON object ' 'in route "{0}" in file "{1}".' ''.format(name, path))
def _initializeBundles(self): """Initializes the data structures related to the bundle management. - the bundles property maps a bundle name to the bundle instance, - the bundleMap property maps a bundle name to the bundle inheritance hierarchy (most derived bundle first). @raise LogicException: if two bundles share a common name @raise LogicException: if a bundle tries to extend a non-registered bundle @raise LogicException: if a bundle tries to extend itself @raise LogicException: if two bundles extend the same ancestor """ # init bundle self._bundles = dict() topMostBundles = dict() directChildren = dict() for bundle in self.registerBundles(): assert isinstance(bundle, BundleInterface) name = bundle.getName() if name in self._bundles.keys(): raise LogicException( 'Trying to register two bundles with the same name "{0}"' ''.format(name)) self._bundles[name] = bundle parentName = bundle.getParent() if parentName: if parentName in directChildren.keys(): raise LogicException( 'Bundle "{0}" is directly extended by two bundles ' '"{1}" and "{2}".' ''.format(parentName, name, directChildren[parentName])) if parentName == name: raise LogicException( 'Bundle "{0}" can not extend itself.'.format(name)) directChildren[parentName] = name else: topMostBundles[name] = bundle # look for orphans diff = Array.diff( list(directChildren.keys()), list(self._bundles.keys()), ) if diff: raise LogicException( 'Bundle "{0}" extends bundle "{1}", which is not registered.' ''.format(directChildren[diff[0]], diff[0])) # inheritance self._bundleMap = dict() for name, bundle in topMostBundles.items(): bundleMap = [bundle] hierarchy = [name] while name in directChildren.keys(): name = directChildren[name] bundleMap.insert(0, self._bundles[name]) hierarchy.append(name) for name in hierarchy: self._bundleMap[name] = list(bundleMap) bundleMap.pop()
def get(self, path, default = None, deep = False): """Returns a parameter by name. @param string path The key @param mixed default The default value if the parameter key does not exist @param boolean deep If True, a path like foo[bar] will find deeper items @return mixed @raise InvalidArgumentException @api """ try: pos = str(path).index('['); except ValueError: pos = False; if ( not deep or False is pos) : if path in self._parameters: return self._parameters[path]; else: return default; root = str(path)[0:pos]; if not root in self._parameters : return default; value = self._parameters[root]; currentKey = None; i = pos - 1; for char in range(len(path)): i += 1; if ('[' == char) : if (None is not currentKey) : raise InvalidArgumentException( 'Malformed path. Unexpected "[" at position {0}.' ''.format(str(i)) ); currentKey = ''; elif (']' == char) : if (None is currentKey) : raise InvalidArgumentException( 'Malformed path. Unexpected "]" at position {0}.' ''.format(str(i)) ); if isinstance(value, list): value = Array.toDict(value, True); if not isinstance(value, dict) or currentKey not in value : return default; value = value[currentKey]; currentKey = None; else : if (None is currentKey) : raise InvalidArgumentException( 'Malformed path. Unexpected "{0}" at position {1}.' ''.format(char, str(i)) ); currentKey += char; if (None is not currentKey) : raise InvalidArgumentException( 'Malformed path. Path must end with "]".' ); return value;
def testDiffKeyName(self): input_array = {0 : '0', 1 : '1', -10 : '-10', 'True' : 1, 'False' : 0}; boolean_indx_array = {True : 'boolt', False : 'boolf', True : 'boolT', False : 'boolF'}; expected = {-10 :"-10","True":1,"False":0} self.assertEqual(expected, Array.diffKey(input_array, boolean_indx_array));
def testDiffKeyBasic(self): array1 = {'blue' : 1, 'red': 2, 'green' : 3, 'purple' : 4}; array2 = {'green': 5, 'blue' : 6, 'yellow': 7, 'cyan' : 8}; self.assertEqual({"red":2,"purple":4}, Array.diffKey(array1, array2));