def testBuild(self): self._orderdedDict = OrderedDict() clone = CloneBuilder.build(self) self.assertFalse(self is clone) self.assertFalse(self._orderdedDict is clone._orderdedDict) self.assertEqual(self._orderdedDict, clone._orderdedDict) self.assertFalse(self.testBuild is clone.testBuild)
def testProcess(self): container = ContainerBuilder() container._ContainerBuilder__definitions = OrderedDict() ref1 = Reference('b') a = container.register('a').addArgument(ref1) ref2 = Reference('a') b = container.register('b').addMethodCall('setA', [ref2]) ref3 = Reference('a') ref4 = Reference('b') c = container.register('c').addArgument(ref3).addArgument(ref4) ref5 = Reference('b') d = container.register('d').setProperty('foo', ref5) ref6 = Reference('b') e = container.register('e').setConfigurator([ref6, 'methodName']) graph = self._process(container) edges = graph.getNode('b').getInEdges() self.assertEqual(4, len(edges)) self.assertEqual(ref1, edges[0].getValue()) self.assertEqual(ref4, edges[1].getValue()) self.assertEqual(ref5, edges[2].getValue()) self.assertEqual(ref6, edges[3].getValue())
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 testObjectSupportEnabled(self): dump = self._dumper.dump(OrderedDict([('foo', A()), ('bar', 1)]), 0, 0, False, True) self.assertEqual( '{{ foo: !!python/object:{0}, bar: 1 }}'.format(serialize(A())), dump, '->dump() is able to dump objects')
def __krsort(self, d): assert isinstance(d, dict) ret = OrderedDict() for k in sorted(list(d.keys()), reverse=True): ret[k] = d[k] return ret
def resolveString(self, value, resolving=None): if resolving is None: resolving = OrderedDict() assert isinstance(resolving, dict) resolving = resolving.copy() # we do this to deal with non string values (Boolean, integer, ...) # as the preg_replace_callback throw an exception when trying # a non-string in a parameter value match = re.search(r"^%([^%\s]+)%$", value) if match: key = match.group(1).lower() if key in resolving.keys(): raise ParameterCircularReferenceException( list(resolving.keys())) resolving[key] = True if self._resolved: return self.get(key) else: return self.resolveValue(self.get(key), resolving) def callback(match): _resolving = resolving.copy() key = match.group(1) if not key: return "%%" key = key.lower() if key in _resolving.keys(): raise ParameterCircularReferenceException( list(_resolving.keys())) resolved = self.get(key) if not isinstance(resolved, (String, float, int, complex)): raise RuntimeException( 'A string value must be composed of strings and/or ' 'numbers, but found parameter "{0}" of type {1} inside ' 'string value "{2}".' "".format(key, type(resolved).__name__, value)) resolved = str(resolved) _resolving[key] = True if self.isResolved(): return resolved else: return self.resolveString(resolved, _resolving) return re.sub(r"%%|%([^%\s]+)%", callback, value)
def __init__(self, name, parent=None): """Constructor. @param name: string The Node's name @param parent: NodeInterface The node parent """ BaseNode.__init__(self, name, parent=parent) self._xmlRemappings = list() self._children = OrderedDict() self._allowFalse = False self._allowNewKeys = True self._addIfNotSet = False self._performDeepMerging = True self._ignoreExtraKeys = None self._normalizeKeys = True
def __init__(self, name, parent=None): NodeDefinition.__init__(self, name, parent=parent); self._performDeepMerging = True; self._ignoreExtraKeys = None; self._children = OrderedDict(); self._prototype = None; self._atLeastOne = False; self._allowNewKeys = True; self._key = None; self._removeKeyItem = None; self._addDefaults = False; self._addDefaultChildren = False; self._nodeBuilder = None; self._normalizeKeys = True; self._allowEmptyValue = True; self._nullEquivalent = dict(); self._trueEquivalent = dict();
def testObjectSupportDisabledButNoExceptions(self): inputv = """ foo: !!python/object:{0} bar: 1 """.format(serialize(B())) self.assertEqual(OrderedDict([('foo', None), ('bar', 1)]), self._parser.parse(inputv), '->parse() does not parse objects')
def testObjectSupportDisabledWithExceptions(self): """@expectedException: Symfony\Component\Yaml\Exception\DumpException """ try: self._dumper.dump(OrderedDict([('foo', A()), ('bar', 1)]), 0, 0, True, False) self.fail() except Exception as e: self.assertTrue(isinstance(e, DumpException))
def resolveValue(self, value, resolving=None): if resolving is None: resolving = OrderedDict() assert isinstance(resolving, dict) resolving = resolving.copy() if isinstance(value, dict): args = dict() for k, v in value.items(): args[self.resolveValue(k, resolving)] = self.resolveValue( v, resolving) return args elif isinstance(value, list): args = list() for v in value: args.append(self.resolveValue(v, resolving)) return args if not isinstance(value, String): return value return self.resolveString(value, resolving)
def __init__(self, name, parent=None): """Constructor. @param name: string The name of the node @param parent: NodeInterface The parent of this node @raise InvalidArgumentException: if the name contains a period. """ assert isinstance(name, String) if parent is not None: assert isinstance(parent, NodeInterface) self._attributes = OrderedDict() self._name = name self._parent = parent self._normalizationClosures = list() self._finalValidationClosures = list() self._allowOverwrite = True self._required = False self._equivalentValues = list() if '.' in name: raise InvalidArgumentException('The name must not contain ".".')
def __init__(self, name, parent=None): """Constructor. @param name: string The Node's name @param parent: NodeInterface The node parent """ BaseNode.__init__(self, name, parent=parent); self._xmlRemappings = list(); self._children = OrderedDict(); self._allowFalse = False; self._allowNewKeys = True; self._addIfNotSet = False; self._performDeepMerging = True; self._ignoreExtraKeys = None; self._normalizeKeys = True;
def JSONObject(match, context, _w=decoder.WHITESPACE.match): pairs = OrderedDict(); # Change to an ordered dict s = match.string end = _w(s, match.end()).end() nextchar = s[end:end + 1] # Trivial empty object if nextchar == '}': return pairs, end + 1 if nextchar != '"': raise ValueError(decoder.errmsg("Expecting property name", s, end)) end += 1 encoding = getattr(context, 'encoding', None) strict = getattr(context, 'strict', True) iterscan = JSONScanner.iterscan while True: key, end = decoder.scanstring(s, end, encoding, strict) end = _w(s, end).end() if s[end:end + 1] != ':': raise ValueError(decoder.errmsg("Expecting : delimiter", s, end)) end = _w(s, end + 1).end() try: value, end = iterscan(s, idx=end, context=context).next() except StopIteration: raise ValueError(decoder.errmsg("Expecting object", s, end)) pairs[key] = value end = _w(s, end).end() nextchar = s[end:end + 1] end += 1 if nextchar == '}': break if nextchar != ',': raise ValueError(decoder.errmsg("Expecting , delimiter", s, end - 1)) end = _w(s, end).end() nextchar = s[end:end + 1] end += 1 if nextchar != '"': raise ValueError(decoder.errmsg("Expecting property name", s, end - 1)) object_hook = getattr(context, 'object_hook', None) if object_hook is not None: pairs = object_hook(pairs) return pairs, end
def __init__(self, name, parent=None): """Constructor. @param name: string The name of the node @param parent: NodeInterface The parent of this node @raise InvalidArgumentException: if the name contains a period. """ assert isinstance(name, String); if parent is not None: assert isinstance(parent, NodeInterface); self._attributes = OrderedDict(); self._name = name; self._parent = parent; self._normalizationClosures = list(); self._finalValidationClosures = list(); self._allowOverwrite = True; self._required = False; self._equivalentValues = list(); if '.' in name: raise InvalidArgumentException('The name must not contain ".".');
def clear(self): """Clears all nodes. """ self.__nodes = OrderedDict()
class ArrayNode(BaseNode, PrototypeNodeInterface): """Represents an Array node in the config tree. @author Johannes M. Schmitt <*****@*****.**> """ def __init__(self, name, parent=None): """Constructor. @param name: string The Node's name @param parent: NodeInterface The node parent """ BaseNode.__init__(self, name, parent=parent); self._xmlRemappings = list(); self._children = OrderedDict(); self._allowFalse = False; self._allowNewKeys = True; self._addIfNotSet = False; self._performDeepMerging = True; self._ignoreExtraKeys = None; self._normalizeKeys = True; def setNormalizeKeys(self, normalizeKeys): self._normalizeKeys = bool(normalizeKeys); def _preNormalize(self, value): """Normalizes keys between the different configuration formats. Namely, you mostly have foo_bar in YAML while you have foo-bar in XML. After running this method, all keys are normalized to foo_bar. If you have a mixed key like foo-bar_moo, it will not be altered. The key will also not be altered if the target key already exists. @param value: mixed @return dict The value with normalized keys """ if not self._normalizeKeys or not isinstance(value, dict): return value; for k, v in value.items(): if '-' in str(k): if not '_' in str(k): normalizedKey = str(k).replace('-', '_'); if not normalizedKey in value: value[normalizedKey] = v; value.pop(k); return value; def getChildren(self): """Retrieves the children of this node. @return: dict The children """ return self._children; def setXmlRemappings(self, xmlRemappings): """Sets the xml remappings that should be performed. @param xmlRemappings: an list of the form list(list(string, string)) """ self._xmlRemappings = list(xmlRemappings); def setAddIfNotSet(self, boolean): """Sets whether to add default values for this array if it has not been defined in any of the configuration files. @param boolean: Boolean """ self._addIfNotSet = bool(boolean); def setAllowFalse(self, allow): """Sets whether false is allowed as value indicating that the array should be unset. @param allow: Boolean """ self._allowFalse = bool(allow); def setAllowNewKeys(self, allow): """Sets whether new keys can be defined in subsequent configurations. @param allow: Boolean """ self._allowNewKeys = bool(allow); def setPerformDeepMerging(self, boolean): """Sets if deep merging should occur. @param boolean: Boolean """ self._performDeepMerging = bool(boolean); def setIgnoreExtraKeys(self, boolean): """Whether extra keys should just be ignore without an exception. @param boolean: Boolean To allow extra keys """ self._ignoreExtraKeys = bool(boolean); def setName(self, name): """Sets the node Name. @param name: string The node's name """ self._name = str(name); def hasDefaultValue(self): """Checks if the node has a default value. @return Boolean """ return self._addIfNotSet; def getDefaultValue(self): """Retrieves the default value. @return: dict The default value @raise RuntimeException: if the node has no default value """ if not self.hasDefaultValue(): raise RuntimeException( 'The node at path "{0}" has no default value.' ''.format(self.getPath()) ); default = dict(); for name, child in self._children.items(): if child.hasDefaultValue(): default[name] = child.getDefaultValue(); return default; def addChild(self, node): """Adds a child node. @param child: NodeInterface The child node to add @raise InvalidArgumentException: when the child node has no name @raise InvalidArgumentException: when the child node's name is not unique """ assert isinstance(node, NodeInterface); name = node.getName(); if not name: raise InvalidArgumentException('Child nodes must be named.'); if name in self._children: raise InvalidArgumentException( 'A child node named "{0}" already exists.' ''.format(name) ); self._children[name] = node; def _finalizeValue(self, value): """Finalizes the value of this node. @param value: mixed @return: mixed The finalised value @raise UnsetKeyException: @raise InvalidConfigurationException: if the node doesn't have enough children """ if value is False: raise UnsetKeyException( 'Unsetting key for path "{0}", value: {1}' ''.format(self.getPath(), json.dumps(value)) ); for name, child in self._children.items(): assert isinstance(child, NodeInterface); if not name in value: if child.isRequired(): ex = InvalidConfigurationException( 'The child node "{0}" at path "{1}" must be ' 'configured.'.format(name, self.getPath()) ); ex.setPath(self.getPath()); raise ex; if child.hasDefaultValue(): value[name] = child.getDefaultValue(); continue; try: value[name] = child.finalize(value[name]); except UnsetKeyException: value.pop(name); return value; def _validateType(self, value): """Validates the type of the value. @param value: mixed @raise InvalidTypeException: """ if not isinstance(value, (dict, list)): if not self._allowFalse or value: ex = InvalidTypeException( 'Invalid type for path "{0}". Expected array, but got {1}' ''.format(self.getPath(), type(value).__name__) ); ex.setPath(self.getPath()) raise ex; 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 _remapXml(self, value): """Remaps multiple singular values to a single plural value. @param value: cict The source values @return: dict The remapped values """ assert isinstance(value, dict); for singular, plural in self._xmlRemappings: if not singular in value: continue; value[plural] = Processor.normalizeConfig(value, singular, plural); value.pop(singular); return 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;
class BaseNode(NodeInterface): """The base node class @author Johannes M. Schmitt <*****@*****.**> """ def __init__(self, name, parent=None): """Constructor. @param name: string The name of the node @param parent: NodeInterface The parent of this node @raise InvalidArgumentException: if the name contains a period. """ assert isinstance(name, String); if parent is not None: assert isinstance(parent, NodeInterface); self._attributes = OrderedDict(); self._name = name; self._parent = parent; self._normalizationClosures = list(); self._finalValidationClosures = list(); self._allowOverwrite = True; self._required = False; self._equivalentValues = list(); if '.' in name: raise InvalidArgumentException('The name must not contain ".".'); def setAttribute(self, key, value): self._attributes[key] = value; def hasAttribute(self, key): return key in self._attributes; def getAttribute(self, key, default=None): if self.hasAttribute(key): return self._attributes[key]; return default; def getAttributes(self, key): return self._attributes; def setAttributes(self, attributes): assert isinstance(attributes, dict); self._attributes = attributes; def removeAttribute(self, key): return self._attributes.pop(key); def setInfo(self, info): """Sets an info message. @param info: string """ self.setAttribute('info', info); def getInfo(self): """Returns info message. @return: string The info message. """ return self.getAttribute('info'); def setExample(self, example): """Sets the example configuration for this node. @param example: string|array """ self.setAttribute('example', example); def getExample(self): """Retrieves the example configuration for this node. @return: string|array """ return self.getAttribute('example'); def addEquivalentValue(self, originalValue, equivalentValue): """Adds an equivalent value. @param originalValue: mixed @param equivalentValue: mixed """ self._equivalentValues.append([originalValue, equivalentValue]); def setRequired(self, boolean): """Set this node as required. @param boolean: Boolean Required node """ self._required = bool(boolean); def setAllowOverwrite(self, allow): """Sets if this node can be overridden. @param allow: Boolean """ self._allowOverwrite = bool(allow); def setNormalizationClosures(self, closures): """Sets the closures used for normalization. @param closures: callable[] An array of Closures used for normalization """ assert isinstance(closures, list); self._normalizationClosures = closures; def setFinalValidationClosures(self, closures): """Sets the closures used for final validation. @param closures: callable[] An array of Closures used for final validation """ assert isinstance(closures, list); self._finalValidationClosures = closures; def isRequired(self): """Checks if this node is required. @return Boolean """ return self._required; def getName(self): """Returns the name of this node @return string The Node's name. """ return self._name; def getPath(self): """Retrieves the path of this node. @return string The Node's path """ path = self._name; if not self._parent is None: path = ".".join([self._parent.getPath(), self._name]); return path; @final def merge(self, leftSide, rightSide): """Merges two values together. @param leftSide: mixed @param rightSide: mixed @return mixed The merged value @raise ForbiddenOverwriteException: """ if not self._allowOverwrite: raise ForbiddenOverwriteException( 'Configuration path "{0}" cannot be overwritten. You have to ' 'define all options for this path, and any of its sub-paths ' 'in one configuration section.'.format(self.getPath()) ); self._validateType(leftSide); self._validateType(rightSide); return self._mergeValues(leftSide, rightSide); @final def normalize(self, value): """Normalizes a value, applying all normalization closures. @param value: mixed Value to normalize. @return: mixed The normalized value. """ # pre-normalize value value = self._preNormalize(value); # run custom normalization closures for closure in self._normalizationClosures: value = closure(value); # replace value with their equivalent for data in self._equivalentValues: if data[0] == value: value = data[1]; # validate type self._validateType(value); # normalize value return self._normalizeValue(value); def _preNormalize(self, value): """Normalizes the value before any other normalization is applied. @param value: @return: mixed The normalized array value """ return value; @final def finalize(self, value): """Finalizes a value, applying all finalization closures. @param mixed $value The value to finalize @return mixed The finalized value @raise InvalidConfigurationException: """ self._validateType(value); value = self._finalizeValue(value); # Perform validation on the final value if a closure has been set. # The closure is also allowed to return another value. for closure in self._finalValidationClosures: try: value = closure(value); except DefinitionException as correctEx: raise correctEx; except Exception as invalid: raise InvalidConfigurationException( 'Invalid configuration for path "{0}": {1}' ''.format(self.getPath(), str(invalid)), previous=invalid ); return value; @abstract def _validateType(self, value): """Validates the type of a Node. @param value: mixed The value to validate @raise InvalidTypeException: When the value is invalid """ pass; @abstract def _normalizeValue(self, value): """Normalizes the value. @param value: mixed The value to normalize. @return: mixed The normalized value """ pass; @abstract def _mergeValues(self, leftSide, rightSide): """Merges two values together. @param leftSide: mixed @param rightSide: mixed @return: mixed """ pass; @abstract def _finalizeValue(self, value): """Finalizes a value. @param value: The value to finalize @return: mixed The finalized value """ pass;
class BaseNode(NodeInterface): """The base node class @author Johannes M. Schmitt <*****@*****.**> """ def __init__(self, name, parent=None): """Constructor. @param name: string The name of the node @param parent: NodeInterface The parent of this node @raise InvalidArgumentException: if the name contains a period. """ assert isinstance(name, String) if parent is not None: assert isinstance(parent, NodeInterface) self._attributes = OrderedDict() self._name = name self._parent = parent self._normalizationClosures = list() self._finalValidationClosures = list() self._allowOverwrite = True self._required = False self._equivalentValues = list() if '.' in name: raise InvalidArgumentException('The name must not contain ".".') def setAttribute(self, key, value): self._attributes[key] = value def hasAttribute(self, key): return key in self._attributes def getAttribute(self, key, default=None): if self.hasAttribute(key): return self._attributes[key] return default def getAttributes(self, key): return self._attributes def setAttributes(self, attributes): assert isinstance(attributes, dict) self._attributes = attributes def removeAttribute(self, key): return self._attributes.pop(key) def setInfo(self, info): """Sets an info message. @param info: string """ self.setAttribute('info', info) def getInfo(self): """Returns info message. @return: string The info message. """ return self.getAttribute('info') def setExample(self, example): """Sets the example configuration for this node. @param example: string|array """ self.setAttribute('example', example) def getExample(self): """Retrieves the example configuration for this node. @return: string|array """ return self.getAttribute('example') def addEquivalentValue(self, originalValue, equivalentValue): """Adds an equivalent value. @param originalValue: mixed @param equivalentValue: mixed """ self._equivalentValues.append([originalValue, equivalentValue]) def setRequired(self, boolean): """Set this node as required. @param boolean: Boolean Required node """ self._required = bool(boolean) def setAllowOverwrite(self, allow): """Sets if this node can be overridden. @param allow: Boolean """ self._allowOverwrite = bool(allow) def setNormalizationClosures(self, closures): """Sets the closures used for normalization. @param closures: callable[] An array of Closures used for normalization """ assert isinstance(closures, list) self._normalizationClosures = closures def setFinalValidationClosures(self, closures): """Sets the closures used for final validation. @param closures: callable[] An array of Closures used for final validation """ assert isinstance(closures, list) self._finalValidationClosures = closures def isRequired(self): """Checks if this node is required. @return Boolean """ return self._required def getName(self): """Returns the name of this node @return string The Node's name. """ return self._name def getPath(self): """Retrieves the path of this node. @return string The Node's path """ path = self._name if not self._parent is None: path = ".".join([self._parent.getPath(), self._name]) return path @final def merge(self, leftSide, rightSide): """Merges two values together. @param leftSide: mixed @param rightSide: mixed @return mixed The merged value @raise ForbiddenOverwriteException: """ if not self._allowOverwrite: raise ForbiddenOverwriteException( 'Configuration path "{0}" cannot be overwritten. You have to ' 'define all options for this path, and any of its sub-paths ' 'in one configuration section.'.format(self.getPath())) self._validateType(leftSide) self._validateType(rightSide) return self._mergeValues(leftSide, rightSide) @final def normalize(self, value): """Normalizes a value, applying all normalization closures. @param value: mixed Value to normalize. @return: mixed The normalized value. """ # pre-normalize value value = self._preNormalize(value) # run custom normalization closures for closure in self._normalizationClosures: value = closure(value) # replace value with their equivalent for data in self._equivalentValues: if data[0] == value: value = data[1] # validate type self._validateType(value) # normalize value return self._normalizeValue(value) def _preNormalize(self, value): """Normalizes the value before any other normalization is applied. @param value: @return: mixed The normalized array value """ return value @final def finalize(self, value): """Finalizes a value, applying all finalization closures. @param mixed $value The value to finalize @return mixed The finalized value @raise InvalidConfigurationException: """ self._validateType(value) value = self._finalizeValue(value) # Perform validation on the final value if a closure has been set. # The closure is also allowed to return another value. for closure in self._finalValidationClosures: try: value = closure(value) except DefinitionException as correctEx: raise correctEx except Exception as invalid: raise InvalidConfigurationException( 'Invalid configuration for path "{0}": {1}' ''.format(self.getPath(), str(invalid)), previous=invalid) return value @abstract def _validateType(self, value): """Validates the type of a Node. @param value: mixed The value to validate @raise InvalidTypeException: When the value is invalid """ pass @abstract def _normalizeValue(self, value): """Normalizes the value. @param value: mixed The value to normalize. @return: mixed The normalized value """ pass @abstract def _mergeValues(self, leftSide, rightSide): """Merges two values together. @param leftSide: mixed @param rightSide: mixed @return: mixed """ pass @abstract def _finalizeValue(self, value): """Finalizes a value. @param value: The value to finalize @return: mixed The finalized value """ pass
class ArrayNodeDefinition(NodeDefinition, ParentNodeDefinitionInterface): """This class provides a fluent interface for defining an array node. @author: Johannes M. Schmitt <*****@*****.**> """ def __init__(self, name, parent=None): NodeDefinition.__init__(self, name, parent=parent); self._performDeepMerging = True; self._ignoreExtraKeys = None; self._children = OrderedDict(); self._prototype = None; self._atLeastOne = False; self._allowNewKeys = True; self._key = None; self._removeKeyItem = None; self._addDefaults = False; self._addDefaultChildren = False; self._nodeBuilder = None; self._normalizeKeys = True; self._allowEmptyValue = True; self._nullEquivalent = dict(); self._trueEquivalent = dict(); def setBuilder(self, builder): """Sets a custom children builder. @param builder: NodeBuilder A custom NodeBuilder """ assert isinstance(builder, NodeBuilder); self._nodeBuilder = builder; def children(self): """Returns a builder to add children nodes. @return: NodeBuilder """ return self._getNodeBuilder(); def prototype(self, nodeType): """Sets a prototype for child nodes. @param nodeType: string the type of node @return: NodeDefinition """ self._prototype = \ self._getNodeBuilder().node(None, nodeType).setParent(self); return self._prototype; def addDefaultsIfNotSet(self): """Adds the default value if the node is not set in the configuration. This method is applicable to concrete nodes only (not to prototype nodes). If this function has been called and the node is not set during the finalization phase, it's default value will be derived from its children default values. @return: ArrayNodeDefinition """ self._addDefaults = True; return self; def addDefaultChildrenIfNoneSet(self, children=None): """Adds children with a default value when none are defined. This method is applicable to prototype nodes only. @param children: integer|string|dict|None The number of children|The child name|The children names to be added @return: ArrayNodeDefinition """ self._addDefaultChildren = children; return self; def requiresAtLeastOneElement(self): """Requires the node to have at least one element. This method is applicable to prototype nodes only. @return: ArrayNodeDefinition """ self._atLeastOne = True; return self; def disallowNewKeysInSubsequentConfigs(self): """Disallows adding news keys in a subsequent configuration. If used all keys have to be defined in the same configuration file. @return: ArrayNodeDefinition """ self._allowNewKeys = False; return self; def fixXmlConfig(self, singular, plural=None): """Sets a normalization rule for XML configurations. @param singular: string The key to remap @param plural: string The plural of the key for irregular plurals @return: ArrayNodeDefinition """ self._normalization().remap(singular, plural); return self; def useAttributeAsKey(self, name, removeKeyItem=True): """Sets the attribute which value is to be used as key. This method is applicable to prototype nodes only. This is useful when you have an indexed array that should be an associative array. You can select an item from within the array to be the key of the particular item. For example, if "id" is the "key", then: { {'id': 'my_name', 'foo': 'bar'}, } becomes { my_name', {'foo': 'bar'}, } If you'd like "'id' => 'my_name'" to still be present in the resulting array, then you can set the second argument of this method to false. @param name: string The name of the key @param removeKeyItem: Boolean Whether or not the key item should be removed. @return: ArrayNodeDefinition """ self._key = name; self._removeKeyItem = removeKeyItem; return self; def canBeUnset(self, allow=True): """Sets whether the node can be unset. @param allow: Boolean @return: ArrayNodeDefinition """ self._merge().allowUnset(allow); return self; def canBeEnabled(self): """Adds an "enabled" boolean to enable the current section. By default, the section is disabled. @return: ArrayNodeDefinition """ self.treatFalseLike({'enabled': False}); self.treatTrueLike({'enabled': True}); self.treatNullLike({'enabled': True}); self.children().booleanNode('enabled').defaultFalse(); return self; def canBeDisabled(self): """Adds an "enabled" boolean to enable the current section. By default, the section is enabled. @return: ArrayNodeDefinition """ self.treatFalseLike({'enabled': False}); self.treatTrueLike({'enabled': True}); self.treatNullLike({'enabled': True}); self.children().booleanNode('enabled').defaultFalse(); return self; def performNoDeepMerging(self): """Disables the deep merging of the node. @return: ArrayNodeDefinition """ self._performDeepMerging = False; return self; def ignoreExtraKeys(self): """Allows extra config keys to be specified under an array without throwing an exception. Those config values are simply ignored. This should be used only in special cases where you want to send an entire configuration array through a special tree that processes only part of the array. @return: ArrayNodeDefinition """ self._ignoreExtraKeys = True; return self; def normalizeKeys(self, boolean): """Sets key normalization. @param boolean: boolean Whether to enable key normalization @return: ArrayNodeDefinition """ self._normalizeKeys = bool(boolean); return self; def append(self, node): """Appends a node definition. $node = ArrayNodeDefinition() ->children() ->scalarNode('foo')->end() ->scalarNode('baz')->end() ->end() ->append($this->getBarNodeDefinition()) ; @param node: NodeDefinition A NodeDefinition instance @return: ArrayNodeDefinition """ assert isinstance(node, NodeDefinition); self._children[node._name] = node.setParent(self); return self; def _getNodeBuilder(self): """Returns a node builder to be used to add children and prototype @return: NodeBuilder The node builder """ if self._nodeBuilder is None: self._nodeBuilder = NodeBuilder(); return self._nodeBuilder.setParent(self); def _createNode(self): if self._prototype is None: node = ArrayNode(self._name, self._parent); self._validateConcreteNode(node); node.setAddIfNotSet(self._addDefaults); for child in self._children.values(): child._parent = node; node.addChild(child.getNode()); else: node = PrototypedArrayNode(self._name, self._parent); self._validatePrototypeNode(node); if not self._key is None: node.setKeyAttribute(self._key, self._removeKeyItem); if self._atLeastOne: node.setMinNumberOfElements(1); if self._default: node.setDefaultValue(self._defaultValue); if not self._addDefaultChildren is False: node.setAddChildrenIfNoneSet(self._addDefaultChildren); if isinstance(self._prototype, type(self)): if self._prototype._prototype is None: self._prototype.addDefaultsIfNotSet(); self._prototype._parent = node; node.setPrototype(self._prototype.getNode()); node.setAllowNewKeys(self._allowNewKeys); node.addEquivalentValue(None, self._nullEquivalent); node.addEquivalentValue(True, self._trueEquivalent); node.addEquivalentValue(False, self._falseEquivalent); node.setPerformDeepMerging(self._performDeepMerging); node.setRequired(self._required); node.setIgnoreExtraKeys(self._ignoreExtraKeys); node.setNormalizeKeys(self._normalizeKeys); if not self._normalizationBuilder is None: node.setNormalizationClosures(self._normalizationBuilder.befores); node.setXmlRemappings(self._normalizationBuilder.remappings); if not self._mergeBuilder is None: node.setAllowOverwrite(self._mergeBuilder.allowOverwrite); node.setAllowFalse(self._mergeBuilder.allowFalse); if not self._validationBuilder is None: node.setFinalValidationClosures(self._validationBuilder.rules); return node; def _validateConcreteNode(self, node): """Validate the configuration of a concrete node. @param node: ArrayNode The related node @raise InvalidDefinitionException: """ assert isinstance(node, ArrayNode); path = node.getPath(); if not self._key is None: raise InvalidDefinitionException( '.useAttributeAsKey() is not applicable to concrete ' 'nodes at path "{0}"'.format(path) ); if self._atLeastOne: raise InvalidDefinitionException( '.requiresAtLeastOneElement() is not applicable ' 'to concrete nodes at path "{0}"'.format(path) ); if self._default: raise InvalidDefinitionException( '.defaultValue() is not applicable to concrete nodes ' 'at path "{0}"'.format(path) ); if not self._addDefaultChildren is False: raise InvalidDefinitionException( '.addDefaultChildrenIfNoneSet() is not applicable ' 'to concrete nodes at path "{0}"'.format(path) ); def _validatePrototypeNode(self, node): """Validate the configuration of a prototype node. @param node: PrototypedArrayNode The related node @raise InvalidDefinitionException: """ assert isinstance(node, PrototypedArrayNode); path = node.getPath(); if self._addDefaults: raise InvalidDefinitionException( '.addDefaultsIfNotSet() is not applicable to prototype ' 'nodes at path "{0}"'.format(path) ); if not self._addDefaultChildren is False: if self._default: raise InvalidDefinitionException( 'A default value and default children might not be ' 'used together at path "{0}"'.format(path) ); if not self._key is None and ( self._addDefaultChildren is None or \ isinstance(self._addDefaultChildren, int) and \ self._addDefaultChildren > 0 ): raise InvalidDefinitionException( '.addDefaultChildrenIfNoneSet() should set default ' 'children names as ->useAttributeAsKey() is used ' 'at path "{0}"'.format(path) ); if self._key is None and ( isinstance(self._addDefaultChildren, String) or \ isinstance(self._addDefaultChildren, dict) ): raise InvalidDefinitionException( '->addDefaultChildrenIfNoneSet() might not set default ' 'children names as ->useAttributeAsKey() is not used ' 'at path "{0}"'.format(path) );
def getBlockChompingTests(self): tests = list() yaml = """ foo: |- one two bar: |- one two """ expected = OrderedDict([ ('foo', 'one\ntwo'), ('bar', 'one\ntwo'), ]) tests.append([ 'Literal block chomping strip with single trailing newline', expected, yaml ]) yaml = """ foo: |- one two bar: |- one two """ expected = OrderedDict([ ('foo', "one\ntwo"), ('bar', "one\ntwo"), ]) tests.append([ 'Literal block chomping strip with multiple trailing newlines', expected, yaml ]) yaml = """ foo: |- one two bar: |- one two""" expected = OrderedDict([ ('foo', "one\ntwo"), ('bar', "one\ntwo"), ]) tests.append([ 'Literal block chomping strip without trailing newline', expected, yaml ]) yaml = """ foo: | one two bar: | one two """ expected = OrderedDict([ ('foo', "one\ntwo\n"), ('bar', "one\ntwo\n"), ]) tests.append([ 'Literal block chomping clip with single trailing newline', expected, yaml ]) yaml = """ foo: | one two bar: | one two """ expected = OrderedDict([ ('foo', "one\ntwo\n"), ('bar', "one\ntwo\n"), ]) tests.append([ 'Literal block chomping clip with multiple trailing newlines', expected, yaml ]) yaml = """ foo: | one two bar: | one two""" expected = OrderedDict([ ('foo', "one\ntwo\n"), ('bar', "one\ntwo"), ]) tests.append([ 'Literal block chomping clip without trailing newline', expected, yaml ]) yaml = """ foo: |+ one two bar: |+ one two """ expected = OrderedDict([ ('foo', "one\ntwo\n"), ('bar', "one\ntwo\n"), ]) tests.append([ 'Literal block chomping keep with single trailing newline', expected, yaml ]) yaml = """ foo: |+ one two bar: |+ one two """ expected = OrderedDict([ ('foo', "one\ntwo\n\n"), ('bar', "one\ntwo\n\n"), ]) tests.append([ 'Literal block chomping keep with multiple trailing newlines', expected, yaml ]) yaml = """ foo: |+ one two bar: |+ one two""" expected = OrderedDict([ ('foo', "one\ntwo\n"), ('bar', "one\ntwo"), ]) tests.append([ 'Literal block chomping keep without trailing newline', expected, yaml ]) yaml = """ foo: >- one two bar: >- one two """ expected = OrderedDict([ ('foo', "one two"), ('bar', "one two"), ]) tests.append([ 'Folded block chomping strip with single trailing newline', expected, yaml ]) yaml = """ foo: >- one two bar: >- one two """ expected = OrderedDict([ ('foo', "one two"), ('bar', "one two"), ]) tests.append([ 'Folded block chomping strip with multiple trailing newlines', expected, yaml ]) yaml = """ foo: >- one two bar: >- one two""" expected = OrderedDict([ ('foo', "one two"), ('bar', "one two"), ]) tests.append([ 'Folded block chomping strip without trailing newline', expected, yaml ]) yaml = """ foo: > one two bar: > one two """ expected = OrderedDict([ ('foo', "one two\n"), ('bar', "one two\n"), ]) tests.append([ 'Folded block chomping clip with single trailing newline', expected, yaml ]) yaml = """ foo: > one two bar: > one two """ expected = OrderedDict([ ('foo', "one two\n"), ('bar', "one two\n"), ]) tests.append([ 'Folded block chomping clip with multiple trailing newlines', expected, yaml ]) yaml = """ foo: > one two bar: > one two""" expected = OrderedDict([ ('foo', "one two\n"), ('bar', "one two"), ]) tests.append([ 'Folded block chomping clip without trailing newline', expected, yaml ]) yaml = """ foo: >+ one two bar: >+ one two """ expected = OrderedDict([ ('foo', "one two\n"), ('bar', "one two\n"), ]) tests.append([ 'Folded block chomping keep with single trailing newline', expected, yaml ]) yaml = """ foo: >+ one two bar: >+ one two """ expected = OrderedDict([ ('foo', "one two\n\n"), ('bar', "one two\n\n"), ]) tests.append([ 'Folded block chomping keep with multiple trailing newlines', expected, yaml ]) yaml = """ foo: >+ one two bar: >+ one two""" expected = OrderedDict([ ('foo', "one two\n"), ('bar', "one two"), ]) tests.append([ 'Folded block chomping keep without trailing newline', expected, yaml ]) return tests
def _getTestsForDump(self): return { 'null': None, 'false': False, 'true': True, '12': 12, "'quoted string'": 'quoted string', '1230.0': 12.30e+02, '1234': 0x4D2, '1243': 0o2333, '.Inf': 1e10000, '-.Inf': -1e10000, "'686e444'": '686e444', '.Inf': 646e444, '"foo\\r\\nbar"': "foo\r\nbar", "'foo#bar'": 'foo#bar', "'foo # bar'": 'foo # bar', "'#cfcfcf'": '#cfcfcf', "'a \"string\" with ''quoted strings inside'''": 'a "string" with \'quoted strings inside\'', # sequences '[foo, bar, false, null, 12]': ['foo', 'bar', False, None, 12], '[\'foo,bar\', \'foo bar\']': ['foo,bar', 'foo bar'], # mappings '{ foo: bar, bar: foo, \'false\': false, \'null\': null, integer: 12 }': OrderedDict([('foo', 'bar'), ('bar', 'foo'), ('false', False), ('null', None), ('integer', 12)]), '{ foo: bar, bar: \'foo: bar\' }': OrderedDict([('foo', 'bar'), ('bar', 'foo: bar')]), # nested sequences and mappings '[foo, [bar, foo]]': ['foo', ['bar', 'foo']], '[foo, [bar, [foo, [bar, foo]], foo]]': ['foo', ['bar', ['foo', ['bar', 'foo']], 'foo']], '{ foo: { bar: foo } }': { 'foo': { 'bar': 'foo' } }, '[foo, { bar: foo }]': ['foo', { 'bar': 'foo' }], '[foo, { bar: foo, foo: [foo, { bar: foo }] }, [foo, { bar: foo }]]': [ 'foo', OrderedDict([('bar', 'foo'), ('foo', ['foo', { 'bar': 'foo' }])]), ['foo', { 'bar': 'foo' }] ], '[foo, \'@foo.baz\', { \'%foo%\': \'foo is %foo%\', bar: \'%foo%\' }, true, \'@service_container\']': [ 'foo', '@foo.baz', OrderedDict([ ('%foo%', 'foo is %foo%'), ('bar', '%foo%'), ]), True, '@service_container', ], }
def testInlineLevel(self): # inline level array = OrderedDict([ ('', 'bar'), ('foo', '#bar'), ('foo\'bar', dict()), ('bar', [1, 'foo']), ('foobar', OrderedDict([ ('foo', 'bar'), ('bar', [1, 'foo']), ('foobar', OrderedDict([ ('foo', 'bar'), ('bar', [1, 'foo']), ])), ])), ]) expected = """{ '': bar, foo: '#bar', 'foo''bar': { }, bar: [1, foo], foobar: { foo: bar, bar: [1, foo], foobar: { foo: bar, bar: [1, foo] } } }""" self.assertEqual(expected, self._dumper.dump(array, -10), '->dump() takes an inline level argument') self.assertEqual(expected, self._dumper.dump(array, 0), '->dump() takes an inline level argument') expected = """'': bar foo: '#bar' 'foo''bar': { } bar: [1, foo] foobar: { foo: bar, bar: [1, foo], foobar: { foo: bar, bar: [1, foo] } } """ self.assertEqual(expected, self._dumper.dump(array, 1), '->dump() takes an inline level argument') expected = """'': bar foo: '#bar' 'foo''bar': { } bar: - 1 - foo foobar: foo: bar bar: [1, foo] foobar: { foo: bar, bar: [1, foo] } """ self.assertEqual(expected, self._dumper.dump(array, 2), '->dump() takes an inline level argument') expected = """'': bar foo: '#bar' 'foo''bar': { } bar: - 1 - foo foobar: foo: bar bar: - 1 - foo foobar: foo: bar bar: [1, foo] """ self.assertEqual(expected, self._dumper.dump(array, 3), '->dump() takes an inline level argument') expected = """'': bar foo: '#bar' 'foo''bar': { } bar: - 1 - foo foobar: foo: bar bar: - 1 - foo foobar: foo: bar bar: - 1 - foo """ self.assertEqual(expected, self._dumper.dump(array, 4), '->dump() takes an inline level argument') self.assertEqual(expected, self._dumper.dump(array, 10), '->dump() takes an inline level argument')
class ArrayNode(BaseNode, PrototypeNodeInterface): """Represents an Array node in the config tree. @author Johannes M. Schmitt <*****@*****.**> """ def __init__(self, name, parent=None): """Constructor. @param name: string The Node's name @param parent: NodeInterface The node parent """ BaseNode.__init__(self, name, parent=parent) self._xmlRemappings = list() self._children = OrderedDict() self._allowFalse = False self._allowNewKeys = True self._addIfNotSet = False self._performDeepMerging = True self._ignoreExtraKeys = None self._normalizeKeys = True def setNormalizeKeys(self, normalizeKeys): self._normalizeKeys = bool(normalizeKeys) def _preNormalize(self, value): """Normalizes keys between the different configuration formats. Namely, you mostly have foo_bar in YAML while you have foo-bar in XML. After running this method, all keys are normalized to foo_bar. If you have a mixed key like foo-bar_moo, it will not be altered. The key will also not be altered if the target key already exists. @param value: mixed @return dict The value with normalized keys """ if not self._normalizeKeys or not isinstance(value, dict): return value for k, v in value.items(): if '-' in str(k): if not '_' in str(k): normalizedKey = str(k).replace('-', '_') if not normalizedKey in value: value[normalizedKey] = v value.pop(k) return value def getChildren(self): """Retrieves the children of this node. @return: dict The children """ return self._children def setXmlRemappings(self, xmlRemappings): """Sets the xml remappings that should be performed. @param xmlRemappings: an list of the form list(list(string, string)) """ self._xmlRemappings = list(xmlRemappings) def setAddIfNotSet(self, boolean): """Sets whether to add default values for this array if it has not been defined in any of the configuration files. @param boolean: Boolean """ self._addIfNotSet = bool(boolean) def setAllowFalse(self, allow): """Sets whether false is allowed as value indicating that the array should be unset. @param allow: Boolean """ self._allowFalse = bool(allow) def setAllowNewKeys(self, allow): """Sets whether new keys can be defined in subsequent configurations. @param allow: Boolean """ self._allowNewKeys = bool(allow) def setPerformDeepMerging(self, boolean): """Sets if deep merging should occur. @param boolean: Boolean """ self._performDeepMerging = bool(boolean) def setIgnoreExtraKeys(self, boolean): """Whether extra keys should just be ignore without an exception. @param boolean: Boolean To allow extra keys """ self._ignoreExtraKeys = bool(boolean) def setName(self, name): """Sets the node Name. @param name: string The node's name """ self._name = str(name) def hasDefaultValue(self): """Checks if the node has a default value. @return Boolean """ return self._addIfNotSet def getDefaultValue(self): """Retrieves the default value. @return: dict The default value @raise RuntimeException: if the node has no default value """ if not self.hasDefaultValue(): raise RuntimeException( 'The node at path "{0}" has no default value.' ''.format(self.getPath())) default = dict() for name, child in self._children.items(): if child.hasDefaultValue(): default[name] = child.getDefaultValue() return default def addChild(self, node): """Adds a child node. @param child: NodeInterface The child node to add @raise InvalidArgumentException: when the child node has no name @raise InvalidArgumentException: when the child node's name is not unique """ assert isinstance(node, NodeInterface) name = node.getName() if not name: raise InvalidArgumentException('Child nodes must be named.') if name in self._children: raise InvalidArgumentException( 'A child node named "{0}" already exists.' ''.format(name)) self._children[name] = node def _finalizeValue(self, value): """Finalizes the value of this node. @param value: mixed @return: mixed The finalised value @raise UnsetKeyException: @raise InvalidConfigurationException: if the node doesn't have enough children """ if value is False: raise UnsetKeyException('Unsetting key for path "{0}", value: {1}' ''.format(self.getPath(), json.dumps(value))) for name, child in self._children.items(): assert isinstance(child, NodeInterface) if not name in value: if child.isRequired(): ex = InvalidConfigurationException( 'The child node "{0}" at path "{1}" must be ' 'configured.'.format(name, self.getPath())) ex.setPath(self.getPath()) raise ex if child.hasDefaultValue(): value[name] = child.getDefaultValue() continue try: value[name] = child.finalize(value[name]) except UnsetKeyException: value.pop(name) return value def _validateType(self, value): """Validates the type of the value. @param value: mixed @raise InvalidTypeException: """ if not isinstance(value, (dict, list)): if not self._allowFalse or value: ex = InvalidTypeException( 'Invalid type for path "{0}". Expected array, but got {1}' ''.format(self.getPath(), type(value).__name__)) ex.setPath(self.getPath()) raise ex 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 _remapXml(self, value): """Remaps multiple singular values to a single plural value. @param value: cict The source values @return: dict The remapped values """ assert isinstance(value, dict) for singular, plural in self._xmlRemappings: if not singular in value: continue value[plural] = Processor.normalizeConfig(value, singular, plural) value.pop(singular) return 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 testObjectSupportDisabledButNoExceptions(self): dump = self._dumper.dump(OrderedDict([('foo', A()), ('bar', 1)])) self.assertEqual('{ foo: null, bar: 1 }', dump, '->dump() does not dump objects when disabled')
def __object_pairs_hook(self, seq): return OrderedDict(seq)