diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f532ef33b9..32f62d3a2d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -45,10 +45,8 @@ include your changes. the name of the thing you're changing in at the beginning of the message, followed by a colon: the plugin name for plugins, "docs" for documentation files, "coretasks" for `coretasks.py`, "db" for the database feature, etc. -* Python files should always have `# coding=utf-8` as the first line (or the - second, if the first is `#!/usr/bin/env python`), and `from __future__ import - unicode_literals, absolute_import, print_function, division` as the first - line after the module docstring. +* Python files should always have `from __future__ import generator_stop` + as the first line after the module docstring. Documenting Code ---------------- diff --git a/README.rst b/README.rst index 1ff5da25c1..6540d82a91 100644 --- a/README.rst +++ b/README.rst @@ -31,14 +31,7 @@ First, either clone the repository with ``git clone git://github.com/sopel-irc/sopel.git`` or download a tarball `from GitHub `_. -Note: Sopel requires Python 2.7.x or Python 3.3+ to run. On Python 2.7, -Sopel requires ``backports.ssl_match_hostname`` to be installed. Use -``pip install backports.ssl_match_hostname`` or -``yum install python-backports.ssl_match_hostname`` to install it, or download -and install it manually `from PyPI `_. - -Important: Sopel 8.0 will drop support for many old Python versions, -including Python 2.7! +Note: Sopel requires Python 3.6+ to run. In the source directory (whether cloned or from the tarball) run ``pip install -e .``. You can then run ``sopel`` to configure and start the bot. diff --git a/checkstyle.sh b/checkstyle.sh index 72b5fa1d74..57febe811e 100755 --- a/checkstyle.sh +++ b/checkstyle.sh @@ -1,9 +1,5 @@ #!/bin/sh -find_source_files() { - find . -name '*.py' -size +0 -print | grep -ve './docs' -e 'env' -e './contrib' -e './conftest.py' -} -files=$(find_source_files) # For now, go through all the checking stages and only die at the end exit_code=0 @@ -12,20 +8,4 @@ if ! flake8; then exit_code=1 fi -# Find files which use the unicode type but (heuristically) don't make it py3 -# safe -fail_py3_unicode=false -for file in $(find_source_files); do - if grep -qle 'unicode(' -e 'class .*(unicode)' $file; then - if ! grep -ql 'unicode = str' $file; then - echo "Suspicious 'unicode' use: $file" - fail_py3_unicode=true - fi - fi -done -if $fail_py3_unicode; then - echo "ERROR: Above files use unicode() but do not make it safe for Python 3." - exit_code=1 -fi - exit $exit_code diff --git a/dev-requirements.txt b/dev-requirements.txt index a150208986..8d9f4fd8d6 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,31 +1,13 @@ -coveralls<2.0; python_version < '3.5' -coveralls>=2.0; python_version >= '3.5' -flake8<3.6.0; python_version == '3.3' -flake8>=3.7.0,<3.8.0; python_version != '3.3' +coveralls>=2.0 +flake8 flake8-coding -flake8-future-import<0.4.6 -flake8-import-order; python_version > '3.3' -flake8-import-order<=1.18.1; python_version <= '3.3' -# transitive dependency of Sphinx -# added f-strings in 2.x, but it's not worth doing a whole suite of version markers -MarkupSafe<2.0 -pytest<3.3; python_version == '3.3' -pytest>=4.6,<4.7; python_version != '3.3' -pytest-vcr==1.0.2; python_version != '3.3' -pytest-vcr==0.3.0; python_version == '3.3' -PyYAML<5.1; python_version == '3.3' -PyYAML<5.3; python_version == '3.4' +flake8-future-import +flake8-import-order +pytest>=4.6,<4.7 +pytest-vcr==1.0.2 requests-mock==1.9.1 -setuptools<40.0; python_version == '3.3' -# use Sphinx 3.x until dev begins on Sopel 8 and we drop the dead snakes -sphinx<4.0 -# autoprogram extension added type annotations in 0.1.6 -# such annotations require Python 3.5 -sphinxcontrib-autoprogram<0.1.6; python_version < '3.5' +sphinx # further constrain autoprogram version because the new (in 2021) maintainer # already demonstrated a willingness to make major changes in patch versions -sphinxcontrib-autoprogram<=0.1.7; python_version >= '3.5' -vcrpy==2.1.1; python_version == '2.7' -vcrpy<1.12.0; python_version == '3.3' -vcrpy<2.1.0; python_version == '3.4' -vcrpy<3.0.0; python_version >= '3.5' +sphinxcontrib-autoprogram<=0.1.7 +vcrpy<3.0.0 diff --git a/docs/source/conf.py b/docs/source/conf.py index f227476e3b..eda29d5268 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -1,5 +1,4 @@ -# -*- coding: utf-8 -*- -# +from __future__ import generator_stop # Sopel IRC Bot documentation build configuration file, created by # sphinx-quickstart on Mon Jul 16 23:45:29 2012. # diff --git a/pytest_run.py b/pytest_run.py index a0a1dd8ad9..023914483e 100755 --- a/pytest_run.py +++ b/pytest_run.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# coding=utf-8 """This is a script for running pytest from the command line. This script exists so that the project directory gets added to sys.path, which @@ -11,7 +10,7 @@ https://sopel.chat """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop if __name__ == "__main__": import sys diff --git a/requirements.txt b/requirements.txt index 60572eb803..b68186af9b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,23 +1,7 @@ -xmltodict<0.12.0; python_version == '3.3' -xmltodict==0.12; python_version != '3.3' +xmltodict==0.12 pytz praw>=4.0.0,<6.0.0 -# transitive dependency of praw; v0.18 introduced f-string syntax -update-checker<0.18; python_version < '3.6' -geoip2<3.0; python_version <= '3.5' and python_version != '2.7' -geoip2>=3.0,<4.0; python_version == '2.7' -geoip2>=4.0,<5.0; python_version >= '3.6' -# transitive dependency of geoip2; v2 dropped py2.7 & py3 < 3.6 -maxminddb<2.0; python_version < '3.6' -ipaddress<2.0; python_version < '3.3' +geoip2>=4.0,<5.0 requests>=2.0.0,<3.0.0 -# transitive dependency of requests -# 2.0 will drop EOL Python 2.7 & 3.5, just like Sopel 8 plans to -urllib3<1.27; python_version != '3.3' and python_version != '3.4' -urllib3<1.23; python_version == '3.3' -urllib3<1.25; python_version == '3.4' -dnspython<2.0; python_version == '2.7' -dnspython<1.16.0; python_version == '3.3' -dnspython<3.0; python_version >= '3.4' -sqlalchemy<1.3; python_version == '3.3' -sqlalchemy<1.4; python_version != '3.3' +dnspython<3.0 +sqlalchemy<1.4 diff --git a/setup.cfg b/setup.cfg index 7fc3c896e1..ca5ab4fae8 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,17 +16,14 @@ classifiers = License :: Eiffel Forum License (EFL) License :: OSI Approved :: Eiffel Forum License Operating System :: POSIX :: Linux - Programming Language :: Python :: 2.7 - Programming Language :: Python :: 3.3 - Programming Language :: Python :: 3.4 - Programming Language :: Python :: 3.5 Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 Topic :: Communications :: Chat :: Internet Relay Chat [options] -python_requires = >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, <4 +python_requires = >=3.6, <4 packages = find: zip_safe = false @@ -53,14 +50,10 @@ ignore = # These are forbidding certain __future__ imports. The future-import plugin # has errors both for having and not having them; we want to have these until # Sopel no longer supports Python versions that require them. - FI50,FI51,FI53,FI54,FI55, + FI55, # These would require future imports that are not needed any more on Sopel's - # oldest supported Python version (2.7). - FI12,FI16,FI17, - # We ignore the error for missing generator_stop because it's only available - # in Python 3.5+ (switch this to FI55 in the above list when Sopel drops - # support for Python older than 3.5) - FI15, + # oldest supported Python version (3.6). + FI10,FI11,FI12,FI13,FI14,FI16,FI17, # Ignore "annotations" future import, since it's not available before 3.7 FI18 exclude = @@ -68,4 +61,4 @@ exclude = env/*, contrib/*, conftest.py -accept-encodings = utf-8 +no-accept-encodings = True diff --git a/setup.py b/setup.py index 3ce6ae2994..1e16b6afe9 100755 --- a/setup.py +++ b/setup.py @@ -1,6 +1,5 @@ #!/usr/bin/env python -# coding=utf-8 -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import sys @@ -34,16 +33,17 @@ # We check Python's version ourselves in case someone installed Sopel on an # old version of pip (<9.0.0), which doesn't know about `python_requires`. -if sys.version_info < (2, 7) or ( - sys.version_info.major >= 3 and sys.version_info < (3, 3) -): +if sys.version_info < (3, 6): # Maybe not the best way to do this, but this question is tiring. - raise ImportError('Sopel requires Python 2.7+ or 3.3+.') -# Py2 EOL: https://www.python.org/dev/peps/pep-0373/#maintenance-releases -if sys.version_info.major == 2: + raise ImportError('Sopel requires Python 3.6+.') + +# Py3.6 EOL: https://www.python.org/dev/peps/pep-0494/#lifespan +if sys.version_info < (3, 7): + # TODO check this warning before releasing Sopel 8.0 print( - 'Warning: Python 2.x has reached end of life and will receive ' - 'no further updates. Sopel 8.0 will drop support for it.', + 'Warning: Python 3.6 will reach end of life by the end of 2021 ' + 'and will receive no further updates. ' + 'Sopel 9.0 will drop support for it.', file=sys.stderr, ) @@ -54,8 +54,6 @@ def read_reqs(path): requires = read_reqs('requirements.txt') -if sys.version_info[0] < 3: - requires.append('backports.ssl_match_hostname') dev_requires = requires + read_reqs('dev-requirements.txt') setup( diff --git a/sopel.py b/sopel.py index 34ef947e92..3b98ccc424 100755 --- a/sopel.py +++ b/sopel.py @@ -1,6 +1,5 @@ #!/usr/bin/env python3 -# coding=utf-8 -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import sys diff --git a/sopel/__init__.py b/sopel/__init__.py index 1add04f02a..12ef61d824 100644 --- a/sopel/__init__.py +++ b/sopel/__init__.py @@ -1,4 +1,3 @@ -# coding=utf-8 # ASCII ONLY IN THIS FILE THOUGH!!!!!!! # Python does some stupid bullshit of respecting LC_ALL over the encoding on the # file, so in order to undo Python's ridiculous fucking idiocy, we have to have @@ -10,7 +9,7 @@ # # Licensed under the Eiffel Forum License 2. -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop from collections import namedtuple import locale @@ -35,12 +34,11 @@ ] loc = locale.getlocale() -if sys.version_info.major > 2: - if not loc[1] or 'UTF-8' not in loc[1]: - print('WARNING!!! You are running with a non-UTF8 locale environment ' - 'variables (e.g. LC_ALL is set to "C"), which makes Python 3 do ' - 'stupid things. If you get strange errors, please set it to ' - 'something like "en_US.UTF-8".', file=sys.stderr) +if not loc[1] or 'UTF-8' not in loc[1]: + print('WARNING!!! You are running with a non-UTF8 locale environment ' + 'variable (e.g. LC_ALL is set to "C"), which makes Python 3 do ' + 'stupid things. If you get strange errors, please set it to ' + 'something like "en_US.UTF-8".', file=sys.stderr) __version__ = pkg_resources.get_distribution('sopel').version diff --git a/sopel/bot.py b/sopel/bot.py index f5d4ebfc9f..dea31b6b5c 100644 --- a/sopel/bot.py +++ b/sopel/bot.py @@ -1,4 +1,3 @@ -# coding=utf-8 # Copyright 2008, Sean B. Palmer, inamidst.com # Copyright © 2012, Elad Alfassa # Copyright 2012-2015, Elsie Powell, http://embolalia.com @@ -6,7 +5,7 @@ # # Licensed under the Eiffel Forum License 2. -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop from ast import literal_eval from datetime import datetime @@ -14,7 +13,6 @@ import logging import re import signal -import sys import threading import time @@ -44,14 +42,6 @@ SIGNALS = QUIT_SIGNALS + RESTART_SIGNALS -if sys.version_info.major >= 3: - unicode = str - basestring = str - py3 = True -else: - py3 = False - - class Sopel(irc.AbstractBot): def __init__(self, config, daemon=False): super(Sopel, self).__init__(config) @@ -358,7 +348,7 @@ def __setup_plugins_check_manual_url_callbacks(self, name): # nothing to check return - for key, callback in tools.iteritems(self.memory['url_callbacks']): + for key, callback in self.memory['url_callbacks'].items(): is_checked = getattr( callback, '_sopel_url_callbacks_checked', False) if is_checked: @@ -1129,7 +1119,7 @@ def register_url_callback(self, pattern, callback): if 'url_callbacks' not in self.memory: self.memory['url_callbacks'] = tools.SopelMemory() - if isinstance(pattern, basestring): + if isinstance(pattern, str): pattern = re.compile(pattern) # Mark the callback as checked: using this method is safe. @@ -1180,7 +1170,7 @@ def unregister_url_callback(self, pattern, callback): # nothing to unregister return - if isinstance(pattern, basestring): + if isinstance(pattern, str): pattern = re.compile(pattern) try: @@ -1216,7 +1206,7 @@ def search_url_callbacks(self, url): # nothing to search return - for regex, function in tools.iteritems(self.memory['url_callbacks']): + for regex, function in self.memory['url_callbacks'].items(): match = regex.search(url) if match: yield function, match diff --git a/sopel/cli/__init__.py b/sopel/cli/__init__.py index 623d5265a3..cefdc40fd4 100644 --- a/sopel/cli/__init__.py +++ b/sopel/cli/__init__.py @@ -1,5 +1,4 @@ -# coding=utf-8 -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop # Shortcut imports from .utils import ( # noqa diff --git a/sopel/cli/config.py b/sopel/cli/config.py index 1c8923cf93..f19d79465f 100644 --- a/sopel/cli/config.py +++ b/sopel/cli/config.py @@ -1,6 +1,5 @@ -# coding=utf-8 """Sopel Config Command Line Interface (CLI): ``sopel-config``""" -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import argparse import os diff --git a/sopel/cli/plugins.py b/sopel/cli/plugins.py index 8cd34f6250..b40dec92d0 100644 --- a/sopel/cli/plugins.py +++ b/sopel/cli/plugins.py @@ -1,6 +1,5 @@ -# coding=utf-8 """Sopel Plugins Command Line Interface (CLI): ``sopel-plugins``""" -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import argparse import inspect diff --git a/sopel/cli/run.py b/sopel/cli/run.py index 3c47251711..68d2ce3444 100755 --- a/sopel/cli/run.py +++ b/sopel/cli/run.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python2.7 -# coding=utf-8 """ Sopel - An IRC Bot Copyright 2008, Sean B. Palmer, inamidst.com @@ -8,7 +6,7 @@ https://sopel.chat """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import argparse import logging @@ -24,15 +22,19 @@ # This is in case someone somehow manages to install Sopel on an old version # of pip (<9.0.0), which doesn't know about `python_requires`, or tries to run # from source on an unsupported version of Python. -if sys.version_info < (2, 7) or ( - sys.version_info.major >= 3 and sys.version_info < (3, 3) -): - tools.stderr('Error: Sopel requires Python 2.7+ or 3.3+.') +if sys.version_info < (3, 6): + tools.stderr('Error: Sopel requires Python 3.6+.') sys.exit(1) -if sys.version_info.major == 2: - tools.stderr( - 'Warning: Python 2.x has reached end of life and will receive ' - 'no further updates. Sopel 8.0 will drop support for it.') + +# Py3.6 EOL: https://www.python.org/dev/peps/pep-0494/#lifespan +if sys.version_info < (3, 7): + # TODO check this warning before releasing Sopel 8.0 + print( + 'Warning: Python 3.6 will reach end of life by the end of 2021 ' + 'and will receive no further updates. ' + 'Sopel 9.0 will drop support for it.', + file=sys.stderr, + ) LOGGER = logging.getLogger(__name__) diff --git a/sopel/cli/utils.py b/sopel/cli/utils.py index 4f3623c6fd..8a6facd8fa 100644 --- a/sopel/cli/utils.py +++ b/sopel/cli/utils.py @@ -1,5 +1,4 @@ -# coding=utf-8 -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import inspect import logging diff --git a/sopel/config/__init__.py b/sopel/config/__init__.py index 8781008e41..21e334bd3c 100644 --- a/sopel/config/__init__.py +++ b/sopel/config/__init__.py @@ -1,4 +1,3 @@ -# coding=utf-8 """Sopel's configuration module. The :class:`~sopel.config.Config` object provides an interface to access Sopel's @@ -48,20 +47,14 @@ class SpamSection(config.types.StaticSection): # Copyright © 2012, Elad Alfassa # Licensed under the Eiffel Forum License 2. -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop +import configparser import os -import sys from sopel import tools from . import core_section, types -if sys.version_info.major < 3: - import ConfigParser -else: - basestring = str - import configparser as ConfigParser - __all__ = [ 'core_section', @@ -131,7 +124,7 @@ def __init__(self, filename, validate=True): The config's ``basename`` is useful as a component :ref:`of log file names `, for example. """ - self.parser = ConfigParser.RawConfigParser(allow_no_value=True) + self.parser = configparser.RawConfigParser(allow_no_value=True) """The configuration parser object that does the heavy lifting. .. seealso:: @@ -238,7 +231,7 @@ def add_section(self, name): """ try: return self.parser.add_section(name) - except ConfigParser.DuplicateSectionError: + except configparser.DuplicateSectionError: return False def define_section(self, name, cls_, validate=True): @@ -336,7 +329,7 @@ def get_list(self, name): value = getattr(self, name) if not value: return [] - if isinstance(value, basestring): + if isinstance(value, str): value = value.split(',') # Keep the split value, so we don't have to keep doing this setattr(self, name, value) diff --git a/sopel/config/core_section.py b/sopel/config/core_section.py index 025a897196..020851c99a 100644 --- a/sopel/config/core_section.py +++ b/sopel/config/core_section.py @@ -1,6 +1,4 @@ -# coding=utf-8 - -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import os.path diff --git a/sopel/config/types.py b/sopel/config/types.py index a7701f2d60..e1dcaebaae 100644 --- a/sopel/config/types.py +++ b/sopel/config/types.py @@ -1,4 +1,3 @@ -# coding=utf-8 """Types for creating section definitions. A section definition consists of a subclass of :class:`StaticSection`, on which @@ -24,19 +23,14 @@ ValueError: ListAttribute value must be a list. """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import getpass import os.path import re -import sys from sopel.tools import deprecated, get_input -if sys.version_info.major >= 3: - unicode = str - basestring = (str, bytes) - class NO_DEFAULT(object): """A special value to indicate that there should be no default.""" @@ -267,7 +261,7 @@ def __delete__(self, instance): def _parse_boolean(value): if value is True or value == 1: return value - if isinstance(value, basestring): + if isinstance(value, str): return value.lower() in ['1', 'yes', 'y', 'true', 'on'] return bool(value) @@ -325,7 +319,7 @@ def serialize(self, value): :param value: the option value :rtype: str """ - return unicode(value) + return str(value) def parse(self, value): """No-op: simply returns the given ``value``, unchanged. @@ -392,7 +386,7 @@ def parse(self, value): """ if value is True or value == 1: return True - if isinstance(value, basestring): + if isinstance(value, str): return value.lower() in [ '1', 'enable', diff --git a/sopel/coretasks.py b/sopel/coretasks.py index 979d42c8a0..a02269c908 100644 --- a/sopel/coretasks.py +++ b/sopel/coretasks.py @@ -1,4 +1,3 @@ -# coding=utf-8 """Core Sopel plugin that handles IRC protocol functions. This plugin allows the bot to run without user-facing functionality: @@ -21,7 +20,7 @@ # Copyright 2019, Florian Strzelecki # # Licensed under the Eiffel Forum License 2. -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import base64 import collections @@ -29,19 +28,15 @@ import functools import logging import re -import sys import time from sopel import loader, module, plugin from sopel.config import ConfigurationError from sopel.irc import isupport from sopel.irc.utils import CapReq, MyInfo -from sopel.tools import events, Identifier, iteritems, SopelMemory, target, web +from sopel.tools import events, Identifier, SopelMemory, target, web -if sys.version_info.major >= 3: - unicode = str - LOGGER = logging.getLogger(__name__) CORE_QUERYTYPE = '999' @@ -455,7 +450,7 @@ def handle_names(bot, trigger): for name in names: priv = 0 - for prefix, value in iteritems(mapping): + for prefix, value in mapping.items(): if prefix in name: priv = priv | value nick = Identifier(name.lstrip(''.join(mapping.keys()))) @@ -937,7 +932,7 @@ def acct_warn(bot, cap): if cap not in bot._cap_reqs: bot._cap_reqs[cap] = [CapReq('', 'coretasks', acct_warn)] - for cap, reqs in iteritems(bot._cap_reqs): + for cap, reqs in bot._cap_reqs.items(): # At this point, we know mandatory and prohibited don't co-exist, but # we need to call back for optionals if they're also prohibited prefix = '' @@ -1187,13 +1182,13 @@ def blocks(bot, trigger): if len(text) == 3 and text[1] == "list": if text[2] == "hostmask": if len(masks) > 0: - blocked = ', '.join(unicode(mask) for mask in masks) + blocked = ', '.join(str(mask) for mask in masks) bot.say("Blocked hostmasks: {}".format(blocked)) else: bot.reply(STRINGS['nonelisted'] % ('hostmasks')) elif text[2] == "nick": if len(nicks) > 0: - blocked = ', '.join(unicode(nick) for nick in nicks) + blocked = ', '.join(str(nick) for nick in nicks) bot.say("Blocked nicks: {}".format(blocked)) else: bot.reply(STRINGS['nonelisted'] % ('nicks')) @@ -1220,7 +1215,7 @@ def blocks(bot, trigger): bot.reply(STRINGS['no_nick'] % (text[3])) return nicks.remove(Identifier(text[3])) - bot.config.core.nick_blocks = [unicode(n) for n in nicks] + bot.config.core.nick_blocks = [str(n) for n in nicks] bot.config.save() bot.reply(STRINGS['success_del'] % (text[3])) elif text[2] == "hostmask": @@ -1229,7 +1224,7 @@ def blocks(bot, trigger): bot.reply(STRINGS['no_host'] % (text[3])) return masks.remove(mask) - bot.config.core.host_blocks = [unicode(m) for m in masks] + bot.config.core.host_blocks = [str(m) for m in masks] bot.config.save() bot.reply(STRINGS['success_del'] % (text[3])) else: diff --git a/sopel/db.py b/sopel/db.py index c29b519565..c06b41c7e5 100644 --- a/sopel/db.py +++ b/sopel/db.py @@ -1,11 +1,9 @@ -# coding=utf-8 -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import errno import json import logging import os.path -import sys import traceback from sqlalchemy import Column, create_engine, ForeignKey, Integer, String @@ -16,10 +14,6 @@ from sopel.tools import Identifier -if sys.version_info.major >= 3: - unicode = str - basestring = str - LOGGER = logging.getLogger(__name__) @@ -29,7 +23,7 @@ def _deserialize(value): return None # sqlite likes to return ints for strings that look like ints, even though # the column type is string. That's how you do dynamic typing wrong. - value = unicode(value) + value = str(value) # Just in case someone's mucking with the DB in a way we can't account for, # ignore json parsing errors try: diff --git a/sopel/formatting.py b/sopel/formatting.py index 4272a060b5..8674001f92 100644 --- a/sopel/formatting.py +++ b/sopel/formatting.py @@ -1,4 +1,3 @@ -# coding=utf-8 """The formatting module includes functions to apply IRC formatting to text. *Availability: 4.5+* @@ -6,11 +5,10 @@ # Copyright 2014, Elsie Powell, embolalia.com # Copyright 2019, dgw, technobabbl.es # Licensed under the Eiffel Forum License 2. -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import re import string -import sys __all__ = [ @@ -38,9 +36,6 @@ 'colors', ] -if sys.version_info.major >= 3: - unicode = str - # Color names are as specified at http://www.mirc.com/colors.html CONTROL_NORMAL = '\x0f' @@ -173,7 +168,7 @@ def _get_color(color): if isinstance(color, int): if color > 99: raise ValueError('Can not specify a color above 99.') - return unicode(color).rjust(2, '0') + return str(color).rjust(2, '0') # You can also pass the name of the color color_name = color.upper() diff --git a/sopel/irc/__init__.py b/sopel/irc/__init__.py index 2e888baca9..ae265f7073 100644 --- a/sopel/irc/__init__.py +++ b/sopel/irc/__init__.py @@ -1,4 +1,3 @@ -# coding=utf-8 """:mod:`sopel.irc` is the core IRC module for Sopel. This sub-package contains everything that is related to the IRC protocol @@ -23,35 +22,19 @@ # Copyright 2019, Florian Strzelecki # # Licensed under the Eiffel Forum License 2. -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop from datetime import datetime import logging import os -import sys import threading import time -try: - import ssl - if not hasattr(ssl, 'match_hostname'): - # Attempt to import ssl_match_hostname from python-backports - # TODO: Remove when dropping Python 2 support - import backports.ssl_match_hostname - ssl.match_hostname = backports.ssl_match_hostname.match_hostname - ssl.CertificateError = backports.ssl_match_hostname.CertificateError - has_ssl = True -except ImportError: - # no SSL support - has_ssl = False - from sopel import tools, trigger from .backends import AsynchatBackend, SSLAsynchatBackend from .isupport import ISupport from .utils import CapReq, safe -if sys.version_info.major >= 3: - unicode = str __all__ = ['abstract_backends', 'backends', 'utils'] @@ -150,16 +133,11 @@ def get_irc_backend(self): } if self.settings.core.use_ssl: - if has_ssl: - backend_class = SSLAsynchatBackend - backend_kwargs.update({ - 'verify_ssl': self.settings.core.verify_ssl, - 'ca_certs': self.settings.core.ca_certs, - }) - else: - LOGGER.warning( - 'SSL is not available on your system; ' - 'attempting connection without it') + backend_class = SSLAsynchatBackend + backend_kwargs.update({ + 'verify_ssl': self.settings.core.verify_ssl, + 'ca_certs': self.settings.core.ca_certs, + }) return backend_class(*backend_args, **backend_kwargs) @@ -602,7 +580,7 @@ def say(self, text, recipient, max_messages=1, truncation='', trailing=''): """ excess = '' - if not isinstance(text, unicode): + if not isinstance(text, str): # Make sure we are dealing with a Unicode string text = text.decode('utf-8') diff --git a/sopel/irc/abstract_backends.py b/sopel/irc/abstract_backends.py index 758d27c7d1..3ba5fda288 100644 --- a/sopel/irc/abstract_backends.py +++ b/sopel/irc/abstract_backends.py @@ -1,8 +1,7 @@ -# coding=utf-8 # Copyright 2019, Florian Strzelecki # # Licensed under the Eiffel Forum License 2. -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop from .utils import safe diff --git a/sopel/irc/backends.py b/sopel/irc/backends.py index 12d0eb2b0a..9203e08899 100644 --- a/sopel/irc/backends.py +++ b/sopel/irc/backends.py @@ -1,10 +1,9 @@ -# coding=utf-8 # Copyright 2019, Florian Strzelecki # # Licensed under the Eiffel Forum License 2. # When working on core IRC protocol related features, consult protocol # documentation at http://www.irchelp.org/irchelp/rfc/ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import asynchat import asyncore @@ -13,7 +12,7 @@ import logging import os import socket -import sys +import ssl import threading from sopel import loader, plugin @@ -21,22 +20,6 @@ from .abstract_backends import AbstractIRCBackend from .utils import get_cnames -try: - import ssl - if not hasattr(ssl, 'match_hostname'): - # Attempt to import ssl_match_hostname from python-backports - # TODO: Remove when dropping Python 2 support - import backports.ssl_match_hostname - ssl.match_hostname = backports.ssl_match_hostname.match_hostname - ssl.CertificateError = backports.ssl_match_hostname.CertificateError - has_ssl = True -except ImportError: - # no SSL support - has_ssl = False - -if sys.version_info.major >= 3: - unicode = str - LOGGER = logging.getLogger(__name__) @@ -215,15 +198,15 @@ def collect_incoming_data(self, data): """ # We can't trust clients to pass valid Unicode. try: - data = unicode(data, encoding='utf-8') + data = str(data, encoding='utf-8') except UnicodeDecodeError: # not Unicode; let's try CP-1252 try: - data = unicode(data, encoding='cp1252') + data = str(data, encoding='cp1252') except UnicodeDecodeError: # Okay, let's try ISO 8859-1 try: - data = unicode(data, encoding='iso8859-1') + data = str(data, encoding='iso8859-1') except UnicodeDecodeError: # Discard line if encoding is unknown return diff --git a/sopel/irc/isupport.py b/sopel/irc/isupport.py index 12ae56780f..48b2622c9d 100644 --- a/sopel/irc/isupport.py +++ b/sopel/irc/isupport.py @@ -1,4 +1,3 @@ -# coding=utf-8 """IRC Tools for ISUPPORT management. When a server wants to advertise its features and settings, it can use the @@ -12,7 +11,7 @@ # Copyright 2019, Florian Strzelecki # # Licensed under the Eiffel Forum License 2. -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import functools import itertools diff --git a/sopel/irc/utils.py b/sopel/irc/utils.py index 6a65342754..f4d53a95a7 100644 --- a/sopel/irc/utils.py +++ b/sopel/irc/utils.py @@ -1,19 +1,14 @@ -# coding=utf-8 # Copyright 2019, Florian Strzelecki # # Licensed under the Eiffel Forum License 2. -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import collections -import sys from dns import rdtypes, resolver from sopel.tools import deprecated -if sys.version_info.major >= 3: - unicode = str - MYINFO_ARGS = ['client', 'servername', 'version'] @@ -45,8 +40,7 @@ def safe(string): :raises TypeError: when ``string`` is ``None`` This function removes newlines from a string and always returns a unicode - string (as in ``str`` on Python 3 and ``unicode`` on Python 2), but doesn't - strip or alter it in any other way:: + string (``str``), but doesn't strip or alter it in any other way:: >>> safe('some text\\r\\n') 'some text' @@ -61,11 +55,8 @@ def safe(string): """ if string is None: raise TypeError('safe function requires a string, not NoneType') - if sys.version_info.major >= 3 and isinstance(string, bytes): + if isinstance(string, bytes): string = string.decode("utf8") - elif sys.version_info.major < 3: - if not isinstance(string, unicode): - string = unicode(string, encoding='utf8') string = string.replace('\n', '') string = string.replace('\r', '') return string diff --git a/sopel/loader.py b/sopel/loader.py index eb55d76cd1..93a57372b0 100644 --- a/sopel/loader.py +++ b/sopel/loader.py @@ -1,4 +1,3 @@ -# coding=utf-8 """Utility functions to manage plugin callables from a Python module. .. important:: @@ -10,7 +9,7 @@ Do **not** build your plugin based on what is here, you do **not** need to. """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import inspect import logging @@ -18,11 +17,7 @@ import sys from sopel.config.core_section import COMMAND_DEFAULT_HELP_PREFIX -from sopel.tools import deprecated, itervalues - - -if sys.version_info.major >= 3: - basestring = (str, bytes) +from sopel.tools import deprecated LOGGER = logging.getLogger(__name__) @@ -106,7 +101,7 @@ def clean_callable(func, config): if not hasattr(func, 'event'): func.event = ['PRIVMSG'] else: - if isinstance(func.event, basestring): + if isinstance(func.event, (str, bytes)): func.event = [func.event.upper()] else: func.event = [event.upper() for event in func.event] @@ -114,7 +109,7 @@ def clean_callable(func, config): # TODO: remove in Sopel 8 # Stay compatible with old Phenny/Jenni "modules" (plugins) # that set the attribute directly - if hasattr(func, 'rule') and isinstance(func.rule, basestring): + if hasattr(func, 'rule') and isinstance(func.rule, (str, bytes)): LOGGER.warning( 'The `rule` attribute of %s.%s should be a list, not a string; ' 'this behavior is deprecated in Sopel 7.1 ' @@ -282,7 +277,7 @@ def clean_module(module, config): shutdowns = [] jobs = [] urls = [] - for obj in itervalues(vars(module)): + for obj in vars(module).values(): if callable(obj): is_sopel_callable = getattr(obj, '_sopel_callable', False) is True if getattr(obj, '__name__', None) == 'shutdown': diff --git a/sopel/logger.py b/sopel/logger.py index 75cd163199..50dd5ebd49 100644 --- a/sopel/logger.py +++ b/sopel/logger.py @@ -1,5 +1,4 @@ -# coding=utf-8 -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import logging from logging.config import dictConfig diff --git a/sopel/module.py b/sopel/module.py index 9da952a9d9..4d0b249274 100644 --- a/sopel/module.py +++ b/sopel/module.py @@ -1,4 +1,3 @@ -# coding=utf-8 """The :mod:`sopel.module` sub-module is replaced by :mod:`sopel.plugin`. .. deprecated:: 7.1 @@ -6,7 +5,7 @@ Use :mod:`sopel.plugin` instead. This will be removed in Sopel 9. """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop # Import everything from sopel.plugin at the time of replacement. # Everything new from this point on must *not* leak here. diff --git a/sopel/modules/__init__.py b/sopel/modules/__init__.py index c5b3997ac1..c813b73b6b 100644 --- a/sopel/modules/__init__.py +++ b/sopel/modules/__init__.py @@ -1,2 +1 @@ -# coding=utf-8 -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop diff --git a/sopel/modules/admin.py b/sopel/modules/admin.py index 50f3c11fd8..deabd2b65b 100644 --- a/sopel/modules/admin.py +++ b/sopel/modules/admin.py @@ -1,4 +1,3 @@ -# coding=utf-8 """ admin.py - Sopel Admin Plugin Copyright 2010-2011, Sean B. Palmer (inamidst.com) and Michael Yanovich @@ -10,7 +9,7 @@ https://sopel.chat """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import logging diff --git a/sopel/modules/adminchannel.py b/sopel/modules/adminchannel.py index 42c0c21c40..1e49b643ee 100644 --- a/sopel/modules/adminchannel.py +++ b/sopel/modules/adminchannel.py @@ -1,4 +1,3 @@ -# coding=utf-8 """ adminchannel.py - Sopel Channel Admin Plugin Copyright 2010-2011, Michael Yanovich, Alek Rollyson, and Elsie Powell @@ -7,7 +6,7 @@ https://sopel.chat """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import re diff --git a/sopel/modules/announce.py b/sopel/modules/announce.py index 2d2997981e..b0bcea5ee1 100644 --- a/sopel/modules/announce.py +++ b/sopel/modules/announce.py @@ -1,4 +1,3 @@ -# coding=utf-8 """ announce.py - Sopel Announcement Plugin Sends announcements to all channels the bot has joined. @@ -7,7 +6,7 @@ https://sopel.chat """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop from sopel import plugin diff --git a/sopel/modules/bugzilla.py b/sopel/modules/bugzilla.py index 6c92265405..56b039a3aa 100644 --- a/sopel/modules/bugzilla.py +++ b/sopel/modules/bugzilla.py @@ -1,4 +1,3 @@ -# coding=utf-8 """ bugzilla.py - Sopel Bugzilla Plugin Copyright 2013-2015, Embolalia, embolalia.com @@ -6,7 +5,7 @@ https://sopel.chat """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import logging import re diff --git a/sopel/modules/calc.py b/sopel/modules/calc.py index 521c850859..73b8bc629b 100644 --- a/sopel/modules/calc.py +++ b/sopel/modules/calc.py @@ -1,4 +1,3 @@ -# coding=utf-8 """ calc.py - Sopel Calculator Plugin Copyright 2008, Sean B. Palmer, inamidst.com @@ -6,7 +5,7 @@ https://sopel.chat """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop from sopel import plugin from sopel.tools.calculation import eval_equation diff --git a/sopel/modules/choose.py b/sopel/modules/choose.py index 69ecc17119..3008377b51 100644 --- a/sopel/modules/choose.py +++ b/sopel/modules/choose.py @@ -1,4 +1,3 @@ -# coding=utf-8 """ choose.py - Sopel Choice Plugin Copyright 2010-2013, Dimitri "Tyrope" Molenaars, TyRope.nl @@ -9,19 +8,13 @@ https://sopel.chat """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import random import unicodedata from sopel import formatting, plugin -# Remove when dropping py2 support -try: - str = unicode -except NameError: - pass - def _format_safe(text): """Remove excess whitespace and terminate IRC formatting. diff --git a/sopel/modules/clock.py b/sopel/modules/clock.py index 1b4eaeb953..4c4b13fb56 100644 --- a/sopel/modules/clock.py +++ b/sopel/modules/clock.py @@ -1,4 +1,3 @@ -# coding=utf-8 """clock.py - Sopel Clock Plugin Copyright 2008-9, Sean B. Palmer, inamidst.com @@ -7,7 +6,7 @@ https://sopel.chat """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop from sopel import plugin, tools from sopel.tools.time import ( diff --git a/sopel/modules/countdown.py b/sopel/modules/countdown.py index 182f39da48..65025e1067 100644 --- a/sopel/modules/countdown.py +++ b/sopel/modules/countdown.py @@ -1,4 +1,3 @@ -# coding=utf-8 """ countdown.py - Sopel Countdown Plugin Copyright 2011, Michael Yanovich, yanovich.net @@ -6,7 +5,7 @@ https://sopel.chat """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import datetime diff --git a/sopel/modules/currency.py b/sopel/modules/currency.py index cc1f8a7350..1048b94e26 100644 --- a/sopel/modules/currency.py +++ b/sopel/modules/currency.py @@ -1,4 +1,3 @@ -# coding=utf-8 """ currency.py - Sopel Currency Conversion Plugin Copyright 2013, Elsie Powell, embolalia.com @@ -7,7 +6,7 @@ https://sopel.chat """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import logging import re diff --git a/sopel/modules/dice.py b/sopel/modules/dice.py index 583f8ec3ee..18a7a21aba 100644 --- a/sopel/modules/dice.py +++ b/sopel/modules/dice.py @@ -1,4 +1,3 @@ -# coding=utf-8 """ dice.py - Sopel Dice Plugin Copyright 2010-2013, Dimitri "Tyrope" Molenaars, TyRope.nl @@ -7,7 +6,7 @@ https://sopel.chat """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import operator import random diff --git a/sopel/modules/emoticons.py b/sopel/modules/emoticons.py index 5f55f1adbb..3eedaa59f3 100644 --- a/sopel/modules/emoticons.py +++ b/sopel/modules/emoticons.py @@ -1,4 +1,3 @@ -# coding=utf-8 """ emoticons.py - Sopel Emoticons Plugin Copyright 2018, brasstax @@ -6,7 +5,7 @@ https://sopel.chat """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop from sopel import plugin diff --git a/sopel/modules/find.py b/sopel/modules/find.py index 647c4ac068..ef649f3b88 100644 --- a/sopel/modules/find.py +++ b/sopel/modules/find.py @@ -1,4 +1,3 @@ -# coding=utf-8 """ find.py - Sopel Spelling Correction Plugin This plugin will fix spelling errors if someone corrects them @@ -12,7 +11,7 @@ https://sopel.chat """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop from collections import deque import re diff --git a/sopel/modules/find_updates.py b/sopel/modules/find_updates.py index fcb49724a8..01893c18bb 100644 --- a/sopel/modules/find_updates.py +++ b/sopel/modules/find_updates.py @@ -1,4 +1,3 @@ -# coding=utf-8 """ find_updates.py - Sopel Update Check Plugin This is separated from version.py, so that it can be easily overridden by @@ -9,7 +8,7 @@ https://sopel.chat """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import requests diff --git a/sopel/modules/help.py b/sopel/modules/help.py index 569f1b25f4..90705eb06b 100644 --- a/sopel/modules/help.py +++ b/sopel/modules/help.py @@ -1,4 +1,3 @@ -# coding=utf-8 """ help.py - Sopel Help Plugin Copyright 2008, Sean B. Palmer, inamidst.com @@ -10,7 +9,7 @@ https://sopel.chat """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import collections import logging @@ -274,8 +273,8 @@ def respond(text): bot.reply('The documentation for this command is too long; ' 'I\'m sending it to you in a private message.') - def msgfun(l): - bot.say(l, trigger.nick) + def msgfun(message): + bot.say(message, trigger.nick) else: msgfun = respond diff --git a/sopel/modules/invite.py b/sopel/modules/invite.py index 885f547999..fcd3654bd0 100644 --- a/sopel/modules/invite.py +++ b/sopel/modules/invite.py @@ -1,4 +1,3 @@ -# coding=utf-8 """ invite.py - Sopel Invite Plugin Copyright © 2016, João Vanzuita, https://github.com/converge @@ -7,7 +6,7 @@ https://sopel.chat """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop from sopel import plugin, tools diff --git a/sopel/modules/ip.py b/sopel/modules/ip.py index e6606ac801..59310d4e41 100644 --- a/sopel/modules/ip.py +++ b/sopel/modules/ip.py @@ -1,4 +1,3 @@ -# coding=utf-8 """ ip.py - Sopel GeoIP Lookup Plugin Copyright 2011, Dimitri Molenaars, TyRope.nl, @@ -8,12 +7,13 @@ https://sopel.chat """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import logging import os import socket import tarfile +from urllib.request import urlretrieve import geoip2.database @@ -21,18 +21,6 @@ from sopel.config import types from sopel.tools import web -urlretrieve = None -try: - from urllib import urlretrieve -except ImportError: - try: - # urlretrieve has been put under urllib.request in Python 3. - # It's also deprecated so this should probably be replaced with - # urllib2. - from urllib.request import urlretrieve - except ImportError: - pass - LOGGER = logging.getLogger(__name__) diff --git a/sopel/modules/isup.py b/sopel/modules/isup.py index 7290548f77..0b98858366 100644 --- a/sopel/modules/isup.py +++ b/sopel/modules/isup.py @@ -1,4 +1,3 @@ -# coding=utf-8 """ isup.py - Sopel Website Status Check Plugin Copyright 2011, Elsie Powell http://embolalia.com @@ -6,7 +5,7 @@ https://sopel.chat """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import requests diff --git a/sopel/modules/lmgtfy.py b/sopel/modules/lmgtfy.py index ee4cb943e9..a79dee25f4 100644 --- a/sopel/modules/lmgtfy.py +++ b/sopel/modules/lmgtfy.py @@ -1,4 +1,3 @@ -# coding=utf-8 """ lmgtfy.py - Sopel Let Me Google That For You Plugin Copyright 2013, Dimitri Molenaars http://tyrope.nl/ @@ -6,14 +5,11 @@ https://sopel.chat/ """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop -from sopel import plugin +from urllib.parse import urlencode -try: - from urllib.parse import urlencode -except ImportError: - from urllib import urlencode +from sopel import plugin @plugin.command('lmgtfy', 'lmgify', 'gify', 'gtfy') diff --git a/sopel/modules/meetbot.py b/sopel/modules/meetbot.py index 3eea864c31..fceae33e83 100644 --- a/sopel/modules/meetbot.py +++ b/sopel/modules/meetbot.py @@ -1,4 +1,3 @@ -# coding=utf-8 """ meetbot.py - Sopel Meeting Logger Plugin This plugin is an attempt to implement some of the functionality of Debian's meetbot @@ -7,7 +6,7 @@ https://sopel.chat """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import codecs import collections diff --git a/sopel/modules/ping.py b/sopel/modules/ping.py index 2bac281354..d45d94ed6a 100644 --- a/sopel/modules/ping.py +++ b/sopel/modules/ping.py @@ -1,11 +1,10 @@ -# coding=utf-8 """ ping.py - Sopel Ping Plugin Copyright 2008 (?), Sean B. Palmer, inamidst.com https://sopel.chat """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import random diff --git a/sopel/modules/pronouns.py b/sopel/modules/pronouns.py index 1723317371..cc57876c34 100644 --- a/sopel/modules/pronouns.py +++ b/sopel/modules/pronouns.py @@ -1,4 +1,3 @@ -# coding=utf-8 """ pronouns.py - Sopel Pronouns Plugin Copyright © 2016, Elsie Powell @@ -6,7 +5,7 @@ https://sopel.chat """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop from sopel import plugin diff --git a/sopel/modules/py.py b/sopel/modules/py.py index 3753ac1b2b..2f3dd8b986 100644 --- a/sopel/modules/py.py +++ b/sopel/modules/py.py @@ -1,4 +1,3 @@ -# coding=utf-8 """ py.py - Sopel Python Eval Plugin Copyright 2008, Sean B. Palmer, inamidst.com @@ -6,7 +5,7 @@ https://sopel.chat """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop from requests import get diff --git a/sopel/modules/rand.py b/sopel/modules/rand.py index b6daeb11c8..0b46de09f0 100644 --- a/sopel/modules/rand.py +++ b/sopel/modules/rand.py @@ -1,4 +1,3 @@ -# coding=utf-8 """ rand.py - Rand Plugin Copyright 2013, Ari Koivula, @@ -6,7 +5,7 @@ https://sopel.chat """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import random import sys diff --git a/sopel/modules/reddit.py b/sopel/modules/reddit.py index 5f53908d43..b9c77ffc87 100644 --- a/sopel/modules/reddit.py +++ b/sopel/modules/reddit.py @@ -1,4 +1,3 @@ -# coding=utf-8 """ reddit.py - Sopel Reddit Plugin Copyright 2012, Elsie Powell, embolalia.com @@ -8,11 +7,11 @@ https://sopel.chat """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import datetime as dt +import html import re -import sys import praw import prawcore @@ -23,17 +22,6 @@ from sopel.tools import time from sopel.tools.web import USER_AGENT -# clean up all of this when dropping py2/old py3 versions -if sys.version_info.major >= 3: - unicode = str - if sys.version_info.minor >= 4: - from html import unescape - else: - from html.parser import HTMLParser - unescape = HTMLParser().unescape -else: - from HTMLParser import HTMLParser - unescape = HTMLParser().unescape PLUGIN_OUTPUT_PREFIX = '[reddit] ' @@ -204,7 +192,7 @@ def say_post_info(bot, trigger, id_, show_link=True, show_comments_link=False): # the value assigned earlier will be used pass - title = unescape(s.title) + title = html.unescape(s.title) message = message.format( title=title, link=link, nsfw=nsfw, points=s.score, points_text=points_text, percent=percent, comments=s.num_comments, comments_text=comments_text, diff --git a/sopel/modules/reload.py b/sopel/modules/reload.py index 63c2755128..dfca7c1c59 100644 --- a/sopel/modules/reload.py +++ b/sopel/modules/reload.py @@ -1,4 +1,3 @@ -# coding=utf-8 """ reload.py - Sopel Plugin Reloader Plugin Copyright 2008, Sean B. Palmer, inamidst.com @@ -6,7 +5,7 @@ https://sopel.chat """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import logging import subprocess diff --git a/sopel/modules/remind.py b/sopel/modules/remind.py index 5282860f42..632f05cd9c 100644 --- a/sopel/modules/remind.py +++ b/sopel/modules/remind.py @@ -1,4 +1,3 @@ -# coding=utf-8 """ remind.py - Sopel Reminder Plugin Copyright 2011, Sean B. Palmer, inamidst.com @@ -7,7 +6,7 @@ https://sopel.chat """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import collections from datetime import datetime @@ -19,7 +18,7 @@ import pytz -from sopel import plugin, tools +from sopel import plugin from sopel.tools.time import format_time, get_timezone, validate_timezone @@ -95,7 +94,7 @@ def dump_database(filename, data): If the file does not exist, it is created. """ with io.open(filename, 'w', encoding='utf-8') as database: - for unixtime, reminders in tools.iteritems(data): + for unixtime, reminders in data.items(): for channel, nick, message in reminders: line = '%s\t%s\t%s\t%s\n' % (unixtime, channel, nick, message) database.write(line) diff --git a/sopel/modules/safety.py b/sopel/modules/safety.py index f6974b79a1..bfba68ea83 100644 --- a/sopel/modules/safety.py +++ b/sopel/modules/safety.py @@ -1,4 +1,3 @@ -# coding=utf-8 """ safety.py - Alerts about malicious URLs Copyright © 2014, Elad Alfassa, @@ -6,36 +5,23 @@ This plugin uses virustotal.com """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop +import json import logging import os.path import re -import sys import threading import time +from urllib.parse import urlparse +from urllib.request import urlretrieve + import requests from sopel import formatting, plugin, tools from sopel.config import types -try: - # This is done separately from the below version if/else because JSONDecodeError - # didn't appear until Python 3.5, but Sopel claims support for 3.3+ - # Redo this whole block of nonsense when dropping py2/old py3 support - from json import JSONDecodeError as InvalidJSONResponse -except ImportError: - InvalidJSONResponse = ValueError - -if sys.version_info.major > 2: - unicode = str - from urllib.request import urlretrieve - from urllib.parse import urlparse -else: - from urllib import urlretrieve - from urlparse import urlparse - LOGGER = logging.getLogger(__name__) PLUGIN_OUTPUT_PREFIX = '[safety] ' @@ -108,7 +94,7 @@ def setup(bot): _download_domain_list(loc) with open(loc, 'r') as f: for line in f: - clean_line = unicode(line).strip().lower() + clean_line = str(line).strip().lower() if not clean_line or clean_line[0] == '#': # blank line or comment continue @@ -176,7 +162,7 @@ def url_handler(bot, trigger): apikey = bot.config.safety.vt_api_key try: if apikey is not None and use_vt: - payload = {'resource': unicode(trigger), + payload = {'resource': str(trigger), 'apikey': apikey, 'scan': '1'} @@ -202,11 +188,11 @@ def url_handler(bot, trigger): except requests.exceptions.RequestException: # Ignoring exceptions with VT so domain list will always work LOGGER.debug('[VirusTotal] Error obtaining response.', exc_info=True) - except InvalidJSONResponse: + except json.JSONDecodeError: # Ignoring exceptions with VT so domain list will always work LOGGER.debug('[VirusTotal] Malformed response (invalid JSON).', exc_info=True) - if unicode(netloc).lower() in malware_domains: + if str(netloc).lower() in malware_domains: positives += 1 total += 1 @@ -253,7 +239,7 @@ def _clean_cache(bot): # clean up by age first cutoff = time.time() - (7 * 24 * 60 * 60) # 7 days ago old_keys = [] - for key, data in tools.iteritems(bot.memory['safety_cache']): + for key, data in bot.memory['safety_cache'].items(): if data['fetched'] <= cutoff: old_keys.append(key) for key in old_keys: diff --git a/sopel/modules/search.py b/sopel/modules/search.py index 91804328ef..7519bc14d2 100644 --- a/sopel/modules/search.py +++ b/sopel/modules/search.py @@ -1,4 +1,3 @@ -# coding=utf-8 """ search.py - Sopel Search Engine Plugin Copyright 2008-9, Sean B. Palmer, inamidst.com @@ -7,7 +6,7 @@ https://sopel.chat """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import re diff --git a/sopel/modules/seen.py b/sopel/modules/seen.py index 2b609a1d01..675506c275 100644 --- a/sopel/modules/seen.py +++ b/sopel/modules/seen.py @@ -1,4 +1,3 @@ -# coding=utf-8 """ seen.py - Sopel Seen Plugin Copyright 2008, Sean B. Palmer, inamidst.com @@ -8,7 +7,7 @@ https://sopel.chat """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import datetime import time diff --git a/sopel/modules/tell.py b/sopel/modules/tell.py index f96f3111bb..92ad9a6518 100644 --- a/sopel/modules/tell.py +++ b/sopel/modules/tell.py @@ -1,4 +1,3 @@ -# coding=utf-8 """ tell.py - Sopel Tell and Ask Plugin Copyright 2008, Sean B. Palmer, inamidst.com @@ -7,7 +6,7 @@ https://sopel.chat """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop from collections import defaultdict import io # don't use `codecs` for loading the DB; it will split lines on some IRC formatting diff --git a/sopel/modules/tld.py b/sopel/modules/tld.py index 6ee33567fc..e9fc9840f8 100644 --- a/sopel/modules/tld.py +++ b/sopel/modules/tld.py @@ -1,4 +1,3 @@ -# coding=utf-8 """ tld.py - Sopel TLD Plugin Copyright 2009-10, Michael Yanovich, yanovich.net @@ -7,25 +6,19 @@ https://sopel.chat """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop from datetime import datetime from encodings import idna +from html.parser import HTMLParser import logging import re -import sys import pytz import requests from sopel import formatting, plugin, tools -if sys.version_info.major >= 3: - unicode = str - from html.parser import HTMLParser -else: - from HTMLParser import HTMLParser - LOGGER = logging.getLogger(__name__) diff --git a/sopel/modules/translate.py b/sopel/modules/translate.py index ebd4ef11b7..8d09eb9778 100644 --- a/sopel/modules/translate.py +++ b/sopel/modules/translate.py @@ -1,4 +1,3 @@ -# coding=utf-8 """ translate.py - Sopel Translation Plugin Copyright 2008, Sean B. Palmer, inamidst.com @@ -7,19 +6,16 @@ https://sopel.chat """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import json import random -import sys import requests from sopel import plugin, tools from sopel.tools import web -if sys.version_info.major >= 3: - unicode = str PLUGIN_OUTPUT_PREFIX = '[translate] ' @@ -38,7 +34,7 @@ def shutdown(bot): def translate(text, in_lang='auto', out_lang='en'): raw = False - if unicode(out_lang).endswith('-raw'): + if str(out_lang).endswith('-raw'): out_lang = out_lang[:-4] raw = True @@ -116,9 +112,6 @@ def tr(bot, trigger): ) return - if sys.version_info.major < 3 and isinstance(msg, str): - msg = msg.decode('utf-8') - msg = web.decode(msg) msg = '"%s" (%s to %s, translate.google.com)' % (msg, in_lang, out_lang) bot.say(msg) @@ -182,9 +175,6 @@ def langcode(p): % (src, dest)) return - if sys.version_info.major < 3 and isinstance(msg, str): - msg = msg.decode('utf-8') - msg = web.decode(msg) # msg.replace(''', "'") msg = '"%s" (%s to %s, translate.google.com)' % (msg, src, dest) diff --git a/sopel/modules/unicode_info.py b/sopel/modules/unicode_info.py index 735b2ba4f2..556d637373 100644 --- a/sopel/modules/unicode_info.py +++ b/sopel/modules/unicode_info.py @@ -1,4 +1,3 @@ -# coding=utf-8 """ unicode_info.py - Sopel Codepoints Plugin Copyright 2013, Elsie Powell, embolalia.com @@ -7,28 +6,17 @@ https://sopel.chat """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop -import sys import unicodedata from sopel import plugin -if sys.version_info.major >= 3: - # Note on unicode and str (required for py2 compatibility) - # the `hex` function returns a `str`, both in py2 and py3 - # however, a `str` is a unicode string in py3, but a bytestring in py2 - # in order to prevent that, we encode the return from `hex` as `unicode` - # and since this class does not exist anymore on py3, we create an alias - # for `str` in py3 - unichr = chr - unicode = str - def get_codepoint_name(char): """Retrieve the code point (and name, if possible) for a given character""" # Get the hex value for the code point, and drop the 0x from the front - point = unicode(hex(ord(char)))[2:] + point = hex(ord(char))[2:] # Make the hex 4 characters long with preceding 0s, and all upper case point = point.rjust(4, '0').upper() @@ -60,7 +48,7 @@ def codepoint(bot, trigger): if arg.startswith('U+'): arg = arg[2:] try: - arg = unichr(int(arg, 16)) + arg = chr(int(arg, 16)) except (ValueError, TypeError): bot.reply("That's not a valid code point.") return plugin.NOLIMIT diff --git a/sopel/modules/units.py b/sopel/modules/units.py index f170329613..12fdf0d546 100644 --- a/sopel/modules/units.py +++ b/sopel/modules/units.py @@ -1,4 +1,3 @@ -# coding=utf-8 """ units.py - Sopel Unit Conversion Plugin Copyright © 2013, Elad Alfassa, @@ -7,7 +6,7 @@ https://sopel.chat """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import re diff --git a/sopel/modules/uptime.py b/sopel/modules/uptime.py index 8af9d93a14..8c45dcd730 100644 --- a/sopel/modules/uptime.py +++ b/sopel/modules/uptime.py @@ -1,4 +1,3 @@ -# coding=utf-8 """ uptime.py - Sopel Uptime Plugin Copyright 2014, Fabian Neundorf @@ -6,7 +5,7 @@ https://sopel.chat """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import datetime diff --git a/sopel/modules/url.py b/sopel/modules/url.py index 9524919b8a..fcff7700e5 100644 --- a/sopel/modules/url.py +++ b/sopel/modules/url.py @@ -1,4 +1,3 @@ -# coding=utf-8 """ url.py - Sopel URL Title Plugin Copyright 2010-2011, Michael Yanovich (yanovich.net) & Kenneth Sham @@ -9,11 +8,12 @@ https://sopel.chat """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import ipaddress import logging import re +from urllib.parse import urlparse import dns.resolver import requests @@ -23,11 +23,6 @@ from sopel.config import types from sopel.tools import web -# Python3 vs Python2 -try: - from urllib.parse import urlparse -except ImportError: - from urlparse import urlparse LOGGER = logging.getLogger(__name__) USER_AGENT = ( diff --git a/sopel/modules/version.py b/sopel/modules/version.py index 50a1a0d67b..d1aeab6a49 100644 --- a/sopel/modules/version.py +++ b/sopel/modules/version.py @@ -1,4 +1,3 @@ -# coding=utf-8 """ version.py - Sopel Version Plugin Copyright 2009, Silas Baronda @@ -7,7 +6,7 @@ https://sopel.chat """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import datetime import os diff --git a/sopel/modules/wikipedia.py b/sopel/modules/wikipedia.py index 0f22db3b43..5e33547b3f 100644 --- a/sopel/modules/wikipedia.py +++ b/sopel/modules/wikipedia.py @@ -1,4 +1,3 @@ -# coding=utf-8 """ wikipedia.py - Sopel Wikipedia Plugin Copyright 2013 Elsie Powell - embolalia.com @@ -6,8 +5,9 @@ https://sopel.chat """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop +from html.parser import HTMLParser import re from requests import get @@ -16,10 +16,6 @@ from sopel.config import types from sopel.tools.web import quote, unquote -try: # TODO: Remove fallback when dropping py2 - from html.parser import HTMLParser -except ImportError: - from HTMLParser import HTMLParser REDIRECT = re.compile(r'^REDIRECT (.*)') PLUGIN_OUTPUT_PREFIX = '[wikipedia] ' diff --git a/sopel/modules/wiktionary.py b/sopel/modules/wiktionary.py index 53b3dc2d50..ff02c95f78 100644 --- a/sopel/modules/wiktionary.py +++ b/sopel/modules/wiktionary.py @@ -1,4 +1,3 @@ -# coding=utf-8 """ wiktionary.py - Sopel Wiktionary Plugin Copyright 2009, Sean B. Palmer, inamidst.com @@ -6,7 +5,7 @@ https://sopel.chat """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import re diff --git a/sopel/modules/xkcd.py b/sopel/modules/xkcd.py index dc369010de..1be5de696d 100644 --- a/sopel/modules/xkcd.py +++ b/sopel/modules/xkcd.py @@ -1,4 +1,3 @@ -# coding=utf-8 """ xkcd.py - Sopel xkcd Plugin Copyright 2010, Michael Yanovich (yanovich.net), and Morgan Goose @@ -8,7 +7,7 @@ https://sopel.chat """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import random import re diff --git a/sopel/plugin.py b/sopel/plugin.py index 28f407c488..50a25005d4 100644 --- a/sopel/plugin.py +++ b/sopel/plugin.py @@ -1,4 +1,3 @@ -# coding=utf-8 """This contains decorators and other tools for creating Sopel plugins.""" # Copyright 2013, Ari Koivula, # Copyright © 2013, Elad Alfassa @@ -8,7 +7,7 @@ # Copyright 2019, Florian Strzelecki # Licensed under the Eiffel Forum License 2. -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import functools import re diff --git a/sopel/plugins/__init__.py b/sopel/plugins/__init__.py index dbaea5a78b..4213e0fa42 100644 --- a/sopel/plugins/__init__.py +++ b/sopel/plugins/__init__.py @@ -1,4 +1,3 @@ -# coding=utf-8 """Sopel's plugins interface. .. versionadded:: 7.0 @@ -28,7 +27,7 @@ # Copyright 2019, Florian Strzelecki # # Licensed under the Eiffel Forum License 2. -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import collections import imp diff --git a/sopel/plugins/exceptions.py b/sopel/plugins/exceptions.py index 1d840e4afa..1e7284401f 100644 --- a/sopel/plugins/exceptions.py +++ b/sopel/plugins/exceptions.py @@ -1,9 +1,8 @@ -# coding=utf-8 """Sopel's plugins exceptions.""" # Copyright 2019, Florian Strzelecki # # Licensed under the Eiffel Forum License 2. -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop class PluginError(Exception): diff --git a/sopel/plugins/handlers.py b/sopel/plugins/handlers.py index 76902aa077..e0847e7c93 100644 --- a/sopel/plugins/handlers.py +++ b/sopel/plugins/handlers.py @@ -1,4 +1,3 @@ -# coding=utf-8 """Sopel's plugin handlers. .. versionadded:: 7.0 @@ -42,7 +41,7 @@ # Copyright 2019, Florian Strzelecki # # Licensed under the Eiffel Forum License 2. -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import imp import importlib diff --git a/sopel/plugins/jobs.py b/sopel/plugins/jobs.py index c9ebe6968f..a03073991e 100644 --- a/sopel/plugins/jobs.py +++ b/sopel/plugins/jobs.py @@ -1,4 +1,3 @@ -# coding=utf-8 """Sopel's plugin jobs management. .. versionadded:: 7.1 @@ -15,7 +14,7 @@ # Copyright 2020, Florian Strzelecki # # Licensed under the Eiffel Forum License 2. -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import itertools import logging diff --git a/sopel/plugins/rules.py b/sopel/plugins/rules.py index 6abb1157b9..43a9c02c0f 100644 --- a/sopel/plugins/rules.py +++ b/sopel/plugins/rules.py @@ -1,4 +1,3 @@ -# coding=utf-8 """Sopel's plugin rules management. .. versionadded:: 7.1 @@ -15,7 +14,7 @@ # Copyright 2020, Florian Strzelecki # # Licensed under the Eiffel Forum License 2. -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import datetime @@ -25,6 +24,7 @@ import logging import re import threading +from urllib.parse import urlparse from sopel import tools @@ -32,19 +32,6 @@ COMMAND_DEFAULT_HELP_PREFIX, COMMAND_DEFAULT_PREFIX, URL_DEFAULT_SCHEMES) -try: - from urllib.parse import urlparse -except ImportError: - # TODO: remove when dropping Python 2.7 - from urlparse import urlparse - -try: - from inspect import getfullargspec as inspect_getargspec -except ImportError: - # TODO: remove when dropping Python 2.7 - from inspect import getargspec as inspect_getargspec - - __all__ = [ 'Manager', 'Rule', @@ -1600,7 +1587,7 @@ def from_callable(cls, settings, handler): # account for the 'self' parameter when the handler is a method match_count = 4 - argspec = inspect_getargspec(handler) + argspec = inspect.getfullargspec(handler) if len(argspec.args) >= match_count: @functools.wraps(handler) diff --git a/sopel/test_tools.py b/sopel/test_tools.py index 8e25c6fd4b..c5a5570bb9 100644 --- a/sopel/test_tools.py +++ b/sopel/test_tools.py @@ -1,4 +1,3 @@ -# coding=utf-8 """This module provided tools that helped to write tests. .. deprecated:: 7.1 @@ -17,18 +16,13 @@ # Copyright 2013, Ari Koivula, # Copyright 2019, Florian Strzelecki # Licensed under the Eiffel Forum License 2. -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop +import configparser import os import re -import sys import tempfile -try: - import ConfigParser -except ImportError: - import configparser as ConfigParser - from sopel import bot, config, tools @@ -42,15 +36,12 @@ 'run_example_tests', ] -if sys.version_info.major >= 3: - basestring = str - class MockConfig(config.Config): @tools.deprecated('use configfactory fixture instead', '7.0', '8.0') def __init__(self): self.filename = tempfile.mkstemp()[1] - self.parser = ConfigParser.RawConfigParser(allow_no_value=True) + self.parser = configparser.RawConfigParser(allow_no_value=True) self.parser.add_section('core') self.parser.set('core', 'owner', 'Embolalia') self.define_section('core', config.core_section.CoreSection) @@ -103,13 +94,13 @@ def _init_config(self): cfg.parser.set('core', 'homedir', home_dir) def register_url_callback(self, pattern, callback): - if isinstance(pattern, basestring): + if isinstance(pattern, str): pattern = re.compile(pattern) self.memory['url_callbacks'][pattern] = callback def unregister_url_callback(self, pattern, callback): - if isinstance(pattern, basestring): + if isinstance(pattern, str): pattern = re.compile(pattern) try: @@ -118,7 +109,7 @@ def unregister_url_callback(self, pattern, callback): pass def search_url_callbacks(self, url): - for regex, function in tools.iteritems(self.memory['url_callbacks']): + for regex, function in self.memory['url_callbacks'].items(): match = regex.search(url) if match: yield function, match diff --git a/sopel/tests/__init__.py b/sopel/tests/__init__.py index c67efcfc5d..cfbbfc6501 100644 --- a/sopel/tests/__init__.py +++ b/sopel/tests/__init__.py @@ -1,9 +1,8 @@ -# coding=utf-8 """Test tools, factories, pytest fixtures, and mocks. .. versionadded:: 7.0 """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop def rawlist(*args): diff --git a/sopel/tests/factories.py b/sopel/tests/factories.py index 4334702de9..fdfd8048dd 100644 --- a/sopel/tests/factories.py +++ b/sopel/tests/factories.py @@ -1,9 +1,8 @@ -# coding=utf-8 """Test factories: they create objects for testing purposes. .. versionadded:: 7.0 """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import re diff --git a/sopel/tests/mocks.py b/sopel/tests/mocks.py index dadc1c8709..feb33bc80e 100644 --- a/sopel/tests/mocks.py +++ b/sopel/tests/mocks.py @@ -1,9 +1,8 @@ -# coding=utf-8 """Test mocks: they fake objects for testing. .. versionadded:: 7.0 """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop from sopel.irc.abstract_backends import AbstractIRCBackend diff --git a/sopel/tests/pytest_plugin.py b/sopel/tests/pytest_plugin.py index d690b0fc55..cffb22013c 100644 --- a/sopel/tests/pytest_plugin.py +++ b/sopel/tests/pytest_plugin.py @@ -1,9 +1,8 @@ -# coding=utf-8 """Pytest plugin for Sopel. .. versionadded:: 7.0 """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import re import sys diff --git a/sopel/tools/__init__.py b/sopel/tools/__init__.py index 2b0f90b11a..da3dd44761 100644 --- a/sopel/tools/__init__.py +++ b/sopel/tools/__init__.py @@ -1,4 +1,3 @@ -# coding=utf-8 """Useful miscellaneous tools and shortcuts for Sopel plugins *Availability: 3+* @@ -12,7 +11,7 @@ # https://sopel.chat -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import codecs from collections import defaultdict @@ -32,16 +31,13 @@ from ._events import events # NOQA from . import time, web # NOQA -if sys.version_info.major >= 3: - raw_input = input - unicode = str - iteritems = dict.items - itervalues = dict.values - iterkeys = dict.keys -else: - iteritems = dict.iteritems - itervalues = dict.itervalues - iterkeys = dict.iterkeys + +# Kept for backward compatibility +# TODO: consider removing that +raw_input = input +iteritems = dict.items +itervalues = dict.values +iterkeys = dict.keys _channel_prefixes = ('#', '&', '+', '!') @@ -221,10 +217,7 @@ def get_input(prompt): dropped in Sopel 8.0. The function will be removed in Sopel 8.1. """ - if sys.version_info.major >= 3: - return input(prompt) - else: - return raw_input(prompt).decode('utf8') + return input(prompt) @deprecated('rule compilation tools are now private', '7.1', '8.0') @@ -317,7 +310,7 @@ def get_nickname_command_regexp(nick, command, alias_nicks): # Must defer import to avoid cyclic dependency from sopel.plugins.rules import NickCommand - if isinstance(alias_nicks, unicode): + if isinstance(alias_nicks, str): alias_nicks = [alias_nicks] elif not isinstance(alias_nicks, (list, tuple)): raise ValueError('A list or string is required.') @@ -443,26 +436,22 @@ def __getitem__(self, key): return dict.__getitem__(self, key) -class Identifier(unicode): - """A `unicode` subclass which acts appropriately for IRC identifiers. +class Identifier(str): + """A ``str`` subclass which acts appropriately for IRC identifiers. - When used as normal `unicode` objects, case will be preserved. + When used as normal ``str`` objects, case will be preserved. However, when comparing two Identifier objects, or comparing a Identifier - object with a `unicode` object, the comparison will be case insensitive. + object with a ``str`` object, the comparison will be case insensitive. This case insensitivity includes the case convention conventions regarding ``[]``, ``{}``, ``|``, ``\\``, ``^`` and ``~`` described in RFC 2812. """ - # May want to tweak this and update documentation accordingly when dropping - # Python 2 support, since in py3 plain str is Unicode and a "unicode" type - # no longer exists. Probably lots of code will need tweaking, tbh. - def __new__(cls, identifier): # According to RFC2812, identifiers have to be in the ASCII range. # However, I think it's best to let the IRCd determine that, and we'll # just assume unicode. It won't hurt anything, and is more internally # consistent. And who knows, maybe there's another use case for this # weird case convention. - s = unicode.__new__(cls, identifier) + s = str.__new__(cls, identifier) s._lowered = Identifier._lower(identifier) return s @@ -521,29 +510,29 @@ def __hash__(self): return self._lowered.__hash__() def __lt__(self, other): - if isinstance(other, unicode): + if isinstance(other, str): other = Identifier._lower(other) - return unicode.__lt__(self._lowered, other) + return str.__lt__(self._lowered, other) def __le__(self, other): - if isinstance(other, unicode): + if isinstance(other, str): other = Identifier._lower(other) - return unicode.__le__(self._lowered, other) + return str.__le__(self._lowered, other) def __gt__(self, other): - if isinstance(other, unicode): + if isinstance(other, str): other = Identifier._lower(other) - return unicode.__gt__(self._lowered, other) + return str.__gt__(self._lowered, other) def __ge__(self, other): - if isinstance(other, unicode): + if isinstance(other, str): other = Identifier._lower(other) - return unicode.__ge__(self._lowered, other) + return str.__ge__(self._lowered, other) def __eq__(self, other): - if isinstance(other, unicode): + if isinstance(other, str): other = Identifier._lower(other) - return unicode.__eq__(self._lowered, other) + return str.__eq__(self._lowered, other) def __ne__(self, other): return not (self == other) @@ -610,7 +599,7 @@ def write(self, string): logfile.write(string) except UnicodeDecodeError: # we got an invalid string, safely encode it to utf-8 - logfile.write(unicode(string, 'utf8', errors="replace")) + logfile.write(str(string, 'utf8', errors="replace")) def flush(self): """Flush the file writing buffer.""" diff --git a/sopel/tools/_events.py b/sopel/tools/_events.py index 3d3101838a..c60628e03f 100644 --- a/sopel/tools/_events.py +++ b/sopel/tools/_events.py @@ -1,5 +1,4 @@ -# coding=utf-8 -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop class events(object): diff --git a/sopel/tools/calculation.py b/sopel/tools/calculation.py index 4ca0124278..d57c9f0986 100644 --- a/sopel/tools/calculation.py +++ b/sopel/tools/calculation.py @@ -1,6 +1,5 @@ -# coding=utf-8 """Tools to help safely do calculations from user input""" -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import ast import numbers diff --git a/sopel/tools/jobs.py b/sopel/tools/jobs.py index fe7f6a29d4..d183c4b7bc 100644 --- a/sopel/tools/jobs.py +++ b/sopel/tools/jobs.py @@ -1,4 +1,3 @@ -# coding=utf-8 """Sopel's Job Scheduler: internal tool for job management. .. important:: @@ -12,7 +11,7 @@ # Copyright 2019, Florian Strzelecki # # Licensed under the Eiffel Forum License 2. -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import inspect import logging diff --git a/sopel/tools/target.py b/sopel/tools/target.py index 20501619cd..d2b1a79951 100644 --- a/sopel/tools/target.py +++ b/sopel/tools/target.py @@ -1,5 +1,4 @@ -# coding=utf-8 -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import functools diff --git a/sopel/tools/time.py b/sopel/tools/time.py index e52dfb4b92..fb82967629 100644 --- a/sopel/tools/time.py +++ b/sopel/tools/time.py @@ -1,6 +1,5 @@ -# coding=utf-8 """Tools for getting and displaying the time.""" -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import datetime diff --git a/sopel/tools/web.py b/sopel/tools/web.py index 70d7b63147..19d786694e 100644 --- a/sopel/tools/web.py +++ b/sopel/tools/web.py @@ -1,4 +1,3 @@ -# coding=utf-8 """ The ``tools.web`` package contains utility functions for interaction with web applications, APIs, or websites in your plugins. @@ -17,22 +16,15 @@ # Copyright © 2019, dgw, technobabbl.es # Licensed under the Eiffel Forum License 2. -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop +from html.entities import name2codepoint import re -import sys import urllib +from urllib.parse import urlparse, urlunparse from sopel import __version__ -if sys.version_info.major < 3: - from htmlentitydefs import name2codepoint - from urlparse import urlparse, urlunparse -else: - from html.entities import name2codepoint - from urllib.parse import urlparse, urlunparse - unichr = chr - unicode = str __all__ = [ 'USER_AGENT', @@ -107,11 +99,11 @@ def entity(match): """ value = match.group(1).lower() if value.startswith('#x'): - return unichr(int(value[2:], 16)) + return chr(int(value[2:], 16)) elif value.startswith('#'): - return unichr(int(value[1:])) + return chr(int(value[1:])) elif value in name2codepoint: - return unichr(name2codepoint[value]) + return chr(name2codepoint[value]) return '[' + value + ']' @@ -124,7 +116,6 @@ def decode(html): return r_entity.sub(entity, html) -# Identical to urllib2.quote def quote(string, safe='/'): """Safely encodes a string for use in a URL. @@ -137,13 +128,8 @@ def quote(string, safe='/'): This is a shim to make writing cross-compatible plugins for both Python 2 and Python 3 easier. """ - if sys.version_info.major < 3: - if isinstance(string, unicode): - string = string.encode('utf8') - string = urllib.quote(string, safe.encode('utf8')) - else: - string = urllib.parse.quote(str(string), safe) - return string + # TODO deprecated? + return urllib.parse.quote(str(string), safe) # six-like shim for Unicode safety @@ -154,13 +140,11 @@ def unquote(string): :return str: the decoded ``string`` .. note:: - This is a shim to make writing cross-compatible plugins for both - Python 2 and Python 3 easier. + + This is a convenient shortcut for ``urllib.parse.unquote``. """ - if sys.version_info.major < 3: - return urllib.unquote(string.encode('utf-8')).decode('utf-8') - else: - return urllib.parse.unquote(string) + # TODO deprecated? + return urllib.parse.unquote(string) def quote_query(string): @@ -178,30 +162,24 @@ def quote_query(string): def urlencode_non_ascii(b): """Safely encodes non-ASCII characters in a URL.""" - regex = '[\x80-\xFF]' - if sys.version_info.major > 2: - regex = b'[\x80-\xFF]' - return re.sub(regex, lambda c: '%%%02x' % ord(c.group(0)), b) + return re.sub(b'[\x80-\xFF]', lambda c: '%%%02x' % ord(c.group(0)), b) def iri_to_uri(iri): """Decodes an internationalized domain name (IDN).""" parts = urlparse(iri) - parts_seq = (part.encode('idna') if parti == 1 else urlencode_non_ascii(part.encode('utf-8')) for parti, part in enumerate(parts)) - if sys.version_info.major > 2: - parts_seq = list(parts_seq) - + parts_seq = list( + part.encode('idna') + if parti == 1 else urlencode_non_ascii(part.encode('utf-8')) + for parti, part in enumerate(parts) + ) parsed = urlunparse(parts_seq) - if sys.version_info.major > 2: - return parsed.decode() - else: - return parsed + return parsed.decode() -if sys.version_info.major < 3: - urlencode = urllib.urlencode -else: - urlencode = urllib.parse.urlencode +# direct shortcut kept for backward compatibility reasons +# TODO consider removing this +urlencode = urllib.parse.urlencode # Functions for URL detection diff --git a/sopel/trigger.py b/sopel/trigger.py index 3615349f31..6a0cc04664 100644 --- a/sopel/trigger.py +++ b/sopel/trigger.py @@ -1,10 +1,8 @@ -# coding=utf-8 """Triggers are how Sopel tells callables about their runtime context.""" -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import datetime import re -import sys from sopel import formatting, tools from sopel.tools import web @@ -15,10 +13,6 @@ 'Trigger', ] -if sys.version_info.major >= 3: - unicode = str - basestring = str - class PreTrigger(object): """A parsed raw message from the server. @@ -200,7 +194,7 @@ def __init__(self, own_nick, line, url_schemes=None): self.plain = formatting.plain(self.args[-1]) -class Trigger(unicode): +class Trigger(str): """A line from the server, which has matched a callable's rules. :param config: Sopel's current configuration settings object @@ -412,7 +406,7 @@ class Trigger(unicode): """ def __new__(cls, config, message, match, account=None): - self = unicode.__new__(cls, message.args[-1] if message.args else '') + self = str.__new__(cls, message.args[-1] if message.args else '') self._account = account self._pretrigger = message self._match = match diff --git a/sopel/web.py b/sopel/web.py index 03ec2f9b7b..cf4229aae1 100644 --- a/sopel/web.py +++ b/sopel/web.py @@ -1,4 +1,3 @@ -# coding=utf-8 """ *Availability: 3+, deprecated in 6.2.0* @@ -12,18 +11,14 @@ # Copyright © 2019, dgw, technobabbl.es # Licensed under the Eiffel Forum License 2. -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop -import sys +import http.client as httplib import requests from .tools import deprecated -if sys.version_info.major < 3: - import httplib -else: - import http.client as httplib # Imports to facilitate transition from sopel.web to sopel.tools.web from .tools.web import ( # noqa diff --git a/test/cli/test_cli_run.py b/test/cli/test_cli_run.py index f20fe71cc8..fca5e8ba63 100644 --- a/test/cli/test_cli_run.py +++ b/test/cli/test_cli_run.py @@ -1,6 +1,5 @@ -# coding=utf-8 """Tests for command handling""" -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import argparse from contextlib import contextmanager diff --git a/test/cli/test_cli_utils.py b/test/cli/test_cli_utils.py index a9123bb3df..dbfb87d879 100644 --- a/test/cli/test_cli_utils.py +++ b/test/cli/test_cli_utils.py @@ -1,6 +1,5 @@ -# coding=utf-8 """Tests for sopel.cli.utils""" -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import argparse from contextlib import contextmanager diff --git a/test/config/test_config_types.py b/test/config/test_config_types.py index 36f6133e7c..b042b5306a 100644 --- a/test/config/test_config_types.py +++ b/test/config/test_config_types.py @@ -1,5 +1,4 @@ -# coding=utf-8 -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import os diff --git a/test/irc/test_irc_abstract_backends.py b/test/irc/test_irc_abstract_backends.py index 8821bee1d6..a13388d2fc 100644 --- a/test/irc/test_irc_abstract_backends.py +++ b/test/irc/test_irc_abstract_backends.py @@ -1,6 +1,5 @@ -# coding=utf-8 """Tests for core ``sopel.irc.backends``""" -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop from sopel.irc.abstract_backends import AbstractIRCBackend diff --git a/test/irc/test_irc_isupport.py b/test/irc/test_irc_isupport.py index a56d9d0272..3bf2373418 100644 --- a/test/irc/test_irc_isupport.py +++ b/test/irc/test_irc_isupport.py @@ -1,6 +1,5 @@ -# coding=utf-8 """Tests for core ``sopel.irc.isupport``""" -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import pytest diff --git a/test/irc/test_irc_utils.py b/test/irc/test_irc_utils.py index 6bbf9ac672..6c6daa0e50 100644 --- a/test/irc/test_irc_utils.py +++ b/test/irc/test_irc_utils.py @@ -1,6 +1,5 @@ -# coding=utf-8 """Tests for core ``sopel.irc.utils``""" -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import pytest diff --git a/test/modules/test_modules_choose.py b/test/modules/test_modules_choose.py index f956b8da05..902e5a6215 100644 --- a/test/modules/test_modules_choose.py +++ b/test/modules/test_modules_choose.py @@ -1,6 +1,5 @@ -# coding=utf-8 """Tests for Sopel's ``choose`` plugin""" -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import pytest diff --git a/test/modules/test_modules_find_updates.py b/test/modules/test_modules_find_updates.py index aff2124bd1..6a7678de63 100644 --- a/test/modules/test_modules_find_updates.py +++ b/test/modules/test_modules_find_updates.py @@ -1,6 +1,5 @@ -# coding=utf-8 """Tests for Sopel's ``find_updates`` plugin""" -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import pytest import requests.exceptions diff --git a/test/modules/test_modules_isup.py b/test/modules/test_modules_isup.py index 3003358cbe..4debb8608c 100644 --- a/test/modules/test_modules_isup.py +++ b/test/modules/test_modules_isup.py @@ -1,6 +1,5 @@ -# coding=utf-8 """Tests for Sopel's ``isup`` plugin""" -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import pytest import requests.exceptions diff --git a/test/modules/test_modules_remind.py b/test/modules/test_modules_remind.py index 564159356b..4198b42199 100644 --- a/test/modules/test_modules_remind.py +++ b/test/modules/test_modules_remind.py @@ -1,6 +1,5 @@ -# coding=utf-8 """Tests for Sopel's ``remind`` plugin""" -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop from datetime import datetime import os diff --git a/test/modules/test_modules_tell.py b/test/modules/test_modules_tell.py index 66c113f0d0..4da4884a09 100644 --- a/test/modules/test_modules_tell.py +++ b/test/modules/test_modules_tell.py @@ -1,6 +1,5 @@ -# coding=utf-8 """Tests for Sopel's ``tell`` plugin""" -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import datetime import io diff --git a/test/modules/test_modules_url.py b/test/modules/test_modules_url.py index a2c1fbf6d9..44b1c6a2f7 100644 --- a/test/modules/test_modules_url.py +++ b/test/modules/test_modules_url.py @@ -1,6 +1,5 @@ -# coding=utf-8 """Tests for Sopel's ``url`` plugin""" -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import re diff --git a/test/plugins/test_plugins_handlers.py b/test/plugins/test_plugins_handlers.py index 991751301f..91b4a069dc 100644 --- a/test/plugins/test_plugins_handlers.py +++ b/test/plugins/test_plugins_handlers.py @@ -1,6 +1,5 @@ -# coding=utf-8 """Tests for the ``sopel.plugins.handlers`` module.""" -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import os import sys @@ -11,7 +10,7 @@ from sopel.plugins import handlers -MOCK_MODULE_CONTENT = """# coding=utf-8 +MOCK_MODULE_CONTENT = """ \"\"\"plugin label \"\"\" """ diff --git a/test/plugins/test_plugins_rules.py b/test/plugins/test_plugins_rules.py index 9d019a0cbb..c7d2fbe80a 100644 --- a/test/plugins/test_plugins_rules.py +++ b/test/plugins/test_plugins_rules.py @@ -1,6 +1,5 @@ -# coding=utf-8 """Tests for the ``sopel.plugins.rules`` module.""" -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import re diff --git a/test/test_bot.py b/test/test_bot.py index a21f412d7a..28aeddb6bb 100644 --- a/test/test_bot.py +++ b/test/test_bot.py @@ -1,6 +1,5 @@ -# coding=utf-8 """Tests for core ``sopel.bot`` module""" -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import re @@ -19,7 +18,7 @@ enable = coretasks """ -MOCK_MODULE_CONTENT = """# coding=utf-8 +MOCK_MODULE_CONTENT = """from __future__ import generator_stop import sopel.module diff --git a/test/test_config.py b/test/test_config.py index a8651e4043..c2d9826e25 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -1,8 +1,6 @@ -# coding=utf-8 -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import os -import sys import pytest @@ -50,23 +48,6 @@ '""ed"', # quoted, but no #: quotes kept ] -if sys.version_info.major < 3: - # Python 2.7's ConfigParser interprets as comment - # a line that starts with # or ;. - # Python 3, on the other hand, allows comments to be indented. - # As a result, the same config file will result in a different - # config object depending on the Python version used. - # TODO: Deprecated with Python 2.7. - TEST_CHANNELS = [ - '#sopel', - '&peculiar', - '# python 3 only comment', # indented lines cannot be comments in Py2 - '#private', - '"#startquote', - '&endquote"', - '""ed"', - ] - class FakeConfigSection(types.StaticSection): valattr = types.ValidatedAttribute('valattr') diff --git a/test/test_coretasks.py b/test/test_coretasks.py index fd60f3c7ac..4e287d3035 100644 --- a/test/test_coretasks.py +++ b/test/test_coretasks.py @@ -1,6 +1,5 @@ -# coding=utf-8 """coretasks.py tests""" -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import pytest diff --git a/test/test_db.py b/test/test_db.py index ab4ebc7d11..65ceb625e4 100644 --- a/test/test_db.py +++ b/test/test_db.py @@ -1,14 +1,12 @@ -# coding=utf-8 """Tests for the new database functionality. TODO: Most of these tests assume functionality tested in other tests. This is enough to get everything working (and is better than nothing), but best practice would probably be not to do that.""" -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import json import os -import sys import tempfile import pytest @@ -16,17 +14,8 @@ from sopel.db import ChannelValues, Nicknames, NickValues, PluginValues, SopelDB from sopel.tools import Identifier + db_filename = tempfile.mkstemp()[1] -if sys.version_info.major >= 3: - unicode = str - basestring = str - iteritems = dict.items - itervalues = dict.values - iterkeys = dict.keys -else: - iteritems = dict.iteritems - itervalues = dict.itervalues - iterkeys = dict.iterkeys TMP_CONFIG = """ @@ -116,15 +105,15 @@ def test_set_nick_value(db): } def check(): - for key, value in iteritems(data): + for key, value in data.items(): db.set_nick_value(nick, key, value) - for key, value in iteritems(data): + for key, value in data.items(): found_value = session.query(NickValues.value) \ .filter(NickValues.nick_id == nick_id) \ .filter(NickValues.key == key) \ .scalar() - assert json.loads(unicode(found_value)) == value + assert json.loads(str(found_value)) == value check() # Test updates @@ -144,12 +133,12 @@ def test_get_nick_value(db): 'unicode': 'EmbölaliÅ', } - for key, value in iteritems(data): + for key, value in data.items(): nv = NickValues(nick_id=nick_id, key=key, value=json.dumps(value, ensure_ascii=False)) session.add(nv) session.commit() - for key, value in iteritems(data): + for key, value in data.items(): found_value = db.get_nick_value(nick, key) assert found_value == value session.close() @@ -243,7 +232,7 @@ def test_merge_nick_groups(db): .filter(NickValues.nick_id == nick_id) \ .filter(NickValues.key == key) \ .scalar() - assert json.loads(unicode(found)) == value + assert json.loads(str(found)) == value session.close() diff --git a/test/test_formatting.py b/test/test_formatting.py index becb8a5459..78cedf0664 100644 --- a/test/test_formatting.py +++ b/test/test_formatting.py @@ -1,6 +1,5 @@ -# coding=utf-8 """Tests for message formatting""" -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import pytest diff --git a/test/test_irc.py b/test/test_irc.py index a6314bcac9..d9744b7fce 100644 --- a/test/test_irc.py +++ b/test/test_irc.py @@ -1,6 +1,5 @@ -# coding=utf-8 """Tests for core ``sopel.irc``""" -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import pytest diff --git a/test/test_loader.py b/test/test_loader.py index c4d4faa1e6..03d8a1ddc9 100644 --- a/test/test_loader.py +++ b/test/test_loader.py @@ -1,6 +1,5 @@ -# coding=utf-8 """Tests for the ``sopel.loader`` module.""" -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import inspect import re @@ -10,7 +9,7 @@ from sopel import loader, module, plugins -MOCK_MODULE_CONTENT = """# coding=utf-8 +MOCK_MODULE_CONTENT = """from __future__ import generator_stop import re import sopel.module diff --git a/test/test_module.py b/test/test_module.py index 1d33b1c30f..db16895434 100644 --- a/test/test_module.py +++ b/test/test_module.py @@ -1,4 +1,3 @@ -# coding=utf-8 """Tests for sopel.module decorators .. important:: @@ -7,7 +6,7 @@ compatible up to Sopel 9, when it will be removed. """ -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import pytest diff --git a/test/test_plugin.py b/test/test_plugin.py index 098c08be2c..406a6dfe49 100644 --- a/test/test_plugin.py +++ b/test/test_plugin.py @@ -1,6 +1,5 @@ -# coding=utf-8 """Tests for sopel.plugin decorators""" -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop from sopel import plugin from sopel.tests import rawlist diff --git a/test/test_plugins.py b/test/test_plugins.py index 940579aba3..67730f2dfc 100644 --- a/test/test_plugins.py +++ b/test/test_plugins.py @@ -1,6 +1,5 @@ -# coding=utf-8 """Test for the ``sopel.plugins`` module.""" -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import sys @@ -10,7 +9,7 @@ from sopel import plugins -MOCK_MODULE_CONTENT = """# coding=utf-8 +MOCK_MODULE_CONTENT = """from __future__ import generator_stop import sopel.module diff --git a/test/test_regression.py b/test/test_regression.py index 164720da09..89c91359e4 100644 --- a/test/test_regression.py +++ b/test/test_regression.py @@ -1,6 +1,5 @@ -# coding=utf-8 """Regression tests""" -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop from sopel import coretasks, tools diff --git a/test/test_tools.py b/test/test_tools.py index 0815f5f302..f79a8da12e 100644 --- a/test/test_tools.py +++ b/test/test_tools.py @@ -1,6 +1,5 @@ -# coding=utf-8 """Tests sopel.tools""" -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import re diff --git a/test/test_trigger.py b/test/test_trigger.py index f88e404780..357e322ef4 100644 --- a/test/test_trigger.py +++ b/test/test_trigger.py @@ -1,6 +1,5 @@ -# coding=utf-8 """Tests for message parsing""" -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import datetime import re diff --git a/test/tests/test_tests_mocks.py b/test/tests/test_tests_mocks.py index 6c4cf78276..73619cee77 100644 --- a/test/tests/test_tests_mocks.py +++ b/test/tests/test_tests_mocks.py @@ -1,6 +1,5 @@ -# coding=utf-8 """Tests for ``sopel.tests.mocks`` module""" -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop from sopel.tests.mocks import MockIRCBackend diff --git a/test/tools/test_tools_jobs.py b/test/tools/test_tools_jobs.py index 34ac61d9a8..267724c62f 100644 --- a/test/tools/test_tools_jobs.py +++ b/test/tools/test_tools_jobs.py @@ -1,6 +1,5 @@ -# coding=utf-8 """Tests for Job Scheduler""" -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import time diff --git a/test/tools/test_tools_target.py b/test/tools/test_tools_target.py index 3de25bea5d..671aa67799 100644 --- a/test/tools/test_tools_target.py +++ b/test/tools/test_tools_target.py @@ -1,6 +1,5 @@ -# coding=utf-8 """Tests for targets: Channel & User""" -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop from sopel import plugin from sopel.tools import Identifier, target diff --git a/test/tools/test_tools_time.py b/test/tools/test_tools_time.py index d493882572..4de8ce1030 100644 --- a/test/tools/test_tools_time.py +++ b/test/tools/test_tools_time.py @@ -1,6 +1,5 @@ -# coding=utf-8 """Tools for getting and displaying the time.""" -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import datetime diff --git a/test/tools/test_tools_web.py b/test/tools/test_tools_web.py index c42e5f88d9..36d3c33bc7 100644 --- a/test/tools/test_tools_web.py +++ b/test/tools/test_tools_web.py @@ -1,6 +1,5 @@ -# coding=utf-8 """Tests Sopel's web tools""" -from __future__ import absolute_import, division, print_function, unicode_literals +from __future__ import generator_stop import pytest