Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mypy: add --disallow-incomplete-defs option #2616

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ lint-style:
flake8 sopel/ test/

lint-type:
mypy --check-untyped-defs sopel
mypy --check-untyped-defs --disallow-incomplete-defs sopel

.PHONY: test test_norecord test_novcr vcr_rerecord
test:
Expand Down
32 changes: 25 additions & 7 deletions sopel/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
from types import MappingProxyType
from typing import (
Any,
Callable,
Optional,
Sequence,
TYPE_CHECKING,
TypeVar,
Union,
Expand All @@ -36,6 +38,8 @@

if TYPE_CHECKING:
from collections.abc import Iterable, Mapping

from sopel.plugins.handlers import AbstractPluginHandler
from sopel.trigger import PreTrigger


Expand Down Expand Up @@ -198,7 +202,7 @@ def plugins(self) -> Mapping[str, plugins.handlers.AbstractPluginHandler]:
"""
return MappingProxyType(self._plugins)

def has_channel_privilege(self, channel, privilege) -> bool:
def has_channel_privilege(self, channel: str, privilege: int) -> bool:
"""Tell if the bot has a ``privilege`` level or above in a ``channel``.

:param str channel: a channel the bot is in
Expand Down Expand Up @@ -339,7 +343,7 @@ def post_setup(self) -> None:

# plugins management

def reload_plugin(self, name) -> None:
def reload_plugin(self, name: str) -> None:
"""Reload a plugin.

:param str name: name of the plugin to reload
Expand Down Expand Up @@ -391,7 +395,14 @@ def reload_plugins(self) -> None:

# TODO: deprecate both add_plugin and remove_plugin; see #2425

def add_plugin(self, plugin, callables, jobs, shutdowns, urls) -> None:
def add_plugin(
self,
plugin: AbstractPluginHandler,
callables: Sequence[Callable],
jobs: Sequence[Callable],
shutdowns: Sequence[Callable],
urls: Sequence[Callable],
) -> None:
"""Add a loaded plugin to the bot's registry.

:param plugin: loaded plugin to add
Expand All @@ -414,7 +425,14 @@ def add_plugin(self, plugin, callables, jobs, shutdowns, urls) -> None:
self.register_shutdowns(shutdowns)
self.register_urls(urls)

def remove_plugin(self, plugin, callables, jobs, shutdowns, urls) -> None:
def remove_plugin(
self,
plugin: AbstractPluginHandler,
callables: Sequence[Callable],
jobs: Sequence[Callable],
shutdowns: Sequence[Callable],
urls: Sequence[Callable],
) -> None:
"""Remove a loaded plugin from the bot's registry.

:param plugin: loaded plugin to remove
Expand Down Expand Up @@ -993,7 +1011,7 @@ def on_scheduler_error(
self,
scheduler: plugin_jobs.Scheduler,
exc: BaseException,
):
) -> None:
"""Called when the Job Scheduler fails.

:param scheduler: the job scheduler that errored
Expand All @@ -1011,7 +1029,7 @@ def on_job_error(
scheduler: plugin_jobs.Scheduler,
job: tools_jobs.Job,
exc: BaseException,
):
) -> None:
"""Called when a job from the Job Scheduler fails.

:param scheduler: the job scheduler responsible for the errored ``job``
Expand All @@ -1030,7 +1048,7 @@ def error(
self,
trigger: Optional[Trigger] = None,
exception: Optional[BaseException] = None,
):
) -> None:
"""Called internally when a plugin causes an error.

:param trigger: the ``Trigger``\\ing line (if available)
Expand Down
3 changes: 1 addition & 2 deletions sopel/builtins/calc.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@ def c(bot, trigger):
# Account for the silly non-Anglophones and their silly radix point.
eqn = trigger.group(2).replace(',', '.')
try:
result = eval_equation(eqn)
result = "{:.10g}".format(result)
result = "{:.10g}".format(eval_equation(eqn))
except eval_equation.Error as err:
bot.reply("Can't process expression: {}".format(str(err)))
return
Expand Down
2 changes: 1 addition & 1 deletion sopel/builtins/dice.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ def _roll_dice(dice_match: re.Match[str]) -> DicePouch:
@plugin.example(".roll 2d10+3", user_help=True)
@plugin.example(".roll 1d6", user_help=True)
@plugin.output_prefix('[dice] ')
def roll(bot: SopelWrapper, trigger: Trigger):
def roll(bot: SopelWrapper, trigger: Trigger) -> None:
"""Rolls dice and reports the result.

The dice roll follows this format: XdY[vZ][+N][#COMMENT]
Expand Down
16 changes: 8 additions & 8 deletions sopel/builtins/safety.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class SafetySection(types.StaticSection):
"""Optional hosts-file formatted domain blocklist to use instead of StevenBlack's."""


def configure(settings: Config):
def configure(settings: Config) -> None:
"""
| name | example | purpose |
| ---- | ------- | ------- |
Expand Down Expand Up @@ -90,7 +90,7 @@ def configure(settings: Config):
)


def setup(bot: Sopel):
def setup(bot: Sopel) -> None:
bot.settings.define_section("safety", SafetySection)

if bot.settings.safety.default_mode is None:
Expand Down Expand Up @@ -166,7 +166,7 @@ def download_domain_list(bot: Sopel, path: str) -> bool:
return True


def update_local_cache(bot: Sopel, init: bool = False):
def update_local_cache(bot: Sopel, init: bool = False) -> None:
"""Download the current malware domain list and load it into memory.

:param init: Load the file even if it's unchanged
Expand Down Expand Up @@ -202,7 +202,7 @@ def update_local_cache(bot: Sopel, init: bool = False):
bot.memory[SAFETY_CACHE_LOCAL_KEY] = unsafe_domains


def shutdown(bot: Sopel):
def shutdown(bot: Sopel) -> None:
bot.memory.pop(SAFETY_CACHE_KEY, None)
bot.memory.pop(SAFETY_CACHE_LOCAL_KEY, None)
bot.memory.pop(SAFETY_CACHE_LOCK_KEY, None)
Expand All @@ -211,7 +211,7 @@ def shutdown(bot: Sopel):
@plugin.rule(r'(?u).*(https?://\S+).*')
@plugin.priority('high')
@plugin.output_prefix(PLUGIN_OUTPUT_PREFIX)
def url_handler(bot: SopelWrapper, trigger: Trigger):
def url_handler(bot: SopelWrapper, trigger: Trigger) -> None:
"""Checks for malicious URLs."""
mode = bot.db.get_channel_value(
trigger.sender,
Expand Down Expand Up @@ -365,7 +365,7 @@ def virustotal_lookup(
@plugin.example(".virustotal https://malware.wicar.org/")
@plugin.example(".virustotal hxxps://malware.wicar.org/")
@plugin.output_prefix("[safety][VirusTotal] ")
def vt_command(bot: SopelWrapper, trigger: Trigger):
def vt_command(bot: SopelWrapper, trigger: Trigger) -> None:
"""Look up VT results on demand."""
if not bot.settings.safety.vt_api_key:
bot.reply("Sorry, I don't have a VirusTotal API key configured.")
Expand Down Expand Up @@ -421,7 +421,7 @@ def vt_command(bot: SopelWrapper, trigger: Trigger):
@plugin.command('safety')
@plugin.example(".safety on")
@plugin.output_prefix(PLUGIN_OUTPUT_PREFIX)
def toggle_safety(bot: SopelWrapper, trigger: Trigger):
def toggle_safety(bot: SopelWrapper, trigger: Trigger) -> None:
"""Set safety setting for channel."""
if not trigger.admin and bot.channels[trigger.sender].privileges[trigger.nick] < plugin.OP:
bot.reply('Only channel operators can change safety settings')
Expand Down Expand Up @@ -455,7 +455,7 @@ def toggle_safety(bot: SopelWrapper, trigger: Trigger):
# Clean the cache every day
# Code above also calls this if there are too many cache entries
@plugin.interval(24 * 60 * 60)
def _clean_cache(bot: Sopel):
def _clean_cache(bot: Sopel) -> None:
"""Cleans up old entries in URL safety cache."""

update_local_cache(bot)
Expand Down
30 changes: 21 additions & 9 deletions sopel/builtins/units.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,34 +9,40 @@
from __future__ import annotations

import re
from typing import Pattern, TYPE_CHECKING

from sopel import plugin


if TYPE_CHECKING:
from sopel.bot import SopelWrapper
from sopel.trigger import Trigger


PLUGIN_OUTPUT_PREFIX = '[units] '

find_temp = re.compile(r'(-?[0-9]*\.?[0-9]*)[ °]*(K|C|F)', re.IGNORECASE)
find_length = re.compile(r'([0-9]*\.?[0-9]*)[ ]*(mile[s]?|mi|inch|in|foot|feet|ft|yard[s]?|yd|(?:milli|centi|kilo|)meter[s]?|[mkc]?m|ly|light-year[s]?|au|astronomical unit[s]?|parsec[s]?|pc)', re.IGNORECASE)
find_mass = re.compile(r'([0-9]*\.?[0-9]*)[ ]*(lb|lbm|pound[s]?|ounce|oz|(?:kilo|)gram(?:me|)[s]?|[k]?g)', re.IGNORECASE)


def f_to_c(temp):
def f_to_c(temp: float) -> float:
return (float(temp) - 32) * 5 / 9


def c_to_k(temp):
def c_to_k(temp: float) -> float:
return temp + 273.15


def c_to_f(temp):
def c_to_f(temp: float) -> float:
return (9.0 / 5.0 * temp + 32)


def k_to_c(temp):
def k_to_c(temp: float) -> float:
return temp - 273.15


def _extract_source(pattern, trigger) -> tuple[str, ...]:
def _extract_source(pattern: Pattern, trigger: Trigger) -> tuple[str, ...]:
match = pattern.match(trigger.group(2))
if match:
return match.groups()
Expand All @@ -49,7 +55,7 @@ def _extract_source(pattern, trigger) -> tuple[str, ...]:
@plugin.example('.temp 100C', '100.00°C = 212.00°F = 373.15K')
@plugin.example('.temp 100K', '-173.15°C = -279.67°F = 100.00K')
@plugin.output_prefix(PLUGIN_OUTPUT_PREFIX)
def temperature(bot, trigger):
def temperature(bot: SopelWrapper, trigger: Trigger) -> int | None:
"""Convert temperatures"""
try:
source = _extract_source(find_temp, trigger)
Expand All @@ -71,14 +77,16 @@ def temperature(bot, trigger):

if kelvin <= 0:
bot.reply("Physically impossible temperature.")
return
return None

bot.say("{:.2f}°C = {:.2f}°F = {:.2f}K".format(
celsius,
fahrenheit,
kelvin,
))

return None


@plugin.command('length', 'distance')
@plugin.example('.distance 3m', '3.00m = 9 feet, 10.11 inches')
Expand All @@ -92,7 +100,7 @@ def temperature(bot, trigger):
@plugin.example('.length 3 au', '448793612.10km = 278867421.71 miles')
@plugin.example('.length 3 parsec', '92570329129020.20km = 57520535754731.61 miles')
@plugin.output_prefix(PLUGIN_OUTPUT_PREFIX)
def distance(bot, trigger):
def distance(bot: SopelWrapper, trigger: Trigger) -> int | None:
"""Convert distances"""
try:
source = _extract_source(find_length, trigger)
Expand Down Expand Up @@ -160,10 +168,12 @@ def distance(bot, trigger):

bot.say('{} = {}'.format(metric_part, stupid_part))

return None


@plugin.command('weight', 'mass')
@plugin.output_prefix(PLUGIN_OUTPUT_PREFIX)
def mass(bot, trigger):
def mass(bot: SopelWrapper, trigger: Trigger) -> int | None:
"""Convert mass"""
try:
source = _extract_source(find_mass, trigger)
Expand Down Expand Up @@ -199,3 +209,5 @@ def mass(bot, trigger):
stupid_part = '{:.2f} oz'.format(ounce)

bot.say('{} = {}'.format(metric_part, stupid_part))

return None
22 changes: 13 additions & 9 deletions sopel/builtins/url.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ class UrlSection(types.StaticSection):
"""Enable requests to private and local network IP addresses"""


def configure(config: Config):
def configure(config: Config) -> None:
"""
| name | example | purpose |
| ---- | ------- | ------- |
Expand Down Expand Up @@ -111,7 +111,7 @@ def configure(config: Config):
)


def setup(bot: Sopel):
def setup(bot: Sopel) -> None:
bot.config.define_section('url', UrlSection)

if bot.config.url.exclude:
Expand Down Expand Up @@ -140,7 +140,7 @@ def setup(bot: Sopel):
bot.memory['shortened_urls'] = tools.SopelMemory()


def shutdown(bot: Sopel):
def shutdown(bot: Sopel) -> None:
# Unset `url_exclude` and `last_seen_url`, but not `shortened_urls`;
# clearing `shortened_urls` will increase API calls. Leaving it in memory
# should not lead to unexpected behavior.
Expand All @@ -151,7 +151,7 @@ def shutdown(bot: Sopel):
pass


def _user_can_change_excludes(bot: SopelWrapper, trigger: Trigger):
def _user_can_change_excludes(bot: SopelWrapper, trigger: Trigger) -> bool:
if trigger.admin:
return True

Expand All @@ -169,7 +169,7 @@ def _user_can_change_excludes(bot: SopelWrapper, trigger: Trigger):
@plugin.example('.urlpexclude example\\.com/\\w+', user_help=True)
@plugin.example('.urlexclude example.com/path', user_help=True)
@plugin.output_prefix('[url] ')
def url_ban(bot: SopelWrapper, trigger: Trigger):
def url_ban(bot: SopelWrapper, trigger: Trigger) -> None:
"""Exclude a URL from auto title.

Use ``urlpexclude`` to exclude a pattern instead of a URL.
Expand Down Expand Up @@ -220,7 +220,7 @@ def url_ban(bot: SopelWrapper, trigger: Trigger):
@plugin.example('.urlpallow example\\.com/\\w+', user_help=True)
@plugin.example('.urlallow example.com/path', user_help=True)
@plugin.output_prefix('[url] ')
def url_unban(bot: SopelWrapper, trigger: Trigger):
def url_unban(bot: SopelWrapper, trigger: Trigger) -> None:
"""Allow a URL for auto title.

Use ``urlpallow`` to allow a pattern instead of a URL.
Expand Down Expand Up @@ -273,7 +273,7 @@ def url_unban(bot: SopelWrapper, trigger: Trigger):
'Google | www.google.com',
online=True, vcr=True)
@plugin.output_prefix('[url] ')
def title_command(bot: SopelWrapper, trigger: Trigger):
def title_command(bot: SopelWrapper, trigger: Trigger) -> None:
"""
Show the title or URL information for the given URL, or the last URL seen
in this channel.
Expand Down Expand Up @@ -313,7 +313,7 @@ def title_command(bot: SopelWrapper, trigger: Trigger):

@plugin.rule(r'(?u).*(https?://\S+).*')
@plugin.output_prefix('[url] ')
def title_auto(bot: SopelWrapper, trigger: Trigger):
def title_auto(bot: SopelWrapper, trigger: Trigger) -> None:
"""
Automatically show titles for URLs. For shortened URLs/redirects, find
where the URL redirects to and show the title for that.
Expand Down Expand Up @@ -472,7 +472,11 @@ def process_urls(
yield URLInfo(url, title, parsed_url.hostname, tinyurl, False)


def check_callbacks(bot: SopelWrapper, url: str, use_excludes: bool = True) -> bool:
def check_callbacks(
bot: SopelWrapper,
url: str,
use_excludes: bool = True,
) -> bool:
"""Check if ``url`` is excluded or matches any URL callback patterns.

:param bot: Sopel instance
Expand Down
Loading
Loading