diff --git a/tildes/consumers/site_icon_downloader.py b/tildes/consumers/site_icon_downloader.py index d20a331..2c177cb 100644 --- a/tildes/consumers/site_icon_downloader.py +++ b/tildes/consumers/site_icon_downloader.py @@ -7,10 +7,10 @@ from io import BytesIO from os import path from typing import Optional, Sequence -from amqpy import Message -from PIL import Image import publicsuffix import requests +from amqpy import Message +from PIL import Image from tildes.enums import ScraperType from tildes.lib.amqp import PgsqlQueueConsumer diff --git a/tildes/consumers/topic_embedly_extractor.py b/tildes/consumers/topic_embedly_extractor.py index 5fba99a..2e803d2 100644 --- a/tildes/consumers/topic_embedly_extractor.py +++ b/tildes/consumers/topic_embedly_extractor.py @@ -3,8 +3,8 @@ """Consumer that fetches data from Embedly's Extract API for link topics.""" -from datetime import timedelta import os +from datetime import timedelta from typing import Sequence from amqpy import Message diff --git a/tildes/consumers/topic_metadata_generator.py b/tildes/consumers/topic_metadata_generator.py index 44ceed6..e973085 100644 --- a/tildes/consumers/topic_metadata_generator.py +++ b/tildes/consumers/topic_metadata_generator.py @@ -5,8 +5,8 @@ from typing import Any, Dict, Sequence -from amqpy import Message import publicsuffix +from amqpy import Message from sqlalchemy import cast, func from sqlalchemy.dialects.postgresql import JSONB diff --git a/tildes/consumers/topic_youtube_scraper.py b/tildes/consumers/topic_youtube_scraper.py index e9ecdb2..4ebb02e 100644 --- a/tildes/consumers/topic_youtube_scraper.py +++ b/tildes/consumers/topic_youtube_scraper.py @@ -3,8 +3,8 @@ """Consumer that fetches data from YouTube's data API for relevant link topics.""" -from datetime import timedelta import os +from datetime import timedelta from typing import Sequence from amqpy import Message diff --git a/tildes/prospector.yaml b/tildes/prospector.yaml index 545fbc7..8ad5493 100644 --- a/tildes/prospector.yaml +++ b/tildes/prospector.yaml @@ -31,3 +31,4 @@ pylint: - no-self-use # schemas do this a lot, would be nice to only disable for schemas - too-few-public-methods # plenty of classes that don't need multiple methods - too-many-instance-attributes # models have many instance attributes + - ungrouped-imports # let isort handle this diff --git a/tildes/pyproject.toml b/tildes/pyproject.toml index 568b395..3e42604 100644 --- a/tildes/pyproject.toml +++ b/tildes/pyproject.toml @@ -1,2 +1,11 @@ [tool.black] exclude = "(\\.mypy_cache|node_modules|scss|sql|static)" + +[tool.isort] +skip = "alembic" +line_length = 88 +multi_line_output = 3 # "Vertical Hanging Indent" style +include_trailing_comma = true +lines_after_imports = 2 +known_third_party = "alembic" +order_by_type = false diff --git a/tildes/scripts/backup_database.py b/tildes/scripts/backup_database.py index 54879e3..81cde64 100644 --- a/tildes/scripts/backup_database.py +++ b/tildes/scripts/backup_database.py @@ -13,12 +13,12 @@ place, and will probably just crash completely if they aren't: * The specified GPG recipient's public key is in the keyring """ -from datetime import datetime, timedelta -from ftplib import FTP import logging -from netrc import netrc import os import subprocess +from datetime import datetime, timedelta +from ftplib import FTP +from netrc import netrc import click diff --git a/tildes/scripts/clean_private_data.py b/tildes/scripts/clean_private_data.py index 4d81d6d..f3c9659 100644 --- a/tildes/scripts/clean_private_data.py +++ b/tildes/scripts/clean_private_data.py @@ -9,8 +9,8 @@ Other things that should probably be added here eventually: - Delete old used invite codes (30 days after used?) """ -from datetime import datetime, timedelta import logging +from datetime import datetime, timedelta from sqlalchemy.orm.session import Session from sqlalchemy.sql.expression import text diff --git a/tildes/scripts/generate_site_icons_css.py b/tildes/scripts/generate_site_icons_css.py index e84333e..3bb524b 100644 --- a/tildes/scripts/generate_site_icons_css.py +++ b/tildes/scripts/generate_site_icons_css.py @@ -8,6 +8,7 @@ import shutil import stat from tempfile import NamedTemporaryFile + ICON_FOLDER = "/opt/tildes/static/images/site-icons" OUTPUT_FILE = "/opt/tildes/static/css/site-icons.css" diff --git a/tildes/tests/conftest.py b/tildes/tests/conftest.py index 1549ae3..8d84537 100644 --- a/tildes/tests/conftest.py +++ b/tildes/tests/conftest.py @@ -1,8 +1,8 @@ # Copyright (c) 2018 Tildes contributors # SPDX-License-Identifier: AGPL-3.0-or-later -from http.cookiejar import CookieJar import os +from http.cookiejar import CookieJar from pyramid import testing from pyramid.paster import get_app, get_appsettings diff --git a/tildes/tests/test_comment_user_mentions.py b/tildes/tests/test_comment_user_mentions.py index a85d850..b019b63 100644 --- a/tildes/tests/test_comment_user_mentions.py +++ b/tildes/tests/test_comment_user_mentions.py @@ -2,7 +2,6 @@ # SPDX-License-Identifier: AGPL-3.0-or-later from pytest import fixture - from sqlalchemy import and_ from tildes.enums import CommentNotificationType diff --git a/tildes/tests/test_id.py b/tildes/tests/test_id.py index 6224eb6..f04dadf 100644 --- a/tildes/tests/test_id.py +++ b/tildes/tests/test_id.py @@ -5,7 +5,7 @@ from pytest import raises -from tildes.lib.id import id_to_id36, id36_to_id +from tildes.lib.id import id36_to_id, id_to_id36 def test_id_to_id36(): diff --git a/tildes/tests/test_ratelimit.py b/tildes/tests/test_ratelimit.py index bcddcbe..fd6faaf 100644 --- a/tildes/tests/test_ratelimit.py +++ b/tildes/tests/test_ratelimit.py @@ -8,10 +8,10 @@ from random import randint from pytest import raises from tildes.lib.ratelimit import ( + RATE_LIMITED_ACTIONS, RateLimitedAction, RateLimitError, RateLimitResult, - RATE_LIMITED_ACTIONS, ) diff --git a/tildes/tildes/__init__.py b/tildes/tildes/__init__.py index 22b35dc..98284dd 100644 --- a/tildes/tildes/__init__.py +++ b/tildes/tildes/__init__.py @@ -5,13 +5,13 @@ from typing import Any, Dict, Optional, Tuple +import sentry_sdk from marshmallow.exceptions import ValidationError from paste.deploy.config import PrefixMiddleware from pyramid.config import Configurator from pyramid.httpexceptions import HTTPTooManyRequests from pyramid.request import Request from redis import Redis -import sentry_sdk from sentry_sdk.integrations.pyramid import PyramidIntegration from webassets import Bundle diff --git a/tildes/tildes/api.py b/tildes/tildes/api.py index 4677ab8..aa06a85 100644 --- a/tildes/tildes/api.py +++ b/tildes/tildes/api.py @@ -5,8 +5,8 @@ from typing import Any -from cornice import Service import venusian +from cornice import Service class APIv0(Service): diff --git a/tildes/tildes/enums.py b/tildes/tildes/enums.py index 862ba61..9b53fe0 100644 --- a/tildes/tildes/enums.py +++ b/tildes/tildes/enums.py @@ -3,9 +3,8 @@ """Contains Enum classes.""" -from typing import Optional - import enum +from typing import Optional class CommentNotificationType(enum.Enum): diff --git a/tildes/tildes/lib/amqp.py b/tildes/tildes/lib/amqp.py index 27a8aed..239e020 100644 --- a/tildes/tildes/lib/amqp.py +++ b/tildes/tildes/lib/amqp.py @@ -3,9 +3,9 @@ """Contains classes related to handling AMQP (rabbitmq) messages.""" -from abc import abstractmethod import json import os +from abc import abstractmethod from typing import Sequence from amqpy import AbstractConsumer, Connection, Message diff --git a/tildes/tildes/lib/cmark.py b/tildes/tildes/lib/cmark.py index d17b4ed..f6feaa6 100644 --- a/tildes/tildes/lib/cmark.py +++ b/tildes/tildes/lib/cmark.py @@ -1,7 +1,7 @@ """Set up the shared libcmark-gfm library and extensions.""" # pylint: disable=invalid-name -from ctypes import CDLL, c_char_p, c_int, c_size_t, c_void_p +from ctypes import c_char_p, c_int, c_size_t, c_void_p, CDLL CMARK_DLL = CDLL("/usr/local/lib/libcmark-gfm.so") diff --git a/tildes/tildes/lib/database.py b/tildes/tildes/lib/database.py index c6836d6..6379641 100644 --- a/tildes/tildes/lib/database.py +++ b/tildes/tildes/lib/database.py @@ -7,7 +7,6 @@ import enum from typing import Any, Callable, List, Optional from pyramid.paster import bootstrap - from sqlalchemy import cast, func from sqlalchemy.dialects.postgresql import ARRAY from sqlalchemy.engine.interfaces import Dialect diff --git a/tildes/tildes/lib/datetime.py b/tildes/tildes/lib/datetime.py index 590feee..64793ef 100644 --- a/tildes/tildes/lib/datetime.py +++ b/tildes/tildes/lib/datetime.py @@ -3,8 +3,8 @@ """Functions/classes related to dates and times.""" -from datetime import datetime, timedelta, timezone import re +from datetime import datetime, timedelta, timezone from typing import Any, Optional from ago import human diff --git a/tildes/tildes/lib/hash.py b/tildes/tildes/lib/hash.py index ecadfb3..615b14e 100644 --- a/tildes/tildes/lib/hash.py +++ b/tildes/tildes/lib/hash.py @@ -6,6 +6,7 @@ from argon2 import PasswordHasher from argon2.exceptions import VerifyMismatchError + # These parameter values were chosen to achieve a hash-verification time of about 10ms # on the current production server. They can be updated to different values if the # server changes (consider upgrading old password hashes on login as well if that diff --git a/tildes/tildes/lib/markdown.py b/tildes/tildes/lib/markdown.py index 8d1d8ef..3b724c5 100644 --- a/tildes/tildes/lib/markdown.py +++ b/tildes/tildes/lib/markdown.py @@ -3,12 +3,12 @@ """Functions/constants related to markdown handling.""" -from functools import partial import re +from functools import partial from typing import Any, Callable, Iterator, List, Match, Optional, Pattern, Tuple -from bs4 import BeautifulSoup import bleach +from bs4 import BeautifulSoup from html5lib.filters.base import Filter from html5lib.treewalkers.base import NonRecursiveTreeWalker from pygments import highlight @@ -20,11 +20,12 @@ from tildes.enums import HTMLSanitizationContext from tildes.metrics import histogram_timer from tildes.schemas.group import is_valid_group_path from tildes.schemas.user import is_valid_username + from .cmark import ( CMARK_EXTENSIONS, - CMARK_OPTS, cmark_find_syntax_extension, cmark_node_free, + CMARK_OPTS, cmark_parser_attach_syntax_extension, cmark_parser_feed, cmark_parser_finish, diff --git a/tildes/tildes/lib/password.py b/tildes/tildes/lib/password.py index 5fec403..c52927f 100644 --- a/tildes/tildes/lib/password.py +++ b/tildes/tildes/lib/password.py @@ -7,6 +7,7 @@ from hashlib import sha1 from redis import ConnectionError, Redis, ResponseError # noqa + # unix socket path for redis server with the breached passwords bloom filter BREACHED_PASSWORDS_REDIS_SOCKET = "/run/redis_breached_passwords/socket" diff --git a/tildes/tildes/lib/string.py b/tildes/tildes/lib/string.py index 4492307..e2586fa 100644 --- a/tildes/tildes/lib/string.py +++ b/tildes/tildes/lib/string.py @@ -3,12 +3,11 @@ """Functions related to processing/manipulating strings.""" -from xml.etree.ElementTree import Element - import re -from typing import Iterator, List, Optional import unicodedata +from typing import Iterator, List, Optional from urllib.parse import quote +from xml.etree.ElementTree import Element from html5lib import HTMLParser diff --git a/tildes/tildes/lib/url_transform.py b/tildes/tildes/lib/url_transform.py index d1a6acd..6f1c7fe 100644 --- a/tildes/tildes/lib/url_transform.py +++ b/tildes/tildes/lib/url_transform.py @@ -3,13 +3,13 @@ """Functions related to transforming URLs (sanitization, cleanup, etc.).""" +import logging from abc import ABC, abstractmethod from collections import Counter -import logging from urllib.parse import ( - ParseResult, parse_qs, parse_qsl, + ParseResult, urlencode, urlparse, urlunparse, diff --git a/tildes/tildes/models/comment/__init__.py b/tildes/tildes/models/comment/__init__.py index 5871321..e7879c5 100644 --- a/tildes/tildes/models/comment/__init__.py +++ b/tildes/tildes/models/comment/__init__.py @@ -2,9 +2,9 @@ from .comment import Comment, EDIT_GRACE_PERIOD from .comment_bookmark import CommentBookmark +from .comment_label import CommentLabel from .comment_notification import CommentNotification from .comment_notification_query import CommentNotificationQuery from .comment_query import CommentQuery -from .comment_label import CommentLabel from .comment_tree import CommentInTree, CommentTree from .comment_vote import CommentVote diff --git a/tildes/tildes/models/comment/comment_label.py b/tildes/tildes/models/comment/comment_label.py index 67b367a..1314c91 100644 --- a/tildes/tildes/models/comment/comment_label.py +++ b/tildes/tildes/models/comment/comment_label.py @@ -15,6 +15,7 @@ from tildes.enums import CommentLabelOption from tildes.metrics import incr_counter from tildes.models import DatabaseModel from tildes.models.user import User + from .comment import Comment diff --git a/tildes/tildes/models/comment/comment_notification.py b/tildes/tildes/models/comment/comment_notification.py index 784df07..1c1473f 100644 --- a/tildes/tildes/models/comment/comment_notification.py +++ b/tildes/tildes/models/comment/comment_notification.py @@ -3,8 +3,8 @@ """Contains the CommentNotification class.""" -from datetime import datetime import re +from datetime import datetime from typing import Any, List, Sequence, Tuple from pyramid.security import Allow, DENY_ALL @@ -17,6 +17,7 @@ from tildes.enums import CommentNotificationType from tildes.lib.markdown import LinkifyFilter from tildes.models import DatabaseModel from tildes.models.user import User + from .comment import Comment diff --git a/tildes/tildes/models/comment/comment_notification_query.py b/tildes/tildes/models/comment/comment_notification_query.py index e54b4a0..911e764 100644 --- a/tildes/tildes/models/comment/comment_notification_query.py +++ b/tildes/tildes/models/comment/comment_notification_query.py @@ -10,6 +10,7 @@ from sqlalchemy.orm import joinedload from tildes.lib.id import id_to_id36 from tildes.models.pagination import PaginatedQuery, PaginatedResults + from .comment_notification import CommentNotification from .comment_vote import CommentVote diff --git a/tildes/tildes/models/comment/comment_query.py b/tildes/tildes/models/comment/comment_query.py index 7f7f225..d68dff8 100644 --- a/tildes/tildes/models/comment/comment_query.py +++ b/tildes/tildes/models/comment/comment_query.py @@ -10,6 +10,7 @@ from sqlalchemy.sql.expression import and_ from tildes.enums import CommentSortOption from tildes.models.pagination import PaginatedQuery + from .comment import Comment from .comment_bookmark import CommentBookmark from .comment_vote import CommentVote diff --git a/tildes/tildes/models/comment/comment_tree.py b/tildes/tildes/models/comment/comment_tree.py index a78b1b4..abf65ee 100644 --- a/tildes/tildes/models/comment/comment_tree.py +++ b/tildes/tildes/models/comment/comment_tree.py @@ -12,6 +12,7 @@ from wrapt import ObjectProxy from tildes.enums import CommentTreeSortOption from tildes.metrics import get_histogram from tildes.models.user import User + from .comment import Comment diff --git a/tildes/tildes/models/comment/comment_vote.py b/tildes/tildes/models/comment/comment_vote.py index 337c714..cae5913 100644 --- a/tildes/tildes/models/comment/comment_vote.py +++ b/tildes/tildes/models/comment/comment_vote.py @@ -12,6 +12,7 @@ from sqlalchemy.sql.expression import text from tildes.metrics import incr_counter from tildes.models import DatabaseModel from tildes.models.user import User + from .comment import Comment diff --git a/tildes/tildes/models/group/group_query.py b/tildes/tildes/models/group/group_query.py index 5bfc7f7..0339d76 100644 --- a/tildes/tildes/models/group/group_query.py +++ b/tildes/tildes/models/group/group_query.py @@ -8,6 +8,7 @@ from typing import Any from pyramid.request import Request from tildes.models import ModelQuery + from .group import Group from .group_subscription import GroupSubscription diff --git a/tildes/tildes/models/group/group_subscription.py b/tildes/tildes/models/group/group_subscription.py index 2eeff37..6638c39 100644 --- a/tildes/tildes/models/group/group_subscription.py +++ b/tildes/tildes/models/group/group_subscription.py @@ -12,6 +12,7 @@ from sqlalchemy.sql.expression import text from tildes.metrics import incr_counter from tildes.models import DatabaseModel from tildes.models.user import User + from .group import Group diff --git a/tildes/tildes/models/group/group_wiki_page.py b/tildes/tildes/models/group/group_wiki_page.py index bcf6f5a..fd710a7 100644 --- a/tildes/tildes/models/group/group_wiki_page.py +++ b/tildes/tildes/models/group/group_wiki_page.py @@ -21,6 +21,7 @@ from tildes.lib.string import convert_to_url_slug from tildes.models import DatabaseModel from tildes.models.user import User from tildes.schemas.group_wiki_page import GroupWikiPageSchema, PAGE_NAME_MAX_LENGTH + from .group import Group diff --git a/tildes/tildes/models/pagination.py b/tildes/tildes/models/pagination.py index d97ec3a..31cd0fb 100644 --- a/tildes/tildes/models/pagination.py +++ b/tildes/tildes/models/pagination.py @@ -9,7 +9,8 @@ from typing import Any, Iterator, List, Optional, Sequence, TypeVar from pyramid.request import Request from sqlalchemy import Column, func, inspect -from tildes.lib.id import id_to_id36, id36_to_id +from tildes.lib.id import id36_to_id, id_to_id36 + from .model_query import ModelQuery diff --git a/tildes/tildes/models/topic/topic_query.py b/tildes/tildes/models/topic/topic_query.py index 74ec553..773511b 100644 --- a/tildes/tildes/models/topic/topic_query.py +++ b/tildes/tildes/models/topic/topic_query.py @@ -14,6 +14,7 @@ from tildes.enums import TopicSortOption from tildes.lib.datetime import SimpleHoursPeriod, utc_now from tildes.models.group import Group from tildes.models.pagination import PaginatedQuery + from .topic import Topic from .topic_bookmark import TopicBookmark from .topic_visit import TopicVisit diff --git a/tildes/tildes/models/topic/topic_visit.py b/tildes/tildes/models/topic/topic_visit.py index 28e98c2..04b8cb4 100644 --- a/tildes/tildes/models/topic/topic_visit.py +++ b/tildes/tildes/models/topic/topic_visit.py @@ -13,6 +13,7 @@ from sqlalchemy.orm import relationship from tildes.lib.datetime import utc_now from tildes.models import DatabaseModel from tildes.models.user import User + from .topic import Topic diff --git a/tildes/tildes/models/topic/topic_vote.py b/tildes/tildes/models/topic/topic_vote.py index df94a9a..2bdef07 100644 --- a/tildes/tildes/models/topic/topic_vote.py +++ b/tildes/tildes/models/topic/topic_vote.py @@ -12,6 +12,7 @@ from sqlalchemy.sql.expression import text from tildes.metrics import incr_counter from tildes.models import DatabaseModel from tildes.models.user import User + from .topic import Topic diff --git a/tildes/tildes/models/user/user_invite_code.py b/tildes/tildes/models/user/user_invite_code.py index 10de921..187a512 100644 --- a/tildes/tildes/models/user/user_invite_code.py +++ b/tildes/tildes/models/user/user_invite_code.py @@ -3,15 +3,16 @@ """Contains the UserInviteCode class.""" -from datetime import datetime import random import string +from datetime import datetime from sqlalchemy import CheckConstraint, Column, ForeignKey, Integer, Text, TIMESTAMP from sqlalchemy.sql.expression import text from tildes.lib.string import separate_string from tildes.models import DatabaseModel + from .user import User diff --git a/tildes/tildes/schemas/fields.py b/tildes/tildes/schemas/fields.py index c61a7f7..9785742 100644 --- a/tildes/tildes/schemas/fields.py +++ b/tildes/tildes/schemas/fields.py @@ -6,10 +6,10 @@ import enum from typing import Any, Optional, Type +import sqlalchemy_utils from marshmallow.exceptions import ValidationError from marshmallow.fields import Field, String from marshmallow.validate import Length, OneOf, Regexp -import sqlalchemy_utils from tildes.lib.datetime import SimpleHoursPeriod from tildes.lib.id import ID36_REGEX diff --git a/tildes/tildes/schemas/group.py b/tildes/tildes/schemas/group.py index 0c127e9..32e033f 100644 --- a/tildes/tildes/schemas/group.py +++ b/tildes/tildes/schemas/group.py @@ -5,10 +5,10 @@ import re +import sqlalchemy_utils from marshmallow import pre_load, Schema, validates from marshmallow.exceptions import ValidationError from marshmallow.fields import DateTime -import sqlalchemy_utils from tildes.schemas.fields import Ltree, Markdown, SimpleString diff --git a/tildes/tildes/schemas/topic.py b/tildes/tildes/schemas/topic.py index c8019ef..1282cfb 100644 --- a/tildes/tildes/schemas/topic.py +++ b/tildes/tildes/schemas/topic.py @@ -7,15 +7,16 @@ import re import typing from urllib.parse import urlparse +import sqlalchemy_utils from marshmallow import pre_load, Schema, validates, validates_schema, ValidationError from marshmallow.fields import DateTime, List, Nested, String, URL -import sqlalchemy_utils from tildes.lib.url_transform import apply_url_transformations from tildes.schemas.fields import Enum, ID36, Ltree, Markdown, SimpleString from tildes.schemas.group import GroupSchema from tildes.schemas.user import UserSchema + TITLE_MAX_LENGTH = 200 TAG_SYNONYMS = {"spoiler": ["spoilers"]} diff --git a/tildes/tildes/scrapers/youtube_scraper.py b/tildes/tildes/scrapers/youtube_scraper.py index beb287f..e4c506f 100644 --- a/tildes/tildes/scrapers/youtube_scraper.py +++ b/tildes/tildes/scrapers/youtube_scraper.py @@ -3,16 +3,17 @@ """Contains the YoutubeScraper class.""" -from datetime import timedelta import re +from datetime import timedelta from typing import Any, Dict from urllib.parse import parse_qs, urlparse -from dateutil import parser import requests +from dateutil import parser from tildes.enums import ScraperType from tildes.models.scraper import ScraperResult + from .exceptions import ScraperError diff --git a/tildes/tildes/views/api/web/comment.py b/tildes/tildes/views/api/web/comment.py index 7db91fd..8d300e0 100644 --- a/tildes/tildes/views/api/web/comment.py +++ b/tildes/tildes/views/api/web/comment.py @@ -14,7 +14,7 @@ from sqlalchemy.orm.exc import FlushError from webargs.pyramidparser import use_kwargs from zope.sqlalchemy import mark_changed -from tildes.enums import CommentNotificationType, CommentLabelOption, LogEventType +from tildes.enums import CommentLabelOption, CommentNotificationType, LogEventType from tildes.lib.datetime import utc_now from tildes.models.comment import ( Comment, @@ -25,7 +25,7 @@ from tildes.models.comment import ( ) from tildes.models.log import LogComment from tildes.models.topic import TopicVisit -from tildes.schemas.comment import CommentSchema, CommentLabelSchema +from tildes.schemas.comment import CommentLabelSchema, CommentSchema from tildes.views import IC_NOOP from tildes.views.decorators import ic_view_config, rate_limit_view diff --git a/tildes/tildes/views/api/web/topic.py b/tildes/tildes/views/api/web/topic.py index 1880424..259f815 100644 --- a/tildes/tildes/views/api/web/topic.py +++ b/tildes/tildes/views/api/web/topic.py @@ -6,8 +6,8 @@ from marshmallow import ValidationError from marshmallow.fields import String from pyramid.httpexceptions import HTTPNotFound -from pyramid.response import Response from pyramid.request import Request +from pyramid.response import Response from sqlalchemy import cast, Text from sqlalchemy.dialects.postgresql import ARRAY from sqlalchemy.exc import IntegrityError diff --git a/tildes/tildes/views/settings.py b/tildes/tildes/views/settings.py index dad7431..46bade9 100644 --- a/tildes/tildes/views/settings.py +++ b/tildes/tildes/views/settings.py @@ -6,11 +6,11 @@ from io import BytesIO import pyotp +import qrcode from pyramid.httpexceptions import HTTPForbidden, HTTPUnprocessableEntity from pyramid.request import Request from pyramid.response import Response from pyramid.view import view_config -import qrcode from webargs.pyramidparser import use_kwargs from tildes.lib.string import separate_string