Skip to content

Commit

Permalink
drop support for Python 2
Browse files Browse the repository at this point in the history
  • Loading branch information
FelixSchwarz committed Jul 19, 2024
1 parent 3e4f892 commit ca0afa5
Show file tree
Hide file tree
Showing 15 changed files with 22 additions and 202 deletions.
20 changes: 0 additions & 20 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,6 @@ on:


jobs:
# --- Python 2 tests in separate container ----------------------------------
tests-py2:
runs-on: ubuntu-latest
container:
image: python:2.7.18-buster
timeout-minutes: 10

steps:
- uses: actions/checkout@v4

- name: Install dependencies
run: |
pip install --upgrade pip setuptools wheel
pip install -e .[testing]
- name: Run test suite
run: |
pytest
# --- tests running on Ubuntu 20.04 -----------------------------------------
tests-py36:
# "ubuntu-latest" does not have Python 3.6
Expand Down
1 change: 0 additions & 1 deletion schwarz/__init__.py

This file was deleted.

4 changes: 2 additions & 2 deletions schwarz/mailqueue/app_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@

from __future__ import absolute_import, print_function, unicode_literals

import configparser
import logging
import logging.config
import os
import sys

from schwarz.puzzle_plugins import PluginLoader, parse_list_str

from .compat import SafeConfigParser, configparser
from .mailer import SMTPMailer
from .plugins import registry

Expand Down Expand Up @@ -80,7 +80,7 @@ def parse_config(config_path, section_name=None):
sys.stderr.write('config file "%s" not found.\n' % filename)
sys.exit(20)

parser = SafeConfigParser()
parser = configparser.ConfigParser()
exc_msg = None
try:
parser.read(config_path)
Expand Down
90 changes: 0 additions & 90 deletions schwarz/mailqueue/compat.py
Original file line number Diff line number Diff line change
@@ -1,101 +1,11 @@
# -*- coding: utf-8 -*-
# SPDX-License-Identifier: MIT

from __future__ import absolute_import, print_function, unicode_literals

import email.utils
import os
import sys


__all__ = [
'configparser',
'format_datetime_rfc2822',
'make_msgid',
'os_makedirs',
'queue',
'FileNotFoundError',
'SafeConfigParser',
'IS_PYTHON3',
'IS_WINDOWS',
]

IS_PYTHON3 = (sys.version_info >= (3,0))
IS_WINDOWS = (os.name == 'nt')

def os_makedirs(name, mode, exist_ok=False):
if IS_PYTHON3:
os.makedirs(name, mode, exist_ok=exist_ok)
return
# Python 2 version with "exist_ok=True" is racy but that is ok with my
# as Python 2 won't last that long.
if exist_ok is False:
os.makedirs(name, mode)
elif not os.path.exists(name):
os.makedirs(name, mode)

try:
import configparser

# in Python 3.2 "SafeConfigParser" was renamed to "ConfigParser"
from configparser import ConfigParser as SafeConfigParser
except ImportError:
import ConfigParser as configparser
from ConfigParser import SafeConfigParser

try:
import queue
except ImportError:
import Queue as queue

try:
FileNotFoundError
except NameError:
FileNotFoundError = OSError
else:
FileNotFoundError = FileNotFoundError


def format_datetime_rfc2822(dt):
try:
date_str = email.utils.format_datetime(dt)
except AttributeError:
# Python 2
# email.utils.formatdate() in Python always uses UTC timezone ("-0000")
# so the resulting string points to the same time but possibly in a
# different timezone.
# The naive version might something like
# now_time = time.mktime(dt.timetuple())
# date_str = email.utils.formatdate(now_time)
date_str = format_datetime_py2(dt)
return date_str


WEEKDAYS = ('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun')
MONTHS = (None, 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec')

def format_datetime_py2(dt):
tt = dt.timetuple()
utc_offset_str = dt.strftime('%z')
pattern = '%s, %02d %s %d %02d:%02d:%02d %s'
return pattern % (
WEEKDAYS[tt.tm_wday],
tt.tm_mday,
MONTHS[tt.tm_mon],
tt.tm_year,
tt.tm_hour,
tt.tm_min,
tt.tm_sec,
utc_offset_str
)


def make_msgid(domain=None):
if not domain:
return email.utils.make_msgid()
if sys.version_info >= (3, 2):
return email.utils.make_msgid(domain=domain)

msg_id_str = email.utils.make_msgid('@'+domain)
msg_id = msg_id_str.rsplit('@', 1)[0] + '>'
return msg_id
6 changes: 3 additions & 3 deletions schwarz/mailqueue/maildir_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import portalocker
from boltons.fileutils import atomic_rename, atomic_save

from .compat import IS_WINDOWS, FileNotFoundError, os_makedirs
from .compat import IS_WINDOWS


__all__ = ['create_maildir_directories', 'lock_file', 'move_message']
Expand Down Expand Up @@ -47,11 +47,11 @@ def write(self, data):


def create_maildir_directories(basedir, is_folder=False):
os_makedirs(basedir, 0o700, exist_ok=True)
os.makedirs(basedir, 0o700, exist_ok=True)
new_path = None
for subdir_name in ('tmp', 'cur', 'new'):
subdir_path = os.path.join(basedir, subdir_name)
os_makedirs(subdir_path, 0o700, exist_ok=True)
os.makedirs(subdir_path, 0o700, exist_ok=True)
if subdir_name == 'new':
new_path = subdir_path

Expand Down
4 changes: 2 additions & 2 deletions schwarz/mailqueue/mailflow_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@

from datetime import datetime as DateTime
from email.message import Message
from email.utils import format_datetime, make_msgid

from boltons.timeutils import LocalTZ

from .app_helpers import init_app, init_smtp_mailer
from .compat import format_datetime_rfc2822, make_msgid
from .message_handler import InMemoryMsg, MessageHandler
from .message_utils import msg_as_bytes

Expand All @@ -22,7 +22,7 @@ def build_check_message(recipient, sender=None):
mail['From'] = sender
mail['To'] = recipient
now = DateTime.now(tz=LocalTZ)
mail['Date'] = format_datetime_rfc2822(now)
mail['Date'] = format_datetime(now)
# if no domain is specified for ".make_msgid()" the function can take
# a long time in case "socket.getfqdn()" must make some network
# requests (e.g. flaky network connection).
Expand Down
5 changes: 2 additions & 3 deletions schwarz/mailqueue/queue_runner.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
# -*- coding: utf-8 -*-
# SPDX-License-Identifier: MIT

from __future__ import absolute_import, print_function, unicode_literals

import email.utils
import logging
import os
import queue
import time
from mailbox import Maildir, _sync_close

from boltons.timeutils import dt_to_timestamp

from .app_helpers import init_app, init_smtp_mailer
from .compat import IS_WINDOWS, queue
from .compat import IS_WINDOWS
from .maildir_utils import create_maildir_directories, find_messages, move_message
from .message_handler import BaseMsg, MessageHandler
from .message_utils import SendResult, dt_now, msg_as_bytes, parse_message_envelope
Expand Down
6 changes: 2 additions & 4 deletions schwarz/mailqueue/smtpclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@
import socket
from contextlib import contextmanager

import six

from .lib.smtplib_py37 import (
CRLF,
SMTP,
Expand Down Expand Up @@ -49,7 +47,7 @@ def __init__(self, *args, **kwargs):
def sendmail(self, from_addr, to_addrs, msg, mail_options=(), rcpt_options=()):
self.ehlo_or_helo_if_needed()
esmtp_opts = []
if isinstance(msg, six.string_types):
if isinstance(msg, str):
msg = _fix_eols(msg).encode('ascii')
if self.does_esmtp:
if self.has_extn('size'):
Expand All @@ -63,7 +61,7 @@ def sendmail(self, from_addr, to_addrs, msg, mail_options=(), rcpt_options=()):
else:
self._rset()
raise SMTPSenderRefused(code, resp, from_addr)
if isinstance(to_addrs, six.string_types):
if isinstance(to_addrs, str):
to_addrs = [to_addrs]
for each in to_addrs:
(code, resp) = self.rcpt(each, rcpt_options)
Expand Down
21 changes: 5 additions & 16 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,16 @@ long_description_content_type = text/markdown


[options]
python_requires = >= 2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
packages = find:
namespace_packages = schwarz
python_requires = >= 3.6
packages = find_namespace:
zip_safe = false
include_package_data = true

install_requires =
boltons
docopt
# portalocker 2.0 dropped Python 2 support
portalocker < 2.0 ; python_version < '3.0'
portalocker ; python_version > '3.0'
portalocker
PuzzlePluginSystem >= 0.6.0 # >= 0.6.0: .terminate_all_activated_plugins()
six

[options.extras_require]
# "testutils" provides helpers to simplify testing (also usable by 3rd party code)
Expand All @@ -36,15 +32,8 @@ testutils =

testing =
%(testutils)s
# dotmap 1.3.25 started using f-strings without declaring a minimum Python
# version: https://github.com/drgrib/dotmap/issues/83
dotmap <= 1.3.24 ; python_version < '3.0'
dotmap; python_version >= '3.0'
# Even the oldest versions of "time-machine" support only Python 3.6+
freezegun < 1.0 ; python_version < '3.0'
freezegun ; python_version > '3.0' and implementation_name == 'pypy'
time-machine ; python_version > '3.0' and implementation_name != 'pypy'
mock ; python_version < '3.0'
dotmap
time-machine; implementation_name != 'pypy'
pytest
setuptools
testfixtures
Expand Down
34 changes: 0 additions & 34 deletions tests/dates_test.py

This file was deleted.

7 changes: 1 addition & 6 deletions tests/message_handler_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,8 @@

import os
import shutil


try:
from unittest.mock import MagicMock
except ImportError:
from mock import MagicMock
import uuid
from unittest.mock import MagicMock

import pytest
from schwarz.log_utils import l_
Expand Down
7 changes: 1 addition & 6 deletions tests/mq_run_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,7 @@
from __future__ import absolute_import, print_function, unicode_literals

import os


try:
from unittest import mock
except ImportError:
import mock
from unittest import mock

from pkg_resources import Distribution, EntryPoint, WorkingSet
from schwarz.log_utils import l_
Expand Down
11 changes: 4 additions & 7 deletions tests/queue_runner_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,16 @@
from __future__ import absolute_import, print_function, unicode_literals

import os
import sys
from datetime import datetime as DateTime, timedelta as TimeDelta

import pytest
from boltons.timeutils import UTC


try:
from time_machine import travel as freeze_time
import time_machine
except ImportError:
from freezegun import freeze_time
time_machine = None
from testfixtures import LogCapture

from schwarz.mailqueue import (
Expand All @@ -34,9 +33,7 @@ def path_maildir(tmp_path):
return _path_maildir


IS_PYPY3 = (sys.version_info >= (3, 0)) and (sys.implementation.name == 'pypy')

@pytest.mark.skipif(IS_PYPY3, reason='"time-machine" does not work on PyPy')
@pytest.mark.skipif(time_machine is None, reason='"time-machine" dependency not installed')
# https://github.com/adamchainz/time-machine/issues/305
def test_can_move_stale_messages_back_to_new(path_maildir):
mailer = DebugMailer()
Expand All @@ -50,7 +47,7 @@ def test_can_move_stale_messages_back_to_new(path_maildir):
dt_stale = DateTime.now() + TimeDelta(hours=1)
# LogCapture: no logged warning about stale message on the command line
with LogCapture():
with freeze_time(dt_stale):
with time_machine.travel(dt_stale):
send_all_queued_messages(path_maildir, mailer)
assert len(msg_files(path_maildir, folder='new')) == 0
assert len(msg_files(path_maildir, folder='cur')) == 0
Expand Down
Loading

0 comments on commit ca0afa5

Please sign in to comment.