Mercurial > p > roundup > code
comparison roundup/cgi/templating.py @ 5414:3fa026621f69
Python 3 preparation: comparisons.
Python 3 no longer has the cmp function, or cmp= arguments to sorting
functions / methods (key= must be used instead), and requires rich
comparison methods such as __lt__ to be defined instead of using
__cmp__. All of the comparison mechanisms supported in Python 3 are
also supported in Python 2.
This patch makes the corresponding changes in Roundup to use key
functions and rich comparison methods. In the case of the
JournalPassword and Permission classes, only __eq__ and __ne__ are
defined as I don't see ordered comparisons as useful there (and for
Permission, the old __cmp__ function didn't try to provide a valid
ordering). In the case of the Date class, I kept the __cmp__ method
and implemented the others in terms of it, to avoid excess
repetitiveness in duplicating implementation code for all six rich
comparison methods.
In roundup/admin.py, help_commands_html used operator.attrgetter to
produce the second argument of sorted() - which would be reasonable
for a key function, but the second argument is the cmp function in
Python 2, not a key function (and the key function must be a named
argument not a positional argument in Python 3). That function
appears to be completely unused, so I expect that code never worked.
This patch adds the missing key= to that sorted() call, but it would
also be reasonable to remove the unused function completely instead.
| author | Joseph Myers <jsm@polyomino.org.uk> |
|---|---|
| date | Wed, 25 Jul 2018 00:39:37 +0000 |
| parents | 3757449e00c4 |
| children | 56c9bcdea47f |
comparison
equal
deleted
inserted
replaced
| 5413:7a41dd2abbbd | 5414:3fa026621f69 |
|---|---|
| 618 if isinstance(prop, klass): | 618 if isinstance(prop, klass): |
| 619 value = prop.get_default_value() | 619 value = prop.get_default_value() |
| 620 l.append(htmlklass(self._client, self._classname, '', | 620 l.append(htmlklass(self._client, self._classname, '', |
| 621 prop, name, value, self._anonymous)) | 621 prop, name, value, self._anonymous)) |
| 622 if sort: | 622 if sort: |
| 623 l.sort(lambda a,b:cmp(a._name, b._name)) | 623 l.sort(key=lambda a:a._name) |
| 624 return l | 624 return l |
| 625 | 625 |
| 626 def list(self, sort_on=None): | 626 def list(self, sort_on=None): |
| 627 """ List all items in this class. | 627 """ List all items in this class. |
| 628 """ | 628 """ |
| 629 # get the list and sort it nicely | 629 # get the list and sort it nicely |
| 630 l = self._klass.list() | 630 l = self._klass.list() |
| 631 sortfunc = make_sort_function(self._db, self._classname, sort_on) | 631 keyfunc = make_key_function(self._db, self._classname, sort_on) |
| 632 l.sort(sortfunc) | 632 l.sort(key=keyfunc) |
| 633 | 633 |
| 634 # check perms | 634 # check perms |
| 635 check = self._client.db.security.hasPermission | 635 check = self._client.db.security.hasPermission |
| 636 userid = self._client.userid | 636 userid = self._client.userid |
| 637 if not check('Web Access', userid): | 637 if not check('Web Access', userid): |
| 1336 classname = self.__class__.__name__ | 1336 classname = self.__class__.__name__ |
| 1337 return '<%s(0x%x) %s %r %r>'%(classname, id(self), self._formname, | 1337 return '<%s(0x%x) %s %r %r>'%(classname, id(self), self._formname, |
| 1338 self._prop, self._value) | 1338 self._prop, self._value) |
| 1339 def __str__(self): | 1339 def __str__(self): |
| 1340 return self.plain() | 1340 return self.plain() |
| 1341 def __cmp__(self, other): | 1341 def __lt__(self, other): |
| 1342 if isinstance(other, HTMLProperty): | 1342 if isinstance(other, HTMLProperty): |
| 1343 return cmp(self._value, other._value) | 1343 return self._value < other._value |
| 1344 return cmp(self._value, other) | 1344 return self._value < other |
| 1345 def __le__(self, other): | |
| 1346 if isinstance(other, HTMLProperty): | |
| 1347 return self._value <= other._value | |
| 1348 return self._value <= other | |
| 1349 def __eq__(self, other): | |
| 1350 if isinstance(other, HTMLProperty): | |
| 1351 return self._value == other._value | |
| 1352 return self._value == other | |
| 1353 def __ne__(self, other): | |
| 1354 if isinstance(other, HTMLProperty): | |
| 1355 return self._value != other._value | |
| 1356 return self._value != other | |
| 1357 def __gt__(self, other): | |
| 1358 if isinstance(other, HTMLProperty): | |
| 1359 return self._value > other._value | |
| 1360 return self._value > other | |
| 1361 def __ge__(self, other): | |
| 1362 if isinstance(other, HTMLProperty): | |
| 1363 return self._value >= other._value | |
| 1364 return self._value >= other | |
| 1345 | 1365 |
| 1346 def __bool__(self): | 1366 def __bool__(self): |
| 1347 return not not self._value | 1367 return not not self._value |
| 1348 # Python 2 compatibility: | 1368 # Python 2 compatibility: |
| 1349 __nonzero__ = __bool__ | 1369 __nonzero__ = __bool__ |
| 2256 def __init__(self, *args, **kwargs): | 2276 def __init__(self, *args, **kwargs): |
| 2257 HTMLProperty.__init__(self, *args, **kwargs) | 2277 HTMLProperty.__init__(self, *args, **kwargs) |
| 2258 if self._value: | 2278 if self._value: |
| 2259 display_value = lookupIds(self._db, self._prop, self._value, | 2279 display_value = lookupIds(self._db, self._prop, self._value, |
| 2260 fail_ok=1, do_lookup=False) | 2280 fail_ok=1, do_lookup=False) |
| 2261 sortfun = make_sort_function(self._db, self._prop.classname) | 2281 keyfun = make_key_function(self._db, self._prop.classname) |
| 2262 # sorting fails if the value contains | 2282 # sorting fails if the value contains |
| 2263 # items not yet stored in the database | 2283 # items not yet stored in the database |
| 2264 # ignore these errors to preserve user input | 2284 # ignore these errors to preserve user input |
| 2265 try: | 2285 try: |
| 2266 display_value.sort(sortfun) | 2286 display_value.sort(key=keyfun) |
| 2267 except: | 2287 except: |
| 2268 pass | 2288 pass |
| 2269 self._value = display_value | 2289 self._value = display_value |
| 2270 | 2290 |
| 2271 def __len__(self): | 2291 def __len__(self): |
| 2301 return self.viewableGenerator(l) | 2321 return self.viewableGenerator(l) |
| 2302 | 2322 |
| 2303 def sorted(self, property): | 2323 def sorted(self, property): |
| 2304 """ Return this multilink sorted by the given property """ | 2324 """ Return this multilink sorted by the given property """ |
| 2305 value = list(self.__iter__()) | 2325 value = list(self.__iter__()) |
| 2306 value.sort(lambda a,b:cmp(a[property], b[property])) | 2326 value.sort(key=lambda a:a[property]) |
| 2307 return value | 2327 return value |
| 2308 | 2328 |
| 2309 def __contains__(self, value): | 2329 def __contains__(self, value): |
| 2310 """ Support the "in" operator. We have to make sure the passed-in | 2330 """ Support the "in" operator. We have to make sure the passed-in |
| 2311 value is a string first, not a HTMLProperty. | 2331 value is a string first, not a HTMLProperty. |
| 2499 break | 2519 break |
| 2500 else: | 2520 else: |
| 2501 propclasses.append((prop, cls)) | 2521 propclasses.append((prop, cls)) |
| 2502 | 2522 |
| 2503 | 2523 |
| 2504 def make_sort_function(db, classname, sort_on=None): | 2524 def make_key_function(db, classname, sort_on=None): |
| 2505 """Make a sort function for a given class. | 2525 """Make a sort key function for a given class. |
| 2506 | 2526 |
| 2507 The list being sorted may contain mixed ids and labels. | 2527 The list being sorted may contain mixed ids and labels. |
| 2508 """ | 2528 """ |
| 2509 linkcl = db.getclass(classname) | 2529 linkcl = db.getclass(classname) |
| 2510 if sort_on is None: | 2530 if sort_on is None: |
| 2511 sort_on = linkcl.orderprop() | 2531 sort_on = linkcl.orderprop() |
| 2512 def sortfunc(a, b): | 2532 def keyfunc(a): |
| 2513 if num_re.match(a): | 2533 if num_re.match(a): |
| 2514 a = linkcl.get(a, sort_on) | 2534 a = linkcl.get(a, sort_on) |
| 2515 if num_re.match(b): | 2535 return a |
| 2516 b = linkcl.get(b, sort_on) | 2536 return keyfunc |
| 2517 return cmp(a, b) | |
| 2518 return sortfunc | |
| 2519 | 2537 |
| 2520 def handleListCGIValue(value): | 2538 def handleListCGIValue(value): |
| 2521 """ Value is either a single item or a list of items. Each item has a | 2539 """ Value is either a single item or a list of items. Each item has a |
| 2522 .value that we're actually interested in. | 2540 .value that we're actually interested in. |
| 2523 """ | 2541 """ |
