From ae73d063016d4de013eb3da2d0d6d4d6510cddd7 Mon Sep 17 00:00:00 2001 From: Paolo Melchiorre Date: Tue, 5 Nov 2019 21:04:56 +0100 Subject: [PATCH 1/5] Remove Python 2.7 support from settings --- .travis.yml | 1 - THANKS | 1 + pyproject.toml | 4 +--- setup.py | 2 -- tox.ini | 3 +-- 5 files changed, 3 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6641d4cc..af3a6ca8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,6 @@ env: matrix: - TOX_ENV=docs - TOX_ENV=flake8 - - TOX_ENV=py27 - TOX_ENV=py35 - TOX_ENV=py36 matrix: diff --git a/THANKS b/THANKS index 121a8aca..625c56d3 100644 --- a/THANKS +++ b/THANKS @@ -117,6 +117,7 @@ Nico Di Rocco Nicolas Duhamel Nicolas Perriault Nicolas Steinmetz +Paolo Melchiorre Paul Asselin Pavel Puchkin Perry Roper diff --git a/pyproject.toml b/pyproject.toml index 996570d0..f2f2d0f9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,8 +17,6 @@ classifiers = [ "Framework :: Pelican", "License :: OSI Approved :: GNU Affero General Public License v3", "Operating System :: OS Independent", - "Programming Language :: Python :: 2", - "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", @@ -29,7 +27,7 @@ classifiers = [ ] [tool.poetry.dependencies] -python = "~2.7 || ^3.5" +python = "^3.5" feedgenerator = "^1.9" jinja2 = "~2.10.1" pygments = "^2.4" diff --git a/setup.py b/setup.py index 0649bd73..faf28f10 100755 --- a/setup.py +++ b/setup.py @@ -71,8 +71,6 @@ setup( 'Framework :: Pelican', 'License :: OSI Approved :: GNU Affero General Public License v3', 'Operating System :: OS Independent', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', diff --git a/tox.ini b/tox.ini index 15dbaee8..b9178ac9 100644 --- a/tox.ini +++ b/tox.ini @@ -1,9 +1,8 @@ [tox] -envlist = py{27,35,36,37},docs,flake8 +envlist = py{35,36,37},docs,flake8 [testenv] basepython = - py27: python2.7 py35: python3.5 py36: python3.6 py37: python3.7 From 1e0e541b575a377efa31b01bb13a60bb8c890d61 Mon Sep 17 00:00:00 2001 From: Kevin Yap Date: Tue, 5 Nov 2019 23:17:19 -0800 Subject: [PATCH 2/5] Initial pass of removing Python 2 support This commit removes Six as a dependency for Pelican, replacing the relevant aliases with the proper Python 3 imports. It also removes references to Python 2 logic that did not require Six. --- .gitignore | 1 - THANKS | 1 + docs/conf.py | 1 - pelican/__init__.py | 20 +-- pelican/__main__.py | 1 - pelican/cache.py | 4 +- pelican/contents.py | 32 ++--- pelican/generators.py | 11 +- pelican/log.py | 53 +------- pelican/paginator.py | 5 +- pelican/readers.py | 24 ++-- pelican/rstdirectives.py | 5 +- pelican/server.py | 17 +-- pelican/settings.py | 13 +- pelican/signals.py | 1 - pelican/tests/default_conf.py | 1 - pelican/tests/support.py | 4 +- pelican/tests/test_cache.py | 1 - pelican/tests/test_contents.py | 25 ++-- pelican/tests/test_generators.py | 1 - pelican/tests/test_importer.py | 1 - pelican/tests/test_paginator.py | 1 - pelican/tests/test_pelican.py | 1 - pelican/tests/test_readers.py | 11 +- pelican/tests/test_rstdirectives.py | 1 - pelican/tests/test_server.py | 3 +- pelican/tests/test_settings.py | 1 - pelican/tests/test_testsuite.py | 1 - pelican/tests/test_urlwrappers.py | 1 - pelican/tests/test_utils.py | 9 +- pelican/tools/pelican_import.py | 40 ++---- pelican/tools/pelican_quickstart.py | 81 +++++------- pelican/tools/pelican_themes.py | 1 - pelican/tools/templates/pelicanconf.py.jinja2 | 1 - pelican/tools/templates/publishconf.py.jinja2 | 1 - pelican/urlwrappers.py | 16 +-- pelican/utils.py | 51 ++------ pelican/writers.py | 11 +- poetry.lock | 120 +----------------- pyproject.toml | 1 - samples/pelican.conf.py | 1 - samples/pelican.conf_FR.py | 1 - setup.py | 9 +- 43 files changed, 126 insertions(+), 459 deletions(-) diff --git a/.gitignore b/.gitignore index 45946946..c1835e10 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,6 @@ tags .tox .coverage htmlcov -six-*.egg/ *.orig venv samples/output diff --git a/THANKS b/THANKS index 625c56d3..08ac7bb2 100644 --- a/THANKS +++ b/THANKS @@ -92,6 +92,7 @@ Joshua Adelman Julian Berman Justin Mayer Kevin Deldycke +Kevin Yap Kyle Fuller Laureline Guerin Leonard Huang diff --git a/docs/conf.py b/docs/conf.py index 43009cdd..f0589b84 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals import os import sys diff --git a/pelican/__init__.py b/pelican/__init__.py index 499ded04..456d0691 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -1,12 +1,10 @@ # -*- coding: utf-8 -*- -from __future__ import print_function, unicode_literals import argparse try: import collections.abc as collections except ImportError: import collections -import locale import logging import multiprocessing import os @@ -15,8 +13,6 @@ import sys import time import traceback -import six - # pelican.log has to be the first pelican module to be loaded # because logging.setLoggerClass has to be called before logging.getLogger from pelican.log import init as init_logging @@ -76,11 +72,10 @@ class Pelican(object): sys.path.insert(0, pluginpath) for plugin in self.settings['PLUGINS']: # if it's a string, then import it - if isinstance(plugin, six.string_types): + if isinstance(plugin, str): logger.debug("Loading plugin `%s`", plugin) try: - plugin = __import__(plugin, globals(), locals(), - str('module')) + plugin = __import__(plugin, globals(), locals(), 'module') except ImportError as e: logger.error( "Cannot load plugin `%s`\n%s", plugin, e) @@ -375,15 +370,6 @@ def get_config(args): config['BIND'] = args.bind config['DEBUG'] = args.verbosity == logging.DEBUG - # argparse returns bytes in Py2. There is no definite answer as to which - # encoding argparse (or sys.argv) uses. - # "Best" option seems to be locale.getpreferredencoding() - # http://mail.python.org/pipermail/python-list/2006-October/405766.html - if not six.PY3: - enc = locale.getpreferredencoding() - for key in config: - if key in ('PATH', 'OUTPUT_PATH', 'THEME'): - config[key] = config[key].decode(enc) return config @@ -397,7 +383,7 @@ def get_instance(args): settings = read_settings(config_file, override=get_config(args)) cls = settings['PELICAN_CLASS'] - if isinstance(cls, six.string_types): + if isinstance(cls, str): module, cls_name = cls.rsplit('.', 1) module = __import__(module) cls = getattr(module, cls_name) diff --git a/pelican/__main__.py b/pelican/__main__.py index 141823fc..69a5b95d 100644 --- a/pelican/__main__.py +++ b/pelican/__main__.py @@ -1,7 +1,6 @@ """ python -m pelican module entry point to run via python -m """ -from __future__ import absolute_import from . import main diff --git a/pelican/cache.py b/pelican/cache.py index e6c10cb9..0d36234a 100644 --- a/pelican/cache.py +++ b/pelican/cache.py @@ -1,11 +1,9 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals import hashlib import logging import os - -from six.moves import cPickle as pickle +import pickle from pelican.utils import mkdir_p diff --git a/pelican/contents.py b/pelican/contents.py index a862db2d..5193061f 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -1,25 +1,20 @@ # -*- coding: utf-8 -*- -from __future__ import print_function, unicode_literals import copy +import datetime import locale import logging import os import re -import sys +from urllib.parse import urljoin, urlparse, urlunparse import pytz -import six -from six.moves.urllib.parse import urljoin, urlparse, urlunparse - from pelican import signals from pelican.settings import DEFAULT_CONFIG -from pelican.utils import (SafeDatetime, deprecated_attribute, memoized, - path_to_url, posixize_path, - python_2_unicode_compatible, sanitised_join, - set_date_tzinfo, slugify, strftime, - truncate_html_words) +from pelican.utils import (deprecated_attribute, memoized, path_to_url, + posixize_path, sanitised_join, set_date_tzinfo, + slugify, truncate_html_words) # Import these so that they're avalaible when you import from pelican.contents. from pelican.urlwrappers import (Author, Category, Tag, URLWrapper) # NOQA @@ -27,7 +22,6 @@ from pelican.urlwrappers import (Author, Category, Tag, URLWrapper) # NOQA logger = logging.getLogger(__name__) -@python_2_unicode_compatible class Content(object): """Represents a content. @@ -121,9 +115,6 @@ class Content(object): if isinstance(self.date_format, tuple): locale_string = self.date_format[0] - if sys.version_info < (3, ) and isinstance(locale_string, - six.text_type): - locale_string = locale_string.encode('ascii') locale.setlocale(locale.LC_ALL, locale_string) self.date_format = self.date_format[1] @@ -133,11 +124,11 @@ class Content(object): if hasattr(self, 'date'): self.date = set_date_tzinfo(self.date, timezone) - self.locale_date = strftime(self.date, self.date_format) + self.locale_date = self.date.strftime(self.date_format) if hasattr(self, 'modified'): self.modified = set_date_tzinfo(self.modified, timezone) - self.locale_modified = strftime(self.modified, self.date_format) + self.locale_modified = self.modified.strftime(self.date_format) # manage status if not hasattr(self, 'status'): @@ -213,7 +204,7 @@ class Content(object): 'path': path_to_url(path), 'slug': getattr(self, 'slug', ''), 'lang': getattr(self, 'lang', 'en'), - 'date': getattr(self, 'date', SafeDatetime.now()), + 'date': getattr(self, 'date', datetime.datetime.now()), 'author': self.author.slug if hasattr(self, 'author') else '', 'category': self.category.slug if hasattr(self, 'category') else '' }) @@ -512,22 +503,21 @@ class Article(Content): # handle WITH_FUTURE_DATES (designate article to draft based on date) if not self.settings['WITH_FUTURE_DATES'] and hasattr(self, 'date'): if self.date.tzinfo is None: - now = SafeDatetime.now() + now = datetime.datetime.now() else: - now = SafeDatetime.utcnow().replace(tzinfo=pytz.utc) + now = datetime.datetime.utcnow().replace(tzinfo=pytz.utc) if self.date > now: self.status = 'draft' # if we are a draft and there is no date provided, set max datetime if not hasattr(self, 'date') and self.status == 'draft': - self.date = SafeDatetime.max + self.date = datetime.datetime.max def _expand_settings(self, key): klass = 'draft' if self.status == 'draft' else 'article' return super(Article, self)._expand_settings(key, klass) -@python_2_unicode_compatible class Static(Content): mandatory_properties = ('title',) default_status = 'published' diff --git a/pelican/generators.py b/pelican/generators.py index ef021070..27c895e4 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import print_function, unicode_literals import calendar import errno @@ -15,15 +14,12 @@ from operator import attrgetter from jinja2 import (BaseLoader, ChoiceLoader, Environment, FileSystemLoader, PrefixLoader, TemplateNotFound) -import six - from pelican import signals from pelican.cache import FileStampDataCacher from pelican.contents import Article, Page, Static from pelican.readers import Readers from pelican.utils import (DateFormatter, copy, mkdir_p, order_content, - posixize_path, process_translations, - python_2_unicode_compatible) + posixize_path, process_translations) logger = logging.getLogger(__name__) @@ -33,7 +29,6 @@ class PelicanTemplateNotFound(Exception): pass -@python_2_unicode_compatible class Generator(object): """Baseclass generator""" @@ -138,7 +133,7 @@ class Generator(object): extensions are allowed) """ # backward compatibility for older generators - if isinstance(paths, six.string_types): + if isinstance(paths, str): paths = [paths] # group the exclude dir names by parent path, for use with os.walk() @@ -513,8 +508,6 @@ class ArticlesGenerator(CachingGenerator): context["period"] = (_period,) else: month_name = calendar.month_name[_period[1]] - if not six.PY3: - month_name = month_name.decode('utf-8') if key == period_date_key['month']: context["period"] = (_period[0], month_name) diff --git a/pelican/log.py b/pelican/log.py index 6f353264..c971636e 100644 --- a/pelican/log.py +++ b/pelican/log.py @@ -1,17 +1,9 @@ # -*- coding: utf-8 -*- -from __future__ import print_function, unicode_literals -import locale import logging import os import sys from collections import defaultdict -try: - from collections.abc import Mapping -except ImportError: - from collections import Mapping - -import six __all__ = [ 'init' @@ -29,20 +21,17 @@ class BaseFormatter(logging.Formatter): # format multiline messages 'nicely' to make it clear they are together record.msg = record.msg.replace('\n', '\n | ') record.args = tuple(arg.replace('\n', '\n | ') if - isinstance(arg, six.string_types) else + isinstance(arg, str) else arg for arg in record.args) return super(BaseFormatter, self).format(record) def formatException(self, ei): ''' prefix traceback info for better representation ''' - # .formatException returns a bytestring in py2 and unicode in py3 - # since .format will handle unicode conversion, - # str() calls are used to normalize formatting string s = super(BaseFormatter, self).formatException(ei) # fancy format traceback - s = str('\n').join(str(' | ') + line for line in s.splitlines()) + s = '\n'.join(' | ' + line for line in s.splitlines()) # separate the traceback from the preceding lines - s = str(' |___\n{}').format(s) + s = ' |___\n{}'.format(s) return s def _get_levelname(self, name): @@ -140,41 +129,7 @@ class LimitFilter(logging.Filter): return True -class SafeLogger(logging.Logger): - """ - Base Logger which properly encodes Exceptions in Py2 - """ - _exc_encoding = locale.getpreferredencoding() - - def _log(self, level, msg, args, exc_info=None, extra=None): - # if the only argument is a Mapping, Logger uses that for formatting - # format values for that case - if args and len(args) == 1 and isinstance(args[0], Mapping): - args = ({k: self._decode_arg(v) for k, v in args[0].items()},) - # otherwise, format each arg - else: - args = tuple(self._decode_arg(arg) for arg in args) - super(SafeLogger, self)._log( - level, msg, args, exc_info=exc_info, extra=extra) - - def _decode_arg(self, arg): - ''' - properly decode an arg for Py2 if it's Exception - - - localized systems have errors in native language if locale is set - so convert the message to unicode with the correct encoding - ''' - if isinstance(arg, Exception): - text = str('%s: %s') % (arg.__class__.__name__, arg) - if six.PY2: - text = text.decode(self._exc_encoding) - return text - else: - return arg - - -class LimitLogger(SafeLogger): +class LimitLogger(logging.Logger): """ A logger which adds LimitFilter automatically """ diff --git a/pelican/paginator.py b/pelican/paginator.py index fe63863e..61899056 100644 --- a/pelican/paginator.py +++ b/pelican/paginator.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import print_function, unicode_literals import functools import logging @@ -7,8 +6,6 @@ import os from collections import namedtuple from math import ceil -import six - logger = logging.getLogger(__name__) PaginationRule = namedtuple( 'PaginationRule', @@ -131,7 +128,7 @@ class Page(object): prop_value = getattr(rule, key) - if not isinstance(prop_value, six.string_types): + if not isinstance(prop_value, str): logger.warning('%s is set to %s', key, prop_value) return prop_value diff --git a/pelican/readers.py b/pelican/readers.py index 52378c4f..bb4d6d81 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -1,10 +1,12 @@ # -*- coding: utf-8 -*- -from __future__ import print_function, unicode_literals +import datetime import logging import os import re from collections import OrderedDict +from html.parser import HTMLParser +from io import StringIO import docutils import docutils.core @@ -12,16 +14,11 @@ import docutils.io from docutils.parsers.rst.languages import get_language as get_docutils_lang from docutils.writers.html4css1 import HTMLTranslator, Writer -import six -from six import StringIO -from six.moves.html_parser import HTMLParser - from pelican import rstdirectives # NOQA from pelican import signals from pelican.cache import FileStampDataCacher from pelican.contents import Author, Category, Page, Tag -from pelican.utils import SafeDatetime, escape_html, get_date, pelican_open, \ - posixize_path +from pelican.utils import escape_html, get_date, pelican_open, posixize_path try: from markdown import Markdown @@ -79,7 +76,7 @@ def ensure_metadata_list(text): Regardless, all list items undergo .strip() before returning, and empty items are discarded. """ - if isinstance(text, six.text_type): + if isinstance(text, str): if ';' in text: text = text.split(';') else: @@ -212,8 +209,7 @@ class RstReader(BaseReader): """ def __init__(self, *args, **kwargs): - if six.PY3: - kwargs['mode'] = kwargs.get('mode', 'r').replace('U', '') + kwargs['mode'] = kwargs.get('mode', 'r').replace('U', '') docutils.io.FileInput.__init__(self, *args, **kwargs) def __init__(self, *args, **kwargs): @@ -685,10 +681,10 @@ def default_metadata(settings=None, process=None): metadata['category'] = value if settings.get('DEFAULT_DATE', None) and \ settings['DEFAULT_DATE'] != 'fs': - if isinstance(settings['DEFAULT_DATE'], six.string_types): + if isinstance(settings['DEFAULT_DATE'], str): metadata['date'] = get_date(settings['DEFAULT_DATE']) else: - metadata['date'] = SafeDatetime(*settings['DEFAULT_DATE']) + metadata['date'] = datetime.datetime(*settings['DEFAULT_DATE']) return metadata @@ -696,7 +692,7 @@ def path_metadata(full_path, source_path, settings=None): metadata = {} if settings: if settings.get('DEFAULT_DATE', None) == 'fs': - metadata['date'] = SafeDatetime.fromtimestamp( + metadata['date'] = datetime.datetime.fromtimestamp( os.stat(full_path).st_mtime) # Apply EXTRA_PATH_METADATA for the source path and the paths of any @@ -731,7 +727,7 @@ def parse_path_metadata(source_path, settings=None, process=None): ... process=reader.process_metadata) >>> pprint.pprint(metadata) # doctest: +ELLIPSIS {'category': , - 'date': SafeDatetime(2013, 1, 1, 0, 0), + 'date': datetime.datetime(2013, 1, 1, 0, 0), 'slug': 'my-slug'} """ metadata = {} diff --git a/pelican/rstdirectives.py b/pelican/rstdirectives.py index b4f44aa1..dda0e6a7 100644 --- a/pelican/rstdirectives.py +++ b/pelican/rstdirectives.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import print_function, unicode_literals import re @@ -10,8 +9,6 @@ from pygments import highlight from pygments.formatters import HtmlFormatter from pygments.lexers import TextLexer, get_lexer_by_name -import six - import pelican.settings as pys @@ -49,7 +46,7 @@ class Pygments(Directive): # Fetch the defaults if pys.PYGMENTS_RST_OPTIONS is not None: - for k, v in six.iteritems(pys.PYGMENTS_RST_OPTIONS): + for k, v in pys.PYGMENTS_RST_OPTIONS.items(): # Locally set options overrides the defaults if k not in self.options: self.options[k] = v diff --git a/pelican/server.py b/pelican/server.py index 841b8e88..8434ded5 100644 --- a/pelican/server.py +++ b/pelican/server.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import print_function, unicode_literals import argparse import logging @@ -7,16 +6,14 @@ import os import posixpath import ssl import sys +import urllib +from http import server try: from magic import from_file as magic_from_file except ImportError: magic_from_file = None -from six.moves import BaseHTTPServer -from six.moves import SimpleHTTPServer as srvmod -from six.moves import urllib - from pelican.log import init as init_logging logger = logging.getLogger(__name__) @@ -44,7 +41,7 @@ def parse_arguments(): return parser.parse_args() -class ComplexHTTPRequestHandler(srvmod.SimpleHTTPRequestHandler): +class ComplexHTTPRequestHandler(server.SimpleHTTPRequestHandler): SUFFIXES = ['.html', '/index.html', '/', ''] def translate_path(self, path): @@ -76,7 +73,7 @@ class ComplexHTTPRequestHandler(srvmod.SimpleHTTPRequestHandler): if not self.path: return - srvmod.SimpleHTTPRequestHandler.do_GET(self) + server.SimpleHTTPRequestHandler.do_GET(self) def get_path_that_exists(self, original_path): # Try to strip trailing slash @@ -96,7 +93,7 @@ class ComplexHTTPRequestHandler(srvmod.SimpleHTTPRequestHandler): def guess_type(self, path): """Guess at the mime type for the specified file. """ - mimetype = srvmod.SimpleHTTPRequestHandler.guess_type(self, path) + mimetype = server.SimpleHTTPRequestHandler.guess_type(self, path) # If the default guess is too generic, try the python-magic library if mimetype == 'application/octet-stream' and magic_from_file: @@ -105,9 +102,9 @@ class ComplexHTTPRequestHandler(srvmod.SimpleHTTPRequestHandler): return mimetype -class RootedHTTPServer(BaseHTTPServer.HTTPServer): +class RootedHTTPServer(server.HTTPServer): def __init__(self, base_path, *args, **kwargs): - BaseHTTPServer.HTTPServer.__init__(self, *args, **kwargs) + server.HTTPServer.__init__(self, *args, **kwargs) self.RequestHandlerClass.base_path = base_path diff --git a/pelican/settings.py b/pelican/settings.py index 58e6c63c..11bad8c4 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import print_function, unicode_literals import copy import inspect @@ -10,8 +9,6 @@ import re from os.path import isabs from posixpath import join as posix_join -import six - from pelican.log import LimitFilter @@ -278,7 +275,7 @@ def handle_deprecated_settings(settings): del settings['PLUGIN_PATH'] # PLUGIN_PATHS: str -> [str] - if isinstance(settings.get('PLUGIN_PATHS'), six.string_types): + if isinstance(settings.get('PLUGIN_PATHS'), str): logger.warning("Defining PLUGIN_PATHS setting as string " "has been deprecated (should be a list)") settings['PLUGIN_PATHS'] = [settings['PLUGIN_PATHS']] @@ -547,13 +544,13 @@ def configure_settings(settings): # standardize strings to lists for key in ['LOCALE']: - if key in settings and isinstance(settings[key], six.string_types): + if key in settings and isinstance(settings[key], str): settings[key] = [settings[key]] # check settings that must be a particular type for key, types in [ - ('OUTPUT_SOURCES_EXTENSION', six.string_types), - ('FILENAME_METADATA', six.string_types), + ('OUTPUT_SOURCES_EXTENSION', str), + ('FILENAME_METADATA', str), ]: if key in settings and not isinstance(settings[key], types): value = settings.pop(key) @@ -647,7 +644,7 @@ def configure_settings(settings): 'PAGE_PATHS', ) for PATH_KEY in filter(lambda k: k in settings, path_keys): - if isinstance(settings[PATH_KEY], six.string_types): + if isinstance(settings[PATH_KEY], str): logger.warning("Detected misconfiguration with %s setting " "(must be a list), falling back to the default", PATH_KEY) diff --git a/pelican/signals.py b/pelican/signals.py index 18a745b4..253b5fc3 100644 --- a/pelican/signals.py +++ b/pelican/signals.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import print_function, unicode_literals from blinker import signal diff --git a/pelican/tests/default_conf.py b/pelican/tests/default_conf.py index a567dc10..13014572 100644 --- a/pelican/tests/default_conf.py +++ b/pelican/tests/default_conf.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import print_function, unicode_literals AUTHOR = 'Alexis Métaireau' SITENAME = "Alexis' log" SITEURL = 'http://blog.notmyidea.org' diff --git a/pelican/tests/support.py b/pelican/tests/support.py index 751fb5ec..86bf984f 100644 --- a/pelican/tests/support.py +++ b/pelican/tests/support.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import print_function, unicode_literals import locale import logging @@ -10,12 +9,11 @@ import sys import unittest from contextlib import contextmanager from functools import wraps +from io import StringIO from logging.handlers import BufferingHandler from shutil import rmtree from tempfile import mkdtemp -from six import StringIO - from pelican.contents import Article from pelican.readers import default_metadata from pelican.settings import DEFAULT_CONFIG diff --git a/pelican/tests/test_cache.py b/pelican/tests/test_cache.py index ceba649e..7357cd9f 100644 --- a/pelican/tests/test_cache.py +++ b/pelican/tests/test_cache.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals import os from shutil import rmtree diff --git a/pelican/tests/test_contents.py b/pelican/tests/test_contents.py index efc438c8..e9592bd4 100644 --- a/pelican/tests/test_contents.py +++ b/pelican/tests/test_contents.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -from __future__ import absolute_import, unicode_literals +import datetime import locale import logging import os.path @@ -9,15 +9,12 @@ from sys import platform from jinja2.utils import generate_lorem_ipsum -import six - from pelican.contents import Article, Author, Category, Page, Static from pelican.settings import DEFAULT_CONFIG from pelican.signals import content_object_init from pelican.tests.support import (LoggedTestCase, get_context, get_settings, unittest) -from pelican.utils import (SafeDatetime, path_to_url, posixize_path, - truncate_html_words) +from pelican.utils import (path_to_url, posixize_path, truncate_html_words) # generate one paragraph, enclosed with

@@ -185,7 +182,7 @@ class TestPage(LoggedTestCase): def test_datetime(self): # If DATETIME is set to a tuple, it should be used to override LOCALE - dt = SafeDatetime(2015, 9, 13) + dt = datetime.datetime(2015, 9, 13) page_kwargs = self._copy_page_kwargs() @@ -286,9 +283,7 @@ class TestPage(LoggedTestCase): 'link')) def test_intrasite_link(self): - # type does not take unicode in PY2 and bytes in PY3, which in - # combination with unicode literals leads to following insane line: - cls_name = '_DummyArticle' if six.PY3 else b'_DummyArticle' + cls_name = '_DummyArticle' article = type(cls_name, (object,), {'url': 'article.html'}) args = self.page_kwargs.copy() @@ -370,9 +365,7 @@ class TestPage(LoggedTestCase): self.assertEqual(p.custom, linked) def test_intrasite_link_more(self): - # type does not take unicode in PY2 and bytes in PY3, which in - # combination with unicode literals leads to following insane line: - cls_name = '_DummyAsset' if six.PY3 else b'_DummyAsset' + cls_name = '_DummyAsset' args = self.page_kwargs.copy() args['settings'] = get_settings() @@ -487,9 +480,7 @@ class TestPage(LoggedTestCase): ) def test_intrasite_link_markdown_spaces(self): - # Markdown introduces %20 instead of spaces, this tests that - # we support markdown doing this. - cls_name = '_DummyArticle' if six.PY3 else b'_DummyArticle' + cls_name = '_DummyArticle' article = type(cls_name, (object,), {'url': 'article-spaces.html'}) args = self.page_kwargs.copy() @@ -512,7 +503,7 @@ class TestPage(LoggedTestCase): def test_intrasite_link_source_and_generated(self): """Test linking both to the source and the generated article """ - cls_name = '_DummyAsset' if six.PY3 else b'_DummyAsset' + cls_name = '_DummyAsset' args = self.page_kwargs.copy() args['settings'] = get_settings() @@ -538,7 +529,7 @@ class TestPage(LoggedTestCase): def test_intrasite_link_to_static_content_with_filename(self): """Test linking to a static resource with deprecated {filename} """ - cls_name = '_DummyAsset' if six.PY3 else b'_DummyAsset' + cls_name = '_DummyAsset' args = self.page_kwargs.copy() args['settings'] = get_settings() diff --git a/pelican/tests/test_generators.py b/pelican/tests/test_generators.py index 9ade301e..5b03b1d1 100644 --- a/pelican/tests/test_generators.py +++ b/pelican/tests/test_generators.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals import locale import os diff --git a/pelican/tests/test_importer.py b/pelican/tests/test_importer.py index 6eb62852..942b95fe 100644 --- a/pelican/tests/test_importer.py +++ b/pelican/tests/test_importer.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import print_function, unicode_literals import locale import os diff --git a/pelican/tests/test_paginator.py b/pelican/tests/test_paginator.py index 4e3ef035..8080c146 100644 --- a/pelican/tests/test_paginator.py +++ b/pelican/tests/test_paginator.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import absolute_import, unicode_literals import locale diff --git a/pelican/tests/test_pelican.py b/pelican/tests/test_pelican.py index 502a45ac..0d495ac7 100644 --- a/pelican/tests/test_pelican.py +++ b/pelican/tests/test_pelican.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import print_function, unicode_literals try: import collections.abc as collections diff --git a/pelican/tests/test_readers.py b/pelican/tests/test_readers.py index 3f05bb4a..5b87aeac 100644 --- a/pelican/tests/test_readers.py +++ b/pelican/tests/test_readers.py @@ -1,10 +1,7 @@ # -*- coding: utf-8 -*- -from __future__ import print_function, unicode_literals import os -import six - from pelican import readers from pelican.tests.support import get_settings, unittest from pelican.utils import SafeDatetime @@ -64,8 +61,7 @@ class TestAssertDictHasSubset(ReaderTest): self.assertDictHasSubset(self.dictionary, self.dictionary) def test_fail_not_set(self): - six.assertRaisesRegex( - self, + self.assertRaisesRegex( AssertionError, r'Expected.*key-c.*to have value.*val-c.*but was not in Dict', self.assertDictHasSubset, @@ -73,8 +69,7 @@ class TestAssertDictHasSubset(ReaderTest): {'key-c': 'val-c'}) def test_fail_wrong_val(self): - six.assertRaisesRegex( - self, + self.assertRaisesRegex( AssertionError, r'Expected .*key-a.* to have value .*val-b.* but was .*val-a.*', self.assertDictHasSubset, @@ -445,7 +440,7 @@ class RstReaderTest(ReaderTest): def test_parse_error(self): # Verify that it raises an Exception, not nothing and not SystemExit or # some such - with six.assertRaisesRegex(self, Exception, "underline too short"): + with self.assertRaisesRegex(Exception, "underline too short"): self.read_file(path='../parse_error/parse_error.rst') diff --git a/pelican/tests/test_rstdirectives.py b/pelican/tests/test_rstdirectives.py index f6a7221f..0ff28a4a 100644 --- a/pelican/tests/test_rstdirectives.py +++ b/pelican/tests/test_rstdirectives.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import print_function, unicode_literals from pelican.tests.support import unittest diff --git a/pelican/tests/test_server.py b/pelican/tests/test_server.py index 8704c6b5..824e01ef 100644 --- a/pelican/tests/test_server.py +++ b/pelican/tests/test_server.py @@ -1,9 +1,8 @@ import os +from io import BytesIO from shutil import rmtree from tempfile import mkdtemp -from six import BytesIO - from pelican.server import ComplexHTTPRequestHandler from pelican.tests.support import unittest diff --git a/pelican/tests/test_settings.py b/pelican/tests/test_settings.py index 21759d40..d995527c 100644 --- a/pelican/tests/test_settings.py +++ b/pelican/tests/test_settings.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import print_function, unicode_literals import copy import locale diff --git a/pelican/tests/test_testsuite.py b/pelican/tests/test_testsuite.py index 66c992bd..4f567516 100644 --- a/pelican/tests/test_testsuite.py +++ b/pelican/tests/test_testsuite.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import print_function, unicode_literals import warnings diff --git a/pelican/tests/test_urlwrappers.py b/pelican/tests/test_urlwrappers.py index 8ff3d9d6..37af232e 100644 --- a/pelican/tests/test_urlwrappers.py +++ b/pelican/tests/test_urlwrappers.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals from pelican.tests.support import unittest from pelican.urlwrappers import Author, Category, Tag, URLWrapper diff --git a/pelican/tests/test_utils.py b/pelican/tests/test_utils.py index 444190d3..ad90a78e 100644 --- a/pelican/tests/test_utils.py +++ b/pelican/tests/test_utils.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import absolute_import, print_function, unicode_literals import locale import logging @@ -11,8 +10,6 @@ from tempfile import mkdtemp import pytz -import six - from pelican import utils from pelican.generators import TemplatePagesGenerator from pelican.settings import read_settings @@ -720,8 +717,7 @@ class TestSanitisedJoin(unittest.TestCase): @unittest.skipIf(platform == 'win32', "Different filesystem root on Windows") def test_detect_parent_breakout(self): - with six.assertRaisesRegex( - self, + with self.assertRaisesRegex( RuntimeError, "Attempted to break out of output directory to /foo/test"): utils.sanitised_join( @@ -732,8 +728,7 @@ class TestSanitisedJoin(unittest.TestCase): @unittest.skipIf(platform == 'win32', "Different filesystem root on Windows") def test_detect_root_breakout(self): - with six.assertRaisesRegex( - self, + with self.assertRaisesRegex( RuntimeError, "Attempted to break out of output directory to /test"): utils.sanitised_join( diff --git a/pelican/tools/pelican_import.py b/pelican/tools/pelican_import.py index d6566e32..9b2edf4c 100755 --- a/pelican/tools/pelican_import.py +++ b/pelican/tools/pelican_import.py @@ -1,6 +1,5 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -from __future__ import print_function, unicode_literals import argparse import logging @@ -11,10 +10,9 @@ import sys import time from codecs import open from collections import defaultdict - -from six.moves.urllib.error import URLError -from six.moves.urllib.parse import quote, urlparse, urlsplit, urlunsplit -from six.moves.urllib.request import urlretrieve +from urllib.error import URLError +from urllib.parse import quote, urlparse, urlsplit, urlunsplit +from urllib.request import urlretrieve # because logging.setLoggerClass has to be called before logging.getLogger from pelican.log import init @@ -24,7 +22,7 @@ from pelican.utils import SafeDatetime, slugify try: from html import unescape # py3.5+ except ImportError: - from six.moves.html_parser import HTMLParser + from html.parser import HTMLParser unescape = HTMLParser().unescape logger = logging.getLogger(__name__) @@ -396,19 +394,8 @@ def posterous2fields(api_token, email, password): """Imports posterous posts""" import base64 from datetime import timedelta - try: - # py3k import - import json - except ImportError: - # py2 import - import simplejson as json - - try: - # py3k import - import urllib.request as urllib_request - except ImportError: - # py2 import - import urllib2 as urllib_request + import json + import urllib.request as urllib_request def get_posterous_posts(api_token, email, password, page=1): base64string = base64.encodestring( @@ -451,19 +438,8 @@ def posterous2fields(api_token, email, password): def tumblr2fields(api_key, blogname): """ Imports Tumblr posts (API v2)""" - try: - # py3k import - import json - except ImportError: - # py2 import - import simplejson as json - - try: - # py3k import - import urllib.request as urllib_request - except ImportError: - # py2 import - import urllib2 as urllib_request + import json + import urllib.request as urllib_request def get_tumblr_posts(api_key, blogname, offset=0): url = ("http://api.tumblr.com/v2/blog/%s.tumblr.com/" diff --git a/pelican/tools/pelican_quickstart.py b/pelican/tools/pelican_quickstart.py index 463db900..5ff3dc33 100755 --- a/pelican/tools/pelican_quickstart.py +++ b/pelican/tools/pelican_quickstart.py @@ -1,12 +1,10 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -from __future__ import print_function, unicode_literals import argparse import codecs import locale import os -import sys from jinja2 import Environment, FileSystemLoader @@ -23,8 +21,6 @@ try: except ImportError: _DEFAULT_TIMEZONE = 'Europe/Paris' -import six - from pelican import __version__ locale.setlocale(locale.LC_ALL, '') @@ -77,41 +73,24 @@ CONF = { # url for list of valid timezones _TZ_URL = 'http://en.wikipedia.org/wiki/List_of_tz_database_time_zones' -_input_compat = six.moves.input -str_compat = six.text_type - # Create a 'marked' default path, to determine if someone has supplied # a path on the command-line. -class _DEFAULT_PATH_TYPE(str_compat): +class _DEFAULT_PATH_TYPE(str): is_default_path = True _DEFAULT_PATH = _DEFAULT_PATH_TYPE(os.curdir) -def decoding_strings(f): - def wrapper(*args, **kwargs): - out = f(*args, **kwargs) - if isinstance(out, six.string_types) and not six.PY3: - # todo: make encoding configurable? - if six.PY3: - return out - else: - return out.decode(sys.stdin.encoding) - return out - return wrapper - - -@decoding_strings -def ask(question, answer=str_compat, default=None, length=None): - if answer == str_compat: +def ask(question, answer=str, default=None, length=None): + if answer == str: r = '' while True: if default: - r = _input_compat('> {0} [{1}] '.format(question, default)) + r = input('> {0} [{1}] '.format(question, default)) else: - r = _input_compat('> {0} '.format(question, default)) + r = input('> {0} '.format(question, default)) r = r.strip() @@ -133,11 +112,11 @@ def ask(question, answer=str_compat, default=None, length=None): r = None while True: if default is True: - r = _input_compat('> {0} (Y/n) '.format(question)) + r = input('> {0} (Y/n) '.format(question)) elif default is False: - r = _input_compat('> {0} (y/N) '.format(question)) + r = input('> {0} (y/N) '.format(question)) else: - r = _input_compat('> {0} (y/n) '.format(question)) + r = input('> {0} (y/n) '.format(question)) r = r.strip().lower() @@ -157,9 +136,9 @@ def ask(question, answer=str_compat, default=None, length=None): r = None while True: if default: - r = _input_compat('> {0} [{1}] '.format(question, default)) + r = input('> {0} [{1}] '.format(question, default)) else: - r = _input_compat('> {0} '.format(question)) + r = input('> {0} '.format(question)) r = r.strip() @@ -175,14 +154,14 @@ def ask(question, answer=str_compat, default=None, length=None): return r else: raise NotImplementedError( - 'Argument `answer` must be str_compat, bool, or integer') + 'Argument `answer` must be str, bool, or integer') def ask_timezone(question, default, tzurl): """Prompt for time zone and validate input""" lower_tz = [tz.lower() for tz in pytz.all_timezones] while True: - r = ask(question, str_compat, default) + r = ask(question, str, default) r = r.strip().replace(' ', '_').lower() if r in lower_tz: r = pytz.all_timezones[lower_tz.index(r)] @@ -227,20 +206,20 @@ needed by Pelican. else: CONF['basedir'] = os.path.abspath(os.path.expanduser( ask('Where do you want to create your new web site?', - answer=str_compat, default=args.path))) + answer=str, default=args.path))) CONF['sitename'] = ask('What will be the title of this web site?', - answer=str_compat, default=args.title) + answer=str, default=args.title) CONF['author'] = ask('Who will be the author of this web site?', - answer=str_compat, default=args.author) + answer=str, default=args.author) CONF['lang'] = ask('What will be the default language of this web site?', - str_compat, args.lang or CONF['lang'], 2) + str, args.lang or CONF['lang'], 2) if ask('Do you want to specify a URL prefix? e.g., https://example.com ', answer=bool, default=True): CONF['siteurl'] = ask('What is your URL prefix? (see ' 'above example; no trailing slash)', - str_compat, CONF['siteurl']) + str, CONF['siteurl']) CONF['with_pagination'] = ask('Do you want to enable article pagination?', bool, bool(CONF['default_pagination'])) @@ -263,49 +242,49 @@ needed by Pelican. answer=bool, default=False): CONF['ftp'] = True, CONF['ftp_host'] = ask('What is the hostname of your FTP server?', - str_compat, CONF['ftp_host']) + str, CONF['ftp_host']) CONF['ftp_user'] = ask('What is your username on that server?', - str_compat, CONF['ftp_user']) + str, CONF['ftp_user']) CONF['ftp_target_dir'] = ask('Where do you want to put your ' 'web site on that server?', - str_compat, CONF['ftp_target_dir']) + str, CONF['ftp_target_dir']) if ask('Do you want to upload your website using SSH?', answer=bool, default=False): CONF['ssh'] = True, CONF['ssh_host'] = ask('What is the hostname of your SSH server?', - str_compat, CONF['ssh_host']) + str, CONF['ssh_host']) CONF['ssh_port'] = ask('What is the port of your SSH server?', int, CONF['ssh_port']) CONF['ssh_user'] = ask('What is your username on that server?', - str_compat, CONF['ssh_user']) + str, CONF['ssh_user']) CONF['ssh_target_dir'] = ask('Where do you want to put your ' 'web site on that server?', - str_compat, CONF['ssh_target_dir']) + str, CONF['ssh_target_dir']) if ask('Do you want to upload your website using Dropbox?', answer=bool, default=False): CONF['dropbox'] = True, CONF['dropbox_dir'] = ask('Where is your Dropbox directory?', - str_compat, CONF['dropbox_dir']) + str, CONF['dropbox_dir']) if ask('Do you want to upload your website using S3?', answer=bool, default=False): CONF['s3'] = True, CONF['s3_bucket'] = ask('What is the name of your S3 bucket?', - str_compat, CONF['s3_bucket']) + str, CONF['s3_bucket']) if ask('Do you want to upload your website using ' 'Rackspace Cloud Files?', answer=bool, default=False): CONF['cloudfiles'] = True, CONF['cloudfiles_username'] = ask('What is your Rackspace ' - 'Cloud username?', str_compat, + 'Cloud username?', str, CONF['cloudfiles_username']) CONF['cloudfiles_api_key'] = ask('What is your Rackspace ' - 'Cloud API key?', str_compat, + 'Cloud API key?', str, CONF['cloudfiles_api_key']) CONF['cloudfiles_container'] = ask('What is the name of your ' 'Cloud Files container?', - str_compat, + str, CONF['cloudfiles_container']) if ask('Do you want to upload your website using GitHub Pages?', @@ -363,9 +342,7 @@ needed by Pelican. try: with codecs.open(os.path.join(CONF['basedir'], 'Makefile'), 'w', 'utf-8') as fd: - py_v = 'python' - if six.PY3: - py_v = 'python3' + py_v = 'python3' _template = _jinja_env.get_template('Makefile.jinja2') fd.write(_template.render(py_v=py_v, **CONF)) fd.close() diff --git a/pelican/tools/pelican_themes.py b/pelican/tools/pelican_themes.py index 9f9b2328..03acd9ce 100755 --- a/pelican/tools/pelican_themes.py +++ b/pelican/tools/pelican_themes.py @@ -1,6 +1,5 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -from __future__ import print_function, unicode_literals import argparse import os diff --git a/pelican/tools/templates/pelicanconf.py.jinja2 b/pelican/tools/templates/pelicanconf.py.jinja2 index 79f26a01..62939b6a 100644 --- a/pelican/tools/templates/pelicanconf.py.jinja2 +++ b/pelican/tools/templates/pelicanconf.py.jinja2 @@ -1,6 +1,5 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # -from __future__ import unicode_literals AUTHOR = {{author}} SITENAME = {{sitename}} diff --git a/pelican/tools/templates/publishconf.py.jinja2 b/pelican/tools/templates/publishconf.py.jinja2 index 913de7f1..bb18966b 100755 --- a/pelican/tools/templates/publishconf.py.jinja2 +++ b/pelican/tools/templates/publishconf.py.jinja2 @@ -1,6 +1,5 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # -from __future__ import unicode_literals # This file is only used if you use `make publish` or # explicitly specify it as your config file. diff --git a/pelican/urlwrappers.py b/pelican/urlwrappers.py index baa8eb23..edfb11b4 100644 --- a/pelican/urlwrappers.py +++ b/pelican/urlwrappers.py @@ -1,18 +1,14 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals import functools import logging import os -import six - -from pelican.utils import python_2_unicode_compatible, slugify +from pelican.utils import slugify logger = logging.getLogger(__name__) -@python_2_unicode_compatible @functools.total_ordering class URLWrapper(object): def __init__(self, name, settings): @@ -66,26 +62,26 @@ class URLWrapper(object): def _normalize_key(self, key): subs = self.settings.get('SLUG_REGEX_SUBSTITUTIONS', []) - return six.text_type(slugify(key, regex_subs=subs)) + return str(slugify(key, regex_subs=subs)) def __eq__(self, other): if isinstance(other, self.__class__): return self.slug == other.slug - if isinstance(other, six.text_type): + if isinstance(other, str): return self.slug == self._normalize_key(other) return False def __ne__(self, other): if isinstance(other, self.__class__): return self.slug != other.slug - if isinstance(other, six.text_type): + if isinstance(other, str): return self.slug != self._normalize_key(other) return True def __lt__(self, other): if isinstance(other, self.__class__): return self.slug < other.slug - if isinstance(other, six.text_type): + if isinstance(other, str): return self.slug < self._normalize_key(other) return False @@ -105,7 +101,7 @@ class URLWrapper(object): """ setting = "%s_%s" % (self.__class__.__name__.upper(), key) value = self.settings[setting] - if not isinstance(value, six.string_types): + if not isinstance(value, str): logger.warning('%s is set to %s', setting, value) return value else: diff --git a/pelican/utils.py b/pelican/utils.py index 9629c7e0..d031503d 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import print_function, unicode_literals import codecs import datetime @@ -12,12 +11,15 @@ import re import shutil import sys import traceback +import urllib try: from collections.abc import Hashable except ImportError: from collections import Hashable from contextlib import contextmanager from functools import partial +from html import entities +from html.parser import HTMLParser from itertools import groupby from operator import attrgetter @@ -27,10 +29,6 @@ from jinja2 import Markup import pytz -import six -from six.moves import html_entities -from six.moves.html_parser import HTMLParser - try: from html import escape except ImportError: @@ -98,10 +96,6 @@ def strftime(date, date_format): else: formatted = date.strftime(candidate) - # convert Py2 result to unicode - if not six.PY3 and enc is not None: - formatted = formatted.decode(enc) - # strip zeros if '-' prefix is used if conversion: formatted = conversion(formatted) @@ -150,22 +144,6 @@ class DateFormatter(object): return formatted -def python_2_unicode_compatible(klass): - """ - A decorator that defines __unicode__ and __str__ methods under Python 2. - Under Python 3 it does nothing. - - To support Python 2 and 3 with a single code base, define a __str__ method - returning text and apply this decorator to the class. - - From django.utils.encoding. - """ - if not six.PY3: - klass.__unicode__ = klass.__str__ - klass.__str__ = lambda self: self.__unicode__().encode('utf-8') - return klass - - class memoized(object): """Function decorator to cache return values. @@ -214,15 +192,15 @@ def deprecated_attribute(old, new, since=None, remove=None, doc=None): content of the dummy method is ignored. """ def _warn(): - version = '.'.join(six.text_type(x) for x in since) + version = '.'.join(str(x) for x in since) message = ['{} has been deprecated since {}'.format(old, version)] if remove: - version = '.'.join(six.text_type(x) for x in remove) + version = '.'.join(str(x) for x in remove) message.append( ' and will be removed by version {}'.format(version)) message.append('. Use {} instead.'.format(new)) logger.warning(''.join(message)) - logger.debug(''.join(six.text_type(x) for x + logger.debug(''.join(str(x) for x in traceback.format_stack())) def fget(self): @@ -279,10 +257,8 @@ def slugify(value, regex_subs=()): # value must be unicode per se import unicodedata from unidecode import unidecode - # unidecode returns str in Py2 and 3, so in Py2 we have to make - # it unicode again value = unidecode(value) - if isinstance(value, six.binary_type): + if isinstance(value, bytes): value = value.decode('ascii') # still unicode value = unicodedata.normalize('NFKD', value) @@ -584,8 +560,8 @@ class _HTMLWordTruncator(HTMLParser): `name` is the entity ref without ampersand and semicolon (e.g. `mdash`) """ try: - codepoint = html_entities.name2codepoint[name] - char = six.unichr(codepoint) + codepoint = entities.name2codepoint[name] + char = chr(codepoint) except KeyError: char = '' self._handle_ref(name, char) @@ -602,7 +578,7 @@ class _HTMLWordTruncator(HTMLParser): codepoint = int(name[1:], 16) else: codepoint = int(name) - char = six.unichr(codepoint) + char = chr(codepoint) except (ValueError, OverflowError): char = '' self._handle_ref('#' + name, char) @@ -663,7 +639,7 @@ def process_translations(content_list, translation_id=None): if not translation_id: return content_list, [] - if isinstance(translation_id, six.string_types): + if isinstance(translation_id, str): translation_id = {translation_id} index = [] @@ -753,7 +729,7 @@ def order_content(content_list, order_by='slug'): content_list.sort(key=order_by) except Exception: logger.error('Error sorting with function %s', order_by) - elif isinstance(order_by, six.string_types): + elif isinstance(order_by, str): if order_by.startswith('reversed-'): order_reversed = True order_by = order_by.replace('reversed-', '', 1) @@ -901,8 +877,7 @@ def is_selected_for_writing(settings, path): def path_to_file_url(path): '''Convert file-system path to file:// URL''' - return six.moves.urllib_parse.urljoin( - "file://", six.moves.urllib.request.pathname2url(path)) + return urllib.parse.urljoin("file://", urllib.request.pathname2url(path)) def maybe_pluralize(count, singular, plural): diff --git a/pelican/writers.py b/pelican/writers.py index b610e2e2..daeb9dec 100644 --- a/pelican/writers.py +++ b/pelican/writers.py @@ -1,24 +1,18 @@ # -*- coding: utf-8 -*- -from __future__ import print_function, unicode_literals, with_statement import logging import os +from urllib.parse import urljoin from feedgenerator import Atom1Feed, Rss201rev2Feed, get_tag_uri from jinja2 import Markup -import six -from six.moves.urllib.parse import urljoin - from pelican import signals from pelican.paginator import Paginator from pelican.utils import (get_relative_path, is_selected_for_writing, path_to_url, sanitised_join, set_date_tzinfo) -if not six.PY3: - from codecs import open - logger = logging.getLogger(__name__) @@ -165,8 +159,7 @@ class Writer(object): except Exception: pass - encoding = 'utf-8' if six.PY3 else None - with self._open_w(complete_path, encoding, override_output) as fp: + with self._open_w(complete_path, 'utf-8', override_output) as fp: feed.write(fp, 'utf-8') logger.info('Writing %s', complete_path) diff --git a/poetry.lock b/poetry.lock index 971f6520..50ba33bc 100644 --- a/poetry.lock +++ b/poetry.lock @@ -17,15 +17,6 @@ version = "2.7.0" [package.dependencies] pytz = ">=2015.7" -[[package]] -category = "dev" -description = "backports.functools_lru_cache" -marker = "python_version < \"3\"" -name = "backports.functools-lru-cache" -optional = false -python-versions = ">=2.6" -version = "1.5" - [[package]] category = "dev" description = "Screen-scraping library" @@ -54,24 +45,6 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "0.4.1" -[[package]] -category = "dev" -description = "Updated configparser from Python 3.7 for Python 2.6+." -marker = "python_version < \"3.2\"" -name = "configparser" -optional = false -python-versions = ">=2.6" -version = "3.7.4" - -[[package]] -category = "dev" -description = "Backports and enhancements for the contextlib module" -marker = "python_version < \"3\"" -name = "contextlib2" -optional = false -python-versions = "*" -version = "0.5.5" - [[package]] category = "main" description = "Docutils -- Python Documentation Utilities" @@ -88,20 +61,6 @@ optional = false python-versions = ">=2.7" version = "0.3" -[package.dependencies] -[package.dependencies.configparser] -python = ">=2.7,<2.8" -version = ">=3.5" - -[[package]] -category = "dev" -description = "Python 3.4 Enum backported to 3.3, 3.2, 3.1, 2.7, 2.6, 2.5, and 2.4" -marker = "python_version < \"3.4\"" -name = "enum34" -optional = false -python-versions = "*" -version = "1.1.6" - [[package]] category = "main" description = "Standalone version of django.utils.feedgenerator" @@ -136,22 +95,6 @@ mccabe = ">=0.6.0,<0.7.0" pycodestyle = ">=2.5.0,<2.6.0" pyflakes = ">=2.1.0,<2.2.0" -[package.dependencies.configparser] -python = "<3.2" -version = "*" - -[package.dependencies.enum34] -python = "<3.4" -version = "*" - -[package.dependencies.functools32] -python = "<3.2" -version = "*" - -[package.dependencies.typing] -python = "<3.5" -version = "*" - [[package]] category = "dev" description = "Flake8 and pylama plugin that checks the ordering of import statements." @@ -164,28 +107,6 @@ version = "0.18.1" pycodestyle = "*" setuptools = "*" -[package.dependencies.enum34] -python = "<2.8" -version = "*" - -[[package]] -category = "dev" -description = "Python function signatures from PEP362 for Python 2.6, 2.7 and 3.2+" -marker = "python_version < \"3.3\"" -name = "funcsigs" -optional = false -python-versions = "*" -version = "1.0.2" - -[[package]] -category = "dev" -description = "Backport of the functools module from Python 3.2.3 for use on 2.7 and PyPy." -marker = "python_version < \"3.2\"" -name = "functools32" -optional = false -python-versions = "*" -version = "3.2.3-2" - [[package]] category = "dev" description = "Getting image size from png/jpeg/jpeg2000/gif file" @@ -205,14 +126,6 @@ version = "0.18" [package.dependencies] zipp = ">=0.5" -[package.dependencies.configparser] -python = "<3" -version = ">=3.5" - -[package.dependencies.contextlib2] -python = "<3" -version = "*" - [[package]] category = "main" description = "A small but fast and easy to use stand-alone template engine written in pure python." @@ -270,10 +183,6 @@ version = "3.0.5" [package.dependencies] six = "*" -[package.dependencies.funcsigs] -python = "<3.3" -version = ">=1" - [[package]] category = "dev" description = "Core utilities for Python packages" @@ -362,7 +271,7 @@ description = "Python 2 and 3 compatibility utilities" name = "six" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*" -version = "1.12.0" +version = "1.13.0" [[package]] category = "dev" @@ -388,11 +297,6 @@ optional = false python-versions = "*" version = "1.9.2" -[package.dependencies] -[package.dependencies."backports.functools-lru-cache"] -python = "<3" -version = "*" - [[package]] category = "dev" description = "Python documentation generator" @@ -449,15 +353,6 @@ six = ">=1.0.0,<2" toml = ">=0.9.4" virtualenv = ">=14.0.0" -[[package]] -category = "dev" -description = "Type Hints for Python" -marker = "python_version < \"3.5\"" -name = "typing" -optional = false -python-versions = "*" -version = "3.7.4" - [[package]] category = "dev" description = "Filters to enhance web typography, including support for Django & Jinja templates" @@ -497,27 +392,21 @@ version = "0.5.1" markdown = ["markdown"] [metadata] -content-hash = "d22ff0db3331186ab2f809313de1f9efef1f9c708cc354eaa1638a5111404612" -python-versions = "~2.7 || ^3.5" +content-hash = "0163177d87dca0955d111319de9481cf2ed0ef014e63a3740ffaf32a1cf07212" +python-versions = "^3.5" [metadata.hashes] alabaster = ["446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359", "a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"] babel = ["af92e6106cb7c55286b25b38ad7695f8b4efb36a90ba483d7f7a6628c46158ab", "e86135ae101e31e2c8ec20a4e0c5220f4eed12487d5cf3f78be7e98d3a57fc28"] -"backports.functools-lru-cache" = ["9d98697f088eb1b0fa451391f91afb5e3ebde16bbdb272819fd091151fda4f1a", "f0b0e4eba956de51238e17573b7087e852dfe9854afd2e9c873f73fc0ca0a6dd"] beautifulsoup4 = ["034740f6cb549b4e932ae1ab975581e6103ac8f942200a0e9759065984391858", "945065979fb8529dd2f37dbb58f00b661bdbcbebf954f93b32fdf5263ef35348", "ba6d5c59906a85ac23dadfe5c88deaf3e179ef565f4898671253e50a78680718"] blinker = ["471aee25f3992bd325afa3772f1063dbdbbca947a041b8b89466dc00d606f8b6"] colorama = ["05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d", "f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48"] -configparser = ["8be81d89d6e7b4c0d4e44bcc525845f6da25821de80cb5e06e7e0238a2899e32", "da60d0014fd8c55eb48c1c5354352e363e2d30bbf7057e5e171a468390184c75"] -contextlib2 = ["509f9419ee91cdd00ba34443217d5ca51f5a364a404e1dce9e8979cea969ca48", "f5260a6e679d2ff42ec91ec5252f4eeffdcf21053db9113bd0a8e4d953769c00"] docutils = ["02aec4bd92ab067f6ff27a38a38a41173bf01bed8f89157768c1573f53e474a6", "51e64ef2ebfb29cae1faa133b3710143496eca21c530f3f71424d77687764274", "7a4bd47eaf6596e1295ecb11361139febe29b084a87bf005bf899f9a42edc3c6"] entrypoints = ["589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19", "c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"] -enum34 = ["2d81cbbe0e73112bdfe6ef8576f2238f2ba27dd0d55752a776c41d38b7da2850", "644837f692e5f550741432dd3f223bbb9852018674981b1664e5dc339387588a", "6bd0f6ad48ec2aa117d3d141940d484deccda84d4fcd884f5c3d93c23ecd8c79", "8ad8c4783bf61ded74527bffb48ed9b54166685e4230386a9ed9b1279e2df5b1"] feedgenerator = ["5ae05daa9cfa47fa406ee4744d0b7fa1c8a05a7a47ee0ad328ddf55327cfb106"] filelock = ["18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59", "929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"] flake8 = ["859996073f341f2670741b51ec1e67a01da142831aa1fdc6242dbf88dffbe661", "a796a115208f5c03b18f332f7c11729812c8c3ded6c46319c59b53efd3819da8"] flake8-import-order = ["90a80e46886259b9c396b578d75c749801a41ee969a235e163cfe1be7afd2543", "a28dc39545ea4606c1ac3c24e9d05c849c6e5444a50fb7e9cdd430fc94de6e92"] -funcsigs = ["330cc27ccbf7f1e992e69fef78261dc7c6569012cf397db8d3de0234e6c937ca", "a7bb0f2cf3a3fd1ab2732cb49eba4252c2af4240442415b4abce3b87022a8f50"] -functools32 = ["89d824aa6c358c421a234d7f9ee0bd75933a67c29588ce50aaa3acdf4d403fa0", "f6253dfbe0538ad2e387bd8fdfd9293c925d63553f5813c4e587745416501e6d"] imagesize = ["3f349de3eb99145973fefb7dbe38554414e5c30abd0c8e4b970a7c9d09f3a1d8", "f3832918bc3c66617f92e35f5d70729187676313caa60c187eb0f28b8fe5e3b5"] importlib-metadata = ["6dfd58dfe281e8d240937776065dd3624ad5469c835248219bd16cf2e12dbeb7", "cb6ee23b46173539939964df59d3d72c3e0c1b5d54b84f1d8a7e912fe43612db"] jinja2 = ["065c4f02ebe7f7cf559e49ee5a95fb800a9e4528727aec6f24402a5374c65013", "14dd6caf1527abb21f08f86c784eac40853ba93edb79552aa1e4b8aef1b61c7b"] @@ -535,7 +424,7 @@ pygments = ["71e430bc85c88a430f000ac1d9b331d2407f681d6f6aec95e8bcfbc3df5b0127", pyparsing = ["1873c03321fc118f4e9746baf201ff990ceb915f433f23b395f5580d1840cb2a", "9b6323ef4ab914af344ba97510e966d64ba91055d6b9afa6b30799340e89cc03"] python-dateutil = ["7e6584c74aeed623791615e26efd690f29817a27c73085b78e4bad02493df2fb", "c89805f6f4d64db21ed966fda138f8a5ed7a4fdbc1a8ee329ce1b74e3c74da9e"] pytz = ["303879e36b721603cc54604edcac9d20401bdbe31e1e4fdee5b9f98d5d31dfda", "d747dd3d23d77ef44c6a3526e274af6efeb0a6f1afd5a69ba4d5be4098c8e141"] -six = ["3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", "d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"] +six = ["1f1b7d42e254082a9db6279deae68afb421ceba6158efa6131de7b3003ee93fd", "30f610279e8b2578cab6db20741130331735c781b56053c59c4076da27f06b66"] smartypants = ["8db97f7cbdf08d15b158a86037cd9e116b4cf37703d24e0419a0d64ca5808f0d"] snowballstemmer = ["9f3b9ffe0809d174f7047e121431acf99c89a7040f0ca84f94ba53a498e6d0c9"] soupsieve = ["72b5f1aea9101cf720a36bb2327ede866fd6f1a07b1e87c92a1cc18113cbc946", "e4e9c053d59795e440163733a7fec6c5972210e1790c507e4c7b051d6c5259de"] @@ -543,7 +432,6 @@ sphinx = ["82cd2728c906be96e307b81352d3fd9fb731869234c6b835cc25e9a3dfb4b7e4", "b sphinx-rtd-theme = ["00cf895504a7895ee433807c62094cf1e95f065843bf3acd17037c3e9a2becd4", "728607e34d60456d736cc7991fd236afb828b21b82f956c5ea75f94c8414040a"] toml = ["229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c", "235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e", "f1db651f9657708513243e61e6cc67d101a39bad662eaa9b5546f789338e07a3"] tox = ["dab0b0160dd187b654fc33d690ee1d7bf328bd5b8dc6ef3bb3cc468969c659ba", "ee35ffce74933a6c6ac10c9a0182e41763140a5a5070e21b114feca56eaccdcd"] -typing = ["38566c558a0a94d6531012c8e917b1b8518a41e418f7f15f00e129cc80162ad3", "53765ec4f83a2b720214727e319607879fec4acde22c4fbb54fa2604e79e44ce", "84698954b4e6719e912ef9a42a2431407fe3755590831699debda6fba92aac55"] typogrify = ["8be4668cda434163ce229d87ca273a11922cb1614cb359970b7dc96eed13cb38"] unidecode = ["1d7a042116536098d05d599ef2b8616759f02985c85b4fef50c78a5aaf10822a", "2b6aab710c2a1647e928e36d69c21e76b453cd455f4e2621000e54b2a9b8cce8"] virtualenv = ["b7335cddd9260a3dd214b73a2521ffc09647bde3e9457fcca31dc3be3999d04a", "d28ca64c0f3f125f59cabf13e0a150e1c68e5eea60983cc4395d88c584495783"] diff --git a/pyproject.toml b/pyproject.toml index f2f2d0f9..50408d7f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,7 +37,6 @@ unidecode = "^1.1" python-dateutil = "^2.8" docutils = "^0.14" markdown = {version = "~3.1.1", optional = true} -six = "^1.4" [tool.poetry.dev-dependencies] BeautifulSoup4 = "^4.7" diff --git a/samples/pelican.conf.py b/samples/pelican.conf.py index 0f67ee83..861c1f53 100755 --- a/samples/pelican.conf.py +++ b/samples/pelican.conf.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals AUTHOR = 'Alexis Métaireau' SITENAME = "Alexis' log" diff --git a/samples/pelican.conf_FR.py b/samples/pelican.conf_FR.py index b1571e8a..f87653a4 100644 --- a/samples/pelican.conf_FR.py +++ b/samples/pelican.conf_FR.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals AUTHOR = 'Alexis Métaireau' SITENAME = "Alexis' log" diff --git a/setup.py b/setup.py index faf28f10..d79f055a 100755 --- a/setup.py +++ b/setup.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -import sys + from io import open from os import walk from os.path import join, relpath @@ -10,8 +10,7 @@ from setuptools import setup version = "4.2.0" requires = ['feedgenerator >= 1.9', 'jinja2 >= 2.7', 'pygments', 'docutils', - 'pytz >= 0a', 'blinker', 'unidecode', 'six >= 1.4', - 'python-dateutil'] + 'pytz >= 0a', 'blinker', 'unidecode', 'python-dateutil'] entry_points = { 'console_scripts': [ @@ -25,9 +24,7 @@ entry_points = { README = open('README.rst', encoding='utf-8').read() CHANGELOG = open('docs/changelog.rst', encoding='utf-8').read() -description = u'\n'.join([README, CHANGELOG]) -if sys.version_info.major < 3: - description = description.encode('utf-8') +description = '\n'.join([README, CHANGELOG]) setup( name='pelican', From 49bc6ed47f75cb29eade8e2b1a4cf10bfaf73d92 Mon Sep 17 00:00:00 2001 From: Deniz Turgut Date: Sun, 17 Nov 2019 19:19:37 +0300 Subject: [PATCH 3/5] Further remove python2-isms --- pelican/__init__.py | 7 ++-- pelican/generators.py | 1 - pelican/readers.py | 13 ++++---- pelican/settings.py | 19 ++++------- pelican/tests/test_generators.py | 1 - pelican/tests/test_importer.py | 1 - pelican/tests/test_pelican.py | 8 ++--- pelican/tools/pelican_import.py | 7 +--- pelican/tools/pelican_quickstart.py | 17 +++++----- pelican/urlwrappers.py | 2 +- pelican/utils.py | 50 +++++------------------------ 11 files changed, 35 insertions(+), 91 deletions(-) diff --git a/pelican/__init__.py b/pelican/__init__.py index 456d0691..83160684 100644 --- a/pelican/__init__.py +++ b/pelican/__init__.py @@ -1,10 +1,6 @@ # -*- coding: utf-8 -*- import argparse -try: - import collections.abc as collections -except ImportError: - import collections import logging import multiprocessing import os @@ -12,6 +8,7 @@ import pprint import sys import time import traceback +from collections.abc import Iterable # pelican.log has to be the first pelican module to be loaded # because logging.setLoggerClass has to be called before logging.getLogger @@ -184,7 +181,7 @@ class Pelican(object): for pair in signals.get_generators.send(self): (funct, value) = pair - if not isinstance(value, collections.Iterable): + if not isinstance(value, Iterable): value = (value, ) for v in value: diff --git a/pelican/generators.py b/pelican/generators.py index 27c895e4..8782238b 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -5,7 +5,6 @@ import errno import fnmatch import logging import os -from codecs import open from collections import defaultdict from functools import partial from itertools import chain, groupby diff --git a/pelican/readers.py b/pelican/readers.py index bb4d6d81..c650913f 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -5,6 +5,7 @@ import logging import os import re from collections import OrderedDict +from html import escape from html.parser import HTMLParser from io import StringIO @@ -18,7 +19,7 @@ from pelican import rstdirectives # NOQA from pelican import signals from pelican.cache import FileStampDataCacher from pelican.contents import Author, Category, Page, Tag -from pelican.utils import escape_html, get_date, pelican_open, posixize_path +from pelican.utils import get_date, pelican_open, posixize_path try: from markdown import Markdown @@ -411,7 +412,7 @@ class HTMLReader(BaseReader): self._in_body = False self._in_top_level = True elif self._in_body: - self._data_buffer += ''.format(escape_html(tag)) + self._data_buffer += ''.format(escape(tag)) def handle_startendtag(self, tag, attrs): if tag == 'meta' and self._in_head: @@ -432,16 +433,16 @@ class HTMLReader(BaseReader): self._data_buffer += '&#{};'.format(data) def build_tag(self, tag, attrs, close_tag): - result = '<{}'.format(escape_html(tag)) + result = '<{}'.format(escape(tag)) for k, v in attrs: - result += ' ' + escape_html(k) + result += ' ' + escape(k) if v is not None: # If the attribute value contains a double quote, surround # with single quotes, otherwise use double quotes. if '"' in v: - result += "='{}'".format(escape_html(v, quote=False)) + result += "='{}'".format(escape(v, quote=False)) else: - result += '="{}"'.format(escape_html(v, quote=False)) + result += '="{}"'.format(escape(v, quote=False)) if close_tag: return result + ' />' return result + '>' diff --git a/pelican/settings.py b/pelican/settings.py index 11bad8c4..a4033001 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import copy +import importlib.util import inspect import locale import logging @@ -12,19 +13,11 @@ from posixpath import join as posix_join from pelican.log import LimitFilter -try: - # spec_from_file_location is the recommended way in Python 3.5+ - import importlib.util - - def load_source(name, path): - spec = importlib.util.spec_from_file_location(name, path) - mod = importlib.util.module_from_spec(spec) - spec.loader.exec_module(mod) - return mod -except ImportError: - # but it does not exist in Python 2.7, so fall back to imp - import imp - load_source = imp.load_source +def load_source(name, path): + spec = importlib.util.spec_from_file_location(name, path) + mod = importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) + return mod logger = logging.getLogger(__name__) diff --git a/pelican/tests/test_generators.py b/pelican/tests/test_generators.py index 5b03b1d1..3ab341a3 100644 --- a/pelican/tests/test_generators.py +++ b/pelican/tests/test_generators.py @@ -2,7 +2,6 @@ import locale import os -from codecs import open from shutil import copy, rmtree from tempfile import mkdtemp diff --git a/pelican/tests/test_importer.py b/pelican/tests/test_importer.py index 942b95fe..1a6ce404 100644 --- a/pelican/tests/test_importer.py +++ b/pelican/tests/test_importer.py @@ -3,7 +3,6 @@ import locale import os import re -from codecs import open from pelican.settings import DEFAULT_CONFIG from pelican.tests.support import (mute, skipIfNoExecutable, temporary_folder, diff --git a/pelican/tests/test_pelican.py b/pelican/tests/test_pelican.py index 0d495ac7..5625d617 100644 --- a/pelican/tests/test_pelican.py +++ b/pelican/tests/test_pelican.py @@ -1,15 +1,11 @@ # -*- coding: utf-8 -*- -try: - import collections.abc as collections -except ImportError: - import collections - import locale import logging import os import subprocess import sys +from collections.abc import Sequence from shutil import rmtree from tempfile import mkdtemp @@ -94,7 +90,7 @@ class TestPelican(LoggedTestCase): generator_classes[-1] is StaticGenerator, "StaticGenerator must be the last generator, but it isn't!") self.assertIsInstance( - generator_classes, collections.Sequence, + generator_classes, Sequence, "get_generator_classes() must return a Sequence to preserve order") def test_basic_generation_works(self): diff --git a/pelican/tools/pelican_import.py b/pelican/tools/pelican_import.py index 9b2edf4c..21122eb1 100755 --- a/pelican/tools/pelican_import.py +++ b/pelican/tools/pelican_import.py @@ -8,8 +8,8 @@ import re import subprocess import sys import time -from codecs import open from collections import defaultdict +from html import unescape from urllib.error import URLError from urllib.parse import quote, urlparse, urlsplit, urlunsplit from urllib.request import urlretrieve @@ -19,11 +19,6 @@ from pelican.log import init from pelican.settings import read_settings from pelican.utils import SafeDatetime, slugify -try: - from html import unescape # py3.5+ -except ImportError: - from html.parser import HTMLParser - unescape = HTMLParser().unescape logger = logging.getLogger(__name__) diff --git a/pelican/tools/pelican_quickstart.py b/pelican/tools/pelican_quickstart.py index 5ff3dc33..a7801866 100755 --- a/pelican/tools/pelican_quickstart.py +++ b/pelican/tools/pelican_quickstart.py @@ -2,7 +2,6 @@ # -*- coding: utf-8 -*- import argparse -import codecs import locale import os @@ -309,8 +308,8 @@ needed by Pelican. print('Error: {0}'.format(e)) try: - with codecs.open(os.path.join(CONF['basedir'], 'pelicanconf.py'), - 'w', 'utf-8') as fd: + with open(os.path.join(CONF['basedir'], 'pelicanconf.py'), + 'w', 'utf-8') as fd: conf_python = dict() for key, value in CONF.items(): conf_python[key] = repr(value) @@ -322,8 +321,8 @@ needed by Pelican. print('Error: {0}'.format(e)) try: - with codecs.open(os.path.join(CONF['basedir'], 'publishconf.py'), - 'w', 'utf-8') as fd: + with open(os.path.join(CONF['basedir'], 'publishconf.py'), + 'w', 'utf-8') as fd: _template = _jinja_env.get_template('publishconf.py.jinja2') fd.write(_template.render(**CONF)) fd.close() @@ -332,16 +331,16 @@ needed by Pelican. if automation: try: - with codecs.open(os.path.join(CONF['basedir'], 'tasks.py'), - 'w', 'utf-8') as fd: + with open(os.path.join(CONF['basedir'], 'tasks.py'), + 'w', 'utf-8') as fd: _template = _jinja_env.get_template('tasks.py.jinja2') fd.write(_template.render(**CONF)) fd.close() except OSError as e: print('Error: {0}'.format(e)) try: - with codecs.open(os.path.join(CONF['basedir'], 'Makefile'), - 'w', 'utf-8') as fd: + with open(os.path.join(CONF['basedir'], 'Makefile'), + 'w', 'utf-8') as fd: py_v = 'python3' _template = _jinja_env.get_template('Makefile.jinja2') fd.write(_template.render(py_v=py_v, **CONF)) diff --git a/pelican/urlwrappers.py b/pelican/urlwrappers.py index edfb11b4..6b512938 100644 --- a/pelican/urlwrappers.py +++ b/pelican/urlwrappers.py @@ -62,7 +62,7 @@ class URLWrapper(object): def _normalize_key(self, key): subs = self.settings.get('SLUG_REGEX_SUBSTITUTIONS', []) - return str(slugify(key, regex_subs=subs)) + return slugify(key, regex_subs=subs) def __eq__(self, other): if isinstance(other, self.__class__): diff --git a/pelican/utils.py b/pelican/utils.py index d031503d..6491f02e 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- -import codecs import datetime -import errno import fnmatch import locale import logging @@ -12,10 +10,7 @@ import shutil import sys import traceback import urllib -try: - from collections.abc import Hashable -except ImportError: - from collections import Hashable +from collections.abc import Hashable from contextlib import contextmanager from functools import partial from html import entities @@ -29,10 +24,6 @@ from jinja2 import Markup import pytz -try: - from html import escape -except ImportError: - from cgi import escape logger = logging.getLogger(__name__) @@ -51,17 +42,11 @@ def sanitised_join(base_directory, *parts): def strftime(date, date_format): ''' - Replacement for built-in strftime - - This is necessary because of the way Py2 handles date format strings. - Specifically, Py2 strftime takes a bytestring. In the case of text output - (e.g. %b, %a, etc), the output is encoded with an encoding defined by - locale.LC_TIME. Things get messy if the formatting string has chars that - are not valid in LC_TIME defined encoding. + Enhanced replacement for built-in strftime with zero stripping This works by 'grabbing' possible format strings (those starting with %), - formatting them with the date, (if necessary) decoding the output and - replacing formatted output back. + formatting them with the date, stripping any leading zeros if - prefix is + used and replacing formatted output back. ''' def strip_zeros(x): return x.lstrip('0') or '0' @@ -74,10 +59,6 @@ def strftime(date, date_format): # replace candidates with placeholders for later % formatting template = re.sub(format_options, '%s', date_format) - # we need to convert formatted dates back to unicode in Py2 - # LC_TIME determines the encoding for built-in strftime outputs - lang_code, enc = locale.getlocale(locale.LC_TIME) - formatted_candidates = [] for candidate in candidates: # test for valid C89 directives only @@ -232,15 +213,12 @@ def get_date(string): @contextmanager -def pelican_open(filename, mode='rb', strip_crs=(sys.platform == 'win32')): +def pelican_open(filename, mode='r', strip_crs=(sys.platform == 'win32')): """Open a file and return its content""" - with codecs.open(filename, mode, encoding='utf-8') as infile: + # utf-8-sig will clear any BOM if present + with open(filename, mode, encoding='utf-8-sig') as infile: content = infile.read() - if content[:1] == codecs.BOM_UTF8.decode('utf8'): - content = content[1:] - if strip_crs: - content = content.replace('\r\n', '\n') yield content @@ -610,14 +588,6 @@ def truncate_html_words(s, num, end_text='…'): return out -def escape_html(text, quote=True): - """Escape '&', '<' and '>' to HTML-safe sequences. - - In Python 2 this uses cgi.escape and in Python 3 this uses html.escape. We - wrap here to ensure the quote argument has an identical default.""" - return escape(text, quote=quote) - - def process_translations(content_list, translation_id=None): """ Finds translations and returns them. @@ -833,11 +803,7 @@ def set_date_tzinfo(d, tz_name=None): def mkdir_p(path): - try: - os.makedirs(path) - except OSError as e: - if e.errno != errno.EEXIST or not os.path.isdir(path): - raise + os.makedirs(path, exist_ok=True) def split_all(path): From 3cc430b41814b1d7b30a3266ba6a39a6bda3fa32 Mon Sep 17 00:00:00 2001 From: Deniz Turgut Date: Sun, 17 Nov 2019 20:30:07 +0300 Subject: [PATCH 4/5] Update docutils and remove docutils workaround --- pelican/readers.py | 13 ------------- poetry.lock | 8 ++++---- pyproject.toml | 2 +- setup.py | 5 +++-- 4 files changed, 8 insertions(+), 20 deletions(-) diff --git a/pelican/readers.py b/pelican/readers.py index c650913f..653464af 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -201,18 +201,6 @@ class RstReader(BaseReader): writer_class = PelicanHTMLWriter field_body_translator_class = _FieldBodyTranslator - class FileInput(docutils.io.FileInput): - """Patch docutils.io.FileInput to remove "U" mode in py3. - - Universal newlines is enabled by default and "U" mode is deprecated - in py3. - - """ - - def __init__(self, *args, **kwargs): - kwargs['mode'] = kwargs.get('mode', 'r').replace('U', '') - docutils.io.FileInput.__init__(self, *args, **kwargs) - def __init__(self, *args, **kwargs): super(RstReader, self).__init__(*args, **kwargs) @@ -273,7 +261,6 @@ class RstReader(BaseReader): pub = docutils.core.Publisher( writer=self.writer_class(), - source_class=self.FileInput, destination_class=docutils.io.StringOutput) pub.set_components('standalone', 'restructuredtext', 'html') pub.process_programmatic_settings(None, extra_params, None) diff --git a/poetry.lock b/poetry.lock index 50ba33bc..3b85d1d2 100644 --- a/poetry.lock +++ b/poetry.lock @@ -50,8 +50,8 @@ category = "main" description = "Docutils -- Python Documentation Utilities" name = "docutils" optional = false -python-versions = "*" -version = "0.14" +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +version = "0.15.2" [[package]] category = "dev" @@ -392,7 +392,7 @@ version = "0.5.1" markdown = ["markdown"] [metadata] -content-hash = "0163177d87dca0955d111319de9481cf2ed0ef014e63a3740ffaf32a1cf07212" +content-hash = "b8fa239ebaf9bf4bcd5c2e02cf94d065a1add4e19d8354cedf19db6378d6b848" python-versions = "^3.5" [metadata.hashes] @@ -401,7 +401,7 @@ babel = ["af92e6106cb7c55286b25b38ad7695f8b4efb36a90ba483d7f7a6628c46158ab", "e8 beautifulsoup4 = ["034740f6cb549b4e932ae1ab975581e6103ac8f942200a0e9759065984391858", "945065979fb8529dd2f37dbb58f00b661bdbcbebf954f93b32fdf5263ef35348", "ba6d5c59906a85ac23dadfe5c88deaf3e179ef565f4898671253e50a78680718"] blinker = ["471aee25f3992bd325afa3772f1063dbdbbca947a041b8b89466dc00d606f8b6"] colorama = ["05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d", "f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48"] -docutils = ["02aec4bd92ab067f6ff27a38a38a41173bf01bed8f89157768c1573f53e474a6", "51e64ef2ebfb29cae1faa133b3710143496eca21c530f3f71424d77687764274", "7a4bd47eaf6596e1295ecb11361139febe29b084a87bf005bf899f9a42edc3c6"] +docutils = ["6c4f696463b79f1fb8ba0c594b63840ebd41f059e92b31957c46b74a4599b6d0", "9e4d7ecfc600058e07ba661411a2b7de2fd0fafa17d1a7f7361cd47b1175c827", "a2aeea129088da402665e92e0b25b04b073c04b2dce4ab65caaa38b7ce2e1a99"] entrypoints = ["589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19", "c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"] feedgenerator = ["5ae05daa9cfa47fa406ee4744d0b7fa1c8a05a7a47ee0ad328ddf55327cfb106"] filelock = ["18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59", "929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"] diff --git a/pyproject.toml b/pyproject.toml index 50408d7f..41314c99 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,7 +35,7 @@ pytz = "^2019.1" blinker = "^1.4" unidecode = "^1.1" python-dateutil = "^2.8" -docutils = "^0.14" +docutils = "^0.15" markdown = {version = "~3.1.1", optional = true} [tool.poetry.dev-dependencies] diff --git a/setup.py b/setup.py index d79f055a..7c583da2 100755 --- a/setup.py +++ b/setup.py @@ -9,8 +9,9 @@ from setuptools import setup version = "4.2.0" -requires = ['feedgenerator >= 1.9', 'jinja2 >= 2.7', 'pygments', 'docutils', - 'pytz >= 0a', 'blinker', 'unidecode', 'python-dateutil'] +requires = ['feedgenerator >= 1.9', 'jinja2 >= 2.7', 'pygments', + 'docutils>=0.15', 'pytz >= 0a', 'blinker', 'unidecode', + 'python-dateutil'] entry_points = { 'console_scripts': [ From 16968834ce9d72e2e51b06ffe16a1c750c224370 Mon Sep 17 00:00:00 2001 From: Deniz Turgut Date: Mon, 18 Nov 2019 20:28:48 +0300 Subject: [PATCH 5/5] Convert super() calls to py3 style --- pelican/cache.py | 9 +++------ pelican/contents.py | 12 ++++++------ pelican/generators.py | 8 ++++---- pelican/log.py | 12 ++++++------ pelican/readers.py | 18 ++++++------------ pelican/tests/support.py | 6 +++--- pelican/tests/test_contents.py | 4 ++-- pelican/tests/test_paginator.py | 2 +- pelican/tests/test_pelican.py | 4 ++-- pelican/urlwrappers.py | 2 +- pelican/utils.py | 17 ++++------------- 11 files changed, 38 insertions(+), 56 deletions(-) diff --git a/pelican/cache.py b/pelican/cache.py index 0d36234a..f6adbc35 100644 --- a/pelican/cache.py +++ b/pelican/cache.py @@ -80,9 +80,7 @@ class FileStampDataCacher(FileDataCacher): and base path for filestamping operations """ - super(FileStampDataCacher, self).__init__(settings, cache_name, - caching_policy, - load_policy) + super().__init__(settings, cache_name, caching_policy, load_policy) method = self.settings['CHECK_MODIFIED_METHOD'] if method == 'mtime': @@ -104,7 +102,7 @@ class FileStampDataCacher(FileDataCacher): def cache_data(self, filename, data): """Cache stamp and data for the given file""" stamp = self._get_file_stamp(filename) - super(FileStampDataCacher, self).cache_data(filename, (stamp, data)) + super().cache_data(filename, (stamp, data)) def _get_file_stamp(self, filename): """Check if the given file has been modified @@ -132,8 +130,7 @@ class FileStampDataCacher(FileDataCacher): and current file stamp. """ - stamp, data = super(FileStampDataCacher, self).get_cached_data( - filename, (None, default)) + stamp, data = super().get_cached_data(filename, (None, default)) if stamp != self._get_file_stamp(filename): return default return data diff --git a/pelican/contents.py b/pelican/contents.py index 5193061f..6edf5152 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -488,7 +488,7 @@ class Page(Content): def _expand_settings(self, key): klass = 'draft_page' if self.status == 'draft' else None - return super(Page, self)._expand_settings(key, klass) + return super()._expand_settings(key, klass) class Article(Content): @@ -498,7 +498,7 @@ class Article(Content): default_template = 'article' def __init__(self, *args, **kwargs): - super(Article, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) # handle WITH_FUTURE_DATES (designate article to draft based on date) if not self.settings['WITH_FUTURE_DATES'] and hasattr(self, 'date'): @@ -515,7 +515,7 @@ class Article(Content): def _expand_settings(self, key): klass = 'draft' if self.status == 'draft' else 'article' - return super(Article, self)._expand_settings(key, klass) + return super()._expand_settings(key, klass) class Static(Content): @@ -524,7 +524,7 @@ class Static(Content): default_template = None def __init__(self, *args, **kwargs): - super(Static, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self._output_location_referenced = False @deprecated_attribute(old='filepath', new='source_path', since=(3, 2, 0)) @@ -543,13 +543,13 @@ class Static(Content): def url(self): # Note when url has been referenced, so we can avoid overriding it. self._output_location_referenced = True - return super(Static, self).url + return super().url @property def save_as(self): # Note when save_as has been referenced, so we can avoid overriding it. self._output_location_referenced = True - return super(Static, self).save_as + return super().save_as def attach_to(self, content): """Override our output directory with that of the given content object. diff --git a/pelican/generators.py b/pelican/generators.py index 8782238b..8bd2656f 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -241,7 +241,7 @@ class CachingGenerator(Generator, FileStampDataCacher): def _get_file_stamp(self, filename): '''Get filestamp for path relative to generator.path''' filename = os.path.join(self.path, filename) - return super(CachingGenerator, self)._get_file_stamp(filename) + return super()._get_file_stamp(filename) class _FileLoader(BaseLoader): @@ -288,7 +288,7 @@ class ArticlesGenerator(CachingGenerator): self.authors = defaultdict(list) self.drafts = [] # only drafts in default language self.drafts_translations = [] - super(ArticlesGenerator, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) signals.article_generator_init.send(self) def generate_feeds(self, writer): @@ -699,7 +699,7 @@ class PagesGenerator(CachingGenerator): self.hidden_translations = [] self.draft_pages = [] self.draft_translations = [] - super(PagesGenerator, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) signals.page_generator_init.send(self) def generate_context(self): @@ -785,7 +785,7 @@ class StaticGenerator(Generator): to output""" def __init__(self, *args, **kwargs): - super(StaticGenerator, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.fallback_to_symlinks = False signals.static_generator_init.send(self) diff --git a/pelican/log.py b/pelican/log.py index c971636e..bae15b4b 100644 --- a/pelican/log.py +++ b/pelican/log.py @@ -13,7 +13,7 @@ __all__ = [ class BaseFormatter(logging.Formatter): def __init__(self, fmt=None, datefmt=None): FORMAT = '%(customlevelname)s %(message)s' - super(BaseFormatter, self).__init__(fmt=FORMAT, datefmt=datefmt) + super().__init__(fmt=FORMAT, datefmt=datefmt) def format(self, record): customlevel = self._get_levelname(record.levelname) @@ -23,11 +23,11 @@ class BaseFormatter(logging.Formatter): record.args = tuple(arg.replace('\n', '\n | ') if isinstance(arg, str) else arg for arg in record.args) - return super(BaseFormatter, self).format(record) + return super().format(record) def formatException(self, ei): ''' prefix traceback info for better representation ''' - s = super(BaseFormatter, self).formatException(ei) + s = super().formatException(ei) # fancy format traceback s = '\n'.join(' | ' + line for line in s.splitlines()) # separate the traceback from the preceding lines @@ -137,7 +137,7 @@ class LimitLogger(logging.Logger): limit_filter = LimitFilter() def __init__(self, *args, **kwargs): - super(LimitLogger, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.enable_filter() def disable_filter(self): @@ -152,12 +152,12 @@ class FatalLogger(LimitLogger): errors_fatal = False def warning(self, *args, **kwargs): - super(FatalLogger, self).warning(*args, **kwargs) + super().warning(*args, **kwargs) if FatalLogger.warnings_fatal: raise RuntimeError('Warning encountered') def error(self, *args, **kwargs): - super(FatalLogger, self).error(*args, **kwargs) + super().error(*args, **kwargs) if FatalLogger.errors_fatal: raise RuntimeError('Error encountered') diff --git a/pelican/readers.py b/pelican/readers.py index 653464af..b26bd381 100644 --- a/pelican/readers.py +++ b/pelican/readers.py @@ -136,7 +136,7 @@ class BaseReader(object): class _FieldBodyTranslator(HTMLTranslator): def __init__(self, document): - HTMLTranslator.__init__(self, document) + super().__init__(document) self.compact_p = None def astext(self): @@ -158,7 +158,7 @@ def render_node_to_html(document, node, field_body_translator_class): class PelicanHTMLWriter(Writer): def __init__(self): - Writer.__init__(self) + super().__init__() self.translator_class = PelicanHTMLTranslator @@ -202,7 +202,7 @@ class RstReader(BaseReader): field_body_translator_class = _FieldBodyTranslator def __init__(self, *args, **kwargs): - super(RstReader, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) lang_code = self.settings.get('DEFAULT_LANG', 'en') if get_docutils_lang(lang_code): @@ -287,7 +287,7 @@ class MarkdownReader(BaseReader): file_extensions = ['md', 'markdown', 'mkd', 'mdown'] def __init__(self, *args, **kwargs): - super(MarkdownReader, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) settings = self.settings['MARKDOWN'] settings.setdefault('extension_configs', {}) settings.setdefault('extensions', []) @@ -350,11 +350,7 @@ class HTMLReader(BaseReader): class _HTMLParser(HTMLParser): def __init__(self, settings, filename): - try: - # Python 3.5+ - HTMLParser.__init__(self, convert_charrefs=False) - except TypeError: - HTMLParser.__init__(self) + super().__init__(convert_charrefs=False) self.body = '' self.metadata = {} self.settings = settings @@ -527,9 +523,7 @@ class Readers(FileStampDataCacher): self.settings['CONTENT_CACHING_LAYER'] == 'reader') caching_policy = cache_this_level and self.settings['CACHE_CONTENT'] load_policy = cache_this_level and self.settings['LOAD_CONTENT_CACHE'] - super(Readers, self).__init__(settings, cache_name, - caching_policy, load_policy, - ) + super().__init__(settings, cache_name, caching_policy, load_policy) @property def extensions(self): diff --git a/pelican/tests/support.py b/pelican/tests/support.py index 86bf984f..327e672d 100644 --- a/pelican/tests/support.py +++ b/pelican/tests/support.py @@ -186,7 +186,7 @@ class LogCountHandler(BufferingHandler): """Capturing and counting logged messages.""" def __init__(self, capacity=1000): - super(LogCountHandler, self).__init__(capacity) + super().__init__(capacity) def count_logs(self, msg=None, level=None): return len([ @@ -202,13 +202,13 @@ class LoggedTestCase(unittest.TestCase): """A test case that captures log messages.""" def setUp(self): - super(LoggedTestCase, self).setUp() + super().setUp() self._logcount_handler = LogCountHandler() logging.getLogger().addHandler(self._logcount_handler) def tearDown(self): logging.getLogger().removeHandler(self._logcount_handler) - super(LoggedTestCase, self).tearDown() + super().tearDown() def assertLogCountEqual(self, count=None, msg=None, **kwargs): actual = self._logcount_handler.count_logs(msg=msg, **kwargs) diff --git a/pelican/tests/test_contents.py b/pelican/tests/test_contents.py index e9592bd4..256f08bb 100644 --- a/pelican/tests/test_contents.py +++ b/pelican/tests/test_contents.py @@ -25,7 +25,7 @@ TEST_SUMMARY = generate_lorem_ipsum(n=1, html=False) class TestPage(LoggedTestCase): def setUp(self): - super(TestPage, self).setUp() + super().setUp() self.old_locale = locale.setlocale(locale.LC_ALL) locale.setlocale(locale.LC_ALL, str('C')) self.page_kwargs = { @@ -657,7 +657,7 @@ class TestArticle(TestPage): class TestStatic(LoggedTestCase): def setUp(self): - super(TestStatic, self).setUp() + super().setUp() self.settings = get_settings( STATIC_SAVE_AS='{path}', STATIC_URL='{path}', diff --git a/pelican/tests/test_paginator.py b/pelican/tests/test_paginator.py index 8080c146..9b600a9b 100644 --- a/pelican/tests/test_paginator.py +++ b/pelican/tests/test_paginator.py @@ -17,7 +17,7 @@ TEST_SUMMARY = generate_lorem_ipsum(n=1, html=False) class TestPage(unittest.TestCase): def setUp(self): - super(TestPage, self).setUp() + super().setUp() self.old_locale = locale.setlocale(locale.LC_ALL) locale.setlocale(locale.LC_ALL, str('C')) self.page_kwargs = { diff --git a/pelican/tests/test_pelican.py b/pelican/tests/test_pelican.py index 5625d617..2c7a77be 100644 --- a/pelican/tests/test_pelican.py +++ b/pelican/tests/test_pelican.py @@ -42,7 +42,7 @@ class TestPelican(LoggedTestCase): # to run pelican in different situations and see how it behaves def setUp(self): - super(TestPelican, self).setUp() + super().setUp() self.temp_path = mkdtemp(prefix='pelicantests.') self.temp_cache = mkdtemp(prefix='pelican_cache.') self.maxDiff = None @@ -53,7 +53,7 @@ class TestPelican(LoggedTestCase): rmtree(self.temp_path) rmtree(self.temp_cache) locale.setlocale(locale.LC_ALL, self.old_locale) - super(TestPelican, self).tearDown() + super().tearDown() def assertDirsEqual(self, left_path, right_path): out, err = subprocess.Popen( diff --git a/pelican/urlwrappers.py b/pelican/urlwrappers.py index 6b512938..cc276b3f 100644 --- a/pelican/urlwrappers.py +++ b/pelican/urlwrappers.py @@ -122,7 +122,7 @@ class Category(URLWrapper): class Tag(URLWrapper): def __init__(self, name, *args, **kwargs): - super(Tag, self).__init__(name.strip(), *args, **kwargs) + super().__init__(name.strip(), *args, **kwargs) class Author(URLWrapper): diff --git a/pelican/utils.py b/pelican/utils.py index 6491f02e..4b5b7134 100644 --- a/pelican/utils.py +++ b/pelican/utils.py @@ -96,7 +96,7 @@ class SafeDatetime(datetime.datetime): if safe: return strftime(self, fmt) else: - return super(SafeDatetime, self).strftime(fmt) + return super().strftime(fmt) class DateFormatter(object): @@ -407,18 +407,11 @@ class _HTMLWordTruncator(HTMLParser): class TruncationCompleted(Exception): def __init__(self, truncate_at): - super(_HTMLWordTruncator.TruncationCompleted, self).__init__( - truncate_at) + super().__init__(truncate_at) self.truncate_at = truncate_at def __init__(self, max_words): - # In Python 2, HTMLParser is not a new-style class, - # hence super() cannot be used. - try: - HTMLParser.__init__(self, convert_charrefs=False) - except TypeError: - # pre Python 3.3 - HTMLParser.__init__(self) + super().__init__(convert_charrefs=False) self.max_words = max_words self.words_found = 0 @@ -428,9 +421,7 @@ class _HTMLWordTruncator(HTMLParser): def feed(self, *args, **kwargs): try: - # With Python 2, super() cannot be used. - # See the comment for __init__(). - HTMLParser.feed(self, *args, **kwargs) + super().feed(*args, **kwargs) except self.TruncationCompleted as exc: self.truncate_at = exc.truncate_at else: