jenkins-bot has submitted this change and it was merged. ( 
https://gerrit.wikimedia.org/r/263372 )

Change subject: Abandon support for python 2.6
......................................................................

Abandon support for python 2.6

- remove 2.6 tests from appreyor and travis
- update HISTORY.rst and docs
- update requirements
- update docs
- remove backports from doc
- cleanup backports.py (to be deleted later)
- cleanup tools
- update library parts and tests

Bug: T154771
Change-Id: Ied97711c81adebe9c260c8e7c2647b6cc71846fa
---
M .appveyor.yml
M .travis.yml
M HISTORY.rst
M dev-requirements.txt
M docs/api_ref/pywikibot.rst
M docs/index.rst
M pwb.py
M pywikibot/CONTENT.rst
M pywikibot/README.rst
M pywikibot/__init__.py
M pywikibot/backports.py
M pywikibot/bot.py
M pywikibot/diff.py
M pywikibot/family.py
M pywikibot/interwiki_graph.py
M pywikibot/page.py
M pywikibot/site_detect.py
M pywikibot/textlib.py
M pywikibot/tools/__init__.py
M pywikibot/tools/djvu.py
M requests-requirements.txt
M requirements.txt
M setup.py
M tests/__init__.py
M tests/link_tests.py
M tests/python_tests.py
M tests/script_tests.py
M tests/textlib_tests.py
M tests/tools_tests.py
M tests/utils.py
M tox.ini
31 files changed, 89 insertions(+), 413 deletions(-)

Approvals:
  Dalba: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/.appveyor.yml b/.appveyor.yml
index 87a3ef6..85ccee8 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -17,15 +17,6 @@

     # Test the lowest supported release of each major Python version.

-    # Pywikibot support matrix suggests 'should run' on Python 2.6.5+
-    # Only Python 2.6.6 is able to be set up on Appveyor.
-    # https://github.com/ogrisel/python-appveyor-demo/issues/10
-    # fwiw, Redhat Enterprise Linux ships with 2.6.6.
-
-    - PYTHON: "C:\\Python266-x64"
-      PYTHON_VERSION: "2.6.6"
-      PYTHON_ARCH: "64"
-
     - PYTHON: "C:\\Python272"
       PYTHON_VERSION: "2.7.2"
       PYTHON_ARCH: "32"
@@ -92,10 +83,6 @@
   - pip install virtualenv
   - virtualenv env
   - env\Scripts\activate.bat
-  # wheel version 0.29.0 is enforced because version 0.30.0 doesn't support
-  # Python 2.6 and 3.3 anymore. Once we drop support for those versions we
-  # can use the latest version of wheel.
-  - pip install wheel==0.29.0
   - pip install -r dev-requirements.txt
   - pip install -r requests-requirements.txt

diff --git a/.travis.yml b/.travis.yml
index 044670e..cdd20c0 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,7 +5,6 @@
 sudo: false

 python:
-  - '2.6'
   - '2.7'
   - '3.4'
   - '3.5'
@@ -160,7 +159,7 @@
       env: LANGUAGE=test FAMILY=wikidata SITE_ONLY=1
     - python: '3.4'
       env: LANGUAGE=ar FAMILY=wiktionary PYWIKIBOT2_TEST_NO_RC=1
-    - python: '2.6'
+    - python: '3.6'
       env: LANGUAGE=wikidata FAMILY=wikidata SITE_ONLY=1

 notifications:
diff --git a/HISTORY.rst b/HISTORY.rst
index d989c25..3c7d1e2 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -4,6 +4,7 @@
 Current release
 ---------------

+* Dropped support for Python 2.6 (T154771)
 * Dropped support for Python 3.3 (T184508)
 * Bugfixes and improvements
 * Localisation updates
diff --git a/dev-requirements.txt b/dev-requirements.txt
index 5084cdf..d3eb990 100644
--- a/dev-requirements.txt
+++ b/dev-requirements.txt
@@ -1,6 +1,6 @@
 # This is a PIP 6+ requirements file for development dependencies
 #
-unittest2==0.8.0 ; python_full_version < '2.7.3'
+unittest2==0.8.0 ; python_full_version == '2.7.2'

 pytest>=2.8.0
 pytest-timeout
@@ -8,7 +8,7 @@
 pytest-cov
 pytest-attrib
 pytest-httpbin
-httpbin<0.6.0 ; os_name != 'posix' or python_version < '2.7'
+httpbin<0.6.0 ; os_name != 'posix'

 six

@@ -35,5 +35,3 @@
 # are not useful on the Appveyor Win32 builds since the relevant UI tests
 # also require accessing the menu of the console window, which doesnt exist
 # in the Appveyor environment.
-
-setuptools_scm ; python_version == '2.6'
diff --git a/docs/api_ref/pywikibot.rst b/docs/api_ref/pywikibot.rst
index 21c21b3..d683df6 100644
--- a/docs/api_ref/pywikibot.rst
+++ b/docs/api_ref/pywikibot.rst
@@ -21,14 +21,6 @@
 Submodules
 ----------

-pywikibot.backports module
---------------------------
-
-.. automodule:: pywikibot.backports
-    :members:
-    :undoc-members:
-    :show-inheritance:
-
 pywikibot.bot module
 --------------------

diff --git a/docs/index.rst b/docs/index.rst
index 91c773c..8a6cea7 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -13,7 +13,7 @@

 Pywikibot is a Python library and collection of scripts that automate work on 
`MediaWiki <https://mediawiki.org>`_ sites.

-Pywikibot supports Python 2.6.5+, 2.7.2+ and 3.4+.
+Pywikibot supports Python 2.7.2+ and 3.4+.

 Pywikibot and this documentation are licensed under the :ref:`MIT license 
<licenses-MIT>`;
 manual pages on mediawiki.org are licensed under the `CC-BY-SA 3.0`_ license.
diff --git a/pwb.py b/pwb.py
index aed8514..afd2607 100755
--- a/pwb.py
+++ b/pwb.py
@@ -30,22 +30,19 @@

 PYTHON_VERSION = sys.version_info[:3]
 PY2 = (PYTHON_VERSION[0] == 2)
-PY26 = (PYTHON_VERSION < (2, 7))

 versions_required_message = """
 Pywikibot is not available on:
 {version}

-This version of Pywikibot only supports Python 2.6.5+, 2.7.2+ or 3.4+.
+This version of Pywikibot only supports Python 2.7.2+ or 3.4+.
 """


 def python_is_supported():
     """Check that Python is supported."""
     # Any change to this must be copied to setup.py
-    return (PYTHON_VERSION >= (3, 4, 0) or
-            (PY2 and PYTHON_VERSION >= (2, 7, 2)) or
-            (PY26 and PYTHON_VERSION >= (2, 6, 5)))
+    return PYTHON_VERSION >= (3, 4, 0) or PY2 and PYTHON_VERSION >= (2, 7, 2)


 if not python_is_supported():
diff --git a/pywikibot/CONTENT.rst b/pywikibot/CONTENT.rst
index 180c4e4..cd486e8 100644
--- a/pywikibot/CONTENT.rst
+++ b/pywikibot/CONTENT.rst
@@ -9,8 +9,8 @@
     
+---------------------------+-------------------------------------------------------+
     | _wbtypes.py               | Wikibase data type classes                   
         |
     
+---------------------------+-------------------------------------------------------+
-    | backports.py              | Module contains backports to support older 
Python     |
-    |                           | versions                                     
         |
+    | backports.py              | Deprecated module that contained backports 
to support |
+    |                           | older Python versions (could be dropped 
soon)         |
     
+---------------------------+-------------------------------------------------------+
     | bot.py                    | User-interface related functions for 
building bots    |
     
+---------------------------+-------------------------------------------------------+
diff --git a/pywikibot/README.rst b/pywikibot/README.rst
index a9dd281..a817648 100644
--- a/pywikibot/README.rst
+++ b/pywikibot/README.rst
@@ -30,7 +30,7 @@
   * python-tkinter (optional, used by some experimental GUI stuff)


-You need to have at least python version `2.6.5 
<http://www.python.org/download/>`_
+You need to have at least python version `2.7.2 
<http://www.python.org/download/>`_
 or newer installed on your computer to be able to run any of the code in this
 package, but not 3.0-3.3. It works fine with 3.4+ versions of python installed.
 Support for older versions of python is not planned. Some scripts could run 
with
diff --git a/pywikibot/__init__.py b/pywikibot/__init__.py
index 1264b64..cd3e18c 100644
--- a/pywikibot/__init__.py
+++ b/pywikibot/__init__.py
@@ -135,13 +135,6 @@
 deprecate_arg = redirect_func(_deprecate_arg)


-if sys.version_info[:2] == (2, 6):
-    warn(
-        'Pywikibot will soon drop support for Python 2.6',
-        DeprecationWarning,
-    )
-
-
 class Timestamp(datetime.datetime):

     """Class for handling MediaWiki timestamps.
diff --git a/pywikibot/backports.py b/pywikibot/backports.py
index 86c8de7..52a711e 100644
--- a/pywikibot/backports.py
+++ b/pywikibot/backports.py
@@ -1,169 +1,48 @@
 # -*- coding: utf-8 -*-
 """
-This module contains backports to support older Python versions.
+This module contained backports to support older Python versions.

-They contain the backported code originally developed for Python. It is
-therefore distributed under the PSF license.
+Their usage is deprecated and this module could be dropped soon.
 """
 #
-# (C) Python Software Foundation, 2001-2014
-# (C) with modifications from Pywikibot team, 2015
+# (C) Pywikibot team, 2015-2018
 #
-# Distributed under the terms of the PSF license.
+# Distributed under the terms of the MIT license.
 #

 from __future__ import absolute_import, unicode_literals
 
-__license__ = """
-PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
---------------------------------------------
-1. This LICENSE AGREEMENT is between the Python Software Foundation
-("PSF"), and the Individual or Organization ("Licensee") accessing and
-otherwise using this software ("Python") in source or binary form and
-its associated documentation.
-
-2. Subject to the terms and conditions of this License Agreement, PSF hereby
-grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
-analyze, test, perform and/or display publicly, prepare derivative works,
-distribute, and otherwise use Python alone or in any derivative version,
-provided, however, that PSF's License Agreement and PSF's notice of copyright,
-i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 
2010,
-2011, 2012, 2013, 2014 Python Software Foundation; All Rights Reserved" are
-retained in Python alone or in any derivative version prepared by Licensee.
-
-3. In the event Licensee prepares a derivative work that is based on
-or incorporates Python or any part thereof, and wants to make
-the derivative work available to others as provided herein, then
-Licensee hereby agrees to include in any such work a brief summary of
-the changes made to Python.
-
-4. PSF is making Python available to Licensee on an "AS IS"
-basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
-IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
-DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
-FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
-INFRINGE ANY THIRD PARTY RIGHTS.
-
-5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
-FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
-A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
-OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
-
-6. This License Agreement will automatically terminate upon a material
-breach of its terms and conditions.
-
-7. Nothing in this License Agreement shall be deemed to create any
-relationship of agency, partnership, or joint venture between PSF and
-Licensee. This License Agreement does not grant permission to use PSF
-trademarks or trade name in a trademark sense to endorse or promote
-products or services of Licensee, or any third party.
-
-8. By copying, installing or otherwise using Python, Licensee
-agrees to be bound by the terms and conditions of this License
-Agreement.
-"""
-
+from difflib import _format_range_unified
 import logging
-import warnings
+
+from pywikibot.tools import deprecated


+@deprecated('difflib._format_range_unified')
 def format_range_unified(start, stop):
     """
     Convert range to the "ed" format.

-    Copied from C{difflib._format_range_unified()} which was introduced in
-    Python 2.7.2.
-
-    @see: https://hg.python.org/cpython/file/8527427914a2/Lib/difflib.py#l1147
+    DEPRECATED (Python 2.6 backport).
+    Use difflib._format_range_unified instead.
     """
-    # Per the diff spec at http://www.unix.org/single_unix_specification/
-    beginning = start + 1  # lines start numbering with one
-    length = stop - start
-    if length == 1:
-        return '{0}'.format(beginning)
-    if not length:
-        beginning -= 1  # empty ranges begin at line just before the range
-    return '{0},{1}'.format(beginning, length)
+    return _format_range_unified(start, stop)


-# Logging/Warnings integration
+@deprecated('logging.NullHandler')
+class NullHandler(logging.NullHandler):

-_warnings_showwarning = None
+    """This handler does nothing."""
+
+    pass


-class NullHandler(logging.Handler):
-
-    """
-    This handler does nothing.
-
-    It's intended to be used to avoid the "No handlers could be found for
-    logger XXX" one-off warning. This is important for library code, which
-    may contain code to log events. If a user of the library does not configure
-    logging, the one-off warning might be produced; to avoid this, the library
-    developer simply needs to instantiate a NullHandler and add it to the
-    top-level logger of the library module or package.
-
-    Copied from C{logging.NullHandler} which was introduced in Python 2.7.
-
-    @see: http://bugs.python.org/issue4384
-    """
-
-    def handle(self, record):
-        """Dummy handling."""
-        pass
-
-    def emit(self, record):
-        """Dummy handling."""
-        pass
-
-    def createLock(self):
-        """Dummy handling."""
-        self.lock = None
-
-
-def _showwarning(message, category, filename, lineno, file=None, line=None):
-    """
-    Implementation of showwarnings which redirects to logging.
-
-    It will first check to see if the file parameter is None. If a file is
-    specified, it will delegate to the original warnings implementation of
-    showwarning. Otherwise, it will call warnings.formatwarning and will log
-    the resulting string to a warnings logger named "py.warnings" with level
-    logging.WARNING.
-
-    Copied from C{logging._showwarning} which was introduced in Python 2.7.
-
-    @see: http://bugs.python.org/issue4384
-    """
-    if file is not None:
-        if _warnings_showwarning is not None:
-            _warnings_showwarning(message, category, filename, lineno, file, 
line)
-    else:
-        s = warnings.formatwarning(message, category, filename, lineno, line)
-        logger = logging.getLogger("py.warnings")
-        if not logger.handlers:
-            logger.addHandler(NullHandler())
-        logger.warning("%s", s)
-
-
+@deprecated('logging.captureWarnings')
 def captureWarnings(capture):
     """
     Capture warnings into logging.

-    If capture is true, redirect all warnings to the logging package.
-    If capture is False, ensure that warnings are not redirected to logging
-    but to their original destinations.
-
-    Copied from C{logging.captureWarnings} which was introduced in Python 2.7.
-
-    @see: http://bugs.python.org/issue4384
+    DEPRECATED (Python 2.6 backport).
+    Use logging.captureWarnings instead.
     """
-    global _warnings_showwarning
-    if capture:
-        if _warnings_showwarning is None:
-            _warnings_showwarning = warnings.showwarning
-            warnings.showwarning = _showwarning
-    else:
-        if _warnings_showwarning is not None:
-            warnings.showwarning = _warnings_showwarning
-            _warnings_showwarning = None
+    logging.captureWarnings(capture)
diff --git a/pywikibot/bot.py b/pywikibot/bot.py
index 2c8f041..ceb9459 100644
--- a/pywikibot/bot.py
+++ b/pywikibot/bot.py
@@ -77,7 +77,6 @@

 import pywikibot

-from pywikibot import backports
 from pywikibot import config
 from pywikibot import daemonize
 from pywikibot import i18n
@@ -250,10 +249,7 @@

     # If there are command line warnings options, do not override them
     if not sys.warnoptions:
-        if hasattr(logging, 'captureWarnings'):
-            logging.captureWarnings(True)  # introduced in Python >= 2.7
-        else:
-            backports.captureWarnings(True)
+        logging.captureWarnings(True)

         if config.debug_log or 'deprecation' in config.log:
             warnings.filterwarnings("always")
diff --git a/pywikibot/diff.py b/pywikibot/diff.py
index 636be64..95669e9 100644
--- a/pywikibot/diff.py
+++ b/pywikibot/diff.py
@@ -12,6 +12,7 @@
 import sys

 from collections import Sequence
+from difflib import _format_range_unified as format_range_unified
 if sys.version_info[0] > 2:
     from itertools import zip_longest
 else:
@@ -20,7 +21,6 @@
 import pywikibot
 from pywikibot.tools import chars

-from pywikibot.backports import format_range_unified  # introduced in 2.7.2
 from pywikibot.tools import deprecated_args
 from pywikibot.tools.formatter import color_format

diff --git a/pywikibot/family.py b/pywikibot/family.py
index d6524c7..632aff5 100644
--- a/pywikibot/family.py
+++ b/pywikibot/family.py
@@ -16,13 +16,12 @@

 PY3 = sys.version_info[0] > 2
 if PY3:
-    from os.path import basename, dirname, splitext
-    from importlib import import_module
     import urllib.parse as urlparse
 else:
-    import imp
     import urlparse

+from os.path import basename, dirname, splitext
+from importlib import import_module
 from warnings import warn

 import pywikibot
@@ -959,12 +958,8 @@
             #     RuntimeWarning's while loading.
             with warnings.catch_warnings():
                 warnings.simplefilter("ignore", RuntimeWarning)
-                if PY3:
-                    sys.path.append(dirname(family_file))
-                    mod = import_module(splitext(basename(family_file))[0])
-                else:
-                    # Python 2.6 has no importlib.import_module
-                    mod = imp.load_source(fam, family_file)
+                sys.path.append(dirname(family_file))
+                mod = import_module(splitext(basename(family_file))[0])
         except ImportError:
             raise UnknownFamily(u'Family %s does not exist' % fam)
         cls = mod.Family()
diff --git a/pywikibot/interwiki_graph.py b/pywikibot/interwiki_graph.py
index bdad631..e14797b 100644
--- a/pywikibot/interwiki_graph.py
+++ b/pywikibot/interwiki_graph.py
@@ -1,12 +1,13 @@
 # -*- coding: utf-8 -*-
 """Module with the Graphviz drawing calls."""
 #
-# (C) Pywikibot team, 2006-2016
+# (C) Pywikibot team, 2006-2018
 #
 # Distributed under the terms of the MIT license.
 #
 from __future__ import absolute_import, unicode_literals

+from collections import Counter
 import itertools
 import threading

@@ -18,7 +19,6 @@
 import pywikibot

 from pywikibot import config2 as config
-from pywikibot.tools import Counter

 # deprecated value
 pydotfound = not isinstance(pydot, ImportError)
diff --git a/pywikibot/page.py b/pywikibot/page.py
index bf836c2..fa29cf4 100644
--- a/pywikibot/page.py
+++ b/pywikibot/page.py
@@ -29,7 +29,7 @@
 except ImportError:
     import unicodedata

-from collections import defaultdict, namedtuple
+from collections import Counter, defaultdict, namedtuple, OrderedDict
 from warnings import warn

 from pywikibot.tools import PY2
@@ -65,13 +65,11 @@
 from pywikibot.site import DataSite, Namespace, need_version
 from pywikibot.tools import (
     compute_file_hash,
-    PYTHON_VERSION,
     MediaWikiVersion, UnicodeMixin, ComparableMixin, DotReadableDict,
     deprecated, deprecate_arg, deprecated_args, issue_deprecation_warning,
     add_full_name, manage_wrapping,
     ModuleDeprecationWrapper as _ModuleDeprecationWrapper,
     first_upper, redirect_func, remove_last_args, _NotImplementedWarning,
-    OrderedDict, Counter,
 )
 from pywikibot.tools.ip import ip_regexp
 from pywikibot.tools.ip import is_IP
@@ -5421,9 +5419,6 @@
         @type defaultNamespace: int

         @raises UnicodeError: text could not be converted to unicode.
-            On Python 2.6.6 without unicodedata2, this could also be raised
-            if the text contains combining characters.
-            See https://phabricator.wikimedia.org/T102461
         """
         source_is_page = isinstance(source, BasePage)

@@ -5460,16 +5455,6 @@

         # Normalize unicode string to a NFC (composed) format to allow
         # proper string comparisons to strings output from MediaWiki API.
-        # Due to Python issue 10254, this is not possible on Python 2.6.6
-        # if the string contains combining characters. See T102461.
-        if (PYTHON_VERSION == (2, 6, 6) and
-                unicodedata.__name__ != 'unicodedata2' and
-                any(unicodedata.combining(c) for c in t)):
-            raise UnicodeError(
-                'Link(%r, %s): combining characters detected, which are '
-                'not supported by Pywikibot on Python 2.6.6. See '
-                'https://phabricator.wikimedia.org/T102461'
-                % (t, self._source))
         t = unicodedata.normalize('NFC', t)

         # This code was adapted from Title.php : secureAndSplit()
diff --git a/pywikibot/site_detect.py b/pywikibot/site_detect.py
index 56c9b62..534c813 100644
--- a/pywikibot/site_detect.py
+++ b/pywikibot/site_detect.py
@@ -23,9 +23,9 @@
     from html.parser import HTMLParser
     from urllib.parse import urljoin, urlparse
 else:
-    try:
-        from future.backports.html.parser import HTMLParser
-    except ImportError:
+    if PYTHON_VERSION == (2, 7, 2):
+        from future.backports.html.parser import HTMLParser  # T175873
+    else:
         from HTMLParser import HTMLParser
     from urlparse import urljoin, urlparse

diff --git a/pywikibot/textlib.py b/pywikibot/textlib.py
index 2c2b26c..f959673 100644
--- a/pywikibot/textlib.py
+++ b/pywikibot/textlib.py
@@ -13,7 +13,7 @@
 #
 from __future__ import absolute_import, unicode_literals

-import collections
+from collections import OrderedDict, Sequence
 import datetime
 import re
 import sys
@@ -39,7 +39,6 @@
     deprecate_arg,
     deprecated,
     DeprecatedRegex,
-    OrderedDict,
     StringTypes,
     UnicodeType,
     issue_deprecation_warning
@@ -645,7 +644,7 @@
             title += '#' + l.section
         return title

-    if isinstance(replace, collections.Sequence):
+    if isinstance(replace, Sequence):
         if len(replace) != 2:
             raise ValueError('When used as a sequence, the "replace" '
                              'argument must contain exactly 2 items.')
diff --git a/pywikibot/tools/__init__.py b/pywikibot/tools/__init__.py
index 611a957..7438303 100644
--- a/pywikibot/tools/__init__.py
+++ b/pywikibot/tools/__init__.py
@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 """Miscellaneous helper functions (not wiki-dependent)."""
 #
-# (C) Pywikibot team, 2008-2017
+# (C) Pywikibot team, 2008-2018
 #
 # Distributed under the terms of the MIT license.
 #
@@ -100,59 +100,6 @@
         """Constructor."""
         raise NotImplementedError(
             '%s: %s' % (self.__class__.__name__, self.__doc__))
-
-
-if PYTHON_VERSION < (2, 7):
-    try:
-        import future.backports.misc
-    except ImportError:
-        warn("""
-pywikibot support of Python 2.6 relies on package future for many features.
-Please upgrade to Python 2.7+ or Python 3.4+, or run:
-    "pip install future>=0.15.0"
-""", RuntimeWarning)
-        try:
-            from ordereddict import OrderedDict
-        except ImportError:
-            class OrderedDict(NotImplementedClass):
-
-                """OrderedDict not found."""
-
-                pass
-
-        try:
-            from counter import Counter
-        except ImportError:
-            class Counter(NotImplementedClass):
-
-                """Counter not found."""
-
-                pass
-        count = None
-    else:
-        Counter = future.backports.misc.Counter
-        OrderedDict = future.backports.misc.OrderedDict
-
-        try:
-            count = future.backports.misc.count
-        except AttributeError:
-            warn('Please update the "future" package to at least version '
-                 '0.15.0 to use its count.', RuntimeWarning, 2)
-            count = None
-        del future
-
-    if count is None:
-        def count(start=0, step=1):
-            """Backported C{count} to support keyword arguments and step."""
-            while True:
-                yield start
-                start += step
-
-
-else:
-    Counter = collections.Counter
-    OrderedDict = collections.OrderedDict
-    count = itertools.count


 def has_module(module):
@@ -1536,7 +1483,7 @@
         if wrapper.__signature__:
             # Build a new signature with deprecated args added.
             # __signature__ is only available in Python 3 which has OrderedDict
-            params = OrderedDict()
+            params = collections.OrderedDict()
             for param in wrapper.__signature__.parameters.values():
                 params[param.name] = param.replace()
             for old_arg, new_arg in arg_pairs.items():
@@ -1837,3 +1784,9 @@
             bytes_to_read -= len(read_bytes)
             sha.update(read_bytes)
     return sha.hexdigest()
+
+
+wrapper = ModuleDeprecationWrapper(__name__)
+wrapper._add_deprecated_attr('Counter', collections.Counter)
+wrapper._add_deprecated_attr('OrderedDict', collections.OrderedDict)
+wrapper._add_deprecated_attr('count', itertools.count)
diff --git a/pywikibot/tools/djvu.py b/pywikibot/tools/djvu.py
index 4e919fe..c386410 100644
--- a/pywikibot/tools/djvu.py
+++ b/pywikibot/tools/djvu.py
@@ -2,12 +2,13 @@
 # -*- coding: utf-8 -*-
 """Wrapper around djvulibre to access djvu files properties and content."""
 #
-# (C) Pywikibot team, 2015-2017
+# (C) Pywikibot team, 2015-2018
 #
 # Distributed under the terms of the MIT license.
 #
 from __future__ import absolute_import, unicode_literals

+from collections import Counter
 import os
 import re
 import subprocess
@@ -15,7 +16,6 @@
 import pywikibot

 from pywikibot.tools import (
-    Counter,
     deprecated, deprecated_args,
     StringTypes,
     UnicodeType,
diff --git a/requests-requirements.txt b/requests-requirements.txt
index 651eabf..cf986cd 100644
--- a/requests-requirements.txt
+++ b/requests-requirements.txt
@@ -2,11 +2,8 @@

 # requests security extra
 # Bug T105767 on Python 2.7 release 9+
-cryptography>=1.3.4 ; python_version != '2.6' and (python_full_version < 
'2.7.9' or python_version > '3.3')
-cryptography>=1.3.4,<=2.0.3 ; python_version == '2.6' and platform_system == 
'Windows'
-cryptography>=1.3.4,<2.2 ; python_version == '2.6' and platform_system != 
'Windows'
-pyOpenSSL>=0.14,!=17.2.0 ; python_full_version < '2.7.9' and python_version != 
'2.6'
-PyOpenSSL<17.5.0 ; python_version == '2.6'
+cryptography>=1.3.4 ; python_full_version < '2.7.9' or python_version > '3.3'
+pyOpenSSL>=0.14,!=17.2.0 ; python_full_version < '2.7.9'
 idna>=2.0.0 ; python_full_version < '2.7.9' or python_version >= '3'
 # https://github.com/eliben/pycparser/issues/147
 pycparser != 2.14
diff --git a/requirements.txt b/requirements.txt
index f3c051f..ffd1dba 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -22,11 +22,9 @@
 requests>=2.9,!=2.18.2

 # requests security extra
-cryptography>=1.3.4 ; python_version != '2.6' and python_full_version < '2.7.9'
-cryptography>=1.3.4,<=2.0.3 ; python_version == '2.6' and platform_system == 
'Windows'
-cryptography>=1.3.4,<2.2 ; python_version == '2.6' and platform_system != 
'Windows'
-pyOpenSSL>=0.14,!=17.2.0 ; python_full_version < '2.7.9' and python_version != 
'2.6'
-PyOpenSSL<17.5.0 ; python_version == '2.6'
+cryptography>=1.3.4 ; python_full_version < '2.7.9'
+pyOpenSSL>=0.14,!=17.2.0 ; python_full_version < '2.7.9'
+
 idna>=2.0.0 ; python_full_version < '2.7.9'
 # https://github.com/eliben/pycparser/issues/147
 pycparser != 2.14
@@ -46,15 +44,13 @@
 git+https://github.com/nlhepler/pydot#egg=pydot-1.0.29

 # wikistats.py and scripts
-unicodecsv!=0.14.0 ; python_version < '2.7'
-unicodecsv ; python_version < '3' and python_version >= '2.7'
+unicodecsv ; python_version < '3'

 # cosmetic_changes and scripts/isbn
 python-stdnum

 # GUI
-Pillow<4.0.0 ; python_version == '2.6'
-Pillow ; python_version == '2.7' or python_version >= '3.4'
+Pillow

 # core pagegenerators
 google >= 1.7
@@ -63,21 +59,11 @@
 # scripts/script_wui.py:
 crontab

-# scipts/replicate_wiki.py and scripts/editarticle.py
-argparse ; python_version < '2.7'
-
 # scripts/flickrripper.py
-# On Python 2, flickrapi 1.4.x or 2.x may be used.  Only 2.x works on Python 3.
-# The following automatically selects 2.x on all Python versions, which depends
-# on requests 2.x, which may cause pip to report an error like the following:
-#   pkg_resources.VersionConflict: (requests 1.2.3 
(/usr/lib/python2.7/site-packages), Requirement.parse('requests>=2.2.1'))
-# If you see that on Python 2, change this to flickrapi==1.4.5
-# On Python 3, force pip to install requests 2.2.1, or remove flickrapi below.
-flickrapi>=1.4.5,<2 ; python_version < '2.7'
-flickrapi ; python_version >= '2.7'
+flickrapi

 # incomplete core component botirc
-irc ; python_version > '2.6'
+irc

 # textlib.py and patrol.py
 mwparserfromhell>=0.3.3
diff --git a/setup.py b/setup.py
index 1b2ccce..90d1649 100644
--- a/setup.py
+++ b/setup.py
@@ -23,22 +23,19 @@

 PYTHON_VERSION = sys.version_info[:3]
 PY2 = (PYTHON_VERSION[0] == 2)
-PY26 = (PYTHON_VERSION < (2, 7))

 versions_required_message = """
 Pywikibot is not available on:
 {version}

-This version of Pywikibot only supports Python 2.6.5+, 2.7.2+ or 3.4+.
+This version of Pywikibot only supports Python 2.7.2+ or 3.4+.
 """


 def python_is_supported():
     """Check that Python is supported."""
     # Any change to this must be copied to pwb.py
-    return (PYTHON_VERSION >= (3, 4, 0) or
-            (PY2 and PYTHON_VERSION >= (2, 7, 2)) or
-            (PY26 and PYTHON_VERSION >= (2, 6, 5)))
+    return PYTHON_VERSION >= (3, 4, 0) or PY2 and PYTHON_VERSION >= (2, 7, 2)


 if not python_is_supported():
@@ -48,25 +45,15 @@

 dependencies = ['requests>=2.9,!=2.18.2']

-# the irc module has no Python 2.6 support since 10.0
-irc_dep = 'irc==8.9' if sys.version_info < (2, 7) else 'irc'
-csv_dep = 'unicodecsv!=0.14.0' if PYTHON_VERSION < (2, 7) else 'unicodecsv'
-
-# According to https://pillow.readthedocs.io/en/latest/installation.html#notes
-if PY26:
-    pillow = 'Pillow<4.0.0'
-else:
-    pillow = 'Pillow'
-
 extra_deps = {
     # Core library dependencies
     'eventstreams': ['sseclient>=0.0.18'],
     'isbn': ['python-stdnum'],
     'Graphviz': ['pydot>=1.0.28'],
     'Google': ['google>=1.7'],
-    'IRC': [irc_dep],
+    'IRC': ['irc'],
     'mwparserfromhell': ['mwparserfromhell>=0.3.3'],
-    'Tkinter': [pillow],
+    'Tkinter': ['Pillow'],
     'security': ['requests[security]', 'pycparser!=2.14'],
     'mwoauth': ['mwoauth>=0.2.4,!=0.3.1'],
     'html': ['BeautifulSoup4'],
@@ -75,31 +62,21 @@
 if PY2:
     # Additional core library dependencies which are only available on Python 2
     extra_deps.update({
-        'csv': [csv_dep],
+        'csv': ['unicodecsv'],
         'MySQL': ['oursql'],
         'unicode7': ['unicodedata2>=7.0.0-2'],
     })

 script_deps = {
-    'flickrripper.py': [pillow],
+    'flickrripper.py': ['flickrapi', 'Pillow'],
     'states_redirect.py': ['pycountry'],
     'weblinkchecker.py': ['memento_client>=0.5.1,!=0.6.0'],
     'patrol.py': ['mwparserfromhell>=0.3.3'],
 }
-# flickrapi 1.4.4 installs a root logger in verbose mode; 1.4.5 fixes this.
-# The problem doesnt exist in flickrapi 2.x.
-# pywikibot accepts flickrapi 1.4.5+ on Python 2, as it has been stable for a
-# long time, and only depends on python-requests 1.x, whereas flickrapi 2.x
-# depends on python-requests 2.x, which is first packaged in Ubuntu 14.04
-# and will be first packaged for Fedora Core 21.
-# flickrapi 1.4.x does not run on Python 3, and setuptools can only
-# select flickrapi 2.x for Python 3 installs.
-script_deps['flickrripper.py'].append(
-    'flickrapi>=1.4.5,<2' if PY26 else 'flickrapi')

 # lunatic-python is only available for Linux
 if sys.platform.startswith('linux'):
-    script_deps['script_wui.py'] = [irc_dep, 'lunatic-python', 'crontab']
+    script_deps['script_wui.py'] = ['irc', 'lunatic-python', 'crontab']

 # The main pywin32 repository contains a Python 2 only setup.py with a small
 # wrapper setup3.py for Python 3.
@@ -113,7 +90,7 @@
     'git+https://github.com/nlhepler/pydot#egg=pydot-1.0.29',
 ]

-if PYTHON_VERSION < (2, 7, 3):
+if PYTHON_VERSION == (2, 7, 2):
     # work around distutils hardcoded unittest dependency
     # work around T106512
     import unittest
@@ -123,12 +100,6 @@
         sys.modules['unittest'] = unittest2

 if sys.version_info[0] == 2:
-    if PY26:
-        script_deps['replicate_wiki.py'] = ['argparse']
-        dependencies.append('future>=0.15.0')  # provides collections backports
-
-        dependencies += extra_deps['unicode7']  # T102461 workaround
-
     # tools.ip does not have a hard dependency on an IP address module,
     # as it falls back to using regexes if one is not available.
     # The functional backport of py3 ipaddress is acceptable:
@@ -139,7 +110,7 @@
     # ipaddr 2.1.10+ is distributed with Debian and Fedora. See T105443.
     dependencies.append('ipaddr>=2.1.10')

-    if sys.version_info < (2, 7, 3):
+    if sys.version_info == (2, 7, 2):
         dependencies.append('future>=0.15.0')  # Bug fixes for HTMLParser

     if sys.version_info < (2, 7, 9):
@@ -257,7 +228,6 @@
         'License :: OSI Approved :: MIT License',
         'Natural Language :: English',
         'Operating System :: OS Independent',
-        'Programming Language :: Python :: 2.6',
         'Programming Language :: Python :: 2.7',
         'Programming Language :: Python :: 3.4',
         'Programming Language :: Python :: 3.5',
diff --git a/tests/__init__.py b/tests/__init__.py
index f548ec6..75404a3 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 """Package tests."""
 #
-# (C) Pywikibot team, 2007-2015
+# (C) Pywikibot team, 2007-2018
 #
 # Distributed under the terms of the MIT license.
 #
@@ -16,7 +16,6 @@

 # Verify that the unit tests have a base working environment:
 # - requests is mandatory
-# - future is needed as a fallback for python 2.6,
 #   however if unavailable this will fail on use; see pywikibot/tools.py
 # - unittest2; see below
 # - mwparserfromhell is optional, so is only imported in textlib_tests
@@ -24,9 +23,8 @@

 from pywikibot.tools import PYTHON_VERSION

-if PYTHON_VERSION < (2, 7, 3):
-    # unittest2 is a backport of python 2.7s unittest module to python 2.6
-    # Also use unittest2 for python 2.7.2 (T106512)
+if PYTHON_VERSION == (2, 7, 2):
+    # Use unittest2 for python 2.7.2 (T106512)
     import unittest2 as unittest
 else:
     import unittest
diff --git a/tests/link_tests.py b/tests/link_tests.py
index 5b661b5..0fcfcc3 100644
--- a/tests/link_tests.py
+++ b/tests/link_tests.py
@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 """Test Link functionality."""
 #
-# (C) Pywikibot team, 2014-2017
+# (C) Pywikibot team, 2014-2018
 #
 # Distributed under the terms of the MIT license.
 #
@@ -14,7 +14,6 @@
 from pywikibot import config2 as config
 from pywikibot.page import Link, Page
 from pywikibot.exceptions import Error, InvalidTitle
-from pywikibot.tools import PYTHON_VERSION

 from tests.aspects import (
     unittest,
@@ -240,19 +239,6 @@
         title = 'Li̍t-sṳ́'
         link = Link(title, self.site)
         self.assertEqual(link.title, 'Li̍t-sṳ́')
-
-    @unittest.skipIf(PYTHON_VERSION != (2, 6, 6), 'Python 2.6.6-only test')
-    def test_py266_bug_exception(self):
-        """Test Python issue 10254 causes an exception."""
-        pywikibot.page.unicodedata = __import__('unicodedata')
-        title = 'Li̍t-sṳ́'
-        with self.assertRaisesRegex(
-                UnicodeError,
-                re.escape('Link(%r, %s): combining characters detected, which '
-                          'are not supported by Pywikibot on Python 2.6.6. '
-                          'See https://phabricator.wikimedia.org/T102461'
-                          % (title, self.site))):
-            Link(title, self.site)


 # ---- The first set of tests are explicit links, starting with a ':'.
diff --git a/tests/python_tests.py b/tests/python_tests.py
index 0320f8e..6dcbd9f 100755
--- a/tests/python_tests.py
+++ b/tests/python_tests.py
@@ -2,7 +2,7 @@
 # -*- coding: utf-8 -*-
 """Tests Python features."""
 #
-# (C) Pywikibot team, 2015
+# (C) Pywikibot team, 2015-2018
 #
 # Distributed under the terms of the MIT license.
 from __future__ import absolute_import, unicode_literals
@@ -13,15 +13,9 @@
 except ImportError:
     unicodedata2 = None

-from pywikibot.tools import PYTHON_VERSION
-
 from tests.aspects import TestCase, unittest
-from tests.utils import expected_failure_if

 # TODO:
-# very old
-# http://bugs.python.org/issue2517
-#
 # unicode
 # http://sourceforge.net/p/pywikipediabot/bugs/1246/
 # http://bugs.python.org/issue10254
@@ -37,7 +31,6 @@
 # http://sourceforge.net/p/pywikipediabot/bugs/509/
 # https://phabricator.wikimedia.org/T57329
 # http://bugs.python.org/issue1528074
-# http://bugs.python.org/issue1678345


 class PythonTestCase(TestCase):
@@ -46,11 +39,9 @@

     net = False

-    @expected_failure_if((2, 7, 0) <= PYTHON_VERSION < (2, 7, 2) or
-                         PYTHON_VERSION == (2, 6, 6))
     def test_issue_10254(self):
         """Test Python issue #10254."""
-        # Python 2.6.6, 2.7.0 and 2.7.1 have a bug in this routine.
+        # Python 2.7.0 and 2.7.1 have a bug in this routine.
         # See T102461 and http://bugs.python.org/issue10254
         text = 'Li̍t-sṳ́'
         self.assertEqual(text, unicodedata.normalize('NFC', text))
diff --git a/tests/script_tests.py b/tests/script_tests.py
index 647d31e..d542f92 100644
--- a/tests/script_tests.py
+++ b/tests/script_tests.py
@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 """Test that each script can be compiled and executed."""
 #
-# (C) Pywikibot team, 2014-2017
+# (C) Pywikibot team, 2014-2018
 #
 # Distributed under the terms of the MIT license.
 #
@@ -12,7 +12,6 @@

 from pywikibot.tools import (
     PY2,
-    PYTHON_VERSION,
     StringTypes,
 )

@@ -39,10 +38,6 @@
     'states_redirect': ['pycountry'],
     'patrol': ['mwparserfromhell'],
 }
-
-if PYTHON_VERSION < (2, 7):
-    script_deps['replicate_wiki'] = ['argparse']
-    script_deps['editarticle'] = ['argparse']

 if PY2:
     script_deps['data_ingestion'] = ['unicodecsv']
diff --git a/tests/textlib_tests.py b/tests/textlib_tests.py
index ca49ada..49855fd 100644
--- a/tests/textlib_tests.py
+++ b/tests/textlib_tests.py
@@ -8,6 +8,7 @@
 from __future__ import absolute_import, unicode_literals

 import codecs
+from collections import OrderedDict
 import functools
 import os
 import re
@@ -18,7 +19,7 @@

 from pywikibot import config, UnknownSite
 from pywikibot.site import _IWEntry
-from pywikibot.tools import OrderedDict, suppress_warnings
+from pywikibot.tools import suppress_warnings

 from tests.aspects import (
     unittest, require_modules, TestCase, DefaultDrySiteTestCase,
diff --git a/tests/tools_tests.py b/tests/tools_tests.py
index 16d8430..0e1aa04 100644
--- a/tests/tools_tests.py
+++ b/tests/tools_tests.py
@@ -2,7 +2,7 @@
 # -*- coding: utf-8 -*-
 """Test tools package alone which don't fit into other tests."""
 #
-# (C) Pywikibot team, 2016-2017
+# (C) Pywikibot team, 2016-2018
 #
 # Distributed under the terms of the MIT license.
 from __future__ import absolute_import, unicode_literals
@@ -452,7 +452,7 @@
         self.assertEqual(next(deduper), 3)

         if key in (hash, passthrough):
-            if isinstance(deduped, tools.OrderedDict):
+            if isinstance(deduped, collections.OrderedDict):
                 self.assertEqual(list(deduped.keys()), [1, 3])
             elif isinstance(deduped, collections.Mapping):
                 self.assertCountEqual(list(deduped.keys()), [1, 3])
@@ -463,7 +463,7 @@
         self.assertEqual(next(deduper), 4)

         if key in (hash, passthrough):
-            if isinstance(deduped, tools.OrderedDict):
+            if isinstance(deduped, collections.OrderedDict):
                 self.assertEqual(list(deduped.keys()), [1, 3, 2, 4])
             elif isinstance(deduped, collections.Mapping):
                 self.assertCountEqual(list(deduped.keys()), [1, 2, 3, 4])
@@ -513,7 +513,7 @@

     def test_OrderedDict(self):
         """Test filter_unique with a OrderedDict."""
-        deduped = tools.OrderedDict()
+        deduped = collections.OrderedDict()
         deduper = tools.filter_unique(self.ints, container=deduped)
         self._test_dedup_int(deduped, deduper)

diff --git a/tests/utils.py b/tests/utils.py
index 43d883e..4fe6414 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -45,9 +45,6 @@

 OSWIN32 = (sys.platform == 'win32')

-PYTHON_26_CRYPTO_WARN = ('Python 2.6 is no longer supported by the Python core 
'
-                         'team, please upgrade your Python.')
-

 class DrySiteNote(RuntimeWarning):

@@ -638,27 +635,12 @@
     """
     Execute a command and capture outputs.

-    On Python 2.6 it adds an option to ignore the deprecation warning from
-    the cryptography package after the first entry of the command parameter.
-
     @param command: executable to run and arguments to use
     @type command: list of unicode
     """
-    if PYTHON_VERSION < (2, 7):
-        command.insert(
-            1, '-W 
ignore:{0}:DeprecationWarning'.format(PYTHON_26_CRYPTO_WARN))
-    if PYTHON_VERSION[:2] == (2, 6):
-        command.insert(1, '-W ignore:{0}:DeprecationWarning'.format(
-            'Pywikibot will soon drop support for Python 2.6'))
     # Any environment variables added on Windows must be of type
     # str() on Python 2.
     env = os.environ.copy()
-
-    # Python issue 6906
-    if PYTHON_VERSION < (2, 6, 6):
-        for var in ('TK_LIBRARY', 'TCL_LIBRARY', 'TIX_LIBRARY'):
-            if var in env:
-                env[var] = env[var].encode('mbcs')

     # Prevent output by test package; e.g. 'max_retries reduced from x to y'
     env[str('PYWIKIBOT_TEST_QUIET')] = str('1')
diff --git a/tox.ini b/tox.ini
index c42d245..c53553a 100644
--- a/tox.ini
+++ b/tox.ini
@@ -3,12 +3,11 @@
 minversion = 1.7.2
 skipsdist = True
 skip_missing_interpreters = True
-envlist = 
diff-checker,commit-message,flake8,pyflakes-{py26,py3,pypy},doctest-{py27,py34},py26,py27,py34
+envlist = 
diff-checker,commit-message,flake8,pyflakes-{py27,py3,pypy},doctest-{py27,py34},py27,py34

 [tox:jenkins]
 # Override default for WM Jenkins
 # Others are run in their own individual jobs on WM Jenkins
-# Wikimedia Jenkins does not have Python 2.6
 envlist = diff-checker,commit-message,flake8,pyflakes-{py3,pypy}

 [params]
@@ -29,12 +28,9 @@
 deps = commit-message-validator
 commands = commit-message-validator

-[testenv:py26]
-deps = unittest2
-
-[testenv:pyflakes-py26]
+[testenv:pyflakes-py27]
 commands = findx . -name '*.py' -a '!' -path '*/.*' -a '!' -name 
'user-config.py' : pyflakes
-basepython = python2.6
+basepython = python2.7
 deps =
     pyflakes
     findx >= 0.9.9
@@ -170,7 +166,7 @@

 ignore = 
C401,C402,C405,E402,D105,D211,FI10,FI12,FI13,FI15,FI16,FI17,FI5,H101,H236,H301,H404,H405,H903,I100,I101,I202,N802,N803,N806,D401,D413,D103,D412,W503
 exclude = 
.tox,.git,./*.egg,ez_setup.py,build,externals,user-config.py,./scripts/i18n/*,scripts/userscripts/*
-min-version = 2.6
+min-version = 2.7
 max_line_length = 100
 accept-encodings = utf-8
 require-code = true

--
To view, visit https://gerrit.wikimedia.org/r/263372
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-MessageType: merged
Gerrit-Change-Id: Ied97711c81adebe9c260c8e7c2647b6cc71846fa
Gerrit-Change-Number: 263372
Gerrit-PatchSet: 26
Gerrit-Owner: Xqt <[email protected]>
Gerrit-Reviewer: Dalba <[email protected]>
Gerrit-Reviewer: Framawiki <[email protected]>
Gerrit-Reviewer: John Vandenberg <[email protected]>
Gerrit-Reviewer: Magul <[email protected]>
Gerrit-Reviewer: Merlijn van Deen <[email protected]>
Gerrit-Reviewer: Multichill <[email protected]>
Gerrit-Reviewer: Xqt <[email protected]>
Gerrit-Reviewer: Zoranzoki21 <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
Pywikibot-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/pywikibot-commits

Reply via email to