From 663627358fd529ccbcc722ea4c640064cc702296 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 3 Mar 2017 12:00:24 +0100 Subject: [PATCH] Ran 2to3 on pillar + some manual fixups The 'manual fixups' are: - incorrect use of dict.items() where dict.iteritems() was meant; this results in list(dict.items()), which I changed to dict.items(). - removal of 'from __future__ import' lines, which 2to3 changes into empty lines; I removed the empty lines. --- pillar/__init__.py | 8 +- pillar/api/blender_cloud/home_project.py | 14 +-- pillar/api/blender_id.py | 6 +- pillar/api/custom_field_validation.py | 8 +- pillar/api/eve_settings.py | 2 +- pillar/api/file_storage/__init__.py | 2 +- pillar/api/file_storage/moving.py | 2 +- pillar/api/local_auth.py | 2 +- pillar/api/nodes/__init__.py | 10 +- pillar/api/nodes/custom/comment.py | 24 ++-- pillar/api/projects/hooks.py | 4 +- pillar/api/projects/routes.py | 4 +- pillar/api/service.py | 10 +- pillar/api/users/hooks.py | 6 +- pillar/api/utils/__init__.py | 13 +- pillar/api/utils/authentication.py | 2 +- pillar/api/utils/authorization.py | 12 +- pillar/api/utils/gcs.py | 12 +- pillar/api/utils/imaging.py | 14 +-- pillar/auth/__init__.py | 4 +- pillar/cli.py | 26 ++-- pillar/config.py | 4 +- pillar/extension.py | 4 +- pillar/markdown.py | 2 - pillar/sdk.py | 6 +- pillar/tests/__init__.py | 12 +- pillar/tests/common_test_data.py | 146 +++++++++++------------ pillar/tests/config_testing.py | 2 +- pillar/web/jinja.py | 2 - pillar/web/main/routes.py | 4 +- pillar/web/nodes/attachments.py | 16 +-- pillar/web/nodes/custom/comments.py | 2 +- pillar/web/nodes/forms.py | 2 +- pillar/web/nodes/routes.py | 18 +-- pillar/web/projects/routes.py | 6 +- pillar/web/redirects/__init__.py | 4 +- pillar/web/users/routes.py | 12 +- pillar/web/utils/__init__.py | 10 +- pillar/web/utils/forms.py | 82 ++++++------- 39 files changed, 254 insertions(+), 265 deletions(-) diff --git a/pillar/__init__.py b/pillar/__init__.py index 2f149659..2e136a34 100644 --- a/pillar/__init__.py +++ b/pillar/__init__.py @@ -360,12 +360,12 @@ class PillarServer(Eve): if node_type: node_type = node_type.replace('_', ' ').title() if doc_name: - description = u'%s "%s" was deleted.' % (node_type, doc_name) + description = '%s "%s" was deleted.' % (node_type, doc_name) else: - description = u'This %s was deleted.' % (node_type, ) + description = 'This %s was deleted.' % (node_type, ) else: if doc_name: - description = u'"%s" was deleted.' % doc_name + description = '"%s" was deleted.' % doc_name else: description = None @@ -441,7 +441,7 @@ class PillarServer(Eve): web.setup_app(self) authentication.setup_app(self) - for ext in self.pillar_extensions.itervalues(): + for ext in self.pillar_extensions.values(): self.log.info('Setting up extension %s', ext.name) ext.setup_app(self) diff --git a/pillar/api/blender_cloud/home_project.py b/pillar/api/blender_cloud/home_project.py index 7c1566aa..4f764144 100644 --- a/pillar/api/blender_cloud/home_project.py +++ b/pillar/api/blender_cloud/home_project.py @@ -18,7 +18,7 @@ log = logging.getLogger(__name__) HOME_PROJECT_USERS = set() # Users with any of these roles will get full write access to their home project. -HOME_PROJECT_WRITABLE_USERS = {u'subscriber', u'demo'} +HOME_PROJECT_WRITABLE_USERS = {'subscriber', 'demo'} HOME_PROJECT_DESCRIPTION = ('# Your home project\n\n' 'This is your home project. It allows synchronisation ' @@ -30,7 +30,7 @@ HOME_PROJECT_SUMMARY = 'This is your home project. Here you can sync your Blende # 'as a pastebin for text, images and other assets, and ' # 'allows synchronisation of your Blender settings.') # HOME_PROJECT_SUMMARY = 'This is your home project. Pastebin and Blender settings sync in one!' -SYNC_GROUP_NODE_NAME = u'Blender Sync' +SYNC_GROUP_NODE_NAME = 'Blender Sync' SYNC_GROUP_NODE_DESC = ('The [Blender Cloud Addon](https://cloud.blender.org/services' '#blender-addon) will synchronize your Blender settings here.') @@ -135,8 +135,8 @@ def create_home_project(user_id, write_access): # This allows people to comment on shared images and see comments. node_type_comment = assign_permissions( node_type_comment, - subscriber_methods=[u'GET', u'POST'], - world_methods=[u'GET']) + subscriber_methods=['GET', 'POST'], + world_methods=['GET']) project['node_types'] = [ node_type_group, @@ -215,7 +215,7 @@ def home_project(): write_access = write_access_with_roles(roles) create_home_project(user_id, write_access) - resp, _, _, status, _ = get('projects', category=u'home', user=user_id) + resp, _, _, status, _ = get('projects', category='home', user=user_id) if status != 200: return utils.jsonify(resp), status @@ -248,8 +248,8 @@ def home_project_permissions(write_access): """ if write_access: - return [u'GET', u'PUT', u'POST', u'DELETE'] - return [u'GET'] + return ['GET', 'PUT', 'POST', 'DELETE'] + return ['GET'] def has_home_project(user_id): diff --git a/pillar/api/blender_id.py b/pillar/api/blender_id.py index 57568017..86fc57d0 100644 --- a/pillar/api/blender_id.py +++ b/pillar/api/blender_id.py @@ -86,7 +86,7 @@ def upsert_user(db_user, blender_id_user_id): :type: (ObjectId, int) """ - if u'subscriber' in db_user.get('groups', []): + if 'subscriber' in db_user.get('groups', []): log.error('Non-ObjectID string found in user.groups: %s', db_user) raise wz_exceptions.InternalServerError('Non-ObjectID string found in user.groups: %s' % db_user) @@ -117,8 +117,8 @@ def upsert_user(db_user, blender_id_user_id): if status == 422: # Probably non-unique username, so retry a few times with different usernames. log.info('Error creating new user: %s', r) - username_issue = r.get('_issues', {}).get(u'username', '') - if u'not unique' in username_issue: + username_issue = r.get('_issues', {}).get('username', '') + if 'not unique' in username_issue: # Retry db_user['username'] = authentication.make_unique_username(db_user['email']) continue diff --git a/pillar/api/custom_field_validation.py b/pillar/api/custom_field_validation.py index 98929159..773acb82 100644 --- a/pillar/api/custom_field_validation.py +++ b/pillar/api/custom_field_validation.py @@ -61,13 +61,13 @@ class ValidateCustomFields(Validator): Only validates the dict values, not the keys. Modifies the given dict in-place. """ - assert dict_valueschema[u'type'] == u'dict' + assert dict_valueschema['type'] == 'dict' assert isinstance(dict_property, dict) for key, val in dict_property.items(): - item_schema = {u'item': dict_valueschema} - item_prop = {u'item': val} - dict_property[key] = self.convert_properties(item_prop, item_schema)[u'item'] + item_schema = {'item': dict_valueschema} + item_prop = {'item': val} + dict_property[key] = self.convert_properties(item_prop, item_schema)['item'] def _validate_valid_properties(self, valid_properties, field, value): from pillar.api.utils import project_get_node_type diff --git a/pillar/api/eve_settings.py b/pillar/api/eve_settings.py index adf2ebd6..1b3c8453 100644 --- a/pillar/api/eve_settings.py +++ b/pillar/api/eve_settings.py @@ -723,7 +723,7 @@ users = { # By default don't include the 'auth' field. It can still be obtained # using projections, though, so we block that in hooks. - 'datasource': {'projection': {u'auth': 0}}, + 'datasource': {'projection': {'auth': 0}}, 'schema': users_schema } diff --git a/pillar/api/file_storage/__init__.py b/pillar/api/file_storage/__init__.py index 1adf3de7..c630143b 100644 --- a/pillar/api/file_storage/__init__.py +++ b/pillar/api/file_storage/__init__.py @@ -222,7 +222,7 @@ def process_file(gcs, file_id, local_file): mime_category, src_file['format'] = src_file['content_type'].split('/', 1) # Prevent video handling for non-admins. - if not user_has_role(u'admin') and mime_category == 'video': + if not user_has_role('admin') and mime_category == 'video': if src_file['format'].startswith('x-'): xified = src_file['format'] else: diff --git a/pillar/api/file_storage/moving.py b/pillar/api/file_storage/moving.py index cb3e1193..592be437 100644 --- a/pillar/api/file_storage/moving.py +++ b/pillar/api/file_storage/moving.py @@ -29,7 +29,7 @@ def change_file_storage_backend(file_id, dest_backend): Files on the original backend are not deleted automatically. """ - dest_backend = unicode(dest_backend) + dest_backend = str(dest_backend) file_id = ObjectId(file_id) # Fetch file document diff --git a/pillar/api/local_auth.py b/pillar/api/local_auth.py index 41d2bb66..0febed98 100644 --- a/pillar/api/local_auth.py +++ b/pillar/api/local_auth.py @@ -87,7 +87,7 @@ def generate_and_store_token(user_id, days=15, prefix=''): def hash_password(password, salt): - if isinstance(salt, unicode): + if isinstance(salt, str): salt = salt.encode('utf-8') encoded_password = base64.b64encode(hashlib.sha256(password).digest()) return bcrypt.hashpw(encoded_password, salt) diff --git a/pillar/api/nodes/__init__.py b/pillar/api/nodes/__init__.py index 5ac28ba6..fab323fa 100644 --- a/pillar/api/nodes/__init__.py +++ b/pillar/api/nodes/__init__.py @@ -1,7 +1,7 @@ import base64 import functools import logging -import urlparse +import urllib.parse import pymongo.errors import rsa.randnum @@ -20,7 +20,7 @@ from pillar.api.utils.gcs import update_file_name log = logging.getLogger(__name__) blueprint = Blueprint('nodes_api', __name__) -ROLES_FOR_SHARING = {u'subscriber', u'demo'} +ROLES_FOR_SHARING = {'subscriber', 'demo'} def only_for_node_type_decorator(*required_node_type_names): @@ -138,7 +138,7 @@ def make_world_gettable(node): log.debug('Ensuring the world can read node %s', node_id) world_perms = set(node.get('permissions', {}).get('world', [])) - world_perms.add(u'GET') + world_perms.add('GET') world_perms = list(world_perms) result = nodes_coll.update_one({'_id': node_id}, @@ -164,7 +164,7 @@ def create_short_code(node): def short_link_info(short_code): """Returns the short link info in a dict.""" - short_link = urlparse.urljoin(current_app.config['SHORT_LINK_BASE_URL'], short_code) + short_link = urllib.parse.urljoin(current_app.config['SHORT_LINK_BASE_URL'], short_code) return { 'short_code': short_code, @@ -349,7 +349,7 @@ def node_set_default_picture(node, original=None): # Find the colour map, defaulting to the first image map available. image_file_id = None for image in props.get('files', []): - if image_file_id is None or image.get('map_type') == u'color': + if image_file_id is None or image.get('map_type') == 'color': image_file_id = image.get('file') else: log.debug('Not setting default picture on node type %s content type %s', diff --git a/pillar/api/nodes/custom/comment.py b/pillar/api/nodes/custom/comment.py index 4842ce30..5d0a708a 100644 --- a/pillar/api/nodes/custom/comment.py +++ b/pillar/api/nodes/custom/comment.py @@ -11,20 +11,20 @@ from pillar.api.utils import authorization, authentication, jsonify from . import register_patch_handler log = logging.getLogger(__name__) -ROLES_FOR_COMMENT_VOTING = {u'subscriber', u'demo'} -COMMENT_VOTING_OPS = {u'upvote', u'downvote', u'revoke'} -VALID_COMMENT_OPERATIONS = COMMENT_VOTING_OPS.union({u'edit'}) +ROLES_FOR_COMMENT_VOTING = {'subscriber', 'demo'} +COMMENT_VOTING_OPS = {'upvote', 'downvote', 'revoke'} +VALID_COMMENT_OPERATIONS = COMMENT_VOTING_OPS.union({'edit'}) -@register_patch_handler(u'comment') +@register_patch_handler('comment') def patch_comment(node_id, patch): assert_is_valid_patch(node_id, patch) user_id = authentication.current_user_id() - if patch[u'op'] in COMMENT_VOTING_OPS: + if patch['op'] in COMMENT_VOTING_OPS: result, node = vote_comment(user_id, node_id, patch) else: - assert patch[u'op'] == u'edit', 'Invalid patch operation %s' % patch[u'op'] + assert patch['op'] == 'edit', 'Invalid patch operation %s' % patch['op'] result, node = edit_comment(user_id, node_id, patch) return jsonify({'_status': 'OK', @@ -95,9 +95,9 @@ def vote_comment(user_id, node_id, patch): return update actions = { - u'upvote': upvote, - u'downvote': downvote, - u'revoke': revoke, + 'upvote': upvote, + 'downvote': downvote, + 'revoke': revoke, } action = actions[patch['op']] mongo_update = action() @@ -141,7 +141,7 @@ def edit_comment(user_id, node_id, patch): log.warning('User %s wanted to patch non-existing node %s' % (user_id, node_id)) raise wz_exceptions.NotFound('Node %s not found' % node_id) - if node['user'] != user_id and not authorization.user_has_role(u'admin'): + if node['user'] != user_id and not authorization.user_has_role('admin'): raise wz_exceptions.Forbidden('You can only edit your own comments.') # Use Eve to PATCH this node, as that also updates the etag. @@ -173,8 +173,8 @@ def assert_is_valid_patch(node_id, patch): raise wz_exceptions.BadRequest("PATCH should have a key 'op' indicating the operation.") if op not in VALID_COMMENT_OPERATIONS: - raise wz_exceptions.BadRequest(u'Operation should be one of %s', - u', '.join(VALID_COMMENT_OPERATIONS)) + raise wz_exceptions.BadRequest('Operation should be one of %s', + ', '.join(VALID_COMMENT_OPERATIONS)) if op not in COMMENT_VOTING_OPS: # We can't check here, we need the node owner for that. diff --git a/pillar/api/projects/hooks.py b/pillar/api/projects/hooks.py index ab417e52..4a4bdcf1 100644 --- a/pillar/api/projects/hooks.py +++ b/pillar/api/projects/hooks.py @@ -28,7 +28,7 @@ def before_inserting_projects(items): """ # Allow admin users to do whatever they want. - if user_has_role(u'admin'): + if user_has_role('admin'): return for item in items: @@ -70,7 +70,7 @@ def protect_sensitive_fields(document, original): """When not logged in as admin, prevents update to certain fields.""" # Allow admin users to do whatever they want. - if user_has_role(u'admin'): + if user_has_role('admin'): return def revert(name): diff --git a/pillar/api/projects/routes.py b/pillar/api/projects/routes.py index 57a53d04..f312059d 100644 --- a/pillar/api/projects/routes.py +++ b/pillar/api/projects/routes.py @@ -16,7 +16,7 @@ blueprint_api = Blueprint('projects_api', __name__) @blueprint_api.route('/create', methods=['POST']) -@authorization.require_login(require_roles={u'admin', u'subscriber', u'demo'}) +@authorization.require_login(require_roles={'admin', 'subscriber', 'demo'}) def create_project(overrides=None): """Creates a new project.""" @@ -65,7 +65,7 @@ def project_manage_users(): project = projects_collection.find_one({'_id': project_id}) # Check if the current_user is owner of the project, or removing themselves. - if not authorization.user_has_role(u'admin'): + if not authorization.user_has_role('admin'): remove_self = target_user_id == current_user_id and action == 'remove' if project['user'] != current_user_id and not remove_self: utils.abort_with_error(403) diff --git a/pillar/api/service.py b/pillar/api/service.py index da544e7a..ecd03a80 100644 --- a/pillar/api/service.py +++ b/pillar/api/service.py @@ -13,7 +13,7 @@ blueprint = Blueprint('service', __name__) log = logging.getLogger(__name__) signal_user_changed_role = blinker.NamedSignal('badger:user_changed_role') -ROLES_WITH_GROUPS = {u'admin', u'demo', u'subscriber'} +ROLES_WITH_GROUPS = {'admin', 'demo', 'subscriber'} # Map of role name to group ID, for the above groups. role_to_group_id = {} @@ -38,7 +38,7 @@ def fetch_role_to_group_id_map(): @blueprint.route('/badger', methods=['POST']) -@authorization.require_login(require_roles={u'service', u'badger'}, require_all=True) +@authorization.require_login(require_roles={'service', 'badger'}, require_all=True) def badger(): if request.mimetype != 'application/json': log.debug('Received %s instead of application/json', request.mimetype) @@ -117,7 +117,7 @@ def do_badger(action, user_email, role): @blueprint.route('/urler/', methods=['GET']) -@authorization.require_login(require_roles={u'service', u'urler'}, require_all=True) +@authorization.require_login(require_roles={'service', 'urler'}, require_all=True) def urler(project_id): """Returns the URL of any project.""" @@ -189,7 +189,7 @@ def create_service_account(email, roles, service, update_existing=None): raise ValueError('User %s already exists' % email) # Compute the new roles, and assign. - roles = list(set(roles).union({u'service'}).union(user['roles'])) + roles = list(set(roles).union({'service'}).union(user['roles'])) user['roles'] = list(roles) # Let the caller perform any required updates. @@ -204,7 +204,7 @@ def create_service_account(email, roles, service, update_existing=None): expected_status = 200 else: # Create a user with the correct roles. - roles = list(set(roles).union({u'service'})) + roles = list(set(roles).union({'service'})) user = {'username': email, 'groups': [], 'roles': roles, diff --git a/pillar/api/users/hooks.py b/pillar/api/users/hooks.py index 6a285ccf..24de19b6 100644 --- a/pillar/api/users/hooks.py +++ b/pillar/api/users/hooks.py @@ -60,7 +60,7 @@ def check_user_access(request, lookup): current_user_id = current_user['user_id'] if current_user else None # Admins can do anything and get everything, except the 'auth' block. - if user_has_role(u'admin'): + if user_has_role('admin'): return if not lookup and not current_user: @@ -74,7 +74,7 @@ def check_user_access(request, lookup): def check_put_access(request, lookup): """Only allow PUT to the current user, or all users if admin.""" - if user_has_role(u'admin'): + if user_has_role('admin'): return current_user = g.get('current_user') @@ -94,7 +94,7 @@ def after_fetching_user(user): current_user_id = current_user['user_id'] if current_user else None # Admins can do anything and get everything, except the 'auth' block. - if user_has_role(u'admin'): + if user_has_role('admin'): return # Only allow full access to the current user. diff --git a/pillar/api/utils/__init__.py b/pillar/api/utils/__init__.py index 59c36bdd..452e6ef5 100644 --- a/pillar/api/utils/__init__.py +++ b/pillar/api/utils/__init__.py @@ -1,7 +1,7 @@ import copy import hashlib import json -import urllib +import urllib.request, urllib.parse, urllib.error import datetime import functools @@ -103,7 +103,7 @@ def skip_when_testing(func): @functools.wraps(func) def wrapper(*args, **kwargs): if current_app.config['TESTING']: - log.debug('Skipping call to %s(...) due to TESTING', func.func_name) + log.debug('Skipping call to %s(...) due to TESTING', func.__name__) return None return func(*args, **kwargs) @@ -145,19 +145,18 @@ def gravatar(email, size=64): parameters = {'s': str(size), 'd': 'mm'} return "https://www.gravatar.com/avatar/" + \ hashlib.md5(str(email)).hexdigest() + \ - "?" + urllib.urlencode(parameters) + "?" + urllib.parse.urlencode(parameters) class MetaFalsey(type): - def __nonzero__(cls): + def __bool__(cls): return False __bool__ = __nonzero__ # for Python 3 -class DoesNotExist(object): +class DoesNotExist(object, metaclass=MetaFalsey): """Returned as value by doc_diff if a value does not exist.""" - __metaclass__ = MetaFalsey def doc_diff(doc1, doc2, falsey_is_equal=True): @@ -175,7 +174,7 @@ def doc_diff(doc1, doc2, falsey_is_equal=True): """ for key in set(doc1.keys()).union(set(doc2.keys())): - if isinstance(key, basestring) and key[0] == u'_': + if isinstance(key, str) and key[0] == '_': continue val1 = doc1.get(key, DoesNotExist) diff --git a/pillar/api/utils/authentication.py b/pillar/api/utils/authentication.py index 66003dc9..b58fd568 100644 --- a/pillar/api/utils/authentication.py +++ b/pillar/api/utils/authentication.py @@ -124,7 +124,7 @@ def store_token(user_id, token, token_expiry, oauth_subclient_id=False): :returns: the token document from MongoDB """ - assert isinstance(token, (str, unicode)), 'token must be string type, not %r' % type(token) + assert isinstance(token, str), 'token must be string type, not %r' % type(token) token_data = { 'user': user_id, diff --git a/pillar/api/utils/authorization.py b/pillar/api/utils/authorization.py index 9d3c77d4..0902e03a 100644 --- a/pillar/api/utils/authorization.py +++ b/pillar/api/utils/authorization.py @@ -238,22 +238,22 @@ def merge_permissions(*args): asdict0 = {permission[field_name]: permission['methods'] for permission in from0} asdict1 = {permission[field_name]: permission['methods'] for permission in from1} - keys = set(asdict0.keys() + asdict1.keys()) + keys = set(asdict0.keys()).union(set(asdict1.keys())) for key in maybe_sorted(keys): methods0 = asdict0.get(key, []) methods1 = asdict1.get(key, []) methods = maybe_sorted(set(methods0).union(set(methods1))) - effective.setdefault(plural_name, []).append({field_name: key, u'methods': methods}) + effective.setdefault(plural_name, []).append({field_name: key, 'methods': methods}) - merge(u'user') - merge(u'group') + merge('user') + merge('group') # Gather permissions for world world0 = args[0].get('world', []) world1 = args[1].get('world', []) world_methods = set(world0).union(set(world1)) if world_methods: - effective[u'world'] = maybe_sorted(world_methods) + effective['world'] = maybe_sorted(world_methods) # Recurse for longer merges if len(args) > 2: @@ -380,4 +380,4 @@ def user_matches_roles(require_roles=set(), def is_admin(user): """Returns True iff the given user has the admin role.""" - return user_has_role(u'admin', user) + return user_has_role('admin', user) diff --git a/pillar/api/utils/gcs.py b/pillar/api/utils/gcs.py index 713e1cbe..302c5a05 100644 --- a/pillar/api/utils/gcs.py +++ b/pillar/api/utils/gcs.py @@ -173,7 +173,7 @@ class GoogleCloudStorageBucket(Bucket): """Set the ContentDisposition metadata so that when a file is downloaded it has a human-readable name. """ - blob.content_disposition = u'attachment; filename="{0}"'.format(name) + blob.content_disposition = 'attachment; filename="{0}"'.format(name) blob.patch() def copy_blob(self, blob, to_bucket): @@ -215,11 +215,11 @@ def update_file_name(node): if node['properties'].get('status', '') == 'processing': return - def _format_name(name, override_ext, size=None, map_type=u''): + def _format_name(name, override_ext, size=None, map_type=''): root, _ = os.path.splitext(name) - size = u'-{}'.format(size) if size else u'' - map_type = u'-{}'.format(map_type) if map_type else u'' - return u'{}{}{}{}'.format(root, size, map_type, override_ext) + size = '-{}'.format(size) if size else '' + map_type = '-{}'.format(map_type) if map_type else '' + return '{}{}{}{}'.format(root, size, map_type, override_ext) def _update_name(file_id, file_props): files_collection = current_app.data.driver.db['files'] @@ -229,7 +229,7 @@ def update_file_name(node): return # For textures -- the map type should be part of the name. - map_type = file_props.get('map_type', u'') + map_type = file_props.get('map_type', '') storage = GoogleCloudStorageBucket(str(node['project'])) blob = storage.Get(file_doc['file_path'], to_dict=False) diff --git a/pillar/api/utils/imaging.py b/pillar/api/utils/imaging.py index 10d93496..af92480a 100644 --- a/pillar/api/utils/imaging.py +++ b/pillar/api/utils/imaging.py @@ -21,7 +21,7 @@ def generate_local_thumbnails(name_base, src): save_to_base, _ = os.path.splitext(src) name_base, _ = os.path.splitext(name_base) - for size, settings in thumbnail_settings.iteritems(): + for size, settings in thumbnail_settings.items(): dst = '{0}-{1}{2}'.format(save_to_base, size, '.jpg') name = '{0}-{1}{2}'.format(name_base, size, '.jpg') @@ -143,7 +143,7 @@ def get_video_data(filepath): res_y=video_stream['height'], ) if video_stream['sample_aspect_ratio'] != '1:1': - print '[warning] Pixel aspect ratio is not square!' + print('[warning] Pixel aspect ratio is not square!') return outdata @@ -190,14 +190,14 @@ def ffmpeg_encode(src, format, res_y=720): dst = os.path.splitext(src) dst = "{0}-{1}p.{2}".format(dst[0], res_y, format) args.append(dst) - print "Encoding {0} to {1}".format(src, format) + print("Encoding {0} to {1}".format(src, format)) returncode = subprocess.call([current_app.config['BIN_FFMPEG']] + args) if returncode == 0: - print "Successfully encoded {0}".format(dst) + print("Successfully encoded {0}".format(dst)) else: - print "Error during encode" - print "Code: {0}".format(returncode) - print "Command: {0}".format(current_app.config['BIN_FFMPEG'] + " " + " ".join(args)) + print("Error during encode") + print("Code: {0}".format(returncode)) + print("Command: {0}".format(current_app.config['BIN_FFMPEG'] + " " + " ".join(args))) dst = None # return path of the encoded video return dst diff --git a/pillar/auth/__init__.py b/pillar/auth/__init__.py index 313ac30d..72167a83 100644 --- a/pillar/auth/__init__.py +++ b/pillar/auth/__init__.py @@ -55,11 +55,11 @@ def _load_user(token): login_user = UserClass(token) login_user.email = db_user['email'] - login_user.objectid = unicode(db_user['_id']) + login_user.objectid = str(db_user['_id']) login_user.username = db_user['username'] login_user.gravatar = utils.gravatar(db_user['email']) login_user.roles = db_user.get('roles', []) - login_user.groups = [unicode(g) for g in db_user['groups'] or ()] + login_user.groups = [str(g) for g in db_user['groups'] or ()] login_user.full_name = db_user.get('full_name', '') return login_user diff --git a/pillar/cli.py b/pillar/cli.py index 3842b858..57bf2a96 100644 --- a/pillar/cli.py +++ b/pillar/cli.py @@ -3,7 +3,7 @@ Run commands with 'flask ' """ -from __future__ import print_function, division + import copy import logging @@ -57,7 +57,7 @@ def setup_db(admin_email): print("Created user {0}".format(user['_id'])) # Create a default project by faking a POST request. - with current_app.test_request_context(data={'project_name': u'Default Project'}): + with current_app.test_request_context(data={'project_name': 'Default Project'}): from flask import g from pillar.api.projects import routes as proj_routes @@ -89,7 +89,7 @@ def find_duplicate_users(): blender_id = blender_ids[0] found_users[blender_id].append(user) - for blender_id, users in found_users.iteritems(): + for blender_id, users in found_users.items(): if len(users) == 1: continue @@ -344,14 +344,14 @@ def create_badger_account(email, badges): this account can assign and revoke. """ - create_service_account(email, [u'badger'], {'badger': badges.strip().split()}) + create_service_account(email, ['badger'], {'badger': badges.strip().split()}) @manager_setup.command def create_urler_account(email): """Creates a new service account that can fetch all project URLs.""" - create_service_account(email, [u'urler'], {}) + create_service_account(email, ['urler'], {}) @manager_setup.command @@ -704,7 +704,7 @@ def upgrade_attachment_schema(proj_url=None, all_projects=False): 'properties.attachments': {'$exists': True}, }) for node in nodes: - attachments = node[u'properties'][u'attachments'] + attachments = node['properties']['attachments'] if isinstance(attachments, dict): # This node has already been upgraded. continue @@ -713,9 +713,9 @@ def upgrade_attachment_schema(proj_url=None, all_projects=False): new_atts = {} for field_info in attachments: for attachment in field_info.get('files', []): - new_atts[attachment[u'slug']] = {u'oid': attachment[u'file']} + new_atts[attachment['slug']] = {'oid': attachment['file']} - node[u'properties'][u'attachments'] = new_atts + node['properties']['attachments'] = new_atts # Use Eve to PUT, so we have schema checking. db_node = remove_private_keys(node) @@ -774,11 +774,11 @@ def create_blog(proj_url): blog = nodes_coll.find_one({'node_type': 'blog', 'project': proj_id}) if not blog: blog = { - u'node_type': node_type_blog['name'], - u'name': u'Blog', - u'description': u'', - u'properties': {}, - u'project': proj_id, + 'node_type': node_type_blog['name'], + 'name': 'Blog', + 'description': '', + 'properties': {}, + 'project': proj_id, } r, _, _, status = post_internal('nodes', blog) if status != 201: diff --git a/pillar/config.py b/pillar/config.py index 3f7696c2..4f04f207 100644 --- a/pillar/config.py +++ b/pillar/config.py @@ -81,7 +81,7 @@ FILE_LINK_VALIDITY = defaultdict( ) # Roles with full GET-access to all variations of files. -FULL_FILE_ACCESS_ROLES = {u'admin', u'subscriber', u'demo'} +FULL_FILE_ACCESS_ROLES = {'admin', 'subscriber', 'demo'} # Client and Subclient IDs for Blender ID BLENDER_ID_CLIENT_ID = 'SPECIAL-SNOWFLAKE-57' @@ -119,7 +119,7 @@ SHORT_CODE_LENGTH = 6 # characters # People are allowed this many bytes per uploaded file. FILESIZE_LIMIT_BYTES_NONSUBS = 32 * 2 ** 20 # Unless they have one of those roles. -ROLES_FOR_UNLIMITED_UPLOADS = {u'subscriber', u'demo', u'admin'} +ROLES_FOR_UNLIMITED_UPLOADS = {'subscriber', 'demo', 'admin'} ############################################# diff --git a/pillar/extension.py b/pillar/extension.py index 34f7bfed..80732c99 100644 --- a/pillar/extension.py +++ b/pillar/extension.py @@ -18,9 +18,7 @@ can then be registered to the application at app creation time: import abc -class PillarExtension(object): - __metaclass__ = abc.ABCMeta - +class PillarExtension(object, metaclass=abc.ABCMeta): @abc.abstractproperty def name(self): """The name of this extension. diff --git a/pillar/markdown.py b/pillar/markdown.py index 4b06cdad..a0a39233 100644 --- a/pillar/markdown.py +++ b/pillar/markdown.py @@ -3,8 +3,6 @@ This is for user-generated stuff, like comments. """ -from __future__ import absolute_import - import bleach import CommonMark diff --git a/pillar/sdk.py b/pillar/sdk.py index 47017e7b..bb8260cc 100644 --- a/pillar/sdk.py +++ b/pillar/sdk.py @@ -1,7 +1,7 @@ """PillarSDK subclass for direct Flask-internal calls.""" import logging -import urlparse +import urllib.parse from flask import current_app import pillarsdk @@ -23,8 +23,8 @@ class FlaskInternalApi(pillarsdk.Api): self.requests_to_flask_kwargs(kwargs) # Leave out the query string and fragment from the URL. - split_url = urlparse.urlsplit(url) - path = urlparse.urlunsplit(split_url[:-2] + (None, None)) + split_url = urllib.parse.urlsplit(url) + path = urllib.parse.urlunsplit(split_url[:-2] + (None, None)) try: response = client.open(path=path, query_string=split_url.query, method=method, **kwargs) diff --git a/pillar/tests/__init__.py b/pillar/tests/__init__.py index d84ce060..9323c258 100644 --- a/pillar/tests/__init__.py +++ b/pillar/tests/__init__.py @@ -1,9 +1,5 @@ # -*- encoding: utf-8 -*- -from __future__ import print_function - -from __future__ import absolute_import - import base64 import copy import json @@ -16,7 +12,7 @@ import sys try: from urllib.parse import urlencode except ImportError: - from urllib import urlencode + from urllib.parse import urlencode from bson import ObjectId, tz_util @@ -46,7 +42,7 @@ MY_PATH = os.path.dirname(os.path.abspath(__file__)) TEST_EMAIL_USER = 'koro' TEST_EMAIL_ADDRESS = '%s@testing.blender.org' % TEST_EMAIL_USER -TEST_FULL_NAME = u'врач Сергей' +TEST_FULL_NAME = 'врач Сергей' TEST_SUBCLIENT_TOKEN = 'my-subclient-token-for-pillar' BLENDER_ID_USER_RESPONSE = {'status': 'success', 'user': {'email': TEST_EMAIL_ADDRESS, @@ -207,9 +203,9 @@ class AbstractPillarTest(TestMinimal): 'roles': list(roles), 'settings': {'email_communications': 1}, 'auth': [{'token': '', - 'user_id': unicode(ctd.BLENDER_ID_TEST_USERID), + 'user_id': str(ctd.BLENDER_ID_TEST_USERID), 'provider': 'blender-id'}], - 'full_name': u'คนรักของผัดไทย', + 'full_name': 'คนรักของผัดไทย', 'email': TEST_EMAIL_ADDRESS }) diff --git a/pillar/tests/common_test_data.py b/pillar/tests/common_test_data.py index e39ad214..8f3a97cb 100644 --- a/pillar/tests/common_test_data.py +++ b/pillar/tests/common_test_data.py @@ -11,55 +11,55 @@ EXAMPLE_PROJECT_READONLY_GROUP2_ID = ObjectId('564733b56dcaf85da2faee8a') EXAMPLE_PROJECT_ID = ObjectId('5672beecc0261b2005ed1a33') EXAMPLE_PROJECT_OWNER_ID = ObjectId('552b066b41acdf5dec4436f2') -EXAMPLE_FILE = {u'_id': ObjectId('5672e2c1c379cf0007b31995'), - u'_updated': datetime.datetime(2016, 3, 25, 10, 28, 24, tzinfo=tz_util.utc), - u'height': 2048, - u'name': 'c2a5c897769ce1ef0eb10f8fa1c472bcb8e2d5a4.png', u'format': 'png', - u'variations': [ - {u'format': 'jpg', u'height': 160, u'width': 160, u'length': 8558, - u'link': 'http://localhost:8002/file-variant-h', u'content_type': 'image/jpeg', - u'md5': '--', u'file_path': 'c2a5c897769ce1ef0eb10f8fa1c472bcb8e2d5a4-b.jpg', - u'size': 'b'}, - {u'format': 'jpg', u'height': 2048, u'width': 2048, u'length': 819569, - u'link': 'http://localhost:8002/file-variant-h', u'content_type': 'image/jpeg', - u'md5': '--', u'file_path': 'c2a5c897769ce1ef0eb10f8fa1c472bcb8e2d5a4-h.jpg', - u'size': 'h'}, - {u'format': 'jpg', u'height': 64, u'width': 64, u'length': 8195, - u'link': 'http://localhost:8002/file-variant-t', u'content_type': 'image/jpeg', - u'md5': '--', u'file_path': 'c2a5c897769ce1ef0eb10f8fa1c472bcb8e2d5a4-t.jpg', - u'size': 't'}, +EXAMPLE_FILE = {'_id': ObjectId('5672e2c1c379cf0007b31995'), + '_updated': datetime.datetime(2016, 3, 25, 10, 28, 24, tzinfo=tz_util.utc), + 'height': 2048, + 'name': 'c2a5c897769ce1ef0eb10f8fa1c472bcb8e2d5a4.png', 'format': 'png', + 'variations': [ + {'format': 'jpg', 'height': 160, 'width': 160, 'length': 8558, + 'link': 'http://localhost:8002/file-variant-h', 'content_type': 'image/jpeg', + 'md5': '--', 'file_path': 'c2a5c897769ce1ef0eb10f8fa1c472bcb8e2d5a4-b.jpg', + 'size': 'b'}, + {'format': 'jpg', 'height': 2048, 'width': 2048, 'length': 819569, + 'link': 'http://localhost:8002/file-variant-h', 'content_type': 'image/jpeg', + 'md5': '--', 'file_path': 'c2a5c897769ce1ef0eb10f8fa1c472bcb8e2d5a4-h.jpg', + 'size': 'h'}, + {'format': 'jpg', 'height': 64, 'width': 64, 'length': 8195, + 'link': 'http://localhost:8002/file-variant-t', 'content_type': 'image/jpeg', + 'md5': '--', 'file_path': 'c2a5c897769ce1ef0eb10f8fa1c472bcb8e2d5a4-t.jpg', + 'size': 't'}, ], - u'filename': 'brick_dutch_soft_bump.png', - u'project': EXAMPLE_PROJECT_ID, - u'width': 2048, u'length': 6227670, - u'user': ObjectId('56264fc4fa3a250344bd10c5'), - u'content_type': 'image/png', - u'_etag': '044ce3aede2e123e261c0d8bd77212f264d4f7b0', - u'_created': datetime.datetime(2015, 12, 17, 16, 28, 49, tzinfo=tz_util.utc), - u'md5': '', - u'file_path': 'c2a5c897769ce1ef0eb10f8fa1c472bcb8e2d5a4.png', - u'backend': 'pillar', - u'link': 'http://localhost:8002/file', - u'link_expires': datetime.datetime(2016, 3, 22, 9, 28, 22, tzinfo=tz_util.utc)} + 'filename': 'brick_dutch_soft_bump.png', + 'project': EXAMPLE_PROJECT_ID, + 'width': 2048, 'length': 6227670, + 'user': ObjectId('56264fc4fa3a250344bd10c5'), + 'content_type': 'image/png', + '_etag': '044ce3aede2e123e261c0d8bd77212f264d4f7b0', + '_created': datetime.datetime(2015, 12, 17, 16, 28, 49, tzinfo=tz_util.utc), + 'md5': '', + 'file_path': 'c2a5c897769ce1ef0eb10f8fa1c472bcb8e2d5a4.png', + 'backend': 'pillar', + 'link': 'http://localhost:8002/file', + 'link_expires': datetime.datetime(2016, 3, 22, 9, 28, 22, tzinfo=tz_util.utc)} EXAMPLE_PROJECT = { - u'_created': datetime.datetime(2015, 12, 17, 13, 22, 56, tzinfo=tz_util.utc), - u'_etag': u'cc4643e98d3606f87bbfaaa200bfbae941b642f3', - u'_id': EXAMPLE_PROJECT_ID, - u'_updated': datetime.datetime(2016, 1, 7, 18, 59, 4, tzinfo=tz_util.utc), - u'category': u'assets', - u'description': u'Welcome to this curated collection of Blender Institute textures and image ' - u'resources. This collection is an on-going project, as with each project we ' - u'create a number of textures based on our own resources (photographs, scans, ' - u'etc.) or made completely from scratch. At the moment you can find all the ' - u'textures from the past Open Projects that were deemed re-usable. \r\n\r\n' - u'People who have contributed to these textures:\r\n\r\nAndrea Weikert, Andy ' - u'Goralczyk, Basse Salmela, Ben Dansie, Campbell Barton, Enrico Valenza, Ian ' - u'Hubert, Kjartan Tysdal, Manu J\xe4rvinen, Massimiliana Pulieso, Matt Ebb, ' - u'Pablo Vazquez, Rob Tuytel, Roland Hess, Sarah Feldlaufer, S\xf6nke M\xe4ter', - u'is_private': False, - u'name': u'Unittest project', - u'node_types': [ + '_created': datetime.datetime(2015, 12, 17, 13, 22, 56, tzinfo=tz_util.utc), + '_etag': 'cc4643e98d3606f87bbfaaa200bfbae941b642f3', + '_id': EXAMPLE_PROJECT_ID, + '_updated': datetime.datetime(2016, 1, 7, 18, 59, 4, tzinfo=tz_util.utc), + 'category': 'assets', + 'description': 'Welcome to this curated collection of Blender Institute textures and image ' + 'resources. This collection is an on-going project, as with each project we ' + 'create a number of textures based on our own resources (photographs, scans, ' + 'etc.) or made completely from scratch. At the moment you can find all the ' + 'textures from the past Open Projects that were deemed re-usable. \r\n\r\n' + 'People who have contributed to these textures:\r\n\r\nAndrea Weikert, Andy ' + 'Goralczyk, Basse Salmela, Ben Dansie, Campbell Barton, Enrico Valenza, Ian ' + 'Hubert, Kjartan Tysdal, Manu J\xe4rvinen, Massimiliana Pulieso, Matt Ebb, ' + 'Pablo Vazquez, Rob Tuytel, Roland Hess, Sarah Feldlaufer, S\xf6nke M\xe4ter', + 'is_private': False, + 'name': 'Unittest project', + 'node_types': [ PILLAR_NAMED_NODE_TYPES['group_texture'], PILLAR_NAMED_NODE_TYPES['group'], PILLAR_NAMED_NODE_TYPES['asset'], @@ -69,36 +69,36 @@ EXAMPLE_PROJECT = { PILLAR_NAMED_NODE_TYPES['post'], PILLAR_NAMED_NODE_TYPES['texture'], ], - u'nodes_blog': [], - u'nodes_featured': [], - u'nodes_latest': [], - u'permissions': {u'groups': [{u'group': EXAMPLE_ADMIN_GROUP_ID, - u'methods': [u'GET', u'POST', u'PUT', u'DELETE']}], - u'users': [], - u'world': [u'GET']}, - u'picture_header': ObjectId('5673f260c379cf0007b31bc4'), - u'picture_square': ObjectId('5673f256c379cf0007b31bc3'), - u'status': u'published', - u'summary': u'Texture collection from all Blender Institute open projects.', - u'url': u'textures', - u'user': EXAMPLE_PROJECT_OWNER_ID} + 'nodes_blog': [], + 'nodes_featured': [], + 'nodes_latest': [], + 'permissions': {'groups': [{'group': EXAMPLE_ADMIN_GROUP_ID, + 'methods': ['GET', 'POST', 'PUT', 'DELETE']}], + 'users': [], + 'world': ['GET']}, + 'picture_header': ObjectId('5673f260c379cf0007b31bc4'), + 'picture_square': ObjectId('5673f256c379cf0007b31bc3'), + 'status': 'published', + 'summary': 'Texture collection from all Blender Institute open projects.', + 'url': 'textures', + 'user': EXAMPLE_PROJECT_OWNER_ID} EXAMPLE_NODE = { - u'_id': ObjectId('572761099837730efe8e120d'), - u'picture': ObjectId('572761f39837730efe8e1210'), - u'description': u'', - u'node_type': u'asset', - u'user': ObjectId('57164ca1983773118cbaf779'), - u'properties': { - u'status': u'published', - u'content_type': u'image', - u'file': ObjectId('572761129837730efe8e120e') + '_id': ObjectId('572761099837730efe8e120d'), + 'picture': ObjectId('572761f39837730efe8e1210'), + 'description': '', + 'node_type': 'asset', + 'user': ObjectId('57164ca1983773118cbaf779'), + 'properties': { + 'status': 'published', + 'content_type': 'image', + 'file': ObjectId('572761129837730efe8e120e') }, - u'_updated': datetime.datetime(2016, 5, 2, 14, 19, 58, 0, tzinfo=tz_util.utc), - u'name': u'Image test', - u'project': EXAMPLE_PROJECT_ID, - u'_created': datetime.datetime(2016, 5, 2, 14, 19, 37, 0, tzinfo=tz_util.utc), - u'_etag': u'6b8589b42c880e3626f43f3e82a5c5b946742687' + '_updated': datetime.datetime(2016, 5, 2, 14, 19, 58, 0, tzinfo=tz_util.utc), + 'name': 'Image test', + 'project': EXAMPLE_PROJECT_ID, + '_created': datetime.datetime(2016, 5, 2, 14, 19, 37, 0, tzinfo=tz_util.utc), + '_etag': '6b8589b42c880e3626f43f3e82a5c5b946742687' } BLENDER_ID_TEST_USERID = 1533 diff --git a/pillar/tests/config_testing.py b/pillar/tests/config_testing.py index dbb020de..f56facac 100644 --- a/pillar/tests/config_testing.py +++ b/pillar/tests/config_testing.py @@ -8,4 +8,4 @@ TESTING = True CDN_STORAGE_USER = 'u41508580125621' FILESIZE_LIMIT_BYTES_NONSUBS = 20 * 2 ** 10 -ROLES_FOR_UNLIMITED_UPLOADS = {u'subscriber', u'demo', u'admin'} +ROLES_FOR_UNLIMITED_UPLOADS = {'subscriber', 'demo', 'admin'} diff --git a/pillar/web/jinja.py b/pillar/web/jinja.py index 737e3d29..9a374d97 100644 --- a/pillar/web/jinja.py +++ b/pillar/web/jinja.py @@ -1,7 +1,5 @@ """Our custom Jinja filters and other template stuff.""" -from __future__ import absolute_import - import logging import flask diff --git a/pillar/web/main/routes.py b/pillar/web/main/routes.py index 4947be8e..39e2b04f 100644 --- a/pillar/web/main/routes.py +++ b/pillar/web/main/routes.py @@ -304,8 +304,8 @@ def feeds_blogs(): updated = post._updated if post._updated else post._created url = url_for_node(node=post) content = post.properties.content[:500] - content = u'

{0}... Read more

'.format(content, url) - feed.add(post.name, unicode(content), + content = '

{0}... Read more

'.format(content, url) + feed.add(post.name, str(content), content_type='html', author=author, url=url, diff --git a/pillar/web/nodes/attachments.py b/pillar/web/nodes/attachments.py index b336b85f..69ea7f36 100644 --- a/pillar/web/nodes/attachments.py +++ b/pillar/web/nodes/attachments.py @@ -25,7 +25,7 @@ def render_attachments(node, field_value): node_attachments = node.properties.attachments or {} if isinstance(node_attachments, list): log.warning('Old-style attachments property found on node %s. Ignoring them, ' - 'will result in attachments not being found.', node[u'_id']) + 'will result in attachments not being found.', node['_id']) return field_value if not node_attachments: @@ -37,7 +37,7 @@ def render_attachments(node, field_value): try: att = node_attachments[slug] except KeyError: - return u'[attachment "%s" not found]' % slug + return '[attachment "%s" not found]' % slug return render_attachment(att) return shortcode_re.sub(replace, field_value) @@ -46,8 +46,8 @@ def render_attachments(node, field_value): def render_attachment(attachment): """Renders an attachment as HTML""" - oid = ObjectId(attachment[u'oid']) - collection = attachment.collection or u'files' + oid = ObjectId(attachment['oid']) + collection = attachment.collection or 'files' renderers = { 'files': render_attachment_file @@ -56,8 +56,8 @@ def render_attachment(attachment): try: renderer = renderers[collection] except KeyError: - log.error(u'Unable to render attachment from collection %s', collection) - return u'Unable to render attachment' + log.error('Unable to render attachment from collection %s', collection) + return 'Unable to render attachment' return renderer(attachment) @@ -66,7 +66,7 @@ def render_attachment_file(attachment): """Renders a file attachment.""" api = system_util.pillar_api() - sdk_file = pillarsdk.File.find(attachment[u'oid'], api=api) + sdk_file = pillarsdk.File.find(attachment['oid'], api=api) file_renderers = { 'image': render_attachment_file_image @@ -119,7 +119,7 @@ def attachment_form_group_set_data(db_prop_value, schema_prop, field_list): while len(field_list): field_list.pop_entry() - for slug, att_data in sorted(db_prop_value.iteritems()): + for slug, att_data in sorted(db_prop_value.items()): file_select_form_group = _attachment_build_single_field(schema_prop) subform = file_select_form_group() diff --git a/pillar/web/nodes/custom/comments.py b/pillar/web/nodes/custom/comments.py index fcb89e49..a44ac645 100644 --- a/pillar/web/nodes/custom/comments.py +++ b/pillar/web/nodes/custom/comments.py @@ -243,7 +243,7 @@ def comments_rate(comment_id, operation): """ - if operation not in {u'revoke', u'upvote', u'downvote'}: + if operation not in {'revoke', 'upvote', 'downvote'}: raise wz_exceptions.BadRequest('Invalid operation') api = system_util.pillar_api() diff --git a/pillar/web/nodes/forms.py b/pillar/web/nodes/forms.py index 6edaa9ef..00e97ec0 100644 --- a/pillar/web/nodes/forms.py +++ b/pillar/web/nodes/forms.py @@ -33,7 +33,7 @@ def iter_node_properties(node_type): node_schema = node_type['dyn_schema'].to_dict() form_schema = node_type['form_schema'].to_dict() - for prop_name, prop_schema in node_schema.iteritems(): + for prop_name, prop_schema in node_schema.items(): prop_fschema = form_schema.get(prop_name, {}) if not prop_fschema.get('visible', True): diff --git a/pillar/web/nodes/routes.py b/pillar/web/nodes/routes.py index 11ef6f6f..f39f1a93 100644 --- a/pillar/web/nodes/routes.py +++ b/pillar/web/nodes/routes.py @@ -142,7 +142,7 @@ def view(node_id): return 'GET' in node.permissions.world if current_user.is_authenticated: - allowed_roles = {u'subscriber', u'demo', u'admin'} + allowed_roles = {'subscriber', 'demo', 'admin'} return bool(allowed_roles.intersection(current_user.roles or ())) return False @@ -330,7 +330,7 @@ def edit(node_id): log.debug('set_properties(..., prefix=%r, set_data=%r) called', prefix, set_data) - for prop, schema_prop in dyn_schema.iteritems(): + for prop, schema_prop in dyn_schema.items(): prop_name = "{0}{1}".format(prefix, prop) if prop_name not in form: @@ -376,7 +376,7 @@ def edit(node_id): for file_data in db_prop_value: file_form_class = build_file_select_form(subschema) subform = file_form_class() - for key, value in file_data.iteritems(): + for key, value in file_data.items(): setattr(subform, key, value) field_list.append_entry(subform) @@ -481,7 +481,7 @@ def ensure_lists_exist_as_empty(node_doc, node_type): node_properties = node_doc.setdefault('properties', {}) - for prop, schema in node_type.dyn_schema.to_dict().iteritems(): + for prop, schema in node_type.dyn_schema.to_dict().items(): if schema['type'] != 'list': continue @@ -563,7 +563,7 @@ def redirect_to_context(node_id): def url_for_node(node_id=None, node=None): - assert isinstance(node_id, (basestring, type(None))) + assert isinstance(node_id, (str, type(None))) api = system_util.pillar_api() @@ -583,7 +583,7 @@ def url_for_node(node_id=None, node=None): # Import of custom modules (using the same nodes decorator) -import custom.comments -import custom.groups -import custom.storage -import custom.posts +from . import custom.comments +from . import custom.groups +from . import custom.storage +from . import custom.posts diff --git a/pillar/web/projects/routes.py b/pillar/web/projects/routes.py index 5f33bb45..676fd109 100644 --- a/pillar/web/projects/routes.py +++ b/pillar/web/projects/routes.py @@ -449,7 +449,7 @@ def edit(project_url): url=project.url, summary=project.summary, description=project.description, - is_private=u'GET' not in project.permissions.world, + is_private='GET' not in project.permissions.world, category=project.category, status=project.status, ) @@ -471,7 +471,7 @@ def edit(project_url): if form.is_private.data: project.permissions.world = [] else: - project.permissions.world = [u'GET'] + project.permissions.world = ['GET'] project.update(api=api) # Reattach the pictures @@ -721,7 +721,7 @@ def project_update_nodes_list(node, project_id=None, list_name='latest'): elif not project_id: return None project_id = node.project - if type(project_id) is not unicode: + if type(project_id) is not str: project_id = node.project._id api = system_util.pillar_api() project = Project.find(project_id, api=api) diff --git a/pillar/web/redirects/__init__.py b/pillar/web/redirects/__init__.py index c4afba1f..22200f53 100644 --- a/pillar/web/redirects/__init__.py +++ b/pillar/web/redirects/__init__.py @@ -1,6 +1,6 @@ import logging import string -import urlparse +import urllib.parse from flask import Blueprint, redirect, current_app from werkzeug.exceptions import NotFound @@ -54,7 +54,7 @@ def redirect_with_short_code(short_code): # Redirect to 'theatre' view for the node. url = url_for_node(node=node) - url = urlparse.urljoin(url, '?t') + url = urllib.parse.urljoin(url, '?t') log.debug('Found short code %s, redirecting to %s', short_code, url) return redirect(url, code=307) diff --git a/pillar/web/users/routes.py b/pillar/web/users/routes.py index badc50ed..c882395a 100644 --- a/pillar/web/users/routes.py +++ b/pillar/web/users/routes.py @@ -2,7 +2,7 @@ import json import logging import httplib2 # used by the oauth2 package import requests -import urlparse +import urllib.parse from flask import (abort, Blueprint, current_app, flash, redirect, render_template, request, session, url_for) @@ -274,15 +274,15 @@ def user_roles_update(user_id): groups = set(user.groups or []) if grant_subscriber: - roles.add(u'subscriber') + roles.add('subscriber') groups.add(group_subscriber._id) - elif u'admin' not in roles: + elif 'admin' not in roles: # Don't take away roles from admins. - roles.discard(u'subscriber') + roles.discard('subscriber') groups.discard(group_subscriber._id) if grant_demo: - roles.add(u'demo') + roles.add('demo') groups.add(group_demo._id) # Only send an API request when the user has actually changed @@ -316,7 +316,7 @@ def fetch_blenderid_user(): :rtype: dict """ - bid_url = urlparse.urljoin(current_app.config['BLENDER_ID_ENDPOINT'], 'api/user') + bid_url = urllib.parse.urljoin(current_app.config['BLENDER_ID_ENDPOINT'], 'api/user') log.debug('Fetching user info from %s', bid_url) try: diff --git a/pillar/web/utils/__init__.py b/pillar/web/utils/__init__.py index db5d2766..7848ce8b 100644 --- a/pillar/web/utils/__init__.py +++ b/pillar/web/utils/__init__.py @@ -1,6 +1,6 @@ import datetime import hashlib -import urllib +import urllib.request, urllib.parse, urllib.error import logging import traceback import sys @@ -52,7 +52,7 @@ def gravatar(email, size=64): parameters = {'s': str(size), 'd': 'mm'} return "https://www.gravatar.com/avatar/" + \ hashlib.md5(str(email)).hexdigest() + \ - "?" + urllib.urlencode(parameters) + "?" + urllib.parse.urlencode(parameters) def datetime_now(): @@ -73,7 +73,7 @@ def pretty_date(time, detail=False, now=None): # Normalize the 'time' parameter so it's always a datetime. if type(time) is int: time = datetime.datetime.fromtimestamp(time, tz=pillarsdk.utils.utc) - elif isinstance(time, basestring): + elif isinstance(time, str): time = dateutil.parser.parse(time) now = now or datetime.datetime.now(tz=time.tzinfo) @@ -184,10 +184,10 @@ def is_valid_id(some_id): :rtype: bool """ - if not isinstance(some_id, basestring): + if not isinstance(some_id, str): return False - if isinstance(some_id, unicode): + if isinstance(some_id, str): try: some_id = some_id.encode('ascii') except UnicodeEncodeError: diff --git a/pillar/web/utils/forms.py b/pillar/web/utils/forms.py index cbbc2fba..c7dd85b5 100644 --- a/pillar/web/utils/forms.py +++ b/pillar/web/utils/forms.py @@ -32,7 +32,7 @@ class CustomFileSelectWidget(HiddenInput): if file_format and file_format == 'image': file_format_regex = '^image\/(gif|jpe?g|png|tif?f|tga)$' - button = [u'
'] + button = ['
'] if field.data: api = system_util.pillar_api() @@ -42,64 +42,64 @@ class CustomFileSelectWidget(HiddenInput): except ResourceNotFound: pass else: - button.append(u'
') + button.append('
') filename = Markup.escape(file_item.filename) if file_item.content_type.split('/')[0] == 'image': # If a file of type image is available, display the preview - button.append(u''.format( + button.append(''.format( file_item.thumbnail('s', api=api))) - button.append(u'
    ') + button.append('
      ') # File name - button.append(u'
    • {0}
    • '.format(filename)) + button.append('
    • {0}
    • '.format(filename)) # File size - button.append(u'
    • ({0} MB)
    • '.format( + button.append('
    • ({0} MB)
    • '.format( round((file_item.length / 1024) * 0.001, 2))) # Image resolution (if image) if file_item.content_type.split('/')[0] == 'image': - button.append(u'
    • {0}x{1}
    • '.format( + button.append('
    • {0}x{1}
    • '.format( file_item.width, file_item.height)) - button.append(u'
    ') - button.append(u'
      ') + button.append('
    ') + button.append('
      ') # Download button for original file - button.append(u'
    • ' - u' ' - u'Original
    • ' + button.append('
    • ' + ' ' + 'Original
    • ' .format(file_item.link)) # Delete button - button.append(u'
    • ' - u' ' - u' Delete
    • '.format( + button.append('
    • ' + ' ' + ' Delete
    • '.format( field_name=field.name, file_id=field.data)) - button.append(u'
    ') - button.append(u'
') + button.append('') + button.append('
') - upload_url = u'%sstorage/stream/{project_id}' % current_app.config[ + upload_url = '%sstorage/stream/{project_id}' % current_app.config[ 'PILLAR_SERVER_ENDPOINT'] - button.append(u'' - u'
' - u'
' - u'
' - u'
'.format(url=upload_url, + button.append('' + '
' + '
' + '
' + '
'.format(url=upload_url, name=field.name, slug=field.name.replace('oid', 'slug'), token=Markup.escape(current_user.id), file_format=Markup.escape(file_format_regex))) - button.append(u'
') + button.append('
') - return HTMLString(html + u''.join(button)) + return HTMLString(html + ''.join(button)) class FileSelectField(StringField): @@ -112,7 +112,7 @@ def build_file_select_form(schema): class FileSelectForm(Form): pass - for field_name, field_schema in schema.iteritems(): + for field_name, field_schema in schema.items(): if field_schema['type'] == 'boolean': field = BooleanField() elif field_schema['type'] == 'string': @@ -142,19 +142,19 @@ class CustomFormFieldWidget(object): def __call__(self, field, **kwargs): html = [] kwargs.setdefault('id', field.id) - html.append(u'
' % html_params(**kwargs)) - hidden = u'' + html.append('
' % html_params(**kwargs)) + hidden = '' for subfield in field: if subfield.type == 'HiddenField': hidden += text_type(subfield) else: - html.append(u'
%s%s%s
' % ( + html.append('
%s%s%s
' % ( text_type(subfield.label), hidden, text_type(subfield))) - hidden = u'' - html.append(u'
') + hidden = '' + html.append('
') if hidden: html.append(hidden) - return HTMLString(u''.join(html)) + return HTMLString(''.join(html)) class CustomFormField(FormField):