From 9720040cb9a7c75fd590322479e821a0e8f13497 Mon Sep 17 00:00:00 2001 From: Deimos Date: Mon, 12 Jul 2021 23:35:23 -0600 Subject: [PATCH] Use a prospector fork, update dependencies This is kind of dirty, but the prospector tool was broken after updating Python to 3.9, and it seems to no longer be maintained. I forked it to my personal GitHub account, un-pinned its dependencies, fixed a bug that came up after updating pylint, and deleted a few dependencies that I don't use (pylint plugins for Django, Flask, and Celery). This commit also fixes all the new complaints from the updated pylint, which were mostly explicitly re-raising exceptions, and some places where I could use a generator instead of an unnecessary list comprehension. This will work for now, but I probably don't want to leave it in this state. I should probably just stick to using the tools like pylint directly, since this is now the second time I've needed to replace my "tool runner" when it stopped being maintained (the first one was pylama). --- tildes/requirements-dev.in | 2 +- tildes/requirements-dev.txt | 27 +++++++++----------- tildes/tildes/__init__.py | 1 + tildes/tildes/lib/database.py | 4 +-- tildes/tildes/lib/datetime.py | 4 +-- tildes/tildes/metrics.py | 12 ++++----- tildes/tildes/models/comment/comment_tree.py | 2 +- tildes/tildes/models/pagination.py | 10 ++++---- tildes/tildes/models/topic/topic.py | 2 +- tildes/tildes/request_methods.py | 14 +++++----- tildes/tildes/resources/comment.py | 4 +-- tildes/tildes/resources/topic.py | 8 +++--- tildes/tildes/schemas/fields.py | 12 ++++----- tildes/tildes/schemas/topic.py | 4 +-- tildes/tildes/scrapers/youtube_scraper.py | 6 +++-- tildes/tildes/views/api/web/topic.py | 4 +-- tildes/tildes/views/api/web/user.py | 4 +-- tildes/tildes/views/donate.py | 4 +-- tildes/tildes/views/register.py | 6 +++-- tildes/tildes/views/topic.py | 4 +-- 20 files changed, 68 insertions(+), 66 deletions(-) diff --git a/tildes/requirements-dev.in b/tildes/requirements-dev.in index fee5842..a369b4c 100644 --- a/tildes/requirements-dev.in +++ b/tildes/requirements-dev.in @@ -3,7 +3,7 @@ black freezegun html5validator mypy -prospector +prospector @ git+https://github.com/Deimos/prospector.git#egg=prospector pyramid-debugtoolbar pytest pytest-mock diff --git a/tildes/requirements-dev.txt b/tildes/requirements-dev.txt index 8e37d9a..81beb78 100644 --- a/tildes/requirements-dev.txt +++ b/tildes/requirements-dev.txt @@ -2,7 +2,7 @@ ago==0.0.93 alembic==1.4.3 appdirs==1.4.4 argon2-cffi==20.1.0 -astroid==2.4.1 +astroid==2.6.2 attrs==20.2.0 backcall==0.2.0 beautifulsoup4==4.9.3 @@ -17,7 +17,7 @@ cornice==5.0.3 decorator==4.4.2 dodgy==0.2.1 flake8-polyfill==1.0.2 -flake8==3.8.4 +flake8==3.9.2 freezegun==1.0.0 gunicorn==20.0.4 html5lib==1.1 @@ -28,10 +28,10 @@ iniconfig==1.1.1 invoke==1.4.1 ipython-genutils==0.2.0 ipython==7.19.0 -isort==4.3.21 +isort==5.9.2 jedi==0.17.2 jinja2==2.11.2 -lazy-object-proxy==1.4.3 +lazy-object-proxy==1.6.0 lupa==1.9 mako==1.1.3 markupsafe==1.1.1 @@ -44,7 +44,7 @@ parso==0.7.1 pastedeploy==2.1.1 pathspec==0.8.0 pep517==0.10.0 -pep8-naming==0.10.0 +pep8-naming==0.12.0 pexpect==4.8.0 pickleshare==0.7.5 pillow==8.0.1 @@ -54,22 +54,19 @@ plaster==1.0 pluggy==0.13.1 prometheus-client==0.8.0 prompt-toolkit==3.0.8 -prospector==1.3.1 +git+https://github.com/Deimos/prospector.git#egg=prospector psycopg2==2.8.6 ptyprocess==0.6.0 publicsuffix2==2.20160818 py==1.9.0 -pycodestyle==2.6.0 +pycodestyle==2.7.0 pycparser==2.20 -pydocstyle==5.1.1 -pyflakes==2.2.0 +pydocstyle==6.1.1 +pyflakes==2.3.1 pygit2==1.6.1 pygments==2.7.2 -pylint-celery==0.3 -pylint-django==2.1.0 -pylint-flask==0.6 pylint-plugin-utils==0.6 -pylint==2.5.3 +pylint==2.9.3 pyotp==2.4.1 pyparsing==2.4.7 pyramid-debugtoolbar==4.8 @@ -84,7 +81,7 @@ pytest-mock==3.3.1 pytest==6.1.2 python-dateutil==2.8.1 python-editor==1.0.4 -pyyaml==5.3.1 +pyyaml==5.4.1 qrcode==6.1 redis==3.5.3 regex==2020.10.28 @@ -94,7 +91,7 @@ requirements-detector==0.7 sentry-sdk==0.19.1 setoptconf==0.2.0 six==1.15.0 -snowballstemmer==2.0.0 +snowballstemmer==2.1.0 soupsieve==2.0.1 sqlalchemy-utils==0.36.8 sqlalchemy==1.3.20 diff --git a/tildes/tildes/__init__.py b/tildes/tildes/__init__.py index 1997d97..c4d62cc 100644 --- a/tildes/tildes/__init__.py +++ b/tildes/tildes/__init__.py @@ -41,6 +41,7 @@ def main(global_config: Dict[str, str], **settings: str) -> PrefixMiddleware: config.add_static_view("images", "/images") if settings.get("sentry_dsn"): + # pylint: disable=abstract-class-instantiated sentry_sdk.init( dsn=settings["sentry_dsn"], integrations=[PyramidIntegration()], diff --git a/tildes/tildes/lib/database.py b/tildes/tildes/lib/database.py index 6faa98d..f2b81d4 100644 --- a/tildes/tildes/lib/database.py +++ b/tildes/tildes/lib/database.py @@ -48,8 +48,8 @@ def obtain_transaction_lock( if lock_space: try: lock_space_value = LockSpaces[lock_space.upper()].value - except KeyError: - raise ValueError("Invalid lock space: %s" % lock_space) + except KeyError as exc: + raise ValueError("Invalid lock space: %s" % lock_space) from exc session.query(func.pg_advisory_xact_lock(lock_space_value, lock_value)).one() else: diff --git a/tildes/tildes/lib/datetime.py b/tildes/tildes/lib/datetime.py index 696d76a..ae20688 100644 --- a/tildes/tildes/lib/datetime.py +++ b/tildes/tildes/lib/datetime.py @@ -27,8 +27,8 @@ class SimpleHoursPeriod: try: self.timedelta = timedelta(hours=hours) - except OverflowError: - raise ValueError("Time period is too large") + except OverflowError as exc: + raise ValueError("Time period is too large") from exc @classmethod def from_short_form(cls, short_form: str) -> SimpleHoursPeriod: diff --git a/tildes/tildes/metrics.py b/tildes/tildes/metrics.py index 2002db5..5b8dcd2 100644 --- a/tildes/tildes/metrics.py +++ b/tildes/tildes/metrics.py @@ -62,8 +62,8 @@ def incr_counter(name: str, amount: int = 1, **labels: str) -> None: """Increment a Prometheus counter.""" try: counter = _COUNTERS[name] - except KeyError: - raise ValueError("Invalid counter name") + except KeyError as exc: + raise ValueError("Invalid counter name") from exc if labels: counter = counter.labels(**labels) @@ -75,8 +75,8 @@ def get_histogram(name: str, **labels: str) -> Histogram: """Return an (optionally labeled) Prometheus histogram by name.""" try: hist = _HISTOGRAMS[name] - except KeyError: - raise ValueError("Invalid histogram name") + except KeyError as exc: + raise ValueError("Invalid histogram name") from exc if labels: hist = hist.labels(**labels) @@ -93,8 +93,8 @@ def get_summary(name: str, **labels: str) -> Summary: """Return an (optionally labeled) Prometheus summary by name.""" try: hist = _SUMMARIES[name] - except KeyError: - raise ValueError("Invalid summary name") + except KeyError as exc: + raise ValueError("Invalid summary name") from exc if labels: hist = hist.labels(**labels) diff --git a/tildes/tildes/models/comment/comment_tree.py b/tildes/tildes/models/comment/comment_tree.py index 40be94d..f9ec90d 100644 --- a/tildes/tildes/models/comment/comment_tree.py +++ b/tildes/tildes/models/comment/comment_tree.py @@ -254,7 +254,7 @@ class CommentTree: # if all the top-level comments end up fully collapsed, # uncollapse the ones we just collapsed (so we don't have a # comment page that's all collapsed comments) - if all([comment.collapsed_state == "full" for comment in self.tree]): + if all(comment.collapsed_state == "full" for comment in self.tree): for comment, was_unknown in zip(self.tree, top_unknown_initial): if was_unknown: comment.collapsed_state = None diff --git a/tildes/tildes/models/pagination.py b/tildes/tildes/models/pagination.py index aa92bd4..86312bd 100644 --- a/tildes/tildes/models/pagination.py +++ b/tildes/tildes/models/pagination.py @@ -267,16 +267,16 @@ class MixedPaginatedResults(PaginatedResults): """Merge all the supplied results into a single one.""" sort_column_name = paginated_results[0].query._sort_column.name if any( - [r.query._sort_column.name != sort_column_name for r in paginated_results] + r.query._sort_column.name != sort_column_name for r in paginated_results ): raise ValueError("All results must by sorted by the same column.") reverse_sort = paginated_results[0].query.sort_desc - if any([r.query.sort_desc != reverse_sort for r in paginated_results]): + if any(r.query.sort_desc != reverse_sort for r in paginated_results): raise ValueError("All results must by sorted in the same direction.") is_query_reversed = paginated_results[0].query.is_reversed - if any([r.query.is_reversed != is_query_reversed for r in paginated_results]): + if any(r.query.is_reversed != is_query_reversed for r in paginated_results): raise ValueError("All results must have the same directionality.") # merge all the results into one list and sort it @@ -288,8 +288,8 @@ class MixedPaginatedResults(PaginatedResults): self.per_page = min([r.per_page for r in paginated_results]) - self.has_prev_page = any([r.has_prev_page for r in paginated_results]) - self.has_next_page = any([r.has_next_page for r in paginated_results]) + self.has_prev_page = any(r.has_prev_page for r in paginated_results) + self.has_next_page = any(r.has_next_page for r in paginated_results) if len(self.results) > self.per_page: if is_query_reversed: diff --git a/tildes/tildes/models/topic/topic.py b/tildes/tildes/models/topic/topic.py index 143ec60..6e9435b 100644 --- a/tildes/tildes/models/topic/topic.py +++ b/tildes/tildes/models/topic/topic.py @@ -191,7 +191,7 @@ class Topic(DatabaseModel): important_tags = set(self.group.important_topic_tags + global_important_tags) # used with startswith() to check for sub-tags - important_prefixes = tuple([f"{tag}." for tag in important_tags]) + important_prefixes = tuple(f"{tag}." for tag in important_tags) return [ tag diff --git a/tildes/tildes/request_methods.py b/tildes/tildes/request_methods.py index 7274894..8fcfd44 100644 --- a/tildes/tildes/request_methods.py +++ b/tildes/tildes/request_methods.py @@ -45,7 +45,7 @@ def is_bot(request: Request) -> bool: if request.user_agent: return any( - [substring in request.user_agent for substring in bot_user_agent_substrings] + substring in request.user_agent for substring in bot_user_agent_substrings ) return False @@ -83,8 +83,8 @@ def check_rate_limit(request: Request, action_name: str) -> RateLimitResult: if not action: try: action = RATE_LIMITED_ACTIONS[action_name] - except KeyError: - raise ValueError("Invalid action name: %s" % action_name) + except KeyError as exc: + raise ValueError("Invalid action name: %s" % action_name) from exc action.redis = request.redis @@ -136,8 +136,8 @@ def current_listing_base_url( try: base_view_vars = base_vars_by_route[request.matched_route.name] - except KeyError: - raise AttributeError("Current route is not supported.") + except KeyError as exc: + raise AttributeError("Current route is not supported.") from exc query_vars = { key: val for key, val in request.GET.copy().items() if key in base_view_vars @@ -173,8 +173,8 @@ def current_listing_normal_url( try: normal_view_vars = normal_vars_by_route[request.matched_route.name] - except KeyError: - raise AttributeError("Current route is not supported.") + except KeyError as exc: + raise AttributeError("Current route is not supported.") from exc query_vars = { key: val for key, val in request.GET.copy().items() if key in normal_view_vars diff --git a/tildes/tildes/resources/comment.py b/tildes/tildes/resources/comment.py index 45aefa6..dd371e2 100644 --- a/tildes/tildes/resources/comment.py +++ b/tildes/tildes/resources/comment.py @@ -24,8 +24,8 @@ def comment_by_id36(request: Request, comment_id36: str) -> Comment: try: return get_resource(request, query) - except HTTPNotFound: - raise HTTPNotFound("Comment not found (or it was deleted)") + except HTTPNotFound as exc: + raise HTTPNotFound("Comment not found (or it was deleted)") from exc @use_kwargs(CommentSchema(only=("comment_id36",)), location="matchdict") diff --git a/tildes/tildes/resources/topic.py b/tildes/tildes/resources/topic.py index e5e7ac7..522c451 100644 --- a/tildes/tildes/resources/topic.py +++ b/tildes/tildes/resources/topic.py @@ -18,8 +18,8 @@ def topic_by_id36(request: Request, topic_id36: str) -> Topic: """Get a topic specified by {topic_id36} in the route (or 404).""" try: topic_id = id36_to_id(topic_id36) - except ValueError: - raise HTTPNotFound + except ValueError as exc: + raise HTTPNotFound from exc query = ( request.query(Topic) @@ -30,8 +30,8 @@ def topic_by_id36(request: Request, topic_id36: str) -> Topic: try: topic = get_resource(request, query) - except HTTPNotFound: - raise HTTPNotFound("Topic not found (or it was deleted)") + except HTTPNotFound as exc: + raise HTTPNotFound("Topic not found (or it was deleted)") from exc # if there's also a group specified in the route, check that it's the same group as # the topic was posted in, otherwise redirect to correct group diff --git a/tildes/tildes/schemas/fields.py b/tildes/tildes/schemas/fields.py index 684b81f..436fbb9 100644 --- a/tildes/tildes/schemas/fields.py +++ b/tildes/tildes/schemas/fields.py @@ -49,8 +49,8 @@ class Enum(Field): try: return self._enum_class[value.upper()] - except KeyError: - raise ValidationError("Invalid enum member") + except KeyError as exc: + raise ValidationError("Invalid enum member") from exc class ID36(String): @@ -80,8 +80,8 @@ class ShortTimePeriod(Field): try: return SimpleHoursPeriod.from_short_form(value) - except ValueError: - raise ValidationError("Invalid time period") + except ValueError as exc: + raise ValidationError("Invalid time period") from exc def _serialize( self, @@ -197,8 +197,8 @@ class Ltree(Field): try: return sqlalchemy_utils.Ltree(value) - except (TypeError, ValueError): - raise ValidationError("Invalid path") + except (TypeError, ValueError) as exc: + raise ValidationError("Invalid path") from exc class PostType(String): diff --git a/tildes/tildes/schemas/topic.py b/tildes/tildes/schemas/topic.py index af8ef88..289b0f7 100644 --- a/tildes/tildes/schemas/topic.py +++ b/tildes/tildes/schemas/topic.py @@ -112,8 +112,8 @@ class TopicSchema(Schema): for tag in value: try: group_schema.load({"path": tag}) - except ValidationError: - raise ValidationError("Tag %s is invalid" % tag) + except ValidationError as exc: + raise ValidationError("Tag %s is invalid" % tag) from exc @pre_load def prepare_markdown(self, data: dict, many: bool, partial: Any) -> dict: diff --git a/tildes/tildes/scrapers/youtube_scraper.py b/tildes/tildes/scrapers/youtube_scraper.py index e4c506f..c6ac65b 100644 --- a/tildes/tildes/scrapers/youtube_scraper.py +++ b/tildes/tildes/scrapers/youtube_scraper.py @@ -72,8 +72,10 @@ class YoutubeScraper: try: video_data = response.json()["items"][0] - except (KeyError, IndexError): - raise ScraperError(f"No data returned for video with ID {video_id}") + except (KeyError, IndexError) as exc: + raise ScraperError( + f"No data returned for video with ID {video_id}" + ) from exc return ScraperResult(url, ScraperType.YOUTUBE, video_data) diff --git a/tildes/tildes/views/api/web/topic.py b/tildes/tildes/views/api/web/topic.py index 2269c62..2d28557 100644 --- a/tildes/tildes/views/api/web/topic.py +++ b/tildes/tildes/views/api/web/topic.py @@ -185,8 +185,8 @@ def put_tag_topic(request: Request, tags: str, conflict_check: str) -> dict: try: topic.tags = new_tags - except ValidationError: - raise ValidationError({"tags": ["Invalid tags"]}) + except ValidationError as exc: + raise ValidationError({"tags": ["Invalid tags"]}) from exc # if tags weren't changed, don't add a log entry or update page if set(topic.tags) == set(old_tags): diff --git a/tildes/tildes/views/api/web/user.py b/tildes/tildes/views/api/web/user.py index a0bc23c..ef80973 100644 --- a/tildes/tildes/views/api/web/user.py +++ b/tildes/tildes/views/api/web/user.py @@ -387,8 +387,8 @@ def put_filtered_topic_tags(request: Request, tags: str) -> dict: try: schema = TopicSchema(only=("tags",)) result = schema.load({"tags": split_tags}) - except ValidationError: - raise ValidationError({"tags": ["Invalid tags"]}) + except ValidationError as exc: + raise ValidationError({"tags": ["Invalid tags"]}) from exc request.user.filtered_topic_tags = result["tags"] diff --git a/tildes/tildes/views/donate.py b/tildes/tildes/views/donate.py index 96d7743..36881bb 100644 --- a/tildes/tildes/views/donate.py +++ b/tildes/tildes/views/donate.py @@ -51,8 +51,8 @@ def post_donate_stripe( stripe.api_key = request.registry.settings["api_keys.stripe.secret"] publishable_key = request.registry.settings["api_keys.stripe.publishable"] product_id = request.registry.settings["stripe.recurring_donation_product_id"] - except KeyError: - raise HTTPInternalServerError + except KeyError as exc: + raise HTTPInternalServerError from exc incr_counter("donation_initiations", type="stripe") diff --git a/tildes/tildes/views/register.py b/tildes/tildes/views/register.py index d8415c6..bab4a45 100644 --- a/tildes/tildes/views/register.py +++ b/tildes/tildes/views/register.py @@ -86,8 +86,10 @@ def post_register( request.db_session.add(user) try: request.db_session.flush() - except IntegrityError: - raise HTTPUnprocessableEntity("That username has already been registered.") + except IntegrityError as exc: + raise HTTPUnprocessableEntity( + "That username has already been registered." + ) from exc # the flush above will generate the new user's ID, so use that to update the invite # code with info about the user that registered with it diff --git a/tildes/tildes/views/topic.py b/tildes/tildes/views/topic.py index 0892d3e..0d59776 100644 --- a/tildes/tildes/views/topic.py +++ b/tildes/tildes/views/topic.py @@ -118,8 +118,8 @@ def post_group_topics( try: new_topic.tags = tags.split(",") - except ValidationError: - raise ValidationError({"tags": ["Invalid tags"]}) + except ValidationError as exc: + raise ValidationError({"tags": ["Invalid tags"]}) from exc # remove any tag that's the same as the group's name new_topic.tags = [tag for tag in new_topic.tags if tag != str(group.path)]