Browse Source

started porting poobrains to python 3

phryk 3 months ago
parent
commit
7f3b672149

+ 33
- 33
poobrains/__init__.py View File

@@ -31,9 +31,9 @@ from flask.helpers import locked_cached_property
31 31
 from jinja2 import Markup
32 32
 
33 33
 # internal imports
34
-import helpers
35
-import errors
36
-import defaults
34
+from . import helpers
35
+from . import errors
36
+from . import defaults
37 37
 
38 38
 db_url.schemes['sqlite'] = db_url.schemes['sqliteext'] # Make sure we get the extensible sqlite database, so we can make regular expressions case-sensitive. see https://github.com/coleifer/peewee/issues/1221
39 39
 
@@ -79,7 +79,7 @@ class FormDataParser(werkzeug.formparser.FormDataParser):
79 79
             'files': werkzeug.datastructures.MultiDict()
80 80
         }
81 81
 
82
-        for subject, data in flat_data.iteritems():
82
+        for subject, data in flat_data.items():
83 83
 
84 84
             for key in data.keys():
85 85
 
@@ -87,7 +87,7 @@ class FormDataParser(werkzeug.formparser.FormDataParser):
87 87
                 segments = key.split('.')
88 88
 
89 89
                 for segment in segments[:-1]:
90
-                    if not current.has_key(segment):
90
+                    if not segment in current:
91 91
                         current[segment] = werkzeug.datastructures.MultiDict()
92 92
 
93 93
                     current = current[segment]
@@ -95,13 +95,13 @@ class FormDataParser(werkzeug.formparser.FormDataParser):
95 95
                 #current[segments[-1]] = values
96 96
                 current.setlist(segments[-1], data.getlist(key))
97 97
 
98
-            #if form_flat.has_key('submit'):
99
-            if subject == 'form' and data.has_key('submit'):
98
+            #if 'submit' in form_flat:
99
+            if subject == 'form' and 'submit' in data:
100 100
                 current = processed_data[subject]
101 101
                 segments = form_flat['submit'].split('.')
102 102
 
103 103
                 for segment in segments[:-1]:
104
-                    if not current.has_key(segment):
104
+                    if not segment in current:
105 105
                         current[segment] = werkzeug.datastructures.MultiDict()
106 106
 
107 107
                     current = current[segment]
@@ -216,7 +216,7 @@ class Poobrain(flask.Flask):
216 216
 
217 217
     def __init__(self, *args, **kwargs):
218 218
 
219
-        if not kwargs.has_key('root_path'):
219
+        if not 'root_path' in kwargs:
220 220
             kwargs['root_path'] = str(pathlib.Path('.').absolute()) #TODO: pathlib probably isn't really needed here
221 221
 
222 222
         super(Poobrain, self).__init__(*args, **kwargs)
@@ -230,7 +230,7 @@ class Poobrain(flask.Flask):
230 230
                     self.config[name] = getattr(config, name)
231 231
 
232 232
         for name in dir(defaults):
233
-            if name.isupper and not self.config.has_key(name):
233
+            if name.isupper and not name in self.config:
234 234
                 self.config[name] = getattr(defaults, name)
235 235
 
236 236
         try:
@@ -262,12 +262,12 @@ class Poobrain(flask.Flask):
262 262
                 import pudb
263 263
                 if hasattr(signal, 'SIGINFO'):
264 264
                     pudb.set_interrupt_handler(signal.SIGINFO)
265
-                    print "%s: a graphical debugger can be invoked with SIGINFO (^T)" % (self.name.upper())
265
+                    print("%s: a graphical debugger can be invoked with SIGINFO (^T)" % (self.name.upper()))
266 266
 
267 267
                 self.debugger = pudb
268 268
 
269 269
             except ImportError:
270
-                print "pudb not installed, falling back to pdb!"
270
+                print("pudb not installed, falling back to pdb!")
271 271
 
272 272
                 import signal # shouldn't be needed but feels hacky to leave out
273 273
                 import pdb
@@ -279,7 +279,7 @@ class Poobrain(flask.Flask):
279 279
 
280 280
         self.scss_compiler = scss.Compiler(extensions=(SCSSCore,), root=pathlib.Path('/'), search_path=self.theme_paths)
281 281
 
282
-        if self.config.has_key('DATABASE'):
282
+        if 'DATABASE' in self.config:
283 283
             self.db = db_url.connect(self.config['DATABASE'], autocommit=True, autorollback=True)
284 284
 
285 285
         else:
@@ -408,7 +408,7 @@ class Poobrain(flask.Flask):
408 408
 
409 409
         flask.g.user = None
410 410
 
411
-        if not flask.request.environ.has_key('SSL_CLIENT_VERIFY'):
411
+        if not 'SSL_CLIENT_VERIFY' in flask.request.environ:
412 412
             if self.debug:
413 413
                 flask.request.environ['SSL_CLIENT_VERIFY'] = 'FAILURE'
414 414
             else:
@@ -441,7 +441,7 @@ class Poobrain(flask.Flask):
441 441
 
442 442
     def box_setup(self):
443 443
         
444
-        for name, f in self.boxes.iteritems():
444
+        for name, f in self.boxes.items():
445 445
             flask.g.boxes[name] = f()
446 446
 
447 447
     
@@ -461,7 +461,7 @@ class Poobrain(flask.Flask):
461 461
                 self.site.add_listing(cls, rule, mode='teaser', title=title, force_secure=force_secure)
462 462
                 self.site.add_view(cls, os.path.join(rule, '<handle>/'), mode=mode, force_secure=force_secure)
463 463
 
464
-                for related_model, related_fields in cls._meta.model_backrefs.iteritems(): # Add Models that are associated by ForeignKeyField, like /user/foo/userpermissions
464
+                for related_model, related_fields in cls._meta.model_backrefs.items(): # Add Models that are associated by ForeignKeyField, like /user/foo/userpermissions
465 465
 
466 466
                     if len(related_fields) > 1:
467 467
                         self.logger.debug("!!! Apparent multi-field relation for %s: %s !!!" % (related_model.__name__, related_fields))
@@ -595,10 +595,10 @@ class Pooprint(flask.Blueprint):
595 595
 
596 596
     def add_view(self, cls, rule, endpoint=None, view_func=None, mode='full', force_secure=False, **options):
597 597
 
598
-        if not self.views.has_key(cls):
598
+        if not cls in self.views:
599 599
             self.views[cls] = collections.OrderedDict()
600 600
 
601
-        if not self.views[cls].has_key(mode):
601
+        if not mode in self.views[cls]:
602 602
             self.views[cls][mode] = []
603 603
 
604 604
         # Why the fuck does HTML not support DELETE!?
@@ -627,19 +627,19 @@ class Pooprint(flask.Blueprint):
627 627
         if not endpoint:
628 628
             endpoint = self.next_endpoint(cls, related_field, 'related')
629 629
 
630
-        if not self.related_views.has_key(cls):
630
+        if not cls in self.related_views:
631 631
             self.related_views[cls] = collections.OrderedDict()
632 632
 
633
-        if not self.related_views[cls].has_key(related_field):
633
+        if not related_field in self.related_views[cls]:
634 634
             url_segment = '%s:%s' % (related_model.__name__.lower(), related_field.name.lower())
635 635
             rule = os.path.join(rule, url_segment, "") # empty string to get trailing slash
636 636
             self.related_views[cls][related_field] = []
637 637
 
638 638
 
639
-        if not self.related_views_add.has_key(cls):
639
+        if not cls in self.related_views_add:
640 640
             self.related_views_add[cls] = collections.OrderedDict()
641 641
         
642
-        if not self.related_views_add[cls].has_key(related_field):
642
+        if not related_field in self.related_views_add[cls]:
643 643
             self.related_views_add[cls][related_field] = []
644 644
 
645 645
 
@@ -669,7 +669,7 @@ class Pooprint(flask.Blueprint):
669 669
 
670 670
     def box_setup(self):
671 671
         
672
-        for name, f in self.boxes.iteritems():
672
+        for name, f in self.boxes.items():
673 673
             flask.g.boxes[name] = f()
674 674
 
675 675
     
@@ -692,10 +692,10 @@ class Pooprint(flask.Blueprint):
692 692
 
693 693
         rule = os.path.join(rule, '') # make sure rule has trailing slash
694 694
 
695
-        if not self.listings.has_key(cls):
695
+        if not cls in self.listings:
696 696
             self.listings[cls] = collections.OrderedDict()
697 697
 
698
-        if not self.listings[cls].has_key(mode):
698
+        if not mode in self.listings[cls]:
699 699
             self.listings[cls][mode] = []
700 700
 
701 701
         if view_func is None:
@@ -761,7 +761,7 @@ class Pooprint(flask.Blueprint):
761 761
         
762 762
         if not issubclass(cls, storage.Model) or \
763 763
         mode == 'add' or \
764
-        (url_params.has_key('handle') and (mode is None or not mode.startswith('teaser'))):
764
+        'handle' in (url_params and (mode is None or not mode.startswith('teaser'))):
765 765
             return self.get_view_url(cls, mode=mode, **url_params)
766 766
 
767 767
         return self.get_listing_url(cls, mode=mode, **url_params)
@@ -772,10 +772,10 @@ class Pooprint(flask.Blueprint):
772 772
         if mode == None:
773 773
             mode = 'full'
774 774
 
775
-        if not self.views.has_key(cls):
775
+        if not cls in self.views:
776 776
             raise LookupError("No registered views for class %s." % (cls.__name__,))
777 777
 
778
-        if not self.views[cls].has_key(mode):
778
+        if not mode in self.views[cls]:
779 779
             raise LookupError("No registered views for class %s with mode %s." % (cls.__name__, mode))
780 780
 
781 781
 
@@ -807,10 +807,10 @@ class Pooprint(flask.Blueprint):
807 807
 
808 808
             offset = cls.select().where(*clauses).count() - 1
809 809
 
810
-        if not self.listings.has_key(cls):
810
+        if not cls in self.listings:
811 811
             raise LookupError("No registered listings for class %s." % (cls.__name__,))
812 812
 
813
-        if not self.listings[cls].has_key(mode):
813
+        if not mode in self.listings[cls]:
814 814
             raise LookupError("No registered listings for class %s with mode %s." % (cls.__name__, mode))
815 815
 
816 816
         endpoints = ['%s.%s' % (self.name, x) for x in self.listings[cls][mode]]
@@ -836,10 +836,10 @@ class Pooprint(flask.Blueprint):
836 836
             lookup = self.related_views
837 837
 
838 838
 
839
-        if not lookup.has_key(cls):
839
+        if not cls in lookup:
840 840
             raise LookupError("No registered related views for class %s." % (cls.__name__,))
841 841
 
842
-        if not lookup[cls].has_key(related_field):
842
+        if not related_field in lookup[cls]:
843 843
             raise LookupError("No registered related views for %s[%s]<-%s.%s." % (cls.__name__, handle, related_field.model.__name__, related_field.name))
844 844
 
845 845
         endpoints = ['%s.%s' % (self.name, x) for x in lookup[cls][related_field]]
@@ -917,7 +917,7 @@ class ErrorPage(rendering.Renderable):
917 917
         else:
918 918
             # default to 500, but use more specific code if a matching exception is found in app.error_codes
919 919
             self.code = 500
920
-            for cls, code in app.error_codes.iteritems():
920
+            for cls, code in app.error_codes.items():
921 921
                 if isinstance(error, cls):
922 922
                     self.code = code
923 923
                     break

+ 41
- 98
poobrains/auth/__init__.py View File

@@ -13,8 +13,6 @@ import os
13 13
 import random
14 14
 import re
15 15
 import OpenSSL as openssl
16
-import M2Crypto
17
-import pyspkac
18 16
 import time
19 17
 import datetime
20 18
 import werkzeug
@@ -49,18 +47,18 @@ def admin_setup():
49 47
 
50 48
             app.admin.add_listing(cls, rule, title=cls.__name__, mode='teaser', action_func=actions, force_secure=True)
51 49
 
52
-            if cls._meta.modes.has_key('add'):
50
+            if 'add' in cls._meta.modes:
53 51
                 app.admin.add_view(cls, os.path.join(rule, 'add/'), mode='add', force_secure=True)
54 52
 
55 53
             rule = os.path.join(rule, '<handle>/')
56 54
 
57
-            if cls._meta.modes.has_key('edit'):
55
+            if 'edit' in cls._meta.modes:
58 56
                 app.admin.add_view(cls, rule, mode='edit', force_secure=True)
59 57
 
60
-            if cls._meta.modes.has_key('delete'):
58
+            if 'delete' in cls._meta.modes:
61 59
                 app.admin.add_view(cls, os.path.join(rule, 'delete'), mode='delete', force_secure=True)
62 60
 
63
-            for related_model, related_fields in cls._meta.model_backrefs.iteritems(): # Add Models that are associated by ForeignKeyField, like /user/foo/userpermissions
61
+            for related_model, related_fields in cls._meta.model_backrefs.items(): # Add Models that are associated by ForeignKeyField, like /user/foo/userpermissions
64 62
 
65 63
                 if issubclass(related_model, Administerable):
66 64
                     app.admin.add_related_view(cls, related_fields[0], rule, force_secure=True)
@@ -132,7 +130,7 @@ class AutoForm(BoundForm):
132 130
 
133 131
         for field in f.model._meta.sorted_fields:
134 132
 
135
-            if  (not f.fields.has_key(field.name)    and # means this field was already defined in the class definition for this form
133
+            if  (not field.name in f.fields    and # means this field was already defined in the class definition for this form
136 134
                 not field.form_widget is None       and # means this field should by ignored
137 135
                 not (hasattr(cls, field.name) and isinstance(getattr(cls, field.name), poobrains.form.fields.Field))): # second clause is to avoid problems with name collisions (for instance on "name")
138 136
 
@@ -140,7 +138,7 @@ class AutoForm(BoundForm):
140 138
 
141 139
         if op == 'create':
142 140
             for pkfield in f.model._meta.get_primary_keys():
143
-                if f.fields.has_key(pkfield.name):
141
+                if pkfield.name in f.fields:
144 142
                     f.fields[pkfield.name].readonly = True # Make any primary key fields read-only
145 143
 
146 144
         f.controls['reset'] = poobrains.form.Button('reset', label='Reset')
@@ -176,7 +174,7 @@ class AutoForm(BoundForm):
176 174
                 except Exception as e:
177 175
                     self.title = "%s %s" % (self.mode, self.model.__name__)
178 176
 
179
-        for name, field in self.fields.iteritems():
177
+        for name, field in self.fields.items():
180 178
             if hasattr(self.instance, name):
181 179
                 try:
182 180
                     field.value = getattr(self.instance, name)
@@ -285,7 +283,7 @@ class DeleteForm(BoundForm):
285 283
             if hasattr(self.instance, 'title') and self.instance.title:
286 284
                 self.title = "Delete %s %s" % (self.model.__name__, self.instance.title)
287 285
             else:
288
-                self.title = "Delete %s %s" % (self.model.__name__, unicode(self.instance._pk))
286
+                self.title = "Delete %s %s" % (self.model.__name__, str(self.instance._pk))
289 287
 
290 288
     
291 289
     def process(self, submit):
@@ -293,7 +291,7 @@ class DeleteForm(BoundForm):
293 291
         if hasattr(self.instance, 'title') and self.instance.title:
294 292
             message = "Deleted %s '%s'." % (self.model.__name__, self.instance.title)
295 293
         else:
296
-            message = "Deleted %s '%s'." % (self.model.__name__, unicode(self.instance._pk))
294
+            message = "Deleted %s '%s'." % (self.model.__name__, str(self.instance._pk))
297 295
         self.instance.delete_instance(recursive=True)
298 296
         flash(message)
299 297
 
@@ -397,7 +395,7 @@ class Permission(poobrains.helpers.ChildAware):
397 395
     def check(cls, user):
398 396
 
399 397
         # check user-assigned permission state
400
-        if user.own_permissions.has_key(cls.__name__):
398
+        if cls.__name__ in user.own_permissions:
401 399
             access = user.own_permissions[cls.__name__]
402 400
 
403 401
             if access == 'deny':
@@ -410,7 +408,7 @@ class Permission(poobrains.helpers.ChildAware):
410 408
         # group_deny = GroupPermission.select().join(Group).join(UserGroup).join(User).where(UserGroup.user == user, GroupPermission.permission == cls.__name__, GroupPermission.access == 'deny').count()
411 409
         group_deny = False
412 410
         for group in user.groups:
413
-            if group.own_permissions.has_key(cls.__name__) and group.own_permissions[cls.__name__] == 'deny':
411
+            if cls.__name__ in group.own_permissions and group.own_permissions[cls.__name__] == 'deny':
414 412
                 group_deny = True
415 413
                 break
416 414
 
@@ -420,7 +418,7 @@ class Permission(poobrains.helpers.ChildAware):
420 418
         #group_grant = GroupPermission.select().join(Group).join(UserGroup).join(User).where(UserGroup.user == user, GroupPermission.permission == cls.__name__, GroupPermission.access == 'grant').count()
421 419
         group_grant = False
422 420
         for group in user.groups:
423
-            if group.own_permissions.has_key(cls.__name__) and group.own_permissions[cls.__name__] == 'grant':
421
+            if cls.__name__ in group.own_permissions and group.own_permissions[cls.__name__] == 'grant':
424 422
                 group_grant = True
425 423
                 break
426 424
 
@@ -437,7 +435,7 @@ class Permission(poobrains.helpers.ChildAware):
437 435
     @classmethod
438 436
     def list(cls, protected, q, op, user): # FIXME: should op be implied, not directly passed?
439 437
 
440
-        if user.own_permissions.has_key(cls.__name__):
438
+        if cls.__name__ in user.own_permissions:
441 439
             access = user.own_permissions[cls.__name__]
442 440
 
443 441
             if access == 'deny':
@@ -472,7 +470,7 @@ class PermissionInjection(poobrains.helpers.MetaCompatibility):
472 470
         cls.permissions = collections.OrderedDict()
473 471
 
474 472
         #for op in ['create', 'read', 'update', 'delete']:
475
-        for op in set(cls._meta.modes.itervalues()):
473
+        for op in set(cls._meta.modes.values()):
476 474
             perm_name = "%s_%s" % (cls.__name__, op)
477 475
             perm_label = "%s %s" % (op.capitalize(), cls.__name__)
478 476
             #cls._meta.permissions[mode] = type(perm_name, (cls._meta.permission_class,), {})
@@ -547,7 +545,7 @@ class FormPermissionField(poobrains.form.fields.Select):
547 545
 def admin_listing_actions(cls):
548 546
 
549 547
     m = poobrains.rendering.Menu('actions')
550
-    if cls._meta.modes.has_key('add'):
548
+    if 'add' in cls._meta.modes:
551 549
         m.append(cls.url('add'), 'add new %s' % (cls.__name__,))
552 550
 
553 551
     return m
@@ -562,9 +560,9 @@ def admin_menu():
562 560
         menu = poobrains.rendering.Menu('main')
563 561
         menu.title = 'Administration'
564 562
 
565
-        for administerable, listings in app.admin.listings.iteritems():
563
+        for administerable, listings in app.admin.listings.items():
566 564
 
567
-            for mode, endpoints in listings.iteritems():
565
+            for mode, endpoints in listings.items():
568 566
 
569 567
                 for endpoint in endpoints: # iterates through endpoints.keys()
570 568
                     menu.append(url_for('admin.%s' % endpoint), administerable.__name__)
@@ -585,11 +583,11 @@ def admin_index():
585 583
 
586 584
         container = poobrains.rendering.Container(title='Administration', mode='full')
587 585
         
588
-        for administerable, listings in app.admin.listings.iteritems():
586
+        for administerable, listings in app.admin.listings.items():
589 587
 
590 588
             subcontainer = poobrains.rendering.Container(css_class='administerable-actions', mode='full')
591 589
             menu = poobrains.rendering.Menu('listings-%s' % administerable.__name__)
592
-            for mode, endpoints in listings.iteritems():
590
+            for mode, endpoints in listings.items():
593 591
 
594 592
                 for endpoint in endpoints: # iterates through endpoints.keys()
595 593
                     menu.append(url_for('admin.%s' % endpoint), administerable.__name__)
@@ -636,13 +634,13 @@ def protected(func):
636 634
         if not (issubclass(cls, Protected) or isinstance(cls_or_instance, Protected)):
637 635
             raise ValueError("@protected used with non-protected class '%s'." % cls.__name__)
638 636
 
639
-        if not cls._meta.modes.has_key(mode):
637
+        if not mode in cls._meta.modes:
640 638
             raise AccessDenied("Unknown mode '%s' for accessing %s." % (mode, cls.__name__))
641 639
 
642 640
         op = cls._meta.modes[mode]
643 641
         if not op in ['create', 'read', 'update', 'delete']:
644 642
             raise AccessDenied("Unknown access op '%s' for accessing %s." (op, cls.__name__))
645
-        if not cls_or_instance.permissions.has_key(op):
643
+        if not op in cls_or_instance.permissions:
646 644
             raise NotImplementedError("Did not find permission for op '%s' in cls_or_instance of class '%s'." % (op, cls.__name__))
647 645
         
648 646
 
@@ -696,30 +694,7 @@ class ClientCertForm(poobrains.form.Form):
696 694
 
697 695
         not_after = datetime.datetime(year=self.fields['not_after'].value.year, month=self.fields['not_after'].value.month, day=self.fields['not_after'].value.day)
698 696
         
699
-        if submit == 'keygen_submit':
700
-
701
-            try:
702
-                client_cert = token.user.gen_clientcert_from_spkac(token.cert_name, self.fields['key'].value, session['key_challenge'], not_after)
703
-                del session['key_challenge']
704
-
705
-            except pyspkac.spkac.SPKAC_Decode_Error as e:
706
-
707
-                app.logger.error(e.message)
708
-                return poobrains.rendering.RenderString("Client certificate creation failed. Pwease no cwacky!")
709
-
710
-            cert_info.keylength = client_cert.get_pubkey().size() * 8 # .size gives length in byte
711
-            cert_info.fingerprint = client_cert.get_fingerprint('sha512')
712
-
713
-            not_before = client_cert.get_not_before().get_datetime() # contains tzinfo, which confuses peewee ( https://github.com/coleifer/peewee/issues/914)
714
-            cert_info.not_before = datetime.datetime(not_before.year, not_before.month, not_before.day, not_before.hour, not_before.minute, not_before.second)
715
-
716
-            not_after = client_cert.get_not_after().get_datetime() # contains tzinfo, which confuses peewee ( https://github.com/coleifer/peewee/issues/914)
717
-            cert_info.not_after = datetime.datetime(not_after.year, not_after.month, not_after.day, not_after.hour, not_after.minute, not_after.second)
718
-
719
-            r = werkzeug.wrappers.Response(client_cert.as_pem())
720
-            r.mimetype = 'application/x-x509-user-cert'
721
-
722
-        elif submit in ('pgp_submit', 'tls_submit'):
697
+        if submit in ('pgp_submit', 'tls_submit'):
723 698
 
724 699
             passphrase = poobrains.helpers.random_string_light()
725 700
 
@@ -742,7 +717,7 @@ class ClientCertForm(poobrains.form.Form):
742 717
                 r.mimetype = 'application/pkcs-12'
743 718
                 flash(u"The passphrase for this delicious bundle of crypto is '%s'" % passphrase)
744 719
 
745
-            else: # means pgp
720
+            else: # means pgp mail
746 721
 
747 722
                 text = "Hello %s. Here's your new set of keys to the gates of Shambala.\nYour passphrase is '%s'." % (token.user.name, passphrase)
748 723
 
@@ -800,7 +775,7 @@ class OwnedPermission(Permission):
800 775
     @classmethod
801 776
     def check(cls, user):
802 777
 
803
-        if user.own_permissions.has_key(cls.__name__):
778
+        if cls.__name__ in user.own_permissions:
804 779
             access = user.own_permissions[cls.__name__]
805 780
 
806 781
             if access == 'deny':
@@ -830,7 +805,7 @@ class OwnedPermission(Permission):
830 805
 
831 806
         op_abbr = self.op_abbreviations[self.op]
832 807
 
833
-        if user.own_permissions.has_key(self.__class__.__name__):
808
+        if self.__class__.__name__ in user.own_permissions:
834 809
 
835 810
             access = user.own_permissions[self.__class__.__name__]
836 811
 
@@ -899,9 +874,9 @@ class OwnedPermission(Permission):
899 874
 
900 875
         group_access = collections.OrderedDict()
901 876
         for group in user.groups:
902
-            if group.own_permissions.has_key(cls.__name__):
877
+            if cls.__name__ in group.own_permissions:
903 878
                 access = group.own_permissions[cls.__name__]
904
-                if not group_access.has_key(access):
879
+                if not access in group_access:
905 880
                     group_access[access] = []
906 881
                 group_access[access].append(group)
907 882
 
@@ -915,7 +890,7 @@ class OwnedPermission(Permission):
915 890
 
916 891
         op_abbr = op[0]
917 892
 
918
-        if user.own_permissions.has_key(cls.__name__):
893
+        if cls.__name__ in user.own_permissions:
919 894
 
920 895
             access = user.own_permissions[cls.__name__]
921 896
             if access == 'deny':
@@ -988,7 +963,7 @@ class RelatedForm(poobrains.form.Form):
988 963
                 # Fieldset to edit an existing related instance of this instance
989 964
                 setattr(f, key, related_instance.fieldset('edit'))
990 965
 
991
-                if f.fields[key].fields.has_key(related_field.name):
966
+                if related_field.name in f.fields[key].fields:
992 967
                     setattr(f.fields[key], related_field.name, poobrains.form.fields.Value(value=instance._pk))
993 968
 
994 969
             except AccessDenied as e:
@@ -1098,7 +1073,7 @@ class BaseAdministerable(PermissionInjection, poobrains.storage.ModelBase):
1098 1073
     pass
1099 1074
 
1100 1075
 
1101
-class Protected(poobrains.rendering.Renderable):
1076
+class Protected(poobrains.rendering.Renderable, metaclass=PermissionInjection):
1102 1077
 
1103 1078
     __metaclass__ = PermissionInjection
1104 1079
 
@@ -1109,10 +1084,11 @@ class Protected(poobrains.rendering.Renderable):
1109 1084
 
1110 1085
     def __new__(instance, *args, **kwargs):
1111 1086
 
1112
-        instance = super(Protected, instance).__new__(instance, *args, **kwargs)
1087
+        #instance = super(Protected, instance).__new__(instance, *args, **kwargs)
1088
+        instance = super(Protected, instance).__new__(instance)
1113 1089
         instance.permissions = collections.OrderedDict()
1114 1090
         
1115
-        for mode, perm_class in instance.__class__.permissions.iteritems():
1091
+        for mode, perm_class in instance.__class__.permissions.items():
1116 1092
             instance.permissions[mode] = perm_class(instance)
1117 1093
         return instance
1118 1094
 
@@ -1138,7 +1114,7 @@ class Protected(poobrains.rendering.Renderable):
1138 1114
 #    pass # let's just hope this works out of the box
1139 1115
 
1140 1116
 
1141
-class Administerable(poobrains.storage.Storable, Protected):
1117
+class Administerable(poobrains.storage.Storable, Protected, metaclass=BaseAdministerable):
1142 1118
     
1143 1119
     __metaclass__ = BaseAdministerable
1144 1120
 
@@ -1205,7 +1181,7 @@ class Administerable(poobrains.storage.Storable, Protected):
1205 1181
         user = g.user
1206 1182
         menu = poobrains.rendering.Menu('related')
1207 1183
 
1208
-        for related_field, related_model in self._meta.backrefs.iteritems(): # Add Models that are associated by ForeignKeyField, like /user/foo/userpermissions
1184
+        for related_field, related_model in self._meta.backrefs.items(): # Add Models that are associated by ForeignKeyField, like /user/foo/userpermissions
1209 1185
 
1210 1186
             if related_model is not self.__class__ and issubclass(related_model, Administerable) and not related_model._meta.abstract:
1211 1187
                 try:
@@ -1283,7 +1259,7 @@ class Administerable(poobrains.storage.Storable, Protected):
1283 1259
     def related_view(cls, related_field=None, handle=None, offset=0):
1284 1260
 
1285 1261
         if related_field is None:
1286
-            raise TypeError("%s.related_view needs Field instance for parameter 'related_field'. Got %s (%s) instead." % (cls.__name__, type(field).__name__, unicode(field)))
1262
+            raise TypeError("%s.related_view needs Field instance for parameter 'related_field'. Got %s (%s) instead." % (cls.__name__, type(field).__name__, str(field)))
1287 1263
 
1288 1264
         related_model = related_field.model
1289 1265
         instance = cls.load(handle)
@@ -1326,7 +1302,7 @@ class Administerable(poobrains.storage.Storable, Protected):
1326 1302
         f.menu_actions = instance.menu_actions
1327 1303
         f.menu_related = instance.menu_related
1328 1304
 
1329
-        if f.fields.has_key(related_field.name):
1305
+        if related_field.name in f.fields:
1330 1306
             f.fields[related_field.name].value = instance
1331 1307
 
1332 1308
         return f.view()
@@ -1387,39 +1363,6 @@ class User(Named):
1387 1363
             return cls.select().where(cls.name == g.user.name) # [g.user] would kinda-sorta work, but that'd break anything expecting this to be a query object
1388 1364
 
1389 1365
 
1390
-    def gen_clientcert_from_spkac(self, name, spkac, challenge, not_after):
1391
-
1392
-        invalid_after = datetime.datetime.now() + datetime.timedelta(seconds=app.config['CERT_MAX_LIFETIME']) 
1393
-        if not_after > invalid_after:
1394
-            raise poobrains.errors.ExposedError("not_after too far into the future, max allowed %s but got %s" % (invalid_after, not_after))
1395
-
1396
-        try:
1397
-
1398
-            ca_key = M2Crypto.EVP.load_key(app.config['CA_KEY'])
1399
-            ca_cert = M2Crypto.X509.load_cert(app.config['CA_CERT'])
1400
-
1401
-        except Exception as e:
1402
-
1403
-            app.logger.error("Client certificate could not be generated. Invalid CA_KEY or CA_CERT.")
1404
-            app.logger.debug(e)
1405
-            flash(u"Plumbing issue. Invalid CA_KEY or CA_CERT.")
1406
-            raise e
1407
-
1408
-        common_name = '%s:%s@%s' % (self.name, name, app.config['SITE_NAME'])
1409
-        spkac = pyspkac.SPKAC(spkac, challenge, CN=common_name) # TODO: Make sure CN is unique
1410
-        spkac.push_extension(M2Crypto.X509.new_extension('keyUsage', 'digitalSignature, keyEncipherment, keyAgreement', critical=True))
1411
-        spkac.push_extension(M2Crypto.X509.new_extension('extendedKeyUsage', 'clientAuth, emailProtection, nsSGC'))
1412
-
1413
-        #spkac.subject.C = ca_cert.get_subject().C
1414
-
1415
-        not_before = int(time.time())
1416
-        not_after = int(time.mktime(not_after.timetuple()))
1417
-
1418
-        serial = int(time.time())
1419
-
1420
-        return spkac.gen_crt(ca_key, ca_cert, serial, not_before, not_after, hash_algo='sha512')
1421
-
1422
-
1423 1366
     def gen_keypair_and_clientcert(self, name, not_after):
1424 1367
 
1425 1368
         invalid_after = datetime.datetime.now() + datetime.timedelta(seconds=app.config['CERT_MAX_LIFETIME']) # FIXME: DRY!
@@ -1562,14 +1505,14 @@ class User(Named):
1562 1505
 
1563 1506
                     info_sorted.append((row.model, row.id))
1564 1507
 
1565
-                    if not info_by_model.has_key(row.model):
1508
+                    if not row.model in info_by_model:
1566 1509
                         info_by_model[row.model] = []
1567 1510
                         
1568 1511
                     info_by_model[row.model].append(row.id)
1569 1512
 
1570 1513
                 posts_by_model = {}
1571 1514
 
1572
-                for model_name, ids in info_by_model.iteritems():
1515
+                for model_name, ids in info_by_model.items():
1573 1516
                 
1574 1517
                     model = Owned.class_children_keyed()[model_name]
1575 1518
                     posts_by_model[model_name] = {}
@@ -1862,7 +1805,7 @@ class Owned(Administerable):
1862 1805
 
1863 1806
         self.permissions = collections.OrderedDict()
1864 1807
         
1865
-        for mode, perm_class in self.__class__.permissions.iteritems():
1808
+        for mode, perm_class in self.__class__.permissions.items():
1866 1809
             self.permissions[mode] = perm_class(self)
1867 1810
 
1868 1811
     @classmethod
@@ -1916,7 +1859,7 @@ class Page(Owned):
1916 1859
         if op == 'create':
1917 1860
             instance = cls()
1918 1861
 
1919
-        elif op == 'read' and kwargs.has_key('path'):
1862
+        elif op == 'read' and 'path' in kwargs:
1920 1863
 
1921 1864
             path = '/%s' % kwargs['path']
1922 1865
             instance = cls.get(cls.path == path)

+ 5
- 4
poobrains/cli/__init__.py View File

@@ -6,11 +6,12 @@ import functools
6 6
 import codecs
7 7
 import peewee
8 8
 import OpenSSL
9
-import gnupg
10 9
 import flask
11 10
 import click
12 11
 import jinja2
13 12
 
13
+from pretty_bad_protocol import gnupg
14
+
14 15
 from click import argument, option, echo, secho, confirm
15 16
 from playhouse import db_url
16 17
 db_url.schemes['sqlite'] = db_url.schemes['sqliteext'] # Make sure we get the extensible sqlite database, so we can make regular expressions case-sensitive. see https://github.com/coleifer/peewee/issues/1221
@@ -316,7 +317,7 @@ def list(storable):
316 317
 
317 318
     for instance in storable.select():
318 319
 
319
-        print "%s: %s - %s" % (instance.handle_string, instance.title, instance)
320
+        print("%s: %s - %s" % (instance.handle_string, instance.title, instance))
320 321
 
321 322
 
322 323
 @app.cli.command()
@@ -376,7 +377,7 @@ def import_(storable, filepath, skip_pk):
376 377
 
377 378
                 else:
378 379
 
379
-                    if record.has_key(field.name): # only fill fields for which we actually have values
380
+                    if field.name in record: # only fill fields for which we actually have values
380 381
 
381 382
                         if field.null and record[field.name] == u'':
382 383
                             setattr(instance, field.name, None) # insert NULL for empty strings if allowed, cleaner than just spamming the db with empty strings
@@ -423,7 +424,7 @@ def export(storable, filepath, skip_pk):
423 424
                 elif isinstance(field, poobrains.storage.fields.ForeignKeyField):
424 425
                     value = value._pk
425 426
 
426
-                record.append(unicode(value))
427
+                record.append(str(value))
427 428
 
428 429
             writer.write_record(record)
429 430
 

+ 2
- 2
poobrains/commenting.py View File

@@ -54,7 +54,7 @@ class Comment(poobrains.auth.Administerable):
54 54
     def reply_form(self):
55 55
 
56 56
         children = Commentable.class_children_keyed()
57
-        if children.has_key(self.model):
57
+        if self.model in children:
58 58
 
59 59
             model = Commentable.class_children_keyed()[self.model]
60 60
             comments_enabled = model.load(self.handle).comments_enabled
@@ -145,7 +145,7 @@ class CommentForm(poobrains.form.Form):
145 145
             cls = Commentable.class_children_keyed()[model]
146 146
             instance = cls.load(handle)
147 147
 
148
-        reply_to = kwargs.pop('reply_to') if kwargs.has_key('reply_to') else None
148
+        reply_to = kwargs.pop('reply_to') if 'reply_to' in kwargs else None
149 149
         if isinstance(reply_to, int):
150 150
             reply_to = Comment.load(reply_to)
151 151
         super(CommentForm, self).__init__(**kwargs)

+ 10
- 9
poobrains/doc/__init__.py View File

@@ -3,7 +3,7 @@
3 3
 The documentation system.
4 4
 """
5 5
 
6
-import __builtin__
6
+#import __builtin__
7 7
 
8 8
 import os
9 9
 import re
@@ -379,19 +379,19 @@ class PooDoc(pydoc.HTMLDoc, object):
379 379
         result += '<div class="module">' % components
380 380
         result += '  <div class="docstring">%(doc)s</div>' % components
381 381
 
382
-        if components.has_key('modules'):
382
+        if 'modules' in components:
383 383
             result += '  <div class="modules">%(modules)s</div>' % components
384 384
 
385
-        if components.has_key('classes'):
385
+        if 'classes' in components:
386 386
             result += '  <div class="classes">%(classes)s</div>' % components
387 387
 
388
-        if components.has_key('funcs'):
388
+        if 'funcs' in components:
389 389
             result += '  <div class="functions">%(funcs)s</div>' % components
390 390
 
391
-        if components.has_key('author'):
391
+        if 'author' in components:
392 392
             result += '<div class="author">%(author)s</div>' % components
393 393
 
394
-        if components.has_key('credits'):
394
+        if 'credits' in components:
395 395
             result += '<div class="credits">%(credits)s</div>' % components
396 396
 
397 397
         result += '</div>'
@@ -506,7 +506,8 @@ class PooDoc(pydoc.HTMLDoc, object):
506 506
                 thisclass = attrs[0][2]
507 507
             attrs, inherited = pydoc._split_list(attrs, lambda t: t[2] is thisclass)
508 508
 
509
-            if thisclass is __builtin__.object:
509
+            #if thisclass is __builtin__.object: # 2.7 only, no clue why it wasn't just `object` in the first place.
510
+            if thisclass is object:
510 511
                 attrs = inherited
511 512
                 continue
512 513
             elif thisclass is object:
@@ -550,7 +551,7 @@ class PooDoc(pydoc.HTMLDoc, object):
550 551
         result = '<dt class="class">%s</dt>' % self.heading(level, title)
551 552
 
552 553
         result += '<dd class="class">'
553
-        if components.has_key('mro'):
554
+        if 'mro' in components:
554 555
             result += '  <div class="mro">%(mro)s</div>' % components
555 556
 
556 557
         result += '\n  '.join(components['docs'])
@@ -679,7 +680,7 @@ class Documentation(poobrains.auth.Protected):
679 680
             self.title = handle.title()
680 681
             self.text = poobrains.md.MarkdownString(codecs.open(md_path, 'r', encoding='utf-8').read())
681 682
 
682
-        elif sys.modules.has_key(handle):
683
+        elif handle in sys.modules:
683 684
             
684 685
             doc = PooDoc()
685 686
             subject = sys.modules[handle]

+ 14
- 13
poobrains/form/__init__.py View File

@@ -31,7 +31,7 @@ class FormMeta(poobrains.helpers.MetaCompatibility, poobrains.helpers.ClassOrIns
31 31
         return super(FormMeta, cls).__setattr__(name, value)
32 32
 
33 33
 
34
-class BaseForm(poobrains.rendering.Renderable):
34
+class BaseForm(poobrains.rendering.Renderable, metaclass=FormMeta):
35 35
 
36 36
     __metaclass__ = FormMeta
37 37
 
@@ -97,7 +97,7 @@ class BaseForm(poobrains.rendering.Renderable):
97 97
     
98 98
     def __setattr__(self, name, value):
99 99
 
100
-        if name == 'name' and isinstance(value, basestring):
100
+        if name == 'name' and isinstance(value, str):
101 101
             assert not '.' in value, "Form names *must* not contain dots: %s" % value
102 102
             super(BaseForm, self).__setattr__(name, value)
103 103
 
@@ -108,10 +108,10 @@ class BaseForm(poobrains.rendering.Renderable):
108 108
             else:
109 109
                 child_prefix = self.name
110 110
 
111
-            for field in self.fields.itervalues():
111
+            for field in self.fields.values():
112 112
                 field.prefix = child_prefix
113 113
 
114
-            for button in self.controls.itervalues():
114
+            for button in self.controls.values():
115 115
                 button.prefix = child_prefix
116 116
 
117 117
         elif isinstance(value, fields.BaseField) or isinstance(value, Fieldset):
@@ -134,7 +134,7 @@ class BaseForm(poobrains.rendering.Renderable):
134 134
         Iterate over this forms renderable fields.
135 135
         """
136 136
 
137
-        for field in self.fields.itervalues():
137
+        for field in self.fields.values():
138 138
             if isinstance(field, (fields.Field, Fieldset)) and field.name not in self._external_fields:
139 139
                 yield field
140 140
 
@@ -146,7 +146,7 @@ class BaseForm(poobrains.rendering.Renderable):
146 146
         Fields like this can be created by passing a Form object to the Field constructor.
147 147
         """
148 148
 
149
-        if isinstance(field, fields.Checkbox) and self.fields.has_key(field.name) and type(field) == type(self.fields[field.name]): # checkboxes/radio inputs can pop up multiple times, but belong to the same name
149
+        if isinstance(field, fields.Checkbox) and field.name in self.fields and type(field) == type(self.fields[field.name]): # checkboxes/radio inputs can pop up multiple times, but belong to the same name
150 150
             self.fields[field.name].choices.extend(field.choices)
151 151
 
152 152
         else:
@@ -216,7 +216,7 @@ class BaseForm(poobrains.rendering.Renderable):
216 216
                 if not field.readonly:
217 217
                     
218 218
                     source = files if isinstance(field, fields.File) else values
219
-                    if not source.has_key(field.name):
219
+                    if not field.name in source:
220 220
 
221 221
                         if field.multi or isinstance(field, Fieldset):
222 222
                             field_values = werkzeug.datastructures.MultiDict()
@@ -232,7 +232,7 @@ class BaseForm(poobrains.rendering.Renderable):
232 232
 
233 233
                     try:
234 234
                         if isinstance(field, Fieldset):
235
-                            sub_files = files[field.name] if files.has_key(field.name) else werkzeug.datastructures.MultiDict()
235
+                            sub_files = files[field.name] if field.name in files else werkzeug.datastructures.MultiDict()
236 236
                             field.bind(field_values, sub_files)
237 237
                         else:
238 238
                             field.bind(field_values)
@@ -242,7 +242,7 @@ class BaseForm(poobrains.rendering.Renderable):
242 242
                         for e in ce.errors:
243 243
                             compound_error.append(e)
244 244
 
245
-            for name, control in self.controls.iteritems():
245
+            for name, control in self.controls.items():
246 246
                 if isinstance(control, Button):
247 247
                     control.value = values.get(name, False)
248 248
 
@@ -274,7 +274,7 @@ class BaseForm(poobrains.rendering.Renderable):
274 274
 
275 275
         rendered_controls = u''
276 276
 
277
-        for control in self.controls.itervalues():
277
+        for control in self.controls.values():
278 278
             rendered_controls += control.render()
279 279
 
280 280
         return rendered_controls
@@ -430,11 +430,11 @@ class Fieldset(BaseForm):
430 430
     def __setattr__(self, name, value):
431 431
 
432 432
         if name == 'value':
433
-            for field in self.fields.itervalues():
433
+            for field in self.fields.values():
434 434
                 if hasattr(value, field.name):
435 435
                     field.value = getattr(value, field.name)
436 436
 
437
-        elif name == 'name' and isinstance(value, basestring):
437
+        elif name == 'name' and isinstance(value, str):
438 438
             assert not '.' in value, "Form Field names *must* not contain dots: %s" % value
439 439
             super(Fieldset, self).__setattr__(name, value)
440 440
 
@@ -481,7 +481,8 @@ class Button(poobrains.rendering.Renderable):
481 481
     
482 482
     def __new__(cls, *args, **kwargs):
483 483
 
484
-        instance = super(Button, cls).__new__(cls, *args, **kwargs)
484
+        #instance = super(Button, cls).__new__(cls, *args, **kwargs)
485
+        instance = super(Button, cls).__new__(cls)
485 486
         instance._created = time.time()
486 487
 
487 488
         return instance

+ 5
- 4
poobrains/form/fields.py View File

@@ -19,7 +19,7 @@ class BoundFieldMeta(poobrains.helpers.MetaCompatibility, poobrains.helpers.Clas
19 19
     pass
20 20
 
21 21
 
22
-class BaseField(object):
22
+class BaseField(object, metaclass=poobrains.helpers.MetaCompatibility):
23 23
 
24 24
     __metaclass__ = poobrains.helpers.MetaCompatibility
25 25
 
@@ -47,7 +47,8 @@ class BaseField(object):
47 47
 
48 48
     def __new__(cls, *args, **kwargs):
49 49
 
50
-        instance = super(BaseField, cls).__new__(cls, **kwargs)
50
+        #instance = super(BaseField, cls).__new__(cls, **kwargs)
51
+        instance = super(BaseField, cls).__new__(cls)
51 52
         instance._created = time.time()
52 53
 
53 54
         return instance
@@ -111,7 +112,7 @@ class BaseField(object):
111 112
 
112 113
     def __setattr__(self, name, value):
113 114
 
114
-        if name == 'name' and isinstance(value, basestring):
115
+        if name == 'name' and isinstance(value, str):
115 116
             assert not '.' in value, "Form Field names *must* not contain dots: %s" % value
116 117
 
117 118
         super(BaseField, self).__setattr__(name, value)
@@ -272,7 +273,7 @@ class BaseField(object):
272 273
             return value.handle_string
273 274
 
274 275
         else:
275
-            return unicode(value)
276
+            return str(value)
276 277
 
277 278
 
278 279
 class Value(BaseField):

+ 20
- 20
poobrains/helpers.py View File

@@ -49,7 +49,7 @@ def flatten_nested_multidict(v):
49 49
 
50 50
 def choose_primary(d):
51 51
     
52
-    for k,v in d.iteritems():
52
+    for k,v in d.items():
53 53
 
54 54
         if v['primary']:
55 55
            return v
@@ -62,8 +62,8 @@ def clean_string(s):
62 62
     allowed_chars = string.ascii_lowercase + string.digits + '-'
63 63
     clean = ""
64 64
 
65
-    if not isinstance(s, unicode):
66
-        s = unicode(s.decode('utf-8'))
65
+    #if not isinstance(s, unicode):
66
+    #    s = unicode(s.decode('utf-8'))
67 67
 
68 68
     s = s.lower()
69 69
 
@@ -75,7 +75,7 @@ def clean_string(s):
75 75
         u'ß': u'ss'
76 76
     }
77 77
 
78
-    for pattern, substitute in substitutions.iteritems():
78
+    for pattern, substitute in substitutions.items():
79 79
         s = s.replace(pattern, substitute)
80 80
 
81 81
     for char in s:
@@ -126,7 +126,7 @@ def themed(f):
126 126
         else:
127 127
             user = None
128 128
 
129
-        if kwargs.has_key('mode'):
129
+        if 'mode' in kwargs:
130 130
             mode = kwargs['mode']
131 131
         else:
132 132
             #mode = content._meta.modes.keys()[0] # TODO: Default mode option in _meta?
@@ -235,18 +235,18 @@ class MetaCompatibility(type):
235 235
             cls._meta = FakeMetaOptions()
236 236
 
237 237
             #if hasattr(cls, 'Meta'):
238
-            if attrs.has_key('Meta'):
239
-                
238
+            if 'Meta' in attrs:
239
+                print(f"Meta for  {name}")
240 240
                 for option_name in recognized_options:
241 241
                     if hasattr(attrs['Meta'], option_name):
242 242
                         setattr(cls._meta, option_name, getattr(attrs['Meta'], option_name))
243
-                    elif defaults.has_key(option_name):
243
+                    elif option_name in defaults:
244 244
                         setattr(cls._meta, option_name, defaults[option_name])
245 245
 
246 246
                 delattr(cls, 'Meta')
247 247
 
248 248
             else:
249
-                for option_name, default in defaults.iteritems():
249
+                for option_name, default in defaults.items():
250 250
                     setattr(cls._meta, option_name, default)
251 251
 
252 252
         else:
@@ -261,7 +261,7 @@ class MetaCompatibility(type):
261 261
         return cls
262 262
 
263 263
 
264
-class ChildAware(object):
264
+class ChildAware(object, metaclass=MetaCompatibility):
265 265
 
266 266
     __metaclass__ = MetaCompatibility
267 267
 
@@ -311,9 +311,9 @@ class ChildAware(object):
311 311
 
312 312
             tiered[_level].append(base)
313 313
             if hasattr(base, 'ancestors'):
314
-                for lvl, ancestors in base.ancestors(_level+1).iteritems():
314
+                for lvl, ancestors in base.ancestors(_level+1).items():
315 315
 
316
-                    if not tiered.has_key(lvl):
316
+                    if not lvl in tiered:
317 317
                         tiered[lvl] = []
318 318
                     tiered[lvl] += ancestors
319 319
 
@@ -321,7 +321,7 @@ class ChildAware(object):
321 321
             return tiered
322 322
 
323 323
         r = []
324
-        for ancestors in tiered.itervalues():
324
+        for ancestors in tiered.values():
325 325
             r += ancestors
326 326
 
327 327
         return r
@@ -331,7 +331,7 @@ class TrueDict(OrderedDict):
331 331
 
332 332
     def __setitem__(self, key, value):
333 333
 
334
-        if value == True and True in self.itervalues() and self[name] != True:
334
+        if value == True and True in self.values() and self[name] != True:
335 335
             raise ValueError('Only one item may be True.')
336 336
 
337 337
         return super(TrueDict, self).__setitem__(key, value)
@@ -340,7 +340,7 @@ class TrueDict(OrderedDict):
340 340
     def choose(self):
341 341
 
342 342
         if True in self.values():
343
-            for choice, primary in self.iteritems():
343
+            for choice, primary in self.items():
344 344
                 if primary == True:
345 345
                     break
346 346
         else:
@@ -362,7 +362,7 @@ class CustomOrderedDict(dict):
362 362
 
363 363
         repr = '{'
364 364
 
365
-        for k, v in self.iteritems():
365
+        for k, v in self.items():
366 366
             repr += '%s: %s' % (k.__repr__(), v.__repr__())
367 367
 
368 368
         repr += '}'
@@ -387,13 +387,13 @@ class CustomOrderedDict(dict):
387 387
             yield key
388 388
 
389 389
 
390
-    def iteritems(self):
390
+    def items(self):
391 391
 
392 392
         for key in self.keys():
393 393
             yield key, self[key]
394 394
 
395 395
 
396
-    def itervalues(self):
396
+    def values(self):
397 397
 
398 398
         for key in self.keys():
399 399
             yield self[key]
@@ -474,8 +474,8 @@ class ASVWriter(object):
474 474
 
475 475
     fd = None
476 476
 
477
-    unit_separator = unicode(chr(0x1F)) # probably needed in order not to fail the join on records with unicode chars
478
-    record_terminator = unicode(chr(0x1E)) # unicode for consistency
477
+    unit_separator = chr(0x1F)
478
+    record_terminator = chr(0x1E)
479 479
 
480 480
 
481 481
     def __init__(self, filepath):

+ 2
- 2
poobrains/mailing.py View File

@@ -7,7 +7,7 @@ from email.mime.application import MIMEApplication
7 7
 from email.utils import formatdate
8 8
 
9 9
 import smtplib
10
-import gnupg
10
+from pretty_bad_protocol import gnupg
11 11
 
12 12
 import flask
13 13
 #import poobrains
@@ -79,7 +79,7 @@ class Mail(MIMEMultipart):
79 79
 
80 80
     def send(self):
81 81
 
82
-        if not isinstance(self.fingerprint, basestring):
82
+        if not isinstance(self.fingerprint, str):
83 83
             raise MailError('Trying to send mail without selecting public key fingerprint.')
84 84
 
85 85
         if self['To'] is None:

+ 5
- 5
poobrains/md/__init__.py View File

@@ -18,7 +18,7 @@ def magic_markdown_loader(storable, handle):
18 18
 
19 19
     storables = poobrains.storage.Storable.class_children_keyed(lower=True)
20 20
 
21
-    if storables.has_key(storable.lower()):
21
+    if storable.lower() in storables:
22 22
         cls = storables[storable.lower()]
23 23
         return cls.load(handle)
24 24
 
@@ -30,7 +30,7 @@ def magic_markdown_loader(storable, handle):
30 30
     return False
31 31
 
32 32
 
33
-class MarkdownString(unicode):
33
+class MarkdownString(str):
34 34
 
35 35
     def render(self, mode='inline'): # mode is ignored anyways
36 36
         return jinja2.Markup(md.convert(self))
@@ -83,11 +83,11 @@ class DisplayRenderable(markdown.inlinepatterns.Pattern):
83 83
                         
84 84
                         except:
85 85
                             return jinja2.Markup(md.convert("*You are not allowed to view an instance of %s that was placed here.*" % cls.__name__))
86
-                    if instance._meta.modes.has_key('inline'):
86
+                    if 'inline' in instance._meta.modes:
87 87
                         return instance.render('inline')
88
-                    elif instance._meta.modes.has_key('teaser'):
88
+                    elif 'teaser' in instance._meta.modes:
89 89
                         return instance.render('teaser')
90
-                    elif instance._meta.modes.has_key('full'):
90
+                    elif 'full' in instance._meta.modes:
91 91
                         return instance.render('full')
92 92
                     else:
93 93
                         return instance.render()

+ 3
- 3
poobrains/rendering.py View File

@@ -39,7 +39,7 @@ class Renderable(poobrains.helpers.ChildAware):
39 39
         if self.css_class is None:
40 40
             self.css_class = ''
41 41
 
42
-        if kwargs.has_key('handle'):
42
+        if 'handle' in kwargs:
43 43
             self.handle_string = kwargs['handle']
44 44
 
45 45
 
@@ -256,7 +256,7 @@ class TableRow(object):
256 256
 
257 257
         super(TableRow, self).__setattr__('_columns', columns)
258 258
 
259
-        if kwdata.has_key('_classes'):
259
+        if '_classes' in kwdata:
260 260
             self.classes = kwdata.pop('_classes')
261 261
 
262 262
         self._data = []
@@ -267,7 +267,7 @@ class TableRow(object):
267 267
         for i in range(0, len(data)):
268 268
             self[i] = data[i]
269 269
 
270
-        for key, value in kwdata.iteritems():
270
+        for key, value in kwdata.items():
271 271
             self[key] = value
272 272
 
273 273
 

+ 4
- 4
poobrains/search.py View File

@@ -32,10 +32,10 @@ class SearchForm(poobrains.form.Form):
32 32
 
33 33
     def __init__(self, *args, **kwargs):
34 34
 
35
-        if not kwargs.has_key('action'):
35
+        if not 'action' in kwargs:
36 36
             kwargs['action'] = app.site.get_view_url(Search, mode='full')
37 37
 
38
-        if not self.fields['pattern'].value and flask.session.has_key('search_pattern'):
38
+        if not self.fields['pattern'].value and 'search_pattern' in flask.session:
39 39
             self.fields['pattern'].value = flask.session['search_pattern']
40 40
 
41 41
         super(SearchForm, self).__init__(*args, **kwargs)
@@ -68,7 +68,7 @@ class Search(poobrains.auth.Protected):
68 68
             
69 69
             pattern = flask.request.form[self.form.name]['pattern']
70 70
 
71
-            if flask.request.form.has_key('clear'):
71
+            if 'clear' in flask.request.form:
72 72
                 flask.session.pop('search_pattern', None)
73 73
                 return flask.redirect(app.site.get_view_url(self.__class__, mode='full'))
74 74
 
@@ -76,7 +76,7 @@ class Search(poobrains.auth.Protected):
76 76
             flask.session['search_pattern'] = pattern
77 77
             return flask.redirect(self.url('full'))
78 78
 
79
-        if len(self.handle) == 0 and flask.session.has_key('search_pattern'):
79
+        if len(self.handle) == 0 and 'search_pattern' in flask.session:
80 80
             if len(flask.session['search_pattern']) >= 3:
81 81
                 # redirect requests with empty search handle to the saved search
82 82
                 self.handle = flask.session['search_pattern']

+ 3
- 4
poobrains/storage/__init__.py View File

@@ -2,7 +2,6 @@
2 2
 
3 3
 # external imports
4 4
 import math
5
-import types
6 5
 import collections
7 6
 import re
8 7
 import copy
@@ -92,7 +91,7 @@ class ModelBase(poobrains.helpers.MetaCompatibility, peewee.ModelBase):
92 91
         return cls
93 92
 
94 93
 
95
-class Model(peewee.Model, poobrains.helpers.ChildAware):
94
+class Model(peewee.Model, poobrains.helpers.ChildAware, metaclass=ModelBase):
96 95
 
97 96
     __metaclass__ = ModelBase
98 97
 
@@ -108,7 +107,7 @@ class Model(peewee.Model, poobrains.helpers.ChildAware):
108 107
 
109 108
         q = cls.select()
110 109
 
111
-        if isinstance(handle, types.StringTypes):
110
+        if isinstance(handle, str):
112 111
             handle = cls.string_handle(handle)
113 112
 
114 113
         elif type(handle) not in (tuple, list):
@@ -461,7 +460,7 @@ class StorableParamType(poobrains.form.types.ParamType):
461 460
 
462 461
         storables = self.baseclass.class_children_keyed(lower=True)
463 462
 
464
-        if storables.has_key(value.lower()):
463
+        if value.lower() in storables:
465 464
             return storables[value.lower()] # holy shit it's lined up! D:
466 465
 
467 466
         self.fail(u'Not a valid storable: %s. Try one of %s' % (value, storables.keys()))

+ 2
- 2
poobrains/storage/fields.py View File

@@ -53,7 +53,7 @@ class StorableInstanceParamType(poobrains.form.types.ParamType):
53 53
 poobrains.form.types.StorableInstanceParamType = StorableInstanceParamType
54 54
 
55 55
 
56
-class ForeignKeyChoice(poobrains.form.fields.Text):
56
+class ForeignKeyChoice(poobrains.form.fields.Text, metaclass=poobrains.form.fields.BoundFieldMeta):
57 57
 
58 58
     """
59 59
     Note: This field expects to be bound to a ForeignKeyField.
@@ -90,7 +90,7 @@ class Field(poobrains.helpers.ChildAware):
90 90
 
91 91
     def __init__(self, *args, **kwargs):
92 92
 
93
-        if kwargs.has_key('form_widget'):
93
+        if 'form_widget' in kwargs:
94 94
             self.form_widget = kwargs.pop('form_widget')
95 95
 
96 96
         super(Field, self).__init__(*args, **kwargs)

+ 2
- 2
poobrains/tagging.py View File

@@ -104,14 +104,14 @@ class Tag(poobrains.auth.Named):
104 104
 
105 105
         for binding in bindings:
106 106
 
107
-            if not bindings_by_model.has_key(binding.model):
107
+            if not binding.model in bindings_by_model:
108 108
                 bindings_by_model[binding.model] = []
109 109
 
110 110
             bindings_by_model[binding.model].append(binding)
111 111
 
112 112
         #bindings_by_model = sorted(bindings_by_model) # re-order by model name
113 113
 
114
-        for model_name, bindings in bindings_by_model.iteritems():
114
+        for model_name, bindings in bindings_by_model.items():
115 115
 
116 116
             try:
117 117
                 model = poobrains.storage.Storable.class_children_keyed()[model_name]

+ 11
- 11
poobrains/testing.py View File

@@ -85,7 +85,7 @@ def fill_valid(instance):
85 85
                         else:
86 86
                             raise AssertionError("Can't guarantee valid fill for class '%s' because of constraints on field '%s'!" % (instance.__class__.__name__, attr_name))
87 87
 
88
-                    elif not fieldmap.has_key(cls_attr.__class__):
88
+                    elif not cls_attr.__class__ in fieldmap:
89 89
                         raise AssertionError("Can't generate fill for %s.%s of type %s" % (instance.__class__.__name__, attr_name, field_class.__name__))
90 90
                     else:
91 91
                         setattr(instance, attr_name, generators[fieldmap[field_class]]())
@@ -106,7 +106,7 @@ owneds_to_test = list(poobrains.auth.Owned.class_children() - expected_failures)
106 106
 
107 107
 permission_holders = ['user', 'group']
108 108
 
109
-ops = list(poobrains.auth.OwnedPermission.op_abbreviations.iteritems()) # crud operations and their abbreviations
109
+ops = list(poobrains.auth.OwnedPermission.op_abbreviations.items()) # crud operations and their abbreviations
110 110
 
111 111
 @pytest.fixture
112 112
 def client():
@@ -117,7 +117,7 @@ def client():
117 117
     poobrains.app.debug = True
118 118
     client = poobrains.app.test_client()
119 119
 
120
-    if not os.environ.has_key('FLASK_APP'):
120
+    if not 'FLASK_APP' in os.environ:
121 121
         os.environ['FLASK_APP'] = '__main__'
122 122
     #poobrains.project_name = os.environ['FLASK_APP']
123 123
 
@@ -162,7 +162,7 @@ y
162 162
 
163 163
     runner = CliRunner()
164 164
     rv = runner.invoke(poobrains.cli.install, input=input)
165
-    print rv.output
165
+    print(rv.output)
166 166
 
167 167
     assert not rv.exception, rv.exception.message
168 168
     assert "Installation complete!" in rv.output, "Installation apparently didn't complete!"
@@ -187,8 +187,8 @@ def test_redeem_token(client):
187 187
 
188 188
     rv = client.post('/cert/', data={'ClientCertForm.token': token.token, 'submit': 'ClientCertForm.tls_submit'})
189 189
 
190
-    passphrase_request = client.get('/cert/') # reply to the next request in the same session contains a flash() with passphrase
191
-    match = re.search(u">The passphrase for this delicious bundle of crypto is &#39;(.+)&#39;<", passphrase_request.data)
190
+    passphrase_response = client.get('/cert/') # reply to the next request in the same session contains a flash() with passphrase
191
+    match = re.search(u">The passphrase for this delicious bundle of crypto is &#39;(.+)&#39;<", passphrase_response.data.decode('ascii'))
192 192
 
193 193
     assert match, "Couldn't find passphrase flash!"
194 194
 
@@ -226,7 +226,7 @@ def test_crud(client, cls):
226 226
     
227 227
     assert instance.save() > 0, "Update failed for class '%s'!" % cls.__name__
228 228
 
229
-    assert instance.delete() > 0, "Delete failed for class '%s'!" % cls.__name__
229
+    assert instance.delete_instance() > 0, "Delete failed for class '%s'!" % cls.__name__
230 230
 
231 231
 
232 232
 # TODO: use the Page permission tests as basis for auto-generated permission
@@ -242,7 +242,7 @@ def test_permission_grant(client, cls, permission_holder, op_info):
242 242
     op = op_info[0]
243 243
     op_abbr = op_info[1]
244 244
 
245
-    if not cls.permissions.has_key(op):
245
+    if not op in cls.permissions:
246 246
         pytest.skip() # this op has been explicitly disabled and isn't exposed (for which there should also be a test)
247 247
 
248 248
     u = poobrains.auth.User()
@@ -302,7 +302,7 @@ def test_permission_deny(client, cls, permission_holder, op_info):
302 302
     op = op_info[0]
303 303
     op_abbr = op_info[1]
304 304
 
305
-    if not cls.permissions.has_key(op):
305
+    if not op in cls.permissions:
306 306
         pytest.skip() # this op has been explicitly disabled and isn't exposed (for which there should also be a test)
307 307
 
308 308
     u = poobrains.auth.User()
@@ -360,7 +360,7 @@ def test_ownedpermission_instance(client, cls, permission_holder, op_info):
360 360
     op = op_info[0]
361 361
     op_abbr = op_info[1]
362 362
 
363
-    if not cls.permissions.has_key(op):
363
+    if not op in cls.permissions:
364 364
         pytest.skip() # this op has been explicitly disabled and isn't exposed (for which there should also be a test)
365 365
 
366 366
     u = poobrains.auth.User()
@@ -428,7 +428,7 @@ def test_ownedpermission_own_instance(client, cls, permission_holder, op_info):
428 428
     op = op_info[0]
429 429
     op_abbr = op_info[1]
430 430
 
431
-    if not cls.permissions.has_key(op):
431
+    if not op in cls.permissions:
432 432
         pytest.skip() # this op has been explicitly disabled and isn't exposed (for which there should also be a test)
433 433
 
434 434
     u = poobrains.auth.User()

+ 1
- 1
poobrains/themes/default/commentable.jinja View File

@@ -6,7 +6,7 @@
6 6
     <div class="direct-reply">
7 7
         {{ content.comment_form().render('full') }}
8 8
     </div>
9
-    {% for comment, children in content.comments_threaded.iteritems() %}
9
+    {% for comment, children in content.comments_threaded.items() %}
10 10
         {{ comment.render('full') }}
11 11
     {% endfor %}
12 12
 </div>

+ 6
- 2
poobrains/upload/__init__.py View File

@@ -144,7 +144,7 @@ class File(poobrains.auth.NamedOwned):
144 144
 
145 145
     def __setattr__(self, name, value):
146 146
 
147
-        if name == 'filename' and isinstance(value, basestring):
147
+        if name == 'filename' and isinstance(value, str):
148 148
             value = werkzeug.utils.secure_filename(value)
149 149
 
150 150
         return super(File, self).__setattr__(name, value)
@@ -189,12 +189,16 @@ class File(poobrains.auth.NamedOwned):
189 189
 
190 190
     def delete_instance(self, *args, **kwargs):
191 191
 
192
-        if super(File, self).delete_instance(*args, **kwargs):
192
+        r = super(File, self).delete_instance(*args, **kwargs)
193
+
194
+        if r:
193 195
             try:
194 196
                 os.remove(os.path.join(self.path, self.filename))
195 197
             except OSError as e:
196 198
                 flask.flash(u"Could not delete %s '%s'." % (self.__class__.__name__, self.filename))
197 199
 
200
+        return r
201
+
198 202
 
199 203
 class Image(File):
200 204
     extension_whitelist = set(['gif', 'jpg', 'png', 'svg'])

+ 3
- 4
setup.py View File

@@ -5,17 +5,16 @@ setup(
5 5
     author='phryk',
6 6
     classifiers=[
7 7
         'Development Status :: 2 - Pre-Alpha',
8
-        'Programming Language :: Python :: 2.7',
8
+        #'Programming Language :: Python :: 2.7',
9 9
     ],
10 10
     install_requires=[
11 11
         'flask',
12 12
         'peewee',
13
-        'pyspkac',
14
-        'pyOpenSSL', # because apparently this is marked neither by pyspkac nor by m2crypto as dependency…
13
+        'pyOpenSSL', # to create TLS client certs
15 14
         'pyScss',
16 15
         'pillow',
17 16
         'markdown',
18
-        'gnupg'],
17
+        'pretty-bad-protocol'], # formerly 'gnupg'
19 18
     extras_require={
20 19
         'dev': ['pudb'], 
21 20
     },

Loading…
Cancel
Save