Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Merge branch 'yamlconfig' of git://github.com/tardyp/buildbot

  • Loading branch information...
commit ec125d1fc0fb2f36fc94fffebc4d8228164079af 2 parents 1fa1c4f + 1a644e8
Dustin J. Mitchell djmitche authored
5 master/buildbot/test/__init__.py
@@ -20,6 +20,11 @@
20 20 from buildbot import monkeypatches
21 21 monkeypatches.patch_all(for_tests=True)
22 22
  23 +# we set pedantic mode for the unit tests
  24 +# which will ensure that it is always json-able
  25 +from buildbot.util import namespace
  26 +namespace.pedantic = True
  27 +
23 28 # import mock so we bail out early if it's not installed
24 29 try:
25 30 import mock
190 master/buildbot/test/unit/test_util_namespace.py
... ... @@ -0,0 +1,190 @@
  1 +# This file is part of Buildbot. Buildbot is free software: you can
  2 +# redistribute it and/or modify it under the terms of the GNU General Public
  3 +# License as published by the Free Software Foundation, version 2.
  4 +#
  5 +# This program is distributed in the hope that it will be useful, but WITHOUT
  6 +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  7 +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  8 +# details.
  9 +#
  10 +# You should have received a copy of the GNU General Public License along with
  11 +# this program; if not, write to the Free Software Foundation, Inc., 51
  12 +# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  13 +#
  14 +# Copyright Buildbot Team Members
  15 +
  16 +from twisted.trial import unittest
  17 +from buildbot.util import namespace
  18 +from time import time
  19 +from mock import Mock
  20 +import pickle
  21 +from buildbot.util import json
  22 +
  23 +class Namespace(unittest.TestCase):
  24 +
  25 + def test_basic(self):
  26 + n = namespace.Namespace({'a':{'b':{'c':1}}})
  27 +
  28 + self.assertEqual(n.a.b.c, 1)
  29 + self.assertEqual(n['a']['b']['c'], 1)
  30 + self.assertEqual(n['a'].b['c'], 1)
  31 + self.assertEqual(n['a']['b'].c, 1)
  32 + self.assertEqual(n['a'].b.c, 1)
  33 + self.assertEqual(list(n.keys()),['a'])
  34 + self.assertEqual(list(n.a.b.values()),[1])
  35 + self.assertEqual(list(n.a.b.items()),[('c',1)])
  36 + n.a.b.c = 2
  37 + self.assertEqual(n.has_key('a'),True)
  38 + self.assertEqual(n.has_key('b'),False)
  39 +
  40 + self.assertEqual(n['a']['b']['c'], 2)
  41 + n.a.b = {'d':3}
  42 + self.assertEqual(n.a.b.d, 3)
  43 + n.a.b = namespace.Namespace({'e':4})
  44 + self.assertEqual(n.a.b.e, 4)
  45 + self.assertRaises(KeyError, lambda : n.a.b.d == 3)
  46 + self.assertEqual(namespace.Namespace(1),1)
  47 + self.assertEqual(namespace.Namespace([1]),[1])
  48 + self.assertEqual(namespace.Namespace("1"),"1")
  49 + self.assertEqual(namespace.Namespace(["1"]),["1"])
  50 +
  51 + self.assertRaises(KeyError, lambda : n["__getitem__"])
  52 + n.a['b'] = {'f':5}
  53 + self.assertEqual(n.a.b.f, 5)
  54 +
  55 + def test_nonzero(self):
  56 + n = namespace.Namespace({'a':{'b':{'c':1}}})
  57 + self.failUnless(n)
  58 + n = namespace.Namespace({})
  59 + self.failIf(n)
  60 +
  61 + def test_list(self):
  62 + n = namespace.Namespace([{'a':{'b':{'c':1}}},{'a':{'b':{'c':2}}}])
  63 + self.assertEqual(n[0].a.b.c, 1)
  64 + self.assertEqual(n[1].a.b.c, 2)
  65 + for i in n:
  66 + self.assertEqual(i.a.b.c, i.a.b.c)
  67 +
  68 + def test_jsondump(self):
  69 + s = '[{"a": {"b": {"c": 1}}}, {"a": {"b": {"c": 2}}}]'
  70 + n = namespace.Namespace(json.loads(s))
  71 + self.assertEqual(json.dumps(n),s)
  72 +
  73 + def test_prettyprint(self):
  74 + n = namespace.Namespace({'a':[{'b':{'c':1}}]})
  75 + expected = """\
  76 +{
  77 + "a": [
  78 + {
  79 + "b": {
  80 + "c": 1
  81 + }
  82 + }
  83 + ]
  84 +}"""
  85 + self.assertEqual(repr(n), expected)
  86 + expected = """\
  87 +a -> list
  88 +a[i] -> dict
  89 +a[i].b -> dict
  90 +a[i].b.c -> int
  91 +"""
  92 + self.assertEqual(namespace.documentNamespace(n), expected)
  93 +
  94 + def test_pickle(self):
  95 + n = namespace.Namespace([{'a':{'b':{'c':1}}},{'a':{'b':{'c':2}}}])
  96 + s = pickle.dumps(n)
  97 + n = pickle.loads(s)
  98 + self.assertEqual(n[0].a.b.c, 1)
  99 + self.assertEqual(n[1].a.b.c, 2)
  100 + for i in n:
  101 + self.assertEqual(i.a.b.c, i.a.b.c)
  102 + def test_pedantic(self):
  103 + self.assertRaises(TypeError, lambda:namespace.Namespace({'a': set([1,2,3])}))
  104 + self.patch(namespace,"pedantic", False)
  105 + # should not crash if pendentic disabled
  106 + n = namespace.Namespace({'a': set([1,2,3])})
  107 + self.assertRaises(TypeError,lambda:repr(n))
  108 +
  109 + def do_benchmark(self, D, m, f1,f2):
  110 + self.patch(namespace,"pedantic", False)
  111 + numtime = 100
  112 + start = time()
  113 + for i in xrange(numtime*100):
  114 + d = dict(D)
  115 + f1(d)
  116 + t1 = (time()-start)/100
  117 + start = time()
  118 + for i in xrange(numtime*100):
  119 + f1(d)
  120 + t1bis = (time()-start)/100
  121 + start = time()
  122 + for i in xrange(numtime):
  123 + d = namespace.Namespace(D)
  124 + f1(d)
  125 + t2 = time()-start
  126 + start = time()
  127 + for i in xrange(numtime):
  128 + f1(d)
  129 + t2bis = time()-start
  130 + start = time()
  131 + for i in xrange(numtime):
  132 + d = namespace.Namespace(D)
  133 + f2(d)
  134 + t3 = time()-start
  135 + start = time()
  136 + for i in xrange(numtime):
  137 + f2(d)
  138 + t3bis = time()-start
  139 + start = time()
  140 + for i in xrange(numtime):
  141 + f2(m)
  142 + t4bis = time()-start
  143 + def fmt(i):
  144 + return "%d kread/s"%(int(i)/1000)
  145 + print
  146 + print "create + access"
  147 + print "pure dict :",fmt(numtime/t1)
  148 + print "Namespace as dict :",fmt(numtime/t2), "(x",int(t2/t1),")"
  149 + print "Namespace as object:",fmt(numtime/t3), "(x",int(t3/t1),")"
  150 + print "access only"
  151 + print "pure dict :",fmt(numtime/t1bis)
  152 + print "Namespace as dict :",fmt(numtime/t2bis), "(x",int(t2bis/t1bis),")"
  153 + print "Namespace as object:",fmt(numtime/t3bis), "(x",int(t3bis/t1bis),")"
  154 + print "Mock as object:",fmt(numtime/t4bis), "(x",int(t4bis/t1bis),")"
  155 + def test_benchmark1(self):
  156 + m = Mock()
  157 + m.a.b.c = 2
  158 + self.do_benchmark({'a':{'b':{'c':1}}},m,
  159 + lambda d:d['a']['b']['c']==2,
  160 + lambda d:d.a.b.c==2)
  161 + def test_benchmark2(self):
  162 + m = Mock()
  163 + m1 = Mock()
  164 + m1.b.c=1
  165 + m2 = Mock()
  166 + m2.b.d=2
  167 + m.a = [m1,m2]
  168 + self.do_benchmark({'a':[{'b':{'c':1}},{'b':{'d':2}}]},m,
  169 + lambda d:d['a'][0]['b']['c']==2,
  170 + lambda d:d.a[0].b.c==2)
  171 + def test_benchmark3(self):
  172 + d = {}
  173 + m = Mock()
  174 + c = d
  175 + f1 = f2 = "lambda d:d"
  176 + f3= "m"
  177 + for i in xrange(25):
  178 + k = chr(ord('a')+i)
  179 + c[k] = {'z':1}
  180 + c = c[k]
  181 + f1+="['"+k+"']"
  182 + f2+="."+k
  183 + f3+="."+k
  184 + f1 = eval(f1)
  185 + f2 = eval(f2)
  186 + f3+="=2"
  187 + exec f3
  188 + self.do_benchmark(d,m,
  189 + f1,
  190 + f2)
89 master/buildbot/util/namespace.py
... ... @@ -0,0 +1,89 @@
  1 +# This file is part of Buildbot. Buildbot is free software: you can
  2 +# redistribute it and/or modify it under the terms of the GNU General Public
  3 +# License as published by the Free Software Foundation, version 2.
  4 +#
  5 +# This program is distributed in the hope that it will be useful, but WITHOUT
  6 +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  7 +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  8 +# details.
  9 +#
  10 +# You should have received a copy of the GNU General Public License along with
  11 +# this program; if not, write to the Free Software Foundation, Inc., 51
  12 +# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  13 +#
  14 +# Copyright Buildbot Team Members
  15 +
  16 +from buildbot.util import json
  17 +
  18 +pedantic = False
  19 +class Namespace(dict):
  20 + """
  21 + A convenience class that makes a json like dict of (dicts,lists,strings,integers)
  22 + looks like a python object allowing syntax sugar like mydict.key1.key2.key4 = 5
  23 + this object also looks like a dict so that you can walk items(), keys() or values()
  24 +
  25 + input should always be json-able but this is checked only in pedentic mode, for
  26 + obvious performance reason
  27 + """
  28 + __slots__=[]
  29 + def __init__(self,d):
  30 + dict.__init__(self,d)
  31 + for k, v in d.items():
  32 + # if already a Namespace, this will not match
  33 + if type(v) in (dict,list):
  34 + dict.__setitem__(self, k, Namespace(v))
  35 +
  36 + def __new__(cls, v):
  37 + if pedantic: # we raise exception if v is not json able
  38 + json.dumps(v)
  39 + if type(v) == dict:
  40 + return dict.__new__(cls, v)
  41 + elif type(v) == list:
  42 + return [Namespace(i) for i in v]
  43 + else:
  44 + return v
  45 +# pretty printing
  46 + def __repr__(self):
  47 + """ pretty printed __repr__, for debugging"""
  48 + return json.dumps(self, sort_keys=True, indent=4)
  49 +
  50 +# on the fly conversion
  51 + def __setitem__(self, k, v):
  52 + dict.__setitem__(self, k, Namespace(v))
  53 +
  54 +# object like accessors
  55 + __getattr__ = dict.__getitem__
  56 + __setattr__ = __setitem__
  57 +
  58 +
  59 + def __getstate__(self):
  60 + return self
  61 + def __setstate__(self,_dict):
  62 + self.__init__(_dict)
  63 +
  64 +def documentNamespace(n,parent=None):
  65 + """This prints the available keys and subkeys of the data, and their types,
  66 + meant for quick auto-documentation of big json data
  67 + """
  68 + s = ""
  69 + for k,v in n.items():
  70 + if parent:
  71 + me = parent+"."+k
  72 + else:
  73 + me = k
  74 + def do_item(me, v):
  75 + t = type(v).__name__
  76 + if t == "Namespace":
  77 + t = "dict"
  78 + s = me + " -> "+t+"\n"
  79 + if isinstance(v,dict):
  80 + v = Namespace(v)
  81 + s += documentNamespace(v, me)
  82 + elif type(v) == list:
  83 + if len(v)>0:
  84 + v = v[0]
  85 + s += do_item(me+"[i]",v)
  86 + return s
  87 + s += do_item(me,v)
  88 + return s
  89 +

0 comments on commit ec125d1

Please sign in to comment.
Something went wrong with that request. Please try again.