Merge "Ensure dict orders are deterministic"

This commit is contained in:
Jenkins 2014-09-02 19:06:13 +00:00 committed by Gerrit Code Review
commit 32e2912418
10 changed files with 84 additions and 36 deletions

View File

@ -54,16 +54,58 @@ Example:
""" """
import codecs import codecs
try:
from collections import OrderedDict
except ImportError:
from ordereddict import OrderedDict
import functools import functools
import logging import logging
import re import re
import os import os
import yaml import yaml
from yaml.constructor import BaseConstructor
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class LocalLoader(yaml.Loader): class OrderedConstructor(BaseConstructor):
"""The default constructor class for PyYAML loading uses standard python
dictionaries which can have randomized ordering enabled (default in
CPython from version 3.3). The order of the XML elements being outputted
is both important for tests and for ensuring predictable generation based
on the source. This subclass overrides this behaviour to ensure that all
dict's created make use of OrderedDict to have iteration of keys to always
follow the order in which the keys were inserted/created.
"""
def construct_yaml_map(self, node):
data = OrderedDict()
yield data
value = self.construct_mapping(node)
if isinstance(node, yaml.MappingNode):
self.flatten_mapping(node)
else:
raise yaml.constructor.ConstructorError(
None, None,
'expected a mapping node, but found %s' % node.id,
node.start_mark)
mapping = OrderedDict()
for key_node, value_node in node.value:
key = self.construct_object(key_node, deep=False)
try:
hash(key)
except TypeError as exc:
raise yaml.constructor.ConstructorError(
'while constructing a mapping', node.start_mark,
'found unacceptable key (%s)' % exc, key_node.start_mark)
value = self.construct_object(value_node, deep=False)
mapping[key] = value
data.update(mapping)
class LocalLoader(OrderedConstructor, yaml.Loader):
"""Subclass for yaml.Loader which handles the local tags 'include', """Subclass for yaml.Loader which handles the local tags 'include',
'include-raw' and 'include-raw-escaped' to specify a file to include data 'include-raw' and 'include-raw-escaped' to specify a file to include data
from and whether to parse it as additional yaml, treat it as a data blob from and whether to parse it as additional yaml, treat it as a data blob
@ -116,6 +158,11 @@ class LocalLoader(yaml.Loader):
self.add_constructor('!include-raw-escape', self.add_constructor('!include-raw-escape',
self._include_raw_escape_tag) self._include_raw_escape_tag)
# constructor to preserve order of maps and ensure that the order of
# keys returned is consistent across multiple python versions
self.add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
type(self).construct_yaml_map)
if isinstance(self.stream, file): if isinstance(self.stream, file):
self.search_path.add(os.path.normpath( self.search_path.add(os.path.normpath(
os.path.dirname(self.stream.name))) os.path.dirname(self.stream.name)))

View File

@ -2399,7 +2399,7 @@ postbuildscript003.yaml
'generic': 'GenericScript', 'generic': 'GenericScript',
'groovy': 'GroovyScriptFile', 'groovy': 'GroovyScriptFile',
} }
for script_type in script_types.keys(): for script_type in sorted(script_types.keys()):
if script_type + '-script' not in data: if script_type + '-script' not in data:
continue continue

View File

@ -107,14 +107,14 @@ def build_gerrit_triggers(xml_parent, data):
def build_gerrit_skip_votes(xml_parent, data): def build_gerrit_skip_votes(xml_parent, data):
outcomes = {'successful': 'onSuccessful', outcomes = [('successful', 'onSuccessful'),
'failed': 'onFailed', ('failed', 'onFailed'),
'unstable': 'onUnstable', ('unstable', 'onUnstable'),
'notbuilt': 'onNotBuilt'} ('notbuilt', 'onNotBuilt')]
skip_vote_node = XML.SubElement(xml_parent, 'skipVote') skip_vote_node = XML.SubElement(xml_parent, 'skipVote')
skip_vote = data.get('skip-vote', {}) skip_vote = data.get('skip-vote', {})
for result_kind, tag_name in outcomes.iteritems(): for result_kind, tag_name in outcomes:
if skip_vote.get(result_kind, False): if skip_vote.get(result_kind, False):
XML.SubElement(skip_vote_node, tag_name).text = 'true' XML.SubElement(skip_vote_node, tag_name).text = 'true'
else: else:

View File

@ -2,6 +2,7 @@ hacking>=0.5.6,<0.8
coverage>=3.6 coverage>=3.6
discover discover
fixtures fixtures
ordereddict
python-subunit python-subunit
sphinx>=1.1.2,<1.2 sphinx>=1.1.2,<1.2
setuptools_git>=0.4 setuptools_git>=0.4

View File

@ -4,8 +4,8 @@
<hudson.tasks.Ant> <hudson.tasks.Ant>
<buildFile>build.xml</buildFile> <buildFile>build.xml</buildFile>
<antOpts>-ea -Xmx512m</antOpts> <antOpts>-ea -Xmx512m</antOpts>
<properties>failonerror=True <properties>builddir=/tmp/
builddir=/tmp/ failonerror=True
</properties> </properties>
<targets>debug test install</targets> <targets>debug test install</targets>
<antName>Standard Ant</antName> <antName>Standard Ant</antName>

View File

@ -1,6 +1,7 @@
[ [
{ {
"template-job": { "template-job": {
"name": "test-job-include-raw-{num}",
"builders": [ "builders": [
{ {
"shell": "#!/bin/bash\n#\n# Sample script showing how the yaml include-raw tag can be used\n# to inline scripts that are maintained outside of the jenkins\n# job yaml configuration.\n\necho \"hello world\"\n\nexit 0\n" "shell": "#!/bin/bash\n#\n# Sample script showing how the yaml include-raw tag can be used\n# to inline scripts that are maintained outside of the jenkins\n# job yaml configuration.\n\necho \"hello world\"\n\nexit 0\n"
@ -8,17 +9,16 @@
{ {
"shell": "#!/bin/bash\n#\n# sample script to check that brackets aren't escaped\n# when using the include-raw application yaml tag\n\nVAR1=\"hello\"\nVAR2=\"world\"\nVAR3=\"${{VAR1}} ${{VAR2}}\"\n\n[[ -n \"${{VAR3}}\" ]] && {{\n # this next section is executed as one\n echo \"${{VAR3}}\"\n exit 0\n}}\n\n" "shell": "#!/bin/bash\n#\n# sample script to check that brackets aren't escaped\n# when using the include-raw application yaml tag\n\nVAR1=\"hello\"\nVAR2=\"world\"\nVAR3=\"${{VAR1}} ${{VAR2}}\"\n\n[[ -n \"${{VAR3}}\" ]] && {{\n # this next section is executed as one\n echo \"${{VAR3}}\"\n exit 0\n}}\n\n"
} }
], ]
"name": "test-job-include-raw-{num}"
} }
}, },
{ {
"project": { "project": {
"name": "test-job-template-1",
"num": 1, "num": 1,
"jobs": [ "jobs": [
"test-job-include-raw-{num}" "test-job-include-raw-{num}"
], ]
"name": "test-job-template-1"
} }
} }
] ]

View File

@ -1,6 +1,7 @@
[ [
{ {
"job": { "job": {
"name": "test-job-include-raw-1",
"builders": [ "builders": [
{ {
"shell": "#!/bin/bash\n#\n# Sample script showing how the yaml include-raw tag can be used\n# to inline scripts that are maintained outside of the jenkins\n# job yaml configuration.\n\necho \"hello world\"\n\nexit 0\n" "shell": "#!/bin/bash\n#\n# Sample script showing how the yaml include-raw tag can be used\n# to inline scripts that are maintained outside of the jenkins\n# job yaml configuration.\n\necho \"hello world\"\n\nexit 0\n"
@ -8,8 +9,7 @@
{ {
"shell": "#!/bin/bash\n#\n# sample script to check that brackets aren't escaped\n# when using the include-raw application yaml tag\n\nVAR1=\"hello\"\nVAR2=\"world\"\nVAR3=\"${VAR1} ${VAR2}\"\n\n[[ -n \"${VAR3}\" ]] && {\n # this next section is executed as one\n echo \"${VAR3}\"\n exit 0\n}\n\n" "shell": "#!/bin/bash\n#\n# sample script to check that brackets aren't escaped\n# when using the include-raw application yaml tag\n\nVAR1=\"hello\"\nVAR2=\"world\"\nVAR3=\"${VAR1} ${VAR2}\"\n\n[[ -n \"${VAR3}\" ]] && {\n # this next section is executed as one\n echo \"${VAR3}\"\n exit 0\n}\n\n"
} }
], ]
"name": "test-job-include-raw-1"
} }
} }
] ]

View File

@ -1,43 +1,43 @@
[ [
{ {
"job": { "job": {
"name": "test-job-1",
"builders": [ "builders": [
{ {
"copyartifact": { "copyartifact": {
"filter": "*.tar.gz",
"project": "foo", "project": "foo",
"parameter-filters": "PUBLISH=true", "filter": "*.tar.gz",
"target": "/home/foo", "target": "/home/foo",
"flatten": true, "which-build": "last-successful",
"optional": true, "optional": true,
"which-build": "last-successful" "flatten": true,
"parameter-filters": "PUBLISH=true"
} }
}, },
{ {
"copyartifact": { "copyartifact": {
"project": "bar", "project": "bar",
"parameter-filters": "PUBLISH=true",
"target": "/home/foo",
"build-number": 123,
"which-build": "specific-build",
"filter": "*.tar.gz", "filter": "*.tar.gz",
"target": "/home/foo",
"which-build": "specific-build",
"optional": true,
"flatten": true, "flatten": true,
"optional": true "parameter-filters": "PUBLISH=true",
"build-number": 123
} }
}, },
{ {
"copyartifact": { "copyartifact": {
"filter": "*.tar.gz",
"project": "baz", "project": "baz",
"parameter-filters": "PUBLISH=true", "filter": "*.tar.gz",
"target": "/home/foo", "target": "/home/foo",
"flatten": true, "which-build": "upstream-build",
"optional": true, "optional": true,
"which-build": "upstream-build" "flatten": true,
} "parameter-filters": "PUBLISH=true"
} }
], }
"name": "test-job-1" ]
} }
} }
] ]

View File

@ -7,14 +7,14 @@
<hudson.plugins.rubyMetrics.rcov.model.MetricTarget> <hudson.plugins.rubyMetrics.rcov.model.MetricTarget>
<metric>TOTAL_COVERAGE</metric> <metric>TOTAL_COVERAGE</metric>
<healthy>80</healthy> <healthy>80</healthy>
<unstable>0</unstable>
<unhealthy>0</unhealthy> <unhealthy>0</unhealthy>
<unstable>0</unstable>
</hudson.plugins.rubyMetrics.rcov.model.MetricTarget> </hudson.plugins.rubyMetrics.rcov.model.MetricTarget>
<hudson.plugins.rubyMetrics.rcov.model.MetricTarget> <hudson.plugins.rubyMetrics.rcov.model.MetricTarget>
<metric>CODE_COVERAGE</metric> <metric>CODE_COVERAGE</metric>
<healthy>80</healthy> <healthy>80</healthy>
<unstable>0</unstable>
<unhealthy>0</unhealthy> <unhealthy>0</unhealthy>
<unstable>0</unstable>
</hudson.plugins.rubyMetrics.rcov.model.MetricTarget> </hudson.plugins.rubyMetrics.rcov.model.MetricTarget>
</targets> </targets>
</hudson.plugins.rubyMetrics.rcov.RcovPublisher> </hudson.plugins.rubyMetrics.rcov.RcovPublisher>

View File

@ -18,15 +18,15 @@
</types> </types>
<thresholds> <thresholds>
<org.jenkinsci.plugins.xunit.threshold.FailedThreshold> <org.jenkinsci.plugins.xunit.threshold.FailedThreshold>
<failureThreshold/>
<unstableThreshold/> <unstableThreshold/>
<unstableNewThreshold/> <unstableNewThreshold/>
<failureThreshold/>
<failureNewThreshold/> <failureNewThreshold/>
</org.jenkinsci.plugins.xunit.threshold.FailedThreshold> </org.jenkinsci.plugins.xunit.threshold.FailedThreshold>
<org.jenkinsci.plugins.xunit.threshold.SkippedThreshold> <org.jenkinsci.plugins.xunit.threshold.SkippedThreshold>
<failureThreshold/>
<unstableThreshold/> <unstableThreshold/>
<unstableNewThreshold/> <unstableNewThreshold/>
<failureThreshold/>
<failureNewThreshold/> <failureNewThreshold/>
</org.jenkinsci.plugins.xunit.threshold.SkippedThreshold> </org.jenkinsci.plugins.xunit.threshold.SkippedThreshold>
</thresholds> </thresholds>