Skip to content

Commit 2530fc0

Browse files
committed
HTML* classes for cgi are now all new-style
Add regression test for old behaviour: Lookup of a value of a HTMLProperty was possibly via getitem -- for old-style classes this worked because __getattr__ returned the __getitem__ of a newly created HTMLItem object, this does no longer work for new-style classes as these look up special method only on the class not the instance.
1 parent 9f5210e commit 2530fc0

File tree

5 files changed

+121
-19
lines changed

5 files changed

+121
-19
lines changed

roundup/cgi/templating.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,7 @@ def input_xhtml(**attrs):
398398
_set_input_default_args(attrs)
399399
return '<input %s/>'%cgi_escape_attrs(**attrs)
400400

401-
class HTMLInputMixin:
401+
class HTMLInputMixin(object):
402402
""" requires a _client property """
403403
def __init__(self):
404404
html_version = 'html4'
@@ -421,7 +421,7 @@ def gettext(self, msgid):
421421

422422
_ = gettext
423423

424-
class HTMLPermissions:
424+
class HTMLPermissions(object):
425425

426426
def view_check(self):
427427
""" Raise the Unauthorised exception if the user's not permitted to
@@ -1903,7 +1903,7 @@ def field(self, size=30, **kwargs):
19031903
return self.input(name=self._formname, value=value, size=size,
19041904
**kwargs)
19051905

1906-
class LinkHTMLProperty(HTMLProperty, object):
1906+
class LinkHTMLProperty(HTMLProperty):
19071907
""" Link HTMLProperty
19081908
Include the above as well as being able to access the class
19091909
information. Stringifying the object itself results in the value
@@ -1912,9 +1912,6 @@ class LinkHTMLProperty(HTMLProperty, object):
19121912
property accessed (so item/assignedto/name would look up the user
19131913
entry identified by the assignedto property on item, and then the
19141914
name property of that user)
1915-
1916-
(Has been turned into a new-style class to enable comparisons
1917-
of values with None, see issue2550830.)
19181915
"""
19191916
def __init__(self, *args, **kw):
19201917
HTMLProperty.__init__(self, *args, **kw)
@@ -1936,6 +1933,14 @@ def nothing(*args, **kw):
19361933
i = HTMLItem(self._client, self._prop.classname, self._value)
19371934
return getattr(i, attr)
19381935

1936+
def __getitem__(self, item):
1937+
"""Explicitly define __getitem__ -- this used to work earlier
1938+
due to __getattr__ returning the __getitem__ of HTMLItem -- this
1939+
lookup doesn't work for new-style classes.
1940+
"""
1941+
i = HTMLItem(self._client, self._prop.classname, self._value)
1942+
return i[item]
1943+
19391944
def plain(self, escape=0):
19401945
""" Render a "plain" representation of the property
19411946
"""
@@ -3006,7 +3011,7 @@ def html_calendar(self, request):
30063011
res.append('</table></td></tr></table>')
30073012
return "\n".join(res)
30083013

3009-
class MissingValue:
3014+
class MissingValue(object):
30103015
def __init__(self, description, **kwargs):
30113016
self.__description = description
30123017
for key, value in kwargs.items():

test/db_test_base.py

Lines changed: 81 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
from roundup.mailer import Mailer
2626
from roundup import date, password, init, instance, configuration, \
2727
roundupdb, i18n
28+
from roundup.cgi.templating import HTMLItem
2829

2930
from mocknull import MockNull
3031

@@ -2428,18 +2429,27 @@ def testFilteringTransitiveLinkCache(self):
24282429
ae (result, ['4', '5', '6', '7', '8', '1', '2', '3'])
24292430

24302431

2431-
class ClassicInitTest(unittest.TestCase):
2432+
class ClassicInitBase(unittest.TestCase):
24322433
count = 0
24332434
db = None
24342435

24352436
def setUp(self):
2436-
ClassicInitTest.count = ClassicInitTest.count + 1
2437+
ClassicInitBase.count = ClassicInitBase.count + 1
24372438
self.dirname = '_test_init_%s'%self.count
24382439
try:
24392440
shutil.rmtree(self.dirname)
24402441
except OSError, error:
24412442
if error.errno not in (errno.ENOENT, errno.ESRCH): raise
24422443

2444+
def tearDown(self):
2445+
if self.db is not None:
2446+
self.db.close()
2447+
try:
2448+
shutil.rmtree(self.dirname)
2449+
except OSError, error:
2450+
if error.errno not in (errno.ENOENT, errno.ESRCH): raise
2451+
2452+
class ClassicInitTest(ClassicInitBase):
24432453
def testCreation(self):
24442454
ae = self.assertEqual
24452455

@@ -2467,15 +2477,8 @@ def testCreation(self):
24672477
l = db.issue.list()
24682478
ae(l, [])
24692479

2470-
def tearDown(self):
2471-
if self.db is not None:
2472-
self.db.close()
2473-
try:
2474-
shutil.rmtree(self.dirname)
2475-
except OSError, error:
2476-
if error.errno not in (errno.ENOENT, errno.ESRCH): raise
24772480

2478-
class ConcurrentDBTest(ClassicInitTest):
2481+
class ConcurrentDBTest(ClassicInitBase):
24792482
def testConcurrency(self):
24802483
# The idea here is a read-modify-update cycle in the presence of
24812484
# a cache that has to be properly handled. The same applies if
@@ -2506,5 +2509,73 @@ def inc(db):
25062509
self.assertEqual(db2.priority.get(prio, 'order'), 3.0)
25072510
db2.close()
25082511

2512+
class HTMLItemTest(ClassicInitBase):
2513+
class Request :
2514+
""" Fake html request """
2515+
rfile = None
2516+
def start_response (self, a, b) :
2517+
pass
2518+
# end def start_response
2519+
# end class Request
2520+
2521+
def setUp(self):
2522+
super(HTMLItemTest, self).setUp()
2523+
self.tracker = tracker = setupTracker(self.dirname, self.backend)
2524+
db = self.db = tracker.open('admin')
2525+
req = self.Request()
2526+
env = dict (PATH_INFO='', REQUEST_METHOD='GET', QUERY_STRING='')
2527+
self.client = self.tracker.Client(self.tracker, req, env, None)
2528+
self.client.db = db
2529+
self.client.language = None
2530+
self.client.userid = db.getuid()
2531+
self.client.classname = 'issue'
2532+
user = {'username': 'worker5', 'realname': 'Worker', 'roles': 'User'}
2533+
u = self.db.user.create(**user)
2534+
u_m = self.db.msg.create(author = u, content = 'bla'
2535+
, date = date.Date ('2006-01-01'))
2536+
issue = {'title': 'ts1', 'status': '2', 'assignedto': '3',
2537+
'priority': '3', 'messages' : [u_m], 'nosy' : ['3']}
2538+
self.db.issue.create(**issue)
2539+
2540+
def testHTMLItemAttributes(self):
2541+
issue = HTMLItem(self.client, 'issue', '1')
2542+
ae = self.assertEqual
2543+
ae(issue.title.plain(),'ts1')
2544+
ae(issue ['title'].plain(),'ts1')
2545+
ae(issue.status.plain(),'deferred')
2546+
ae(issue ['status'].plain(),'deferred')
2547+
ae(issue.assignedto.plain(),'worker5')
2548+
ae(issue ['assignedto'].plain(),'worker5')
2549+
ae(issue.priority.plain(),'bug')
2550+
ae(issue ['priority'].plain(),'bug')
2551+
ae(issue.messages.plain(),'1')
2552+
ae(issue ['messages'].plain(),'1')
2553+
ae(issue.nosy.plain(),'worker5')
2554+
ae(issue ['nosy'].plain(),'worker5')
2555+
ae(len(issue.messages),1)
2556+
ae(len(issue ['messages']),1)
2557+
ae(len(issue.nosy),1)
2558+
ae(len(issue ['nosy']),1)
2559+
2560+
def testHTMLItemDereference(self):
2561+
issue = HTMLItem(self.client, 'issue', '1')
2562+
ae = self.assertEqual
2563+
ae(str(issue.priority.name),'bug')
2564+
ae(str(issue.priority['name']),'bug')
2565+
ae(str(issue ['priority']['name']),'bug')
2566+
ae(str(issue ['priority'].name),'bug')
2567+
ae(str(issue.assignedto.username),'worker5')
2568+
ae(str(issue.assignedto['username']),'worker5')
2569+
ae(str(issue ['assignedto']['username']),'worker5')
2570+
ae(str(issue ['assignedto'].username),'worker5')
2571+
for n in issue.nosy:
2572+
ae(n.username.plain(),'worker5')
2573+
ae(n['username'].plain(),'worker5')
2574+
for n in issue.messages:
2575+
ae(n.author.username.plain(),'worker5')
2576+
ae(n.author['username'].plain(),'worker5')
2577+
ae(n['author'].username.plain(),'worker5')
2578+
ae(n['author']['username'].plain(),'worker5')
2579+
25092580

25102581
# vim: set et sts=4 sw=4 :

test/test_anydbm.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from roundup.backends import get_backend
2020

2121
from db_test_base import DBTest, ROTest, SchemaTest, ClassicInitTest, config
22+
from db_test_base import HTMLItemTest
2223

2324
class anydbmOpener:
2425
module = get_backend('anydbm')
@@ -38,6 +39,9 @@ class anydbmSchemaTest(anydbmOpener, SchemaTest):
3839
class anydbmClassicInitTest(ClassicInitTest):
3940
backend = 'anydbm'
4041

42+
class anydbmHTMLItemTest(HTMLItemTest):
43+
backend = 'anydbm'
44+
4145
from session_common import DBMTest
4246
class anydbmSessionTest(anydbmOpener, DBMTest):
4347
pass
@@ -49,6 +53,7 @@ def test_suite():
4953
suite.addTest(unittest.makeSuite(anydbmROTest))
5054
suite.addTest(unittest.makeSuite(anydbmSchemaTest))
5155
suite.addTest(unittest.makeSuite(anydbmClassicInitTest))
56+
suite.addTest(unittest.makeSuite(anydbmHTMLItemTest))
5257
suite.addTest(unittest.makeSuite(anydbmSessionTest))
5358
return suite
5459

test/test_mysql.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from roundup.backends import get_backend, have_backend
2222

2323
from db_test_base import DBTest, ROTest, config, SchemaTest, ClassicInitTest
24-
from db_test_base import ConcurrentDBTest, FilterCacheTest
24+
from db_test_base import ConcurrentDBTest, HTMLItemTest, FilterCacheTest
2525

2626

2727
class mysqlOpener:
@@ -71,6 +71,15 @@ def tearDown(self):
7171
ConcurrentDBTest.tearDown(self)
7272
self.nuke_database()
7373

74+
class mysqlHTMLItemTest(mysqlOpener, HTMLItemTest):
75+
backend = 'mysql'
76+
def setUp(self):
77+
mysqlOpener.setUp(self)
78+
HTMLItemTest.setUp(self)
79+
def tearDown(self):
80+
HTMLItemTest.tearDown(self)
81+
self.nuke_database()
82+
7483
class mysqlFilterCacheTest(mysqlOpener, FilterCacheTest):
7584
backend = 'mysql'
7685
def setUp(self):
@@ -110,6 +119,7 @@ def test_suite():
110119
suite.addTest(unittest.makeSuite(mysqlClassicInitTest))
111120
suite.addTest(unittest.makeSuite(mysqlSessionTest))
112121
suite.addTest(unittest.makeSuite(mysqlConcurrencyTest))
122+
suite.addTest(unittest.makeSuite(mysqlHTMLItemTest))
113123
suite.addTest(unittest.makeSuite(mysqlFilterCacheTest))
114124
return suite
115125

test/test_postgresql.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from roundup.hyperdb import DatabaseError
2121

2222
from db_test_base import DBTest, ROTest, config, SchemaTest, ClassicInitTest
23-
from db_test_base import ConcurrentDBTest, FilterCacheTest
23+
from db_test_base import ConcurrentDBTest, HTMLItemTest, FilterCacheTest
2424

2525
from roundup.backends import get_backend, have_backend
2626

@@ -66,6 +66,16 @@ def tearDown(self):
6666
ConcurrentDBTest.tearDown(self)
6767
postgresqlOpener.tearDown(self)
6868

69+
class postgresqlHTMLItemTest(postgresqlOpener, HTMLItemTest):
70+
backend = 'postgresql'
71+
def setUp(self):
72+
postgresqlOpener.setUp(self)
73+
HTMLItemTest.setUp(self)
74+
75+
def tearDown(self):
76+
HTMLItemTest.tearDown(self)
77+
postgresqlOpener.tearDown(self)
78+
6979
class postgresqlFilterCacheTest(postgresqlOpener, FilterCacheTest):
7080
backend = 'postgresql'
7181
def setUp(self):
@@ -122,6 +132,7 @@ def test_suite():
122132
suite.addTest(unittest.makeSuite(postgresqlClassicInitTest))
123133
suite.addTest(unittest.makeSuite(postgresqlSessionTest))
124134
suite.addTest(unittest.makeSuite(postgresqlConcurrencyTest))
135+
suite.addTest(unittest.makeSuite(postgresqlHTMLItemTest))
125136
suite.addTest(unittest.makeSuite(postgresqlFilterCacheTest))
126137
return suite
127138

0 commit comments

Comments
 (0)