From: Rob Browning Date: Sat, 8 Jan 2022 18:04:59 +0000 (-0600) Subject: Drop support for python 2 X-Git-Url: https://arthur.barton.de/gitweb/?p=bup.git;a=commitdiff_plain;h=c8af075fbf5fe45ceaf411dc01f7016290e8d794 Drop support for python 2 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 Tested-by: Rob Browning --- diff --git a/.cirrus.yml b/.cirrus.yml index fd92f24..c7bdd82 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -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: | diff --git a/.pylintrc b/.pylintrc index d324e5b..caae1e8 100644 --- a/.pylintrc +++ b/.pylintrc @@ -1,6 +1,5 @@ # -*-conf-*- [GENERAL OPTIONS] -ignore=py2raise.py [MESSAGES CONTROL] disable=all diff --git a/CODINGSTYLE b/CODINGSTYLE index 348a2f5..13d475f 100644 --- a/CODINGSTYLE +++ b/CODINGSTYLE @@ -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" -`_ -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. diff --git a/Documentation/bup-import-duplicity.md b/Documentation/bup-import-duplicity.md index b75071b..6267200 100644 --- a/Documentation/bup-import-duplicity.md +++ b/Documentation/bup-import-duplicity.md @@ -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 diff --git a/GNUmakefile b/GNUmakefile index 5c0f725..20754e5 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -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 diff --git a/README.md b/README.md index 8b8445d..c88116d 100644 --- 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. diff --git a/config/configure b/config/configure index d467af0..657155a 100755 --- a/config/configure +++ b/config/configure @@ -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 diff --git a/dev/cleanup-mounts-under b/dev/cleanup-mounts-under index b1a9acb..0786034 100755 --- a/dev/cleanup-mounts-under +++ b/dev/cleanup-mounts-under @@ -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 diff --git a/dev/prep-for-debianish-build b/dev/prep-for-debianish-build index 0ded700..56ab0db 100755 --- a/dev/prep-for-debianish-build +++ b/dev/prep-for-debianish-build @@ -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 \ diff --git a/dev/prep-for-freebsd-build b/dev/prep-for-freebsd-build index c95e77d..556b72d 100755 --- a/dev/prep-for-freebsd-build +++ b/dev/prep-for-freebsd-build @@ -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 diff --git a/dev/prep-for-macos-build b/dev/prep-for-macos-build index e559441..a136247 100755 --- a/dev/prep-for-macos-build +++ b/dev/prep-for-macos-build @@ -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 diff --git a/dev/python.c b/dev/python.c index 88df9e2..52acab9 100644 --- a/dev/python.c +++ b/dev/python.c @@ -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 diff --git a/dev/validate-python b/dev/validate-python index 6e538c8..eef0b32 100755 --- a/dev/validate-python +++ b/dev/validate-python @@ -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 diff --git a/lib/bup/_helpers.c b/lib/bup/_helpers.c index 6fe8d6a..9265d49 100644 --- a/lib/bup/_helpers.c +++ b/lib/bup/_helpers.c @@ -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 @@ -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 diff --git a/lib/bup/compat.py b/lib/bup/compat.py index 0057503..645c12a 100644 --- a/lib/bup/compat.py +++ b/lib/bup/compat.py @@ -1,227 +1,86 @@ -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) diff --git a/lib/bup/hlinkdb.py b/lib/bup/hlinkdb.py index 29d575e..615e53f 100644 --- a/lib/bup/hlinkdb.py +++ b/lib/bup/hlinkdb.py @@ -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): diff --git a/lib/bup/io.py b/lib/bup/io.py index a384f10..79a6d83 100644 --- a/lib/bup/io.py +++ b/lib/bup/io.py @@ -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 index e61d029..0000000 --- a/lib/bup/py2raise.py +++ /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] diff --git a/lib/cmd/bup.c b/lib/cmd/bup.c index 61e88db..7f69291 100644 --- a/lib/cmd/bup.c +++ b/lib/cmd/bup.c @@ -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 diff --git a/src/bup/compat.c b/src/bup/compat.c index a1ae8ee..2a29c05 100644 --- a/src/bup/compat.c +++ b/src/bup/compat.c @@ -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