]> arthur.barton.de Git - bup.git/commitdiff
Drop support for python 2
authorRob Browning <rlb@defaultvalue.org>
Sat, 8 Jan 2022 18:04:59 +0000 (12:04 -0600)
committerRob Browning <rlb@defaultvalue.org>
Sun, 16 Jan 2022 20:21:20 +0000 (14:21 -0600)
Don't make broad, disruptive changes for now, but disallow python 2 in
./configure, adjust the docs, and remove code where it's unlikely to
cause conflicts with other pending changes.

Signed-off-by: Rob Browning <rlb@defaultvalue.org>
Tested-by: Rob Browning <rlb@defaultvalue.org>
20 files changed:
.cirrus.yml
.pylintrc
CODINGSTYLE
Documentation/bup-import-duplicity.md
GNUmakefile
README.md
config/configure
dev/cleanup-mounts-under
dev/prep-for-debianish-build
dev/prep-for-freebsd-build
dev/prep-for-macos-build
dev/python.c
dev/validate-python
lib/bup/_helpers.c
lib/bup/compat.py
lib/bup/hlinkdb.py
lib/bup/io.py
lib/bup/py2raise.py [deleted file]
lib/cmd/bup.c
src/bup/compat.c

index fd92f24cb7d15d3ebd7b7b1664d913d6c9566b62..c7bdd82db3653662e6e9e6ac52dbe6377a1f2c4d 100644 (file)
@@ -1,49 +1,6 @@
 
 task:
-  name: debian (py2 check/lint root)
-  container:
-    image: debian:buster
-    cpu: 4
-    memory: 2
-  script: |
-    set -xe
-    dev/prep-for-debianish-build python2
-    dev/system-info
-    BUP_PYTHON_CONFIG=python2.7-config ./configure --with-pylint=yes
-    make -j6 check
-
-task:
-  name: debian (py2 long-check)
-  container:
-    image: debian:buster
-    cpu: 4
-    memory: 2
-  script: |
-    set -xe
-    dev/prep-for-debianish-build python2
-    dev/system-info
-    adduser --disabled-password --gecos '' bup
-    chown -R bup:bup .
-    printf "make -j6 -C %q BUP_PYTHON_CONFIG=python2.7-config long-check" \
-      "$(pwd)" | su -l bup
-
-task:
-  name: debian (py2 check)
-  container:
-    image: debian:buster
-    cpu: 4
-    memory: 2
-  script: |
-    set -xe
-    dev/prep-for-debianish-build python2
-    dev/system-info
-    adduser --disabled-password --gecos '' bup
-    chown -R bup:bup .
-    printf "make -j6 -C %q BUP_PYTHON_CONFIG=python2.7-config check" \
-      "$(pwd)" | su -l bup
-
-task:
-  name: debian (py3 check/lint root)
+  name: debian check/lint root
   container:
     image: debian:buster
     cpu: 4
@@ -56,7 +13,7 @@ task:
     make -j6 check
 
 task:
-  name: debian (py3 long-check)
+  name: debian long-check
   container:
     image: debian:buster
     cpu: 4
@@ -71,7 +28,7 @@ task:
       "$(pwd)" | su -l bup
 
 task:
-  name: debian (py3 check)
+  name: debian check
   container:
     image: debian:buster
     cpu: 4
@@ -86,7 +43,7 @@ task:
       "$(pwd)" | su -l bup
 
 task:
-  name: freebsd (py3 check)
+  name: freebsd check
   freebsd_instance:
     image: freebsd-12-2-release-amd64
     cpu: 4
@@ -98,7 +55,7 @@ task:
     BUP_PYTHON_CONFIG=python3.8-config make -j6 check
 
 task:
-  name: macos (py3 check)
+  name: macos check
   osx_instance:
     image: catalina-base
   script: |
index d324e5bde38c693f46b58575903a8590af45c273..caae1e8202ead07a1fccf1f0b9aded769e076781 100644 (file)
--- a/.pylintrc
+++ b/.pylintrc
@@ -1,6 +1,5 @@
 # -*-conf-*-
 [GENERAL OPTIONS]
-ignore=py2raise.py
 
 [MESSAGES CONTROL]
 disable=all
index 348a2f51340bfe3545b7f9477cde631343126e7c..13d475f7fbd4914deadc450b73a1bea877650422 100644 (file)
@@ -29,57 +29,3 @@ blank line in between). Here's an example from
 
 Module-level docstrings follow exactly the same guidelines but without the
 blank line between the summary and the details.
-
-
-Exception Handling
-------------------
-
-Avoid finally: blocks in favor of explict catches because a throw
-from a finally block will lose any pending exception.  An explicit
-catch can chain it (see below).
-
-To behave similarly under Python 2 and 3, use add_ex_tb() to
-explicitly add stack traces to any exceptions that are going to be
-re-raised by anything other than a no-argument raise (otherwise the
-stack trace will be lost)::
-
-  try:
-      ...
-  except ... as ex:
-      add_ex_tb(ex)
-      pending_ex = ex
-  ...
-  raise pending_ex
-
-When an exception is thrown from an exception handler, the pending
-exception should be the `"context"
-<https://docs.python.org/3/reference/simple_stmts.html#the-raise-statement>`_
-of the new exception, which can be accomplished (portably) via
-``pending_raise()``::
-
-  try:
-      ...
-  except ... as ex:
-      with pending_raise(ex):
-          clean_up()
-
-This should do roughly the same thing in Python 2 and Python 3,
-throwing any exception from ``clean_up()`` after adding ex as the
-``__context__`` if clean_up() throws, and throwing ``ex`` otherwise.
-
-If for some reason, you need more control, you can use
-``add_ex_ctx()`` directly::
-
-  try:
-      ...
-  except ... as ex:
-      add_ex_tb(ex)
-      try:
-          ...
-      except ... as ex2:
-          add_ex_tb(ex2)
-          raise add_ex_ctx(ex2, ex)
-      raise
-
-See the end of ``lib/bup/compat.py`` for a functional example, and all
-of this can be removed once we drop support for Python 2.
index b75071b2c195b41c9f3fd0511e325c32b502081e..626720042c0c334dc5a73fd9a4023326aa6f60a6 100644 (file)
@@ -49,4 +49,4 @@ Since all invocations of duplicity use a temporary `--archive-dir`,
 
 Part of the `bup`(1) suite.
 
-[mkdtemp]: https://docs.python.org/2/library/tempfile.html#tempfile.mkdtemp
+[mkdtemp]: https://docs.python.org/3/library/tempfile.html#tempfile.mkdtemp
index 5c0f72501f0c5f25acaa1b8dce1ee9ac4f020bb1..20754e57aa52441baeb90df1ff685c7ce197496c 100644 (file)
@@ -235,14 +235,9 @@ long-test: test
 long-check: export BUP_TEST_LEVEL=11
 long-check: check
 
-.PHONY: check-py2 check-py3 check-both
+.PHONY: check-py3
 check-py3:
        $(MAKE) clean && BUP_PYTHON_CONFIG=python3-config $(MAKE) check
-check-py2:
-       $(MAKE) clean && BUP_PYTHON_CONFIG=python2.7-config $(MAKE) check
-check-both:
-       $(MAKE) check-py3
-       $(MAKE) check-py2
 
 .PHONY: Documentation/all
 Documentation/all: $(man_roff) $(man_html)
@@ -300,7 +295,5 @@ clean: Documentation/clean
        rm -rf $(clean_paths) .pytest_cache
        rm -f $(generated_dependencies)
        find . -name __pycache__ -exec rm -rf {} +
-        # Remove this *.pyc cleanup once we drop py2
-       rm -f lib/bup/*.pyc lib/bup/cmd/*.pyc test/__init__.pyc
        if test -e test/tmp; then dev/force-delete test/tmp; fi
        dev/configure-sampledata --clean
index 8b8445d4d5a8bf23a1e0d1ff8ba3d6d46c4508b2..c88116dde26e6ef37ea3afce28549e3b0e996533 100644 (file)
--- a/README.md
+++ b/README.md
@@ -71,12 +71,10 @@ Reasons you might want to avoid bup
    more likely to eat your data.  It's also missing some
    probably-critical features, though fewer than it used to be.
    
- - It requires python 3.7 or newer (or 2.7 for a bit longer), a C
-   compiler, and an installed git version >= 1.5.6.  It also requires
-   par2 if you want fsck to be able to generate the information needed
-   to recover from some types of corruption.  While python 2.7 is
-   still supported, please make plans to upgrade.  Python 2 upstream
-   support ended on 2020-01-01, and we plan to drop support soon too.
+ - It requires python 3.7 or newer, a C compiler, and an installed git
+   version >= 1.5.6.  It also requires par2 if you want fsck to be
+   able to generate the information needed to recover from some types
+   of corruption.
  
  - It currently only works on Linux, FreeBSD, NetBSD, OS X >= 10.4,
    Solaris, or Windows (with Cygwin, and WSL).  Patches to support
@@ -159,33 +157,6 @@ From source
     apt-get install python3-tornado # optional (bup web)
 
     ```
-   Or, if you can't yet migrate to Python 3 (please try to soon):
-
-    ```sh
-    apt-get install python2.7-dev python-fuse
-    apt-get install python-pyxattr python-pytest
-    apt-get install pkg-config linux-libc-dev libacl1-dev
-    apt-get install gcc make acl attr rsync
-    apt-get isntall python-pytest-xdist # optional (parallel tests)
-    apt-get install par2 # optional (error correction)
-    apt-get install libreadline-dev # optional (bup ftp)
-    apt-get install python-tornado # optional (bup web)
-    ```
-
-   On CentOS (for CentOS 6, at least), this should be sufficient (run
-   as root):
-
-    ```sh
-    yum groupinstall "Development Tools"
-    yum install python2 python2-devel libacl-devel pylibacl
-    yum install fuse-python pyxattr
-    yum install perl-Time-HiRes
-    yum install readline-devel # optional (bup ftp)
-    yum install python-tornado # optional (bup web)
-    ```
-
-   In addition to the default CentOS repositories, you may need to add
-   RPMForge (for fuse-python) and EPEL (for pyxattr).
 
    On Cygwin, install python, make, rsync, and gcc4.
 
@@ -599,8 +570,8 @@ mailing list (see below) if you'd like to help.
    __setitem__, and __setslice__ [3].
 
      [1] http://comments.gmane.org/gmane.comp.sysutils.backup.bup/613
-     [2] http://docs.python.org/2/library/mmap.html
-     [3] http://docs.python.org/2/reference/datamodel.html#emulating-container-types
+     [2] http://docs.python.org/3/library/mmap.html
+     [3] http://docs.python.org/3/reference/datamodel.html#emulating-container-types
 
  - 'bup index' is slower than it should be.
  
index d467af0e74405ccb9586a16d46ac0aff8f722b58..657155a0fa1f011a4cd078d25f011db0b79d180e 100755 (executable)
@@ -119,14 +119,12 @@ if test "$BUP_PYTHON_CONFIG"; then
                          "$BUP_PYTHON_CONFIG")
     fi
 else
-    for py_maj_ver in 10 9 8 7 6; do
-        bup_python_config="$(bup_find_prog "python3.$py_maj_ver-config" '')"
+    for py_min_ver in 10 9 8 7 6; do
+        bup_python_config="$(bup_find_prog "python3.$py_min_ver-config" '')"
         test -z "$bup_python_config" || break
     done
     test -z "$bup_python_config" \
         && bup_python_config="$(bup_find_prog python3-config '')"
-    test -z "$bup_python_config" \
-        && bup_python_config="$(bup_find_prog python2.7-config '')"
     if test -z "$bup_python_config"; then
         AC_FAIL "ERROR: unable to find a suitable python-config"
     fi
index b1a9acb0a94448b51849620778a45f68cb34a345..0786034b0624839d361ec691388892717cc216f0 100755 (executable)
@@ -9,8 +9,8 @@ for python in \
     python3.8 \
     python3.7 \
     python3.6 \
-    python \
-    python2.7; do \
+    python; \
+do \
     if command -v "$python"; then
         exec "$python" "$0" "$@"
     fi
index 0ded700114b5e5a011719cf8997f17b9db812b5e..56ab0dbd3b2f4ae0bce46263d6ab88b01068bc1a 100755 (executable)
@@ -4,7 +4,7 @@ set -exuo pipefail
 
 usage()
 {
-    echo "Usage: prep-for-debianish-build [python2|python3] [pyxattr|xattr]"
+    echo "Usage: prep-for-debianish-build [python3] [pyxattr|xattr]"
 }
 
 export DEBIAN_FRONTEND=noninteractive
@@ -14,19 +14,12 @@ common_debs='gcc make linux-libc-dev git rsync eatmydata acl attr par2'
 common_debs="$common_debs duplicity rdiff-backup rsnapshot dosfstools kmod"
 common_debs="$common_debs pkg-config libreadline-dev libacl1-dev"
 
-pyver="${1:-python2}"
+pyver="${1:-python3}"
 xattr="${2:-pyxattr}"
 
 # dosfstools: for vfat for the (root) tests
 
 case "$pyver" in
-    python2)
-        apt-get install -y \
-                $common_debs \
-                python2.7-dev python-fuse \
-                python-"$xattr" python-tornado python-pytest \
-                python-pytest-xdist pylint
-        ;;
     python3)
         apt-get install -y \
                 $common_debs \
index c95e77d21faa9e96bd137fbbf9cedd3963cbca5c..556b72deeb4f31a09eafe240b203cb3cc280a60c 100755 (executable)
@@ -4,10 +4,10 @@ set -exu
 
 usage()
 {
-    echo "Usage: prep-for-freebsd-build [python2|python3]"
+    echo "Usage: prep-for-freebsd-build [python3]"
 }
 
-pyver="${1:-python2}"
+pyver="${1:-python3}"
 
 # Install build deps
 export ASSUME_ALWAYS_YES=yes
@@ -20,11 +20,6 @@ pkgs='gmake git bash rsync curl par2cmdline readline duplicity'
 pkgs="$pkgs rsnapshot"
 
 case "$pyver" in
-    python2)
-        pkgs="$pkgs python2 py27-tornado py27-pip"
-        pkg install $pkgs
-        pip-2.7 install --user pytest pytest-xdist
-        ;;
     python3)
         pkgs="$pkgs python38 py38-tornado py38-pytest py38-pytest-xdist"
         pkg install $pkgs
index e5594417cf63aa24e71bc70dd85775e8c7aa88c4..a136247ee87f868d1da60e207303da258442473d 100755 (executable)
@@ -4,10 +4,10 @@ set -exu
 
 usage()
 {
-    echo "Usage: prep-for-macos-build [python2|python3]"
+    echo "Usage: prep-for-macos-build [python3]"
 }
 
-pyver="${1:-python2}"
+pyver="${1:-python3}"
 
 if ! command -v brew; then
     /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
@@ -21,10 +21,6 @@ brew link --force readline
 # "brew unlink readline" will undo this hack
 
 case "$pyver" in
-    python2)
-        easy_install-2.7 --user pip
-        /Users/anka/Library/Python/2.7/bin/pip install --user pytest pytest-xdist
-        ;;
     python3)
         brew install python
         pip3 install --user pytest pytest-xdist
index 88df9e2d2c97ee35868f48d4bce955a97cca38a5..52acab9a20d4d0efbef47c777c668edae64c8b53 100644 (file)
@@ -4,7 +4,6 @@
 #include "../config/config.h"
 
 // According to Python, its header has to go first:
-//   http://docs.python.org/2/c-api/intro.html#include-files
 //   http://docs.python.org/3/c-api/intro.html#include-files
 #include <Python.h>
 
index 6e538c85f48bff73a9b9071b86eaf1f00b240ac7..eef0b32207da823b3e60cff072b19fe8b0be1e5e 100755 (executable)
@@ -11,9 +11,9 @@ majver=$("$python" -c 'import sys; print(sys.version_info[0])')
 minver=$("$python" -c 'import sys; print(sys.version_info[1])')
 
 # May not be correct yet, i.e. actual requirement may be higher.
-if test "$majver" -gt 2 -a "$minver" -lt 7; then
+if test "$majver" -lt 3 || test "$majver" -eq 3 && test "$minver" -lt 7; then
     # utime follow_symlinks >= 3.3
     bup_version_str=$("$python" --version 2>&1)
-    echo "ERROR: found $bup_version_str (must be >= 3.7 if >= 3)" 1>&2
+    echo "ERROR: found $bup_version_str (must be >= 3.7)" 1>&2
     exit 2
 fi
index 6fe8d6abc735d49ec625bc52d207c1ea4cf91b9a..9265d49324a72b08f82132ae427eb105875263b7 100644 (file)
@@ -4,7 +4,6 @@
 #include "../../config/config.h"
 
 // According to Python, its header has to go first:
-//   http://docs.python.org/2/c-api/intro.html#include-files
 //   http://docs.python.org/3/c-api/intro.html#include-files
 #include <Python.h>
 
@@ -103,18 +102,10 @@ typedef struct {
 // rbuf_argf: for read-only byte vectors
 // wbuf_argf: for mutable byte vectors
 
-#if PY_MAJOR_VERSION < 3
-static state_t state;
-#  define get_state(x) (&state)
-#  define cstr_argf "s"
-#  define rbuf_argf "s#"
-#  define wbuf_argf "s*"
-#else
-#  define get_state(x) ((state_t *) PyModule_GetState(x))
-#  define cstr_argf "y"
-#  define rbuf_argf "y#"
-#  define wbuf_argf "y*"
-#endif // PY_MAJOR_VERSION >= 3
+#define get_state(x) ((state_t *) PyModule_GetState(x))
+#define cstr_argf "y"
+#define rbuf_argf "y#"
+#define wbuf_argf "y*"
 
 
 static void *checked_calloc(size_t n, size_t size)
@@ -161,37 +152,9 @@ static uint64_t htonll(uint64_t value)
 #define INTEGER_TO_PY(x) \
     EXPR_SIGNED(x) ? PyLong_FromLongLong(x) : PyLong_FromUnsignedLongLong(x)
 
-#if PY_MAJOR_VERSION < 3
-static int bup_ulong_from_pyint(unsigned long *x, PyObject *py,
-                                const char *name)
-{
-    const long tmp = PyInt_AsLong(py);
-    if (tmp == -1 && PyErr_Occurred())
-    {
-        if (PyErr_ExceptionMatches(PyExc_OverflowError))
-            PyErr_Format(PyExc_OverflowError, "%s too big for unsigned long",
-                         name);
-        return 0;
-    }
-    if (tmp < 0)
-    {
-        PyErr_Format(PyExc_OverflowError,
-                     "negative %s cannot be converted to unsigned long", name);
-        return 0;
-    }
-    *x = tmp;
-    return 1;
-}
-#endif
-
 
 static int bup_ulong_from_py(unsigned long *x, PyObject *py, const char *name)
 {
-#if PY_MAJOR_VERSION < 3
-    if (PyInt_Check(py))
-        return bup_ulong_from_pyint(x, py, name);
-#endif
-
     if (!PyLong_Check(py))
     {
         PyErr_Format(PyExc_TypeError, "expected integer %s", name);
@@ -229,19 +192,6 @@ static int bup_uint_from_py(unsigned int *x, PyObject *py, const char *name)
 static int bup_ullong_from_py(unsigned PY_LONG_LONG *x, PyObject *py,
                               const char *name)
 {
-#if PY_MAJOR_VERSION < 3
-    if (PyInt_Check(py))
-    {
-        unsigned long tmp;
-        if (bup_ulong_from_pyint(&tmp, py, name))
-        {
-            *x = tmp;
-            return 1;
-        }
-        return 0;
-    }
-#endif
-
     if (!PyLong_Check(py))
     {
         PyErr_Format(PyExc_TypeError, "integer argument expected for %s", name);
@@ -569,29 +519,13 @@ static PyObject *blobbits(PyObject *self, PyObject *args)
 
 static PyObject *splitbuf(PyObject *self, PyObject *args)
 {
-    // We stick to buffers in python 2 because they appear to be
-    // substantially smaller than memoryviews, and because
-    // zlib.compress() in python 2 can't accept a memoryview
-    // (cf. hashsplit.py).
     int out = 0, bits = -1;
-    if (PY_MAJOR_VERSION > 2)
-    {
-        Py_buffer buf;
-        if (!PyArg_ParseTuple(args, "y*", &buf))
-            return NULL;
-        assert(buf.len <= INT_MAX);
-        out = bupsplit_find_ofs(buf.buf, buf.len, &bits);
-        PyBuffer_Release(&buf);
-    }
-    else
-    {
-        unsigned char *buf = NULL;
-        Py_ssize_t len = 0;
-        if (!PyArg_ParseTuple(args, "t#", &buf, &len))
-            return NULL;
-        assert(len <= INT_MAX);
-        out = bupsplit_find_ofs(buf, (int) len, &bits);
-    }
+    Py_buffer buf;
+    if (!PyArg_ParseTuple(args, "y*", &buf))
+        return NULL;
+    assert(buf.len <= INT_MAX);
+    out = bupsplit_find_ofs(buf.buf, buf.len, &bits);
+    PyBuffer_Release(&buf);
     if (out) assert(bits >= BUP_BLOBBITS);
     return Py_BuildValue("ii", out, bits);
 }
@@ -2561,25 +2495,6 @@ static int setup_module(PyObject *m)
 }
 
 
-#if PY_MAJOR_VERSION < 3
-
-PyMODINIT_FUNC init_helpers(void)
-{
-    PyObject *m = Py_InitModule("_helpers", helper_methods);
-    if (m == NULL) {
-        PyErr_SetString(PyExc_RuntimeError, "bup._helpers init failed");
-        return;
-    }
-    if (!setup_module(m))
-    {
-        PyErr_SetString(PyExc_RuntimeError, "bup._helpers set up failed");
-        Py_DECREF(m);
-        return;
-    }
-}
-
-# else // PY_MAJOR_VERSION >= 3
-
 static struct PyModuleDef helpers_def = {
     PyModuleDef_HEAD_INIT,
     "_helpers",
@@ -2604,5 +2519,3 @@ PyMODINIT_FUNC PyInit__helpers(void)
     }
     return module;
 }
-
-#endif // PY_MAJOR_VERSION >= 3
index 00575036e27cc46f7fb6b3408e273831526b9daa..645c12aa75c211e31ac18924d5cc470640fe73f4 100644 (file)
 
-from __future__ import absolute_import, print_function
-from binascii import hexlify
-from traceback import print_exception
 import os, sys
 
-# Please see CODINGSTYLE for important exception handling guidelines
-# and the rationale behind add_ex_tb(), add_ex_ctx(), etc.
+py_maj = sys.version_info.major
+assert py_maj >= 3
+
+# pylint: disable=unused-import
+from contextlib import ExitStack, nullcontext
+from os import environb as environ
+from os import fsdecode, fsencode
+from shlex import quote
+ModuleNotFoundError = ModuleNotFoundError
+input = input
+range = range
+str_type = str
+int_types = (int,)
+
+def hexstr(b):
+    """Return hex string (not bytes as with hexlify) representation of b."""
+    return b.hex()
+
+def reraise(ex):
+    raise ex.with_traceback(sys.exc_info()[2])
+
+# These three functions (add_ex_tb, add_ex_ctx, and pending_raise) are
+# vestigial, and code that uses them can probably be rewritten more
+# simply now that we require Python versions that automatically
+# populate the tracebacks and automatically chain pending exceptions.
+
+def add_ex_tb(ex):
+    """Do nothing (already handled by Python 3 infrastructure)."""
+    return ex
+
+def add_ex_ctx(ex, context_ex):
+    """Do nothing (already handled by Python 3 infrastructure)."""
+    return ex
+
+class pending_raise:
+    """If rethrow is true, rethrow ex (if any), unless the body throws.
+
+    (Supports Python 2 compatibility.)
+
+    """
+    # This is completely vestigial, and should be removed
+    def __init__(self, ex, rethrow=True):
+        self.closed = False
+        self.ex = ex
+        self.rethrow = rethrow
+    def __enter__(self):
+        return None
+    def __exit__(self, exc_type, exc_value, traceback):
+        self.closed = True
+        if not exc_type and self.ex and self.rethrow:
+            raise self.ex
+    def __del__(self):
+        assert self.closed
+
+def items(x):
+    return x.items()
+
+def argv_bytes(x):
+    """Return the original bytes passed to main() for an argv argument."""
+    return fsencode(x)
+
+def bytes_from_uint(i):
+    return bytes((i,))
+
+def bytes_from_byte(b):  # python > 2: b[3] returns ord('x'), not b'x'
+    return bytes((b,))
+
+byte_int = lambda x: x
+
+def buffer(object, offset=None, size=None):
+    if size:
+        assert offset is not None
+        return memoryview(object)[offset:offset + size]
+    if offset:
+        return memoryview(object)[offset:]
+    return memoryview(object)
+
+def getcwd():
+    return fsencode(os.getcwd())
 
-py_maj = sys.version_info[0]
-py3 = py_maj >= 3
-
-if py3:
-
-    # pylint: disable=unused-import
-    from contextlib import ExitStack, nullcontext
-    from os import environb as environ
-    from os import fsdecode, fsencode
-    from shlex import quote
-    # pylint: disable=undefined-variable
-    # (for python2 looking here)
-    ModuleNotFoundError = ModuleNotFoundError
-    input = input
-    range = range
-    str_type = str
-    int_types = (int,)
-
-    def hexstr(b):
-        """Return hex string (not bytes as with hexlify) representation of b."""
-        return b.hex()
-
-    def reraise(ex):
-        raise ex.with_traceback(sys.exc_info()[2])
-
-    def add_ex_tb(ex):
-        """Do nothing (already handled by Python 3 infrastructure)."""
-        return ex
-
-    def add_ex_ctx(ex, context_ex):
-        """Do nothing (already handled by Python 3 infrastructure)."""
-        return ex
-
-    class pending_raise:
-        """If rethrow is true, rethrow ex (if any), unless the body throws.
-
-        (Supports Python 2 compatibility.)
-
-        """
-        def __init__(self, ex, rethrow=True):
-            self.closed = False
-            self.ex = ex
-            self.rethrow = rethrow
-        def __enter__(self):
-            return None
-        def __exit__(self, exc_type, exc_value, traceback):
-            self.closed = True
-            if not exc_type and self.ex and self.rethrow:
-                raise self.ex
-        def __del__(self):
-            assert self.closed
-
-    def items(x):
-        return x.items()
-
-    def argv_bytes(x):
-        """Return the original bytes passed to main() for an argv argument."""
-        return fsencode(x)
-
-    def bytes_from_uint(i):
-        return bytes((i,))
-
-    def bytes_from_byte(b):  # python > 2: b[3] returns ord('x'), not b'x'
-        return bytes((b,))
-
-    byte_int = lambda x: x
-
-    def buffer(object, offset=None, size=None):
-        if size:
-            assert offset is not None
-            return memoryview(object)[offset:offset + size]
-        if offset:
-            return memoryview(object)[offset:]
-        return memoryview(object)
-
-    def getcwd():
-        return fsencode(os.getcwd())
-
-else:  # Python 2
-
-    from contextlib import contextmanager
-
-    ModuleNotFoundError = ImportError
-
-    def fsdecode(x):
-        return x
-
-    def fsencode(x):
-        return x
-
-    from pipes import quote
-    # pylint: disable=unused-import
-    from os import environ, getcwd
-
-    # pylint: disable=unused-import
-    from bup.py2raise import reraise
-
-    @contextmanager
-    def nullcontext(enter_result=None):
-        yield enter_result
-
-    # on py3 this causes errors, obviously
-    # pylint: disable=undefined-variable
-    input = raw_input
-    # pylint: disable=undefined-variable
-    range = xrange
-    # pylint: disable=undefined-variable
-    str_type = basestring
-    # pylint: disable=undefined-variable
-    int_types = (int, long)
-
-    hexstr = hexlify
-
-    def add_ex_tb(ex):
-        """Add a traceback to ex if it doesn't already have one.  Return ex.
-
-        """
-        if not getattr(ex, '__traceback__', None):
-            ex.__traceback__ = sys.exc_info()[2]
-        return ex
-
-    def add_ex_ctx(ex, context_ex):
-        """Make context_ex the __context__ of ex (unless it already has one).
-        Return ex.
-
-        """
-        if context_ex:
-            if not getattr(ex, '__context__', None):
-                ex.__context__ = context_ex
-        return ex
-
-    class pending_raise:
-        """If rethrow is true, rethrow ex (if any), unless the body throws.
-
-        If the body does throw, make any provided ex the __context__
-        of the newer exception (assuming there's no existing
-        __context__).  Ensure the exceptions have __tracebacks__.
-        (Supports Python 2 compatibility.)
-
-        """
-        def __init__(self, ex, rethrow=True):
-            self.closed = False
-            self.ex = ex
-            self.rethrow = rethrow
-        def __enter__(self):
-            if self.ex:
-                add_ex_tb(self.ex)
-        def __exit__(self, exc_type, exc_value, traceback):
-            self.closed = True
-            if exc_value:
-                if self.ex:
-                    add_ex_tb(exc_value)
-                    add_ex_ctx(exc_value, self.ex)
-                return
-            if self.rethrow and self.ex:
-                raise self.ex
-        def __del__(self):
-            assert self.closed
-
-    def dump_traceback(ex):
-        stack = [ex]
-        next_ex = getattr(ex, '__context__', None)
-        while next_ex:
-            stack.append(next_ex)
-            next_ex = getattr(next_ex, '__context__', None)
-        stack = reversed(stack)
-        ex = next(stack)
-        tb = getattr(ex, '__traceback__', None)
-        print_exception(type(ex), ex, tb)
-        for ex in stack:
-            print('\nDuring handling of the above exception, another exception occurred:\n',
-                  file=sys.stderr)
-            tb = getattr(ex, '__traceback__', None)
-            print_exception(type(ex), ex, tb)
-
-    class ExitStack:
-        def __init__(self):
-            self.contexts = []
-
-        def __enter__(self):
-            return self
-
-        def __exit__(self, value_type, value, traceback):
-            init_value = value
-            for ctx in reversed(self.contexts):
-                try:
-                    ctx.__exit__(value_type, value, traceback)
-                except BaseException as ex:
-                    add_ex_tb(ex)
-                    if value:
-                        add_ex_ctx(ex, value)
-                    value_type = type(ex)
-                    value = ex
-                    traceback = ex.__traceback__
-            if value is not init_value:
-                raise value
-
-        def enter_context(self, x):
-            self.contexts.append(x)
-
-    def items(x):
-        return x.iteritems()
-
-    def argv_bytes(x):
-        """Return the original bytes passed to main() for an argv argument."""
-        return x
-
-    bytes_from_uint = chr
-
-    def bytes_from_byte(b):
-        return b
-
-    byte_int = ord
-
-    buffer = buffer
 
 try:
     import bup_main
@@ -232,13 +91,9 @@ if bup_main:
     def get_argvb():
         "Return a new list containing the current process argv bytes."
         return bup_main.argv()
-    if py3:
-        def get_argv():
-            "Return a new list containing the current process argv strings."
-            return [x.decode(errors='surrogateescape') for x in bup_main.argv()]
-    else:
-        def get_argv():
-            return bup_main.argv()
+    def get_argv():
+        "Return a new list containing the current process argv strings."
+        return [x.decode(errors='surrogateescape') for x in bup_main.argv()]
 else:
     def get_argvb():
         raise Exception('get_argvb requires the bup_main module');
@@ -255,38 +110,3 @@ def wrap_main(main):
         sys.exit(main())
     except KeyboardInterrupt as ex:
         sys.exit(130)
-    except SystemExit as ex:
-        raise
-    except BaseException as ex:
-        if py3:
-            raise
-        add_ex_tb(ex)
-        dump_traceback(ex)
-        sys.exit(1)
-
-
-# Excepting wrap_main() in the traceback, these should produce similar output:
-#   python2 lib/bup/compat.py
-#   python3 lib/bup/compat.py
-# i.e.:
-#   diff -u <(python2 lib/bup/compat.py 2>&1) <(python3 lib/bup/compat.py 2>&1)
-#
-# Though the python3 output for 'second' will include a stacktrace
-# starting from wrap_main, rather than from outer().
-
-if __name__ == '__main__':
-
-    def inner():
-        raise Exception('first')
-
-    def outer():
-        try:
-            inner()
-        except Exception as ex:
-            add_ex_tb(ex)
-            try:
-                raise Exception('second')
-            except Exception as ex2:
-                raise add_ex_ctx(add_ex_tb(ex2), ex)
-
-    wrap_main(outer)
index 29d575eec3f8aae567b4656140e9185226e31b47..615e53f10937a741a44f5d2c32fbe6973f13b843 100644 (file)
@@ -1,17 +1,12 @@
 
-from __future__ import absolute_import
-import errno, os, tempfile
+import errno, os, pickle, tempfile
 
 from bup import compat
 from bup.compat import pending_raise
 
-if compat.py_maj > 2:
-    import pickle
-    def pickle_load(f):
-        return pickle.load(f, encoding='bytes')
-else:
-    import cPickle as pickle
-    pickle_load = pickle.load
+
+def pickle_load(f):
+    return pickle.load(f, encoding='bytes')
 
 
 class Error(Exception):
index a384f1007e5d5f0452d646633a658b01b1dad9f0..79a6d83d5468c0e026a53415ce5dd91bf5d9bafc 100644 (file)
@@ -1,27 +1,16 @@
 
-from __future__ import absolute_import, print_function
 import mmap as py_mmap
 
-from bup import compat
 from bup.compat import pending_raise
 
 
-if compat.py_maj > 2:
-    def byte_stream(file):
-        return file.buffer
+def byte_stream(file):
+    return file.buffer
 
-    def path_msg(x):
-        """Return a string representation of a path."""
-        # FIXME: configurability (might git-config quotePath be involved?)
-        return x.decode(errors='backslashreplace')
-else:
-    def byte_stream(file):
-        return file
-
-    def path_msg(x):
-        """Return a string representation of a path."""
-        # FIXME: configurability (might git-config quotePath be involved?)
-        return x
+def path_msg(x):
+    """Return a string representation of a path."""
+    # FIXME: configurability (might git-config quotePath be involved?)
+    return x.decode(errors='backslashreplace')
 
 
 assert not hasattr(py_mmap.mmap, '__del__')
diff --git a/lib/bup/py2raise.py b/lib/bup/py2raise.py
deleted file mode 100644 (file)
index e61d029..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-
-import sys
-
-# This file exists because the raise syntax is completely incompatible
-# with Python 3.
-
-def reraise(ex):
-    raise ex, None, sys.exc_info()[2]
index 61e88dbbea2414ed535ffcd7e31df59453d6ccb9..7f69291b6c402f946d66011389da4df15dad94b9 100644 (file)
@@ -4,7 +4,6 @@
 #undef NDEBUG
 
 // According to Python, its header has to go first:
-//   http://docs.python.org/2/c-api/intro.html#include-files
 //   http://docs.python.org/3/c-api/intro.html#include-files
 #include <Python.h>
 
index a1ae8ee75573aa20849ef1e95bfca598231fca07..2a29c05fc9f525e73a710bda5342c5230d325d82 100644 (file)
@@ -4,7 +4,6 @@
 #undef NDEBUG
 
 // According to Python, its header has to go first:
-//   http://docs.python.org/2/c-api/intro.html#include-files
 //   http://docs.python.org/3/c-api/intro.html#include-files
 #include <Python.h>