Browse Source

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).
merge-requests/135/head
Deimos 3 years ago
parent
commit
9720040cb9
  1. 2
      tildes/requirements-dev.in
  2. 27
      tildes/requirements-dev.txt
  3. 1
      tildes/tildes/__init__.py
  4. 4
      tildes/tildes/lib/database.py
  5. 4
      tildes/tildes/lib/datetime.py
  6. 12
      tildes/tildes/metrics.py
  7. 2
      tildes/tildes/models/comment/comment_tree.py
  8. 10
      tildes/tildes/models/pagination.py
  9. 2
      tildes/tildes/models/topic/topic.py
  10. 14
      tildes/tildes/request_methods.py
  11. 4
      tildes/tildes/resources/comment.py
  12. 8
      tildes/tildes/resources/topic.py
  13. 12
      tildes/tildes/schemas/fields.py
  14. 4
      tildes/tildes/schemas/topic.py
  15. 6
      tildes/tildes/scrapers/youtube_scraper.py
  16. 4
      tildes/tildes/views/api/web/topic.py
  17. 4
      tildes/tildes/views/api/web/user.py
  18. 4
      tildes/tildes/views/donate.py
  19. 6
      tildes/tildes/views/register.py
  20. 4
      tildes/tildes/views/topic.py

2
tildes/requirements-dev.in

@ -3,7 +3,7 @@ black
freezegun freezegun
html5validator html5validator
mypy mypy
prospector
prospector @ git+https://github.com/Deimos/prospector.git#egg=prospector
pyramid-debugtoolbar pyramid-debugtoolbar
pytest pytest
pytest-mock pytest-mock

27
tildes/requirements-dev.txt

@ -2,7 +2,7 @@ ago==0.0.93
alembic==1.4.3 alembic==1.4.3
appdirs==1.4.4 appdirs==1.4.4
argon2-cffi==20.1.0 argon2-cffi==20.1.0
astroid==2.4.1
astroid==2.6.2
attrs==20.2.0 attrs==20.2.0
backcall==0.2.0 backcall==0.2.0
beautifulsoup4==4.9.3 beautifulsoup4==4.9.3
@ -17,7 +17,7 @@ cornice==5.0.3
decorator==4.4.2 decorator==4.4.2
dodgy==0.2.1 dodgy==0.2.1
flake8-polyfill==1.0.2 flake8-polyfill==1.0.2
flake8==3.8.4
flake8==3.9.2
freezegun==1.0.0 freezegun==1.0.0
gunicorn==20.0.4 gunicorn==20.0.4
html5lib==1.1 html5lib==1.1
@ -28,10 +28,10 @@ iniconfig==1.1.1
invoke==1.4.1 invoke==1.4.1
ipython-genutils==0.2.0 ipython-genutils==0.2.0
ipython==7.19.0 ipython==7.19.0
isort==4.3.21
isort==5.9.2
jedi==0.17.2 jedi==0.17.2
jinja2==2.11.2 jinja2==2.11.2
lazy-object-proxy==1.4.3
lazy-object-proxy==1.6.0
lupa==1.9 lupa==1.9
mako==1.1.3 mako==1.1.3
markupsafe==1.1.1 markupsafe==1.1.1
@ -44,7 +44,7 @@ parso==0.7.1
pastedeploy==2.1.1 pastedeploy==2.1.1
pathspec==0.8.0 pathspec==0.8.0
pep517==0.10.0 pep517==0.10.0
pep8-naming==0.10.0
pep8-naming==0.12.0
pexpect==4.8.0 pexpect==4.8.0
pickleshare==0.7.5 pickleshare==0.7.5
pillow==8.0.1 pillow==8.0.1
@ -54,22 +54,19 @@ plaster==1.0
pluggy==0.13.1 pluggy==0.13.1
prometheus-client==0.8.0 prometheus-client==0.8.0
prompt-toolkit==3.0.8 prompt-toolkit==3.0.8
prospector==1.3.1
git+https://github.com/Deimos/prospector.git#egg=prospector
psycopg2==2.8.6 psycopg2==2.8.6
ptyprocess==0.6.0 ptyprocess==0.6.0
publicsuffix2==2.20160818 publicsuffix2==2.20160818
py==1.9.0 py==1.9.0
pycodestyle==2.6.0
pycodestyle==2.7.0
pycparser==2.20 pycparser==2.20
pydocstyle==5.1.1
pyflakes==2.2.0
pydocstyle==6.1.1
pyflakes==2.3.1
pygit2==1.6.1 pygit2==1.6.1
pygments==2.7.2 pygments==2.7.2
pylint-celery==0.3
pylint-django==2.1.0
pylint-flask==0.6
pylint-plugin-utils==0.6 pylint-plugin-utils==0.6
pylint==2.5.3
pylint==2.9.3
pyotp==2.4.1 pyotp==2.4.1
pyparsing==2.4.7 pyparsing==2.4.7
pyramid-debugtoolbar==4.8 pyramid-debugtoolbar==4.8
@ -84,7 +81,7 @@ pytest-mock==3.3.1
pytest==6.1.2 pytest==6.1.2
python-dateutil==2.8.1 python-dateutil==2.8.1
python-editor==1.0.4 python-editor==1.0.4
pyyaml==5.3.1
pyyaml==5.4.1
qrcode==6.1 qrcode==6.1
redis==3.5.3 redis==3.5.3
regex==2020.10.28 regex==2020.10.28
@ -94,7 +91,7 @@ requirements-detector==0.7
sentry-sdk==0.19.1 sentry-sdk==0.19.1
setoptconf==0.2.0 setoptconf==0.2.0
six==1.15.0 six==1.15.0
snowballstemmer==2.0.0
snowballstemmer==2.1.0
soupsieve==2.0.1 soupsieve==2.0.1
sqlalchemy-utils==0.36.8 sqlalchemy-utils==0.36.8
sqlalchemy==1.3.20 sqlalchemy==1.3.20

1
tildes/tildes/__init__.py

@ -41,6 +41,7 @@ def main(global_config: Dict[str, str], **settings: str) -> PrefixMiddleware:
config.add_static_view("images", "/images") config.add_static_view("images", "/images")
if settings.get("sentry_dsn"): if settings.get("sentry_dsn"):
# pylint: disable=abstract-class-instantiated
sentry_sdk.init( sentry_sdk.init(
dsn=settings["sentry_dsn"], dsn=settings["sentry_dsn"],
integrations=[PyramidIntegration()], integrations=[PyramidIntegration()],

4
tildes/tildes/lib/database.py

@ -48,8 +48,8 @@ def obtain_transaction_lock(
if lock_space: if lock_space:
try: try:
lock_space_value = LockSpaces[lock_space.upper()].value 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() session.query(func.pg_advisory_xact_lock(lock_space_value, lock_value)).one()
else: else:

4
tildes/tildes/lib/datetime.py

@ -27,8 +27,8 @@ class SimpleHoursPeriod:
try: try:
self.timedelta = timedelta(hours=hours) 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 @classmethod
def from_short_form(cls, short_form: str) -> SimpleHoursPeriod: def from_short_form(cls, short_form: str) -> SimpleHoursPeriod:

12
tildes/tildes/metrics.py

@ -62,8 +62,8 @@ def incr_counter(name: str, amount: int = 1, **labels: str) -> None:
"""Increment a Prometheus counter.""" """Increment a Prometheus counter."""
try: try:
counter = _COUNTERS[name] counter = _COUNTERS[name]
except KeyError:
raise ValueError("Invalid counter name")
except KeyError as exc:
raise ValueError("Invalid counter name") from exc
if labels: if labels:
counter = counter.labels(**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.""" """Return an (optionally labeled) Prometheus histogram by name."""
try: try:
hist = _HISTOGRAMS[name] hist = _HISTOGRAMS[name]
except KeyError:
raise ValueError("Invalid histogram name")
except KeyError as exc:
raise ValueError("Invalid histogram name") from exc
if labels: if labels:
hist = hist.labels(**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.""" """Return an (optionally labeled) Prometheus summary by name."""
try: try:
hist = _SUMMARIES[name] hist = _SUMMARIES[name]
except KeyError:
raise ValueError("Invalid summary name")
except KeyError as exc:
raise ValueError("Invalid summary name") from exc
if labels: if labels:
hist = hist.labels(**labels) hist = hist.labels(**labels)

2
tildes/tildes/models/comment/comment_tree.py

@ -254,7 +254,7 @@ class CommentTree:
# if all the top-level comments end up fully collapsed, # if all the top-level comments end up fully collapsed,
# uncollapse the ones we just collapsed (so we don't have a # uncollapse the ones we just collapsed (so we don't have a
# comment page that's all collapsed comments) # 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): for comment, was_unknown in zip(self.tree, top_unknown_initial):
if was_unknown: if was_unknown:
comment.collapsed_state = None comment.collapsed_state = None

10
tildes/tildes/models/pagination.py

@ -267,16 +267,16 @@ class MixedPaginatedResults(PaginatedResults):
"""Merge all the supplied results into a single one.""" """Merge all the supplied results into a single one."""
sort_column_name = paginated_results[0].query._sort_column.name sort_column_name = paginated_results[0].query._sort_column.name
if any( 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.") raise ValueError("All results must by sorted by the same column.")
reverse_sort = paginated_results[0].query.sort_desc 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.") raise ValueError("All results must by sorted in the same direction.")
is_query_reversed = paginated_results[0].query.is_reversed 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.") raise ValueError("All results must have the same directionality.")
# merge all the results into one list and sort it # 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.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 len(self.results) > self.per_page:
if is_query_reversed: if is_query_reversed:

2
tildes/tildes/models/topic/topic.py

@ -191,7 +191,7 @@ class Topic(DatabaseModel):
important_tags = set(self.group.important_topic_tags + global_important_tags) important_tags = set(self.group.important_topic_tags + global_important_tags)
# used with startswith() to check for sub-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 [ return [
tag tag

14
tildes/tildes/request_methods.py

@ -45,7 +45,7 @@ def is_bot(request: Request) -> bool:
if request.user_agent: if request.user_agent:
return any( 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 return False
@ -83,8 +83,8 @@ def check_rate_limit(request: Request, action_name: str) -> RateLimitResult:
if not action: if not action:
try: try:
action = RATE_LIMITED_ACTIONS[action_name] 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 action.redis = request.redis
@ -136,8 +136,8 @@ def current_listing_base_url(
try: try:
base_view_vars = base_vars_by_route[request.matched_route.name] 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 = { query_vars = {
key: val for key, val in request.GET.copy().items() if key in base_view_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: try:
normal_view_vars = normal_vars_by_route[request.matched_route.name] 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 = { query_vars = {
key: val for key, val in request.GET.copy().items() if key in normal_view_vars key: val for key, val in request.GET.copy().items() if key in normal_view_vars

4
tildes/tildes/resources/comment.py

@ -24,8 +24,8 @@ def comment_by_id36(request: Request, comment_id36: str) -> Comment:
try: try:
return get_resource(request, query) 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") @use_kwargs(CommentSchema(only=("comment_id36",)), location="matchdict")

8
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).""" """Get a topic specified by {topic_id36} in the route (or 404)."""
try: try:
topic_id = id36_to_id(topic_id36) topic_id = id36_to_id(topic_id36)
except ValueError:
raise HTTPNotFound
except ValueError as exc:
raise HTTPNotFound from exc
query = ( query = (
request.query(Topic) request.query(Topic)
@ -30,8 +30,8 @@ def topic_by_id36(request: Request, topic_id36: str) -> Topic:
try: try:
topic = get_resource(request, query) 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 # 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 # the topic was posted in, otherwise redirect to correct group

12
tildes/tildes/schemas/fields.py

@ -49,8 +49,8 @@ class Enum(Field):
try: try:
return self._enum_class[value.upper()] 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): class ID36(String):
@ -80,8 +80,8 @@ class ShortTimePeriod(Field):
try: try:
return SimpleHoursPeriod.from_short_form(value) 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( def _serialize(
self, self,
@ -197,8 +197,8 @@ class Ltree(Field):
try: try:
return sqlalchemy_utils.Ltree(value) 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): class PostType(String):

4
tildes/tildes/schemas/topic.py

@ -112,8 +112,8 @@ class TopicSchema(Schema):
for tag in value: for tag in value:
try: try:
group_schema.load({"path": tag}) 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 @pre_load
def prepare_markdown(self, data: dict, many: bool, partial: Any) -> dict: def prepare_markdown(self, data: dict, many: bool, partial: Any) -> dict:

6
tildes/tildes/scrapers/youtube_scraper.py

@ -72,8 +72,10 @@ class YoutubeScraper:
try: try:
video_data = response.json()["items"][0] 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) return ScraperResult(url, ScraperType.YOUTUBE, video_data)

4
tildes/tildes/views/api/web/topic.py

@ -185,8 +185,8 @@ def put_tag_topic(request: Request, tags: str, conflict_check: str) -> dict:
try: try:
topic.tags = new_tags 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 tags weren't changed, don't add a log entry or update page
if set(topic.tags) == set(old_tags): if set(topic.tags) == set(old_tags):

4
tildes/tildes/views/api/web/user.py

@ -387,8 +387,8 @@ def put_filtered_topic_tags(request: Request, tags: str) -> dict:
try: try:
schema = TopicSchema(only=("tags",)) schema = TopicSchema(only=("tags",))
result = schema.load({"tags": split_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"] request.user.filtered_topic_tags = result["tags"]

4
tildes/tildes/views/donate.py

@ -51,8 +51,8 @@ def post_donate_stripe(
stripe.api_key = request.registry.settings["api_keys.stripe.secret"] stripe.api_key = request.registry.settings["api_keys.stripe.secret"]
publishable_key = request.registry.settings["api_keys.stripe.publishable"] publishable_key = request.registry.settings["api_keys.stripe.publishable"]
product_id = request.registry.settings["stripe.recurring_donation_product_id"] 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") incr_counter("donation_initiations", type="stripe")

6
tildes/tildes/views/register.py

@ -86,8 +86,10 @@ def post_register(
request.db_session.add(user) request.db_session.add(user)
try: try:
request.db_session.flush() 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 # 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 # code with info about the user that registered with it

4
tildes/tildes/views/topic.py

@ -118,8 +118,8 @@ def post_group_topics(
try: try:
new_topic.tags = tags.split(",") 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 # 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)] new_topic.tags = [tag for tag in new_topic.tags if tag != str(group.path)]

Loading…
Cancel
Save