diff options
Diffstat (limited to 'debian/patches')
46 files changed, 2283 insertions, 0 deletions
diff --git a/debian/patches/asyncio366.diff b/debian/patches/asyncio366.diff new file mode 100644 index 0000000..cd7825b --- /dev/null +++ b/debian/patches/asyncio366.diff @@ -0,0 +1,200 @@ +# DP: Fix callbacks race in SelectorLoop.sock_connect. +# DP: https://github.com/python/asyncio/pull/366 + +Index: b/Lib/asyncio/selector_events.py +=================================================================== +--- a/Lib/asyncio/selector_events.py ++++ b/Lib/asyncio/selector_events.py +@@ -382,6 +382,7 @@ class BaseSelectorEventLoop(base_events. + data = data[n:] + self.add_writer(fd, self._sock_sendall, fut, True, sock, data) + ++ @coroutine + def sock_connect(self, sock, address): + """Connect to a remote socket at address. + +@@ -390,24 +391,16 @@ class BaseSelectorEventLoop(base_events. + if self._debug and sock.gettimeout() != 0: + raise ValueError("the socket must be non-blocking") + +- fut = self.create_future() +- if hasattr(socket, 'AF_UNIX') and sock.family == socket.AF_UNIX: +- self._sock_connect(fut, sock, address) +- else: ++ if not hasattr(socket, 'AF_UNIX') or sock.family != socket.AF_UNIX: + resolved = base_events._ensure_resolved( + address, family=sock.family, proto=sock.proto, loop=self) +- resolved.add_done_callback( +- lambda resolved: self._on_resolved(fut, sock, resolved)) +- +- return fut +- +- def _on_resolved(self, fut, sock, resolved): +- try: ++ if not resolved.done(): ++ yield from resolved + _, _, _, _, address = resolved.result()[0] +- except Exception as exc: +- fut.set_exception(exc) +- else: +- self._sock_connect(fut, sock, address) ++ ++ fut = self.create_future() ++ self._sock_connect(fut, sock, address) ++ return (yield from fut) + + def _sock_connect(self, fut, sock, address): + fd = sock.fileno() +@@ -418,8 +411,8 @@ class BaseSelectorEventLoop(base_events. + # connection runs in background. We have to wait until the socket + # becomes writable to be notified when the connection succeed or + # fails. +- fut.add_done_callback(functools.partial(self._sock_connect_done, +- fd)) ++ fut.add_done_callback( ++ functools.partial(self._sock_connect_done, fd)) + self.add_writer(fd, self._sock_connect_cb, fut, sock, address) + except Exception as exc: + fut.set_exception(exc) +Index: b/Lib/test/test_asyncio/test_selector_events.py +=================================================================== +--- a/Lib/test/test_asyncio/test_selector_events.py ++++ b/Lib/test/test_asyncio/test_selector_events.py +@@ -2,6 +2,8 @@ + + import errno + import socket ++import threading ++import time + import unittest + from unittest import mock + try: +@@ -337,18 +339,6 @@ class BaseSelectorEventLoopTests(test_ut + (10, self.loop._sock_sendall, f, True, sock, b'data'), + self.loop.add_writer.call_args[0]) + +- def test_sock_connect(self): +- sock = test_utils.mock_nonblocking_socket() +- self.loop._sock_connect = mock.Mock() +- +- f = self.loop.sock_connect(sock, ('127.0.0.1', 8080)) +- self.assertIsInstance(f, asyncio.Future) +- self.loop._run_once() +- future_in, sock_in, address_in = self.loop._sock_connect.call_args[0] +- self.assertEqual(future_in, f) +- self.assertEqual(sock_in, sock) +- self.assertEqual(address_in, ('127.0.0.1', 8080)) +- + def test_sock_connect_timeout(self): + # asyncio issue #205: sock_connect() must unregister the socket on + # timeout error +@@ -360,16 +350,16 @@ class BaseSelectorEventLoopTests(test_ut + sock.connect.side_effect = BlockingIOError + + # first call to sock_connect() registers the socket +- fut = self.loop.sock_connect(sock, ('127.0.0.1', 80)) ++ fut = self.loop.create_task( ++ self.loop.sock_connect(sock, ('127.0.0.1', 80))) + self.loop._run_once() + self.assertTrue(sock.connect.called) + self.assertTrue(self.loop.add_writer.called) +- self.assertEqual(len(fut._callbacks), 1) + + # on timeout, the socket must be unregistered + sock.connect.reset_mock() +- fut.set_exception(asyncio.TimeoutError) +- with self.assertRaises(asyncio.TimeoutError): ++ fut.cancel() ++ with self.assertRaises(asyncio.CancelledError): + self.loop.run_until_complete(fut) + self.assertTrue(self.loop.remove_writer.called) + +@@ -1778,5 +1768,88 @@ class SelectorDatagramTransportTests(tes + exc_info=(ConnectionRefusedError, MOCK_ANY, MOCK_ANY)) + + ++class SelectorLoopFunctionalTests(unittest.TestCase): ++ ++ def setUp(self): ++ self.loop = asyncio.new_event_loop() ++ asyncio.set_event_loop(None) ++ ++ def tearDown(self): ++ self.loop.close() ++ ++ @asyncio.coroutine ++ def recv_all(self, sock, nbytes): ++ buf = b'' ++ while len(buf) < nbytes: ++ buf += yield from self.loop.sock_recv(sock, nbytes - len(buf)) ++ return buf ++ ++ def test_sock_connect_sock_write_race(self): ++ TIMEOUT = 3.0 ++ PAYLOAD = b'DATA' * 1024 * 1024 ++ ++ class Server(threading.Thread): ++ def __init__(self, *args, srv_sock, **kwargs): ++ super().__init__(*args, **kwargs) ++ self.srv_sock = srv_sock ++ ++ def run(self): ++ with self.srv_sock: ++ srv_sock.listen(100) ++ ++ sock, addr = self.srv_sock.accept() ++ sock.settimeout(TIMEOUT) ++ ++ with sock: ++ sock.sendall(b'helo') ++ ++ buf = bytearray() ++ while len(buf) < len(PAYLOAD): ++ pack = sock.recv(1024 * 65) ++ if not pack: ++ break ++ buf.extend(pack) ++ ++ @asyncio.coroutine ++ def client(addr): ++ sock = socket.socket() ++ with sock: ++ sock.setblocking(False) ++ ++ started = time.monotonic() ++ while True: ++ if time.monotonic() - started > TIMEOUT: ++ self.fail('unable to connect to the socket') ++ return ++ try: ++ yield from self.loop.sock_connect(sock, addr) ++ except OSError: ++ yield from asyncio.sleep(0.05, loop=self.loop) ++ else: ++ break ++ ++ # Give 'Server' thread a chance to accept and send b'helo' ++ time.sleep(0.1) ++ ++ data = yield from self.recv_all(sock, 4) ++ self.assertEqual(data, b'helo') ++ yield from self.loop.sock_sendall(sock, PAYLOAD) ++ ++ srv_sock = socket.socket() ++ srv_sock.settimeout(TIMEOUT) ++ srv_sock.bind(('127.0.0.1', 0)) ++ srv_addr = srv_sock.getsockname() ++ ++ srv = Server(srv_sock=srv_sock, daemon=True) ++ srv.start() ++ ++ try: ++ self.loop.run_until_complete( ++ asyncio.wait_for(client(srv_addr), loop=self.loop, ++ timeout=TIMEOUT)) ++ finally: ++ srv.join() ++ ++ + if __name__ == '__main__': + unittest.main() diff --git a/debian/patches/bdist-wininst-notfound.diff b/debian/patches/bdist-wininst-notfound.diff new file mode 100644 index 0000000..4705ad1 --- /dev/null +++ b/debian/patches/bdist-wininst-notfound.diff @@ -0,0 +1,19 @@ +# DP: suggest installation of the pythonX.Y-dev package, if bdist_wininst +# DP: cannot find the wininst-* files. + +Index: b/Lib/distutils/command/bdist_wininst.py +=================================================================== +--- a/Lib/distutils/command/bdist_wininst.py ++++ b/Lib/distutils/command/bdist_wininst.py +@@ -354,7 +354,10 @@ class bdist_wininst(Command): + sfix = '' + + filename = os.path.join(directory, "wininst-%.1f%s.exe" % (bv, sfix)) +- f = open(filename, "rb") ++ try: ++ f = open(filename, "rb") ++ except IOError as e: ++ raise DistutilsFileError(str(e) + ', %s not included in the Debian packages.' % filename) + try: + return f.read() + finally: diff --git a/debian/patches/ctypes-arm.diff b/debian/patches/ctypes-arm.diff new file mode 100644 index 0000000..a34f02e --- /dev/null +++ b/debian/patches/ctypes-arm.diff @@ -0,0 +1,34 @@ +Index: b/Lib/ctypes/util.py +=================================================================== +--- a/Lib/ctypes/util.py ++++ b/Lib/ctypes/util.py +@@ -257,16 +257,27 @@ elif os.name == "posix": + + def _findSoname_ldconfig(name): + import struct ++ # XXX this code assumes that we know all unames and that a single ++ # ABI is supported per uname; instead we should find what the ++ # ABI is (e.g. check ABI of current process) or simply ask libc ++ # to load the library for us ++ uname = os.uname() ++ # ARM has a variety of unames, e.g. armv7l ++ if uname.machine.startswith("arm"): ++ machine = "arm" + if struct.calcsize('l') == 4: +- machine = os.uname().machine + '-32' ++ machine = uname.machine + '-32' + else: +- machine = os.uname().machine + '-64' ++ machine = uname.machine + '-64' + mach_map = { + 'x86_64-64': 'libc6,x86-64', + 'ppc64-64': 'libc6,64bit', + 'sparc64-64': 'libc6,64bit', + 's390x-64': 'libc6,64bit', + 'ia64-64': 'libc6,IA-64', ++ # this actually breaks on biarch or multiarch as the first ++ # library wins; uname doesn't tell us which ABI we're using ++ 'arm-32': 'libc6(,hard-float)?', + } + abi_type = mach_map.get(machine, 'libc6') + diff --git a/debian/patches/deb-locations.diff b/debian/patches/deb-locations.diff new file mode 100644 index 0000000..22e4c47 --- /dev/null +++ b/debian/patches/deb-locations.diff @@ -0,0 +1,30 @@ +# DP: adjust locations of directories to debian policy + +Index: b/Lib/pydoc.py +=================================================================== +--- a/Lib/pydoc.py ++++ b/Lib/pydoc.py +@@ -28,6 +28,10 @@ to a file named "<name>.html". + + Module docs for core modules are assumed to be in + ++ /usr/share/doc/pythonX.Y/html/library ++ ++if the pythonX.Y-doc package is installed or in ++ + https://docs.python.org/X.Y/library/ + + This can be overridden by setting the PYTHONDOCS environment variable +Index: b/Misc/python.man +=================================================================== +--- a/Misc/python.man ++++ b/Misc/python.man +@@ -326,7 +326,7 @@ exception). Error messages are written + These are subject to difference depending on local installation + conventions; ${prefix} and ${exec_prefix} are installation-dependent + and should be interpreted as for GNU software; they may be the same. +-The default for both is \fI/usr/local\fP. ++On Debian GNU/{Hurd,Linux} the default for both is \fI/usr\fP. + .IP \fI${exec_prefix}/bin/python\fP + Recommended location of the interpreter. + .PP diff --git a/debian/patches/deb-setup.diff b/debian/patches/deb-setup.diff new file mode 100644 index 0000000..08ce316 --- /dev/null +++ b/debian/patches/deb-setup.diff @@ -0,0 +1,33 @@ +# DP: Don't include /usr/local/include and /usr/local/lib as gcc search paths + +Index: b/setup.py +=================================================================== +--- a/setup.py ++++ b/setup.py +@@ -262,8 +262,10 @@ class PyBuildExt(build_ext): + # unfortunately, distutils doesn't let us provide separate C and C++ + # compilers + if compiler is not None: +- (ccshared,cflags) = sysconfig.get_config_vars('CCSHARED','CFLAGS') +- args['compiler_so'] = compiler + ' ' + ccshared + ' ' + cflags ++ (ccshared, cppflags, cflags) = \ ++ sysconfig.get_config_vars('CCSHARED', 'CPPFLAGS', 'CFLAGS') ++ cppflags = ' '.join([f for f in cppflags.split() if not f.startswith('-I')]) ++ args['compiler_so'] = compiler + ' ' + ccshared + ' ' + cppflags + ' ' + cflags + self.compiler.set_executables(**args) + + build_ext.build_extensions(self) +@@ -487,12 +489,7 @@ class PyBuildExt(build_ext): + return ['m'] + + def detect_modules(self): +- # Ensure that /usr/local is always used, but the local build +- # directories (i.e. '.' and 'Include') must be first. See issue +- # 10520. +- if not cross_compiling: +- add_dir_to_list(self.compiler.library_dirs, '/usr/local/lib') +- add_dir_to_list(self.compiler.include_dirs, '/usr/local/include') ++ # On Debian /usr/local is always used, so we don't include it twice + # only change this for cross builds for 3.3, issues on Mageia + if cross_compiling: + self.add_gcc_paths() diff --git a/debian/patches/disable-sem-check.diff b/debian/patches/disable-sem-check.diff new file mode 100644 index 0000000..6dfdae1 --- /dev/null +++ b/debian/patches/disable-sem-check.diff @@ -0,0 +1,38 @@ +# DP: Assume working semaphores, don't rely on running kernel for the check. + +Index: b/configure.ac +=================================================================== +--- a/configure.ac ++++ b/configure.ac +@@ -4382,8 +4382,13 @@ int main(void) { + AC_MSG_RESULT($ac_cv_posix_semaphores_enabled) + if test $ac_cv_posix_semaphores_enabled = no + then +- AC_DEFINE(POSIX_SEMAPHORES_NOT_ENABLED, 1, +- [Define if POSIX semaphores aren't enabled on your system]) ++ case $ac_sys_system in ++ Linux*) # assume yes, see https://launchpad.net/bugs/630511 ++ ;; ++ *) ++ AC_DEFINE(POSIX_SEMAPHORES_NOT_ENABLED, 1, ++ [Define if POSIX semaphores aren't enabled on your system]) ++ esac + fi + + # Multiprocessing check for broken sem_getvalue +@@ -4418,8 +4423,13 @@ int main(void){ + AC_MSG_RESULT($ac_cv_broken_sem_getvalue) + if test $ac_cv_broken_sem_getvalue = yes + then +- AC_DEFINE(HAVE_BROKEN_SEM_GETVALUE, 1, +- [define to 1 if your sem_getvalue is broken.]) ++ case $ac_sys_system in ++ Linux*) # assume yes, see https://launchpad.net/bugs/630511 ++ ;; ++ *) ++ AC_DEFINE(HAVE_BROKEN_SEM_GETVALUE, 1, ++ [define to 1 if your sem_getvalue is broken.]) ++ esac + fi + + # determine what size digit to use for Python's longs diff --git a/debian/patches/disable-some-tests.diff b/debian/patches/disable-some-tests.diff new file mode 100644 index 0000000..0adb144 --- /dev/null +++ b/debian/patches/disable-some-tests.diff @@ -0,0 +1,14 @@ +# DP: Disable some failing tests we are not interested in + +Index: b/Lib/distutils/tests/test_build_ext.py +=================================================================== +--- a/Lib/distutils/tests/test_build_ext.py ++++ b/Lib/distutils/tests/test_build_ext.py +@@ -99,6 +99,7 @@ class BuildExtTestCase(TempdirManager, + build_ext.USER_BASE = self.old_user_base + super(BuildExtTestCase, self).tearDown() + ++ @unittest.skip('Skipping failing Solaris test') + def test_solaris_enable_shared(self): + dist = Distribution({'name': 'xx'}) + cmd = self.build_ext(dist) diff --git a/debian/patches/distutils-init.diff b/debian/patches/distutils-init.diff new file mode 100644 index 0000000..9441640 --- /dev/null +++ b/debian/patches/distutils-init.diff @@ -0,0 +1,49 @@ +# DP: Use _sysconfigdata.py in distutils to initialize distutils + +Index: b/Lib/distutils/sysconfig.py +=================================================================== +--- a/Lib/distutils/sysconfig.py ++++ b/Lib/distutils/sysconfig.py +@@ -435,38 +435,11 @@ _config_vars = None + + def _init_posix(): + """Initialize the module as appropriate for POSIX systems.""" +- g = {} +- # load the installed Makefile: +- try: +- filename = get_makefile_filename() +- parse_makefile(filename, g) +- except OSError as msg: +- my_msg = "invalid Python installation: unable to open %s" % filename +- if hasattr(msg, "strerror"): +- my_msg = my_msg + " (%s)" % msg.strerror +- +- raise DistutilsPlatformError(my_msg) +- +- # load the installed pyconfig.h: +- try: +- filename = get_config_h_filename() +- with open(filename) as file: +- parse_config_h(file, g) +- except OSError as msg: +- my_msg = "invalid Python installation: unable to open %s" % filename +- if hasattr(msg, "strerror"): +- my_msg = my_msg + " (%s)" % msg.strerror +- +- raise DistutilsPlatformError(my_msg) +- +- # On AIX, there are wrong paths to the linker scripts in the Makefile +- # -- these paths are relative to the Python source, but when installed +- # the scripts are in another directory. +- if python_build: +- g['LDSHARED'] = g['BLDSHARED'] +- ++ # _sysconfigdata is generated at build time, see the sysconfig module ++ from _sysconfigdata import build_time_vars + global _config_vars +- _config_vars = g ++ _config_vars = {} ++ _config_vars.update(build_time_vars) + + + def _init_nt(): diff --git a/debian/patches/distutils-install-layout.diff b/debian/patches/distutils-install-layout.diff new file mode 100644 index 0000000..caa4060 --- /dev/null +++ b/debian/patches/distutils-install-layout.diff @@ -0,0 +1,278 @@ +# DP: distutils: Add an option --install-layout=deb, which +# DP: - installs into $prefix/dist-packages instead of $prefix/site-packages. +# DP: - doesn't encode the python version into the egg name. + +Index: b/Lib/distutils/command/install_egg_info.py +=================================================================== +--- a/Lib/distutils/command/install_egg_info.py ++++ b/Lib/distutils/command/install_egg_info.py +@@ -14,18 +14,38 @@ class install_egg_info(Command): + description = "Install package's PKG-INFO metadata as an .egg-info file" + user_options = [ + ('install-dir=', 'd', "directory to install to"), ++ ('install-layout', None, "custom installation layout"), + ] + + def initialize_options(self): + self.install_dir = None ++ self.install_layout = None ++ self.prefix_option = None + + def finalize_options(self): + self.set_undefined_options('install_lib',('install_dir','install_dir')) +- basename = "%s-%s-py%s.egg-info" % ( +- to_filename(safe_name(self.distribution.get_name())), +- to_filename(safe_version(self.distribution.get_version())), +- sys.version[:3] +- ) ++ self.set_undefined_options('install',('install_layout','install_layout')) ++ self.set_undefined_options('install',('prefix_option','prefix_option')) ++ if self.install_layout: ++ if not self.install_layout.lower() in ['deb', 'unix']: ++ raise DistutilsOptionError( ++ "unknown value for --install-layout") ++ no_pyver = (self.install_layout.lower() == 'deb') ++ elif self.prefix_option: ++ no_pyver = False ++ else: ++ no_pyver = True ++ if no_pyver: ++ basename = "%s-%s.egg-info" % ( ++ to_filename(safe_name(self.distribution.get_name())), ++ to_filename(safe_version(self.distribution.get_version())) ++ ) ++ else: ++ basename = "%s-%s-py%s.egg-info" % ( ++ to_filename(safe_name(self.distribution.get_name())), ++ to_filename(safe_version(self.distribution.get_version())), ++ sys.version[:3] ++ ) + self.target = os.path.join(self.install_dir, basename) + self.outputs = [self.target] + +Index: b/Lib/distutils/command/install.py +=================================================================== +--- a/Lib/distutils/command/install.py ++++ b/Lib/distutils/command/install.py +@@ -35,6 +35,20 @@ INSTALL_SCHEMES = { + 'scripts': '$base/bin', + 'data' : '$base', + }, ++ 'unix_local': { ++ 'purelib': '$base/local/lib/python$py_version_short/dist-packages', ++ 'platlib': '$platbase/local/lib/python$py_version_short/dist-packages', ++ 'headers': '$base/local/include/python$py_version_short/$dist_name', ++ 'scripts': '$base/local/bin', ++ 'data' : '$base/local', ++ }, ++ 'deb_system': { ++ 'purelib': '$base/lib/python3/dist-packages', ++ 'platlib': '$platbase/lib/python3/dist-packages', ++ 'headers': '$base/include/python$py_version_short/$dist_name', ++ 'scripts': '$base/bin', ++ 'data' : '$base', ++ }, + 'unix_home': { + 'purelib': '$base/lib/python', + 'platlib': '$base/lib/python', +@@ -131,6 +145,9 @@ class install(Command): + + ('record=', None, + "filename in which to record list of installed files"), ++ ++ ('install-layout=', None, ++ "installation layout to choose (known values: deb, unix)"), + ] + + boolean_options = ['compile', 'force', 'skip-build'] +@@ -151,6 +168,7 @@ class install(Command): + self.exec_prefix = None + self.home = None + self.user = 0 ++ self.prefix_option = None + + # These select only the installation base; it's up to the user to + # specify the installation scheme (currently, that means supplying +@@ -172,6 +190,9 @@ class install(Command): + self.install_userbase = USER_BASE + self.install_usersite = USER_SITE + ++ # enable custom installation, known values: deb ++ self.install_layout = None ++ + self.compile = None + self.optimize = None + +@@ -411,6 +432,7 @@ class install(Command): + self.install_base = self.install_platbase = self.home + self.select_scheme("unix_home") + else: ++ self.prefix_option = self.prefix + if self.prefix is None: + if self.exec_prefix is not None: + raise DistutilsOptionError( +@@ -425,7 +447,26 @@ class install(Command): + + self.install_base = self.prefix + self.install_platbase = self.exec_prefix +- self.select_scheme("unix_prefix") ++ if self.install_layout: ++ if self.install_layout.lower() in ['deb']: ++ self.select_scheme("deb_system") ++ elif self.install_layout.lower() in ['unix']: ++ self.select_scheme("unix_prefix") ++ else: ++ raise DistutilsOptionError( ++ "unknown value for --install-layout") ++ elif ((self.prefix_option and ++ os.path.normpath(self.prefix) != '/usr/local') ++ or sys.base_prefix != sys.prefix ++ or 'PYTHONUSERBASE' in os.environ ++ or 'VIRTUAL_ENV' in os.environ ++ or 'real_prefix' in sys.__dict__): ++ self.select_scheme("unix_prefix") ++ else: ++ if os.path.normpath(self.prefix) == '/usr/local': ++ self.prefix = self.exec_prefix = '/usr' ++ self.install_base = self.install_platbase = '/usr' ++ self.select_scheme("unix_local") + + def finalize_other(self): + """Finalizes options for non-posix platforms""" +Index: b/Lib/distutils/sysconfig.py +=================================================================== +--- a/Lib/distutils/sysconfig.py ++++ b/Lib/distutils/sysconfig.py +@@ -122,6 +122,7 @@ def get_python_lib(plat_specific=0, stan + If 'prefix' is supplied, use it instead of sys.base_prefix or + sys.base_exec_prefix -- i.e., ignore 'plat_specific'. + """ ++ is_default_prefix = not prefix or os.path.normpath(prefix) in ('/usr', '/usr/local') + if prefix is None: + if standard_lib: + prefix = plat_specific and BASE_EXEC_PREFIX or BASE_PREFIX +@@ -133,6 +134,12 @@ def get_python_lib(plat_specific=0, stan + "lib", "python" + get_python_version()) + if standard_lib: + return libpython ++ elif (is_default_prefix and ++ 'PYTHONUSERBASE' not in os.environ and ++ 'VIRTUAL_ENV' not in os.environ and ++ 'real_prefix' not in sys.__dict__ and ++ sys.prefix == sys.base_prefix): ++ return os.path.join(prefix, "lib", "python3", "dist-packages") + else: + return os.path.join(libpython, "site-packages") + elif os.name == "nt": +Index: b/Lib/site.py +=================================================================== +--- a/Lib/site.py ++++ b/Lib/site.py +@@ -7,12 +7,18 @@ + This will append site-specific paths to the module search path. On + Unix (including Mac OSX), it starts with sys.prefix and + sys.exec_prefix (if different) and appends +-lib/python<version>/site-packages. ++lib/python3/dist-packages. + On other platforms (such as Windows), it tries each of the + prefixes directly, as well as with lib/site-packages appended. The + resulting directories, if they exist, are appended to sys.path, and + also inspected for path configuration files. + ++For Debian and derivatives, this sys.path is augmented with directories ++for packages distributed within the distribution. Local addons go ++into /usr/local/lib/python<version>/dist-packages, Debian addons ++install into /usr/lib/python3/dist-packages. ++/usr/lib/python<version>/site-packages is not used. ++ + If a file named "pyvenv.cfg" exists one directory above sys.executable, + sys.prefix and sys.exec_prefix are set to that directory and + it is also checked for site-packages (sys.base_prefix and +@@ -303,9 +309,20 @@ def getsitepackages(prefixes=None): + seen.add(prefix) + + if os.sep == '/': ++ if 'VIRTUAL_ENV' in os.environ or sys.base_prefix != sys.prefix: ++ sitepackages.append(os.path.join(prefix, "lib", ++ "python" + sys.version[:3], ++ "site-packages")) ++ sitepackages.append(os.path.join(prefix, "local/lib", ++ "python" + sys.version[:3], ++ "dist-packages")) ++ sitepackages.append(os.path.join(prefix, "lib", ++ "python3", ++ "dist-packages")) ++ # this one is deprecated for Debian + sitepackages.append(os.path.join(prefix, "lib", +- "python" + sys.version[:3], +- "site-packages")) ++ "python" + sys.version[:3], ++ "dist-packages")) + else: + sitepackages.append(prefix) + sitepackages.append(os.path.join(prefix, "lib", "site-packages")) +Index: b/Lib/test/test_site.py +=================================================================== +--- a/Lib/test/test_site.py ++++ b/Lib/test/test_site.py +@@ -261,9 +261,10 @@ class HelperFunctionsTests(unittest.Test + self.assertEqual(dirs[1], wanted) + elif os.sep == '/': + # OS X non-framwework builds, Linux, FreeBSD, etc +- self.assertEqual(len(dirs), 1) +- wanted = os.path.join('xoxo', 'lib', 'python' + sys.version[:3], +- 'site-packages') ++ self.assertEqual(len(dirs), 3) ++ wanted = os.path.join('xoxo', 'local', 'lib', ++ 'python' + sys.version[:3], ++ 'dist-packages') + self.assertEqual(dirs[0], wanted) + else: + # other platforms +Index: b/Lib/distutils/tests/test_bdist_dumb.py +=================================================================== +--- a/Lib/distutils/tests/test_bdist_dumb.py ++++ b/Lib/distutils/tests/test_bdist_dumb.py +@@ -85,7 +85,7 @@ class BuildDumbTestCase(support.TempdirM + fp.close() + + contents = sorted(os.path.basename(fn) for fn in contents) +- wanted = ['foo-0.1-py%s.%s.egg-info' % sys.version_info[:2], 'foo.py'] ++ wanted = ['foo-0.1.egg-info', 'foo.py'] + if not sys.dont_write_bytecode: + wanted.append('foo.%s.pyc' % sys.implementation.cache_tag) + self.assertEqual(contents, sorted(wanted)) +Index: b/Lib/distutils/tests/test_install.py +=================================================================== +--- a/Lib/distutils/tests/test_install.py ++++ b/Lib/distutils/tests/test_install.py +@@ -192,7 +192,7 @@ class InstallTestCase(support.TempdirMan + found = [os.path.basename(line) for line in content.splitlines()] + expected = ['hello.py', 'hello.%s.pyc' % sys.implementation.cache_tag, + 'sayhi', +- 'UNKNOWN-0.0.0-py%s.%s.egg-info' % sys.version_info[:2]] ++ 'UNKNOWN-0.0.0.egg-info'] + self.assertEqual(found, expected) + + def test_record_extensions(self): +@@ -222,7 +222,7 @@ class InstallTestCase(support.TempdirMan + + found = [os.path.basename(line) for line in content.splitlines()] + expected = [_make_ext_name('xx'), +- 'UNKNOWN-0.0.0-py%s.%s.egg-info' % sys.version_info[:2]] ++ 'UNKNOWN-0.0.0.egg-info'] + self.assertEqual(found, expected) + + def test_debug_mode(self): +Index: b/Lib/pydoc.py +=================================================================== +--- a/Lib/pydoc.py ++++ b/Lib/pydoc.py +@@ -410,6 +410,7 @@ class Doc: + 'marshal', 'posix', 'signal', 'sys', + '_thread', 'zipimport') or + (file.startswith(basedir) and ++ not file.startswith(os.path.join(basedir, 'dist-packages')) and + not file.startswith(os.path.join(basedir, 'site-packages')))) and + object.__name__ not in ('xml.etree', 'test.pydoc_mod')): + if docloc.startswith(("http://", "https://")): diff --git a/debian/patches/distutils-link.diff b/debian/patches/distutils-link.diff new file mode 100644 index 0000000..b8ad3cf --- /dev/null +++ b/debian/patches/distutils-link.diff @@ -0,0 +1,24 @@ +# DP: Don't add standard library dirs to library_dirs and runtime_library_dirs. + +Index: b/Lib/distutils/unixccompiler.py +=================================================================== +--- a/Lib/distutils/unixccompiler.py ++++ b/Lib/distutils/unixccompiler.py +@@ -155,6 +155,17 @@ class UnixCCompiler(CCompiler): + runtime_library_dirs) + libraries, library_dirs, runtime_library_dirs = fixed_args + ++ # filter out standard library paths, which are not explicitely needed ++ # for linking ++ system_libdirs = ['/lib', '/lib64', '/usr/lib', '/usr/lib64'] ++ multiarch = sysconfig.get_config_var("MULTIARCH") ++ if multiarch: ++ system_libdirs.extend(['/lib/%s' % multiarch, '/usr/lib/%s' % multiarch]) ++ library_dirs = [dir for dir in library_dirs ++ if not dir in system_libdirs] ++ runtime_library_dirs = [dir for dir in runtime_library_dirs ++ if not dir in system_libdirs] ++ + lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs, + libraries) + if not isinstance(output_dir, (str, type(None))): diff --git a/debian/patches/distutils-sysconfig.diff b/debian/patches/distutils-sysconfig.diff new file mode 100644 index 0000000..c12e6b3 --- /dev/null +++ b/debian/patches/distutils-sysconfig.diff @@ -0,0 +1,45 @@ +# DP: Get CONFIGURE_CFLAGS, CONFIGURE_CPPFLAGS, CONFIGURE_LDFLAGS from +# DP: the python build, when CFLAGS, CPPFLAGS, LDSHARED) are not set +# DP: in the environment. + +Index: b/Lib/distutils/sysconfig.py +=================================================================== +--- a/Lib/distutils/sysconfig.py ++++ b/Lib/distutils/sysconfig.py +@@ -177,9 +177,11 @@ def customize_compiler(compiler): + _osx_support.customize_compiler(_config_vars) + _config_vars['CUSTOMIZED_OSX_COMPILER'] = 'True' + +- (cc, cxx, opt, cflags, ccshared, ldshared, shlib_suffix, ar, ar_flags) = \ ++ (cc, cxx, opt, cflags, ccshared, ldshared, shlib_suffix, ar, ar_flags, ++ configure_cppflags, configure_cflags, configure_ldflags) = \ + get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS', +- 'CCSHARED', 'LDSHARED', 'SHLIB_SUFFIX', 'AR', 'ARFLAGS') ++ 'CCSHARED', 'LDSHARED', 'SHLIB_SUFFIX', 'AR', 'ARFLAGS', ++ 'CONFIGURE_CPPFLAGS', 'CONFIGURE_CFLAGS', 'CONFIGURE_LDFLAGS') + + if 'CC' in os.environ: + newcc = os.environ['CC'] +@@ -200,13 +202,22 @@ def customize_compiler(compiler): + cpp = cc + " -E" # not always + if 'LDFLAGS' in os.environ: + ldshared = ldshared + ' ' + os.environ['LDFLAGS'] ++ elif configure_ldflags: ++ ldshared = ldshared + ' ' + configure_ldflags + if 'CFLAGS' in os.environ: + cflags = opt + ' ' + os.environ['CFLAGS'] + ldshared = ldshared + ' ' + os.environ['CFLAGS'] ++ elif configure_cflags: ++ cflags = opt + ' ' + configure_cflags ++ ldshared = ldshared + ' ' + configure_cflags + if 'CPPFLAGS' in os.environ: + cpp = cpp + ' ' + os.environ['CPPFLAGS'] + cflags = cflags + ' ' + os.environ['CPPFLAGS'] + ldshared = ldshared + ' ' + os.environ['CPPFLAGS'] ++ elif configure_cppflags: ++ cpp = cpp + ' ' + configure_cppflags ++ cflags = cflags + ' ' + configure_cppflags ++ ldshared = ldshared + ' ' + configure_cppflags + if 'AR' in os.environ: + ar = os.environ['AR'] + if 'ARFLAGS' in os.environ: diff --git a/debian/patches/doc-faq.dpatch b/debian/patches/doc-faq.dpatch new file mode 100644 index 0000000..cf896e3 --- /dev/null +++ b/debian/patches/doc-faq.dpatch @@ -0,0 +1,52 @@ +#! /bin/sh -e + +# DP: Mention the FAQ on the documentation index page. + +dir= +if [ $# -eq 3 -a "$2" = '-d' ]; then + pdir="-d $3" + dir="$3/" +elif [ $# -ne 1 ]; then + echo >&2 "usage: `basename $0`: -patch|-unpatch [-d <srcdir>]" + exit 1 +fi +case "$1" in + -patch) + patch $pdir -f --no-backup-if-mismatch -p0 < $0 + ;; + -unpatch) + patch $pdir -f --no-backup-if-mismatch -R -p0 < $0 + ;; + *) + echo >&2 "usage: `basename $0`: -patch|-unpatch [-d <srcdir>]" + exit 1 +esac +exit 0 + +--- Doc/html/index.html.in~ 2002-04-01 18:11:27.000000000 +0200 ++++ Doc/html/index.html.in 2003-04-05 13:33:35.000000000 +0200 +@@ -123,6 +123,24 @@ + </ul> + </td> + </tr> ++ <tr> ++ <td valign="baseline" class="left-column"> ++ ++ <ul> ++ <li> <a href="../../python/FAQ.html" class="title" ++ >FAQ (local copy)</a> ++ <br>(python package must be installed) ++ </ul> ++ </td> ++ <td valign="baseline" class="right-column"> ++ ++ <ul> ++ <li> <a href="http://www.python.org/cgi-bin/faqw.py" class="title" ++ >FAQ (online wizard)</a> ++ <br>(maybe more recent) ++ </ul> ++ </td> ++ </tr> + </tbody> + </table> + <p> diff --git a/debian/patches/ensurepip-disabled.diff b/debian/patches/ensurepip-disabled.diff new file mode 100644 index 0000000..e6ba0e4 --- /dev/null +++ b/debian/patches/ensurepip-disabled.diff @@ -0,0 +1,87 @@ +# DP: Disable ensurepip for the system installation, only enable it for virtual environments. + +Index: b/Lib/ensurepip/__init__.py +=================================================================== +--- a/Lib/ensurepip/__init__.py ++++ b/Lib/ensurepip/__init__.py +@@ -8,6 +8,34 @@ import tempfile + + __all__ = ["version", "bootstrap"] + ++def _ensurepip_is_disabled_in_debian_for_system(): ++ # Detect if ensurepip is being executed inside of a python-virtualenv ++ # environment and return early if so. ++ if hasattr(sys, 'real_prefix'): ++ return ++ ++ # Detect if ensurepip is being executed inside of a stdlib venv ++ # environment and return early if so. ++ if sys.prefix != getattr(sys, "base_prefix", sys.prefix): ++ return ++ ++ # If we've gotten here, then we are running inside of the system Python ++ # and we don't want to use ensurepip to install into the system Python ++ # so instead we'll redirect the user to using dpkg and apt-get. ++ print('''\ ++ensurepip is disabled in Debian/Ubuntu for the system python. ++ ++Python modules for the system python are usually handled by dpkg and apt-get. ++ ++ apt-get install python-<module name> ++ ++Install the python-pip package to use pip itself. Using pip together ++with the system python might have unexpected results for any system installed ++module, so use it on your own risk, or make sure to only use it in virtual ++environments. ++''') ++ sys.exit(1) ++ + + # pip currently requires ssl support, so we try to provide a nicer + # error message when that is missing (http://bugs.python.org/issue19744) +@@ -69,6 +97,11 @@ def bootstrap(*, root=None, upgrade=Fals + + Note that calling this function will alter both sys.path and os.environ. + """ ++ ++ # Ensure that we are only running this inside of a virtual environment ++ # of some kind. ++ _ensurepip_is_disabled_in_debian_for_system() ++ + if altinstall and default_pip: + raise ValueError("Cannot use altinstall and default_pip together") + +Index: b/Lib/venv/__init__.py +=================================================================== +--- a/Lib/venv/__init__.py ++++ b/Lib/venv/__init__.py +@@ -261,7 +261,28 @@ class EnvBuilder: + # intended for the global Python environment + cmd = [context.env_exe, '-Im', 'ensurepip', '--upgrade', + '--default-pip'] +- subprocess.check_output(cmd, stderr=subprocess.STDOUT) ++ # Debian 2015-09-18 barry@debian.org: <python>-venv is a separate ++ # binary package which might not be installed. In that case, the ++ # following command will produce an unhelpful error. Let's make it ++ # more user friendly. ++ try: ++ subprocess.check_output( ++ cmd, stderr=subprocess.STDOUT, ++ universal_newlines=True) ++ except subprocess.CalledProcessError: ++ print("""\ ++The virtual environment was not created successfully because ensurepip is not ++available. On Debian/Ubuntu systems, you need to install the python3-venv ++package using the following command. ++ ++ apt-get install python3-venv ++ ++You may need to use sudo with that command. After installing the python3-venv ++package, recreate your virtual environment. ++ ++Failing command: {} ++""".format(cmd)) ++ sys.exit(1) + + def setup_scripts(self, context): + """ diff --git a/debian/patches/ensurepip-wheels.diff b/debian/patches/ensurepip-wheels.diff new file mode 100644 index 0000000..b60f956 --- /dev/null +++ b/debian/patches/ensurepip-wheels.diff @@ -0,0 +1,131 @@ +Index: b/Lib/ensurepip/__init__.py +=================================================================== +--- a/Lib/ensurepip/__init__.py ++++ b/Lib/ensurepip/__init__.py +@@ -1,3 +1,4 @@ ++import glob + import os + import os.path + import pkgutil +@@ -8,13 +9,9 @@ import tempfile + __all__ = ["version", "bootstrap"] + + +-_SETUPTOOLS_VERSION = "28.8.0" +- +-_PIP_VERSION = "9.0.1" +- + # pip currently requires ssl support, so we try to provide a nicer + # error message when that is missing (http://bugs.python.org/issue19744) +-_MISSING_SSL_MESSAGE = ("pip {} requires SSL/TLS".format(_PIP_VERSION)) ++_MISSING_SSL_MESSAGE = ("pip requires SSL/TLS") + try: + import ssl + except ImportError: +@@ -26,8 +23,9 @@ else: + pass + + _PROJECTS = [ +- ("setuptools", _SETUPTOOLS_VERSION), +- ("pip", _PIP_VERSION), ++ "setuptools", ++ "pip", ++ "pkg_resources", + ] + + +@@ -45,7 +43,10 @@ def version(): + """ + Returns a string specifying the bundled version of pip. + """ +- return _PIP_VERSION ++ wheel_names = glob.glob('/usr/share/python-wheels/pip-*.whl') ++ assert len(wheel_names) == 1, wheel_names ++ return os.path.basename(wheel_names[0]).split('-')[1] ++ + + def _disable_pip_configuration_settings(): + # We deliberately ignore all pip environment variables +@@ -87,20 +88,44 @@ def bootstrap(*, root=None, upgrade=Fals + # omit pip and easy_install + os.environ["ENSUREPIP_OPTIONS"] = "install" + ++ # Debian: The bundled wheels are useless to us because we must use ones ++ # crafted from source code in the archive. As we build the virtual ++ # environment, copy the wheels from the system location into the virtual ++ # environment, and place those wheels on sys.path. ++ def copy_wheels(wheels, destdir, paths): ++ for project in wheels: ++ wheel_names = glob.glob( ++ '/usr/share/python-wheels/{}-*.whl'.format(project)) ++ if len(wheel_names) == 0: ++ raise RuntimeError('missing dependency wheel %s' % project) ++ assert len(wheel_names) == 1, wheel_names ++ wheel_name = os.path.basename(wheel_names[0]) ++ path = os.path.join('/usr/share/python-wheels', wheel_name) ++ with open(path, 'rb') as fp: ++ whl = fp.read() ++ dest = os.path.join(destdir, wheel_name) ++ with open(dest, 'wb') as fp: ++ fp.write(whl) ++ paths.append(dest) ++ + with tempfile.TemporaryDirectory() as tmpdir: ++ # This directory is a "well known directory" which Debian has patched ++ # pip to look in when attempting to locate wheels to use to satisfy ++ # the dependencies that pip normally bundles but Debian has debundled. ++ # This is critically important and if this directory changes then both ++ # python-pip and python-virtualenv needs updated to match. ++ venv_wheel_dir = os.path.join(sys.prefix, 'share', 'python-wheels') ++ os.makedirs(venv_wheel_dir, exist_ok=True) ++ dependencies = [ ++ os.path.basename(whl).split('-')[0] ++ for whl in glob.glob('/usr/share/python-wheels/*.whl') ++ ] ++ copy_wheels(dependencies, venv_wheel_dir, sys.path) ++ + # Put our bundled wheels into a temporary directory and construct the + # additional paths that need added to sys.path + additional_paths = [] +- for project, version in _PROJECTS: +- wheel_name = "{}-{}-py2.py3-none-any.whl".format(project, version) +- whl = pkgutil.get_data( +- "ensurepip", +- "_bundled/{}".format(wheel_name), +- ) +- with open(os.path.join(tmpdir, wheel_name), "wb") as fp: +- fp.write(whl) +- +- additional_paths.append(os.path.join(tmpdir, wheel_name)) ++ copy_wheels(_PROJECTS, tmpdir, additional_paths) + + # Construct the arguments to be passed to the pip command + args = ["install", "--no-index", "--find-links", tmpdir] +@@ -113,7 +138,7 @@ def bootstrap(*, root=None, upgrade=Fals + if verbosity: + args += ["-" + "v" * verbosity] + +- _run_pip(args + [p[0] for p in _PROJECTS], additional_paths) ++ _run_pip(args + _PROJECTS, additional_paths) + + def _uninstall_helper(*, verbosity=0): + """Helper to support a clean default uninstall process on Windows +@@ -127,7 +152,8 @@ def _uninstall_helper(*, verbosity=0): + return + + # If the pip version doesn't match the bundled one, leave it alone +- if pip.__version__ != _PIP_VERSION: ++ # Disabled for Debian, always using the version from the python3-pip package. ++ if False and pip.__version__ != _PIP_VERSION: + msg = ("ensurepip will only uninstall a matching version " + "({!r} installed, {!r} bundled)") + print(msg.format(pip.__version__, _PIP_VERSION), file=sys.stderr) +@@ -141,7 +167,7 @@ def _uninstall_helper(*, verbosity=0): + if verbosity: + args += ["-" + "v" * verbosity] + +- _run_pip(args + [p[0] for p in reversed(_PROJECTS)]) ++ _run_pip(args + reversed(_PROJECTS)) + + + def _main(argv=None): diff --git a/debian/patches/ext-no-libpython-link.diff b/debian/patches/ext-no-libpython-link.diff new file mode 100644 index 0000000..627d9d1 --- /dev/null +++ b/debian/patches/ext-no-libpython-link.diff @@ -0,0 +1,24 @@ +# DP: Don't link extensions with the shared libpython library. + +Index: b/Lib/distutils/command/build_ext.py +=================================================================== +--- a/Lib/distutils/command/build_ext.py ++++ b/Lib/distutils/command/build_ext.py +@@ -230,7 +230,7 @@ class build_ext(Command): + # For building extensions with a shared Python library, + # Python's library directory must be appended to library_dirs + # See Issues: #1600860, #4366 +- if (sysconfig.get_config_var('Py_ENABLE_SHARED')): ++ if False and (sysconfig.get_config_var('Py_ENABLE_SHARED')): + if not sysconfig.python_build: + # building third party extensions + self.library_dirs.append(sysconfig.get_config_var('LIBDIR')) +@@ -746,7 +746,7 @@ class build_ext(Command): + return ext.libraries + else: + from distutils import sysconfig +- if sysconfig.get_config_var('Py_ENABLE_SHARED'): ++ if False and sysconfig.get_config_var('Py_ENABLE_SHARED'): + pythonlib = 'python{}.{}{}'.format( + sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff, + sysconfig.get_config_var('ABIFLAGS')) diff --git a/debian/patches/gdbm-import.diff b/debian/patches/gdbm-import.diff new file mode 100644 index 0000000..2be2934 --- /dev/null +++ b/debian/patches/gdbm-import.diff @@ -0,0 +1,12 @@ +# DP: suggest installation of python3-gdbm package on failing _gdbm import + +--- a/Lib/dbm/gnu.py ++++ b/Lib/dbm/gnu.py +@@ -1,3 +1,6 @@ + """Provide the _gdbm module as a dbm submodule.""" + +-from _gdbm import * ++try: ++ from _gdbm import * ++except ImportError as msg: ++ raise ImportError(str(msg) + ', please install the python3-gdbm package') diff --git a/debian/patches/git-updates.diff b/debian/patches/git-updates.diff new file mode 100644 index 0000000..e4892ca --- /dev/null +++ b/debian/patches/git-updates.diff @@ -0,0 +1,4 @@ +# DP: updates from the 3.5 branch (until 2017-xx-yy, ). + +# git diff 51ba5b7d0c194b0c2b1e5d647e70e3538b8dde3e ee9de30aa0dbcfd848e4200944674a084d963588 | filterdiff -x ?/.hgignore -x ?/.hgeol -x ?/.hgtags -x ?/.hgtouch -x ?/.gitignore -x ?/.gitattributes -x '?/.github/*' -x '?/.git*' -x ?/.codecov.yml -x ?/.travis.yml -x ?/configure --remove-timestamps + diff --git a/debian/patches/hurd-disable-nonworking-constants.diff b/debian/patches/hurd-disable-nonworking-constants.diff new file mode 100644 index 0000000..d205bb8 --- /dev/null +++ b/debian/patches/hurd-disable-nonworking-constants.diff @@ -0,0 +1,38 @@ +# DP: Comment out constant exposed on the API which are not implemented on +# DP: GNU/Hurd. They would not work at runtime anyway. + +Index: b/Modules/socketmodule.c +=================================================================== +--- a/Modules/socketmodule.c ++++ b/Modules/socketmodule.c +@@ -6485,9 +6485,11 @@ PyInit__socket(void) + #ifdef SO_OOBINLINE + PyModule_AddIntMacro(m, SO_OOBINLINE); + #endif ++#ifndef __GNU__ + #ifdef SO_REUSEPORT + PyModule_AddIntMacro(m, SO_REUSEPORT); + #endif ++#endif + #ifdef SO_SNDBUF + PyModule_AddIntMacro(m, SO_SNDBUF); + #endif +Index: b/Modules/posixmodule.c +=================================================================== +--- a/Modules/posixmodule.c ++++ b/Modules/posixmodule.c +@@ -12559,12 +12559,14 @@ all_ins(PyObject *m) + #ifdef O_LARGEFILE + if (PyModule_AddIntMacro(m, O_LARGEFILE)) return -1; + #endif ++#ifndef __GNU__ + #ifdef O_SHLOCK + if (PyModule_AddIntMacro(m, O_SHLOCK)) return -1; + #endif + #ifdef O_EXLOCK + if (PyModule_AddIntMacro(m, O_EXLOCK)) return -1; + #endif ++#endif + #ifdef O_EXEC + if (PyModule_AddIntMacro(m, O_EXEC)) return -1; + #endif diff --git a/debian/patches/issue21264.diff b/debian/patches/issue21264.diff new file mode 100644 index 0000000..6c6287b --- /dev/null +++ b/debian/patches/issue21264.diff @@ -0,0 +1,31 @@ +# DP: Fix issue #21264, test_compileall test failures in the installed location + +Index: b/Lib/test/test_compileall.py +=================================================================== +--- a/Lib/test/test_compileall.py ++++ b/Lib/test/test_compileall.py +@@ -249,20 +249,20 @@ + os.utime(pycpath, (time.time()-60,)*2) + mtime = os.stat(pycpath).st_mtime + # Without force, no recompilation +- self.assertRunOK(PYTHONPATH=self.directory) ++ self.assertRunOK(self.directory) + mtime2 = os.stat(pycpath).st_mtime + self.assertEqual(mtime, mtime2) + # Now force it. +- self.assertRunOK('-f', PYTHONPATH=self.directory) ++ self.assertRunOK('-f', self.directory) + mtime2 = os.stat(pycpath).st_mtime + self.assertNotEqual(mtime, mtime2) + + def test_no_args_respects_quiet_flag(self): + self._skip_if_sys_path_not_writable() + script_helper.make_script(self.directory, 'baz', '') +- noisy = self.assertRunOK(PYTHONPATH=self.directory) ++ noisy = self.assertRunOK(self.directory) + self.assertIn(b'Listing ', noisy) +- quiet = self.assertRunOK('-q', PYTHONPATH=self.directory) ++ quiet = self.assertRunOK('-q', self.directory) + self.assertNotIn(b'Listing ', quiet) + + # Ensure that the default behavior of compileall's CLI is to create diff --git a/debian/patches/langpack-gettext.diff b/debian/patches/langpack-gettext.diff new file mode 100644 index 0000000..56deb77 --- /dev/null +++ b/debian/patches/langpack-gettext.diff @@ -0,0 +1,36 @@ +# DP: Description: support alternative gettext tree in +# DP: /usr/share/locale-langpack; if a file is present in both trees, +# DP: prefer the newer one +# DP: Upstream status: Ubuntu-Specific + +Index: b/Lib/gettext.py +=================================================================== +--- a/Lib/gettext.py ++++ b/Lib/gettext.py +@@ -491,11 +491,26 @@ def find(domain, localedir=None, languag + if lang == 'C': + break + mofile = os.path.join(localedir, lang, 'LC_MESSAGES', '%s.mo' % domain) ++ mofile_lp = os.path.join("/usr/share/locale-langpack", lang, ++ 'LC_MESSAGES', '%s.mo' % domain) ++ ++ # first look into the standard locale dir, then into the ++ # langpack locale dir ++ ++ # standard mo file + if os.path.exists(mofile): + if all: + result.append(mofile) + else: + return mofile ++ ++ # langpack mofile -> use it ++ if os.path.exists(mofile_lp): ++ if all: ++ result.append(mofile_lp) ++ else: ++ return mofile_lp ++ + return result + + diff --git a/debian/patches/lib-argparse.diff b/debian/patches/lib-argparse.diff new file mode 100644 index 0000000..c9d604a --- /dev/null +++ b/debian/patches/lib-argparse.diff @@ -0,0 +1,22 @@ +# DP: argparse.py: Make the gettext import conditional + +--- a/Lib/argparse.py ++++ b/Lib/argparse.py +@@ -90,7 +90,16 @@ + import sys as _sys + import textwrap as _textwrap + +-from gettext import gettext as _, ngettext ++try: ++ from gettext import gettext as _, ngettext ++except ImportError: ++ def _(message): ++ return message ++ def ngettext(singular,plural,n): ++ if n == 1: ++ return singular ++ else: ++ return plural + + + SUPPRESS = '==SUPPRESS==' diff --git a/debian/patches/lib2to3-no-pgen-caching.diff b/debian/patches/lib2to3-no-pgen-caching.diff new file mode 100644 index 0000000..e04685a --- /dev/null +++ b/debian/patches/lib2to3-no-pgen-caching.diff @@ -0,0 +1,76 @@ +Index: b/Lib/lib2to3/tests/test_parser.py +=================================================================== +--- a/Lib/lib2to3/tests/test_parser.py ++++ b/Lib/lib2to3/tests/test_parser.py +@@ -36,71 +36,6 @@ + self.assertEqual(t.children[1].children[0].type, syms.print_stmt) + + +-class TestPgen2Caching(support.TestCase): +- def test_load_grammar_from_txt_file(self): +- pgen2_driver.load_grammar(support.grammar_path, save=False, force=True) +- +- def test_load_grammar_from_pickle(self): +- # Make a copy of the grammar file in a temp directory we are +- # guaranteed to be able to write to. +- tmpdir = tempfile.mkdtemp() +- try: +- grammar_copy = os.path.join( +- tmpdir, os.path.basename(support.grammar_path)) +- shutil.copy(support.grammar_path, grammar_copy) +- pickle_name = pgen2_driver._generate_pickle_name(grammar_copy) +- +- pgen2_driver.load_grammar(grammar_copy, save=True, force=True) +- self.assertTrue(os.path.exists(pickle_name)) +- +- os.unlink(grammar_copy) # Only the pickle remains... +- pgen2_driver.load_grammar(grammar_copy, save=False, force=False) +- finally: +- shutil.rmtree(tmpdir) +- +- @unittest.skipIf(sys.executable is None, 'sys.executable required') +- def test_load_grammar_from_subprocess(self): +- tmpdir = tempfile.mkdtemp() +- tmpsubdir = os.path.join(tmpdir, 'subdir') +- try: +- os.mkdir(tmpsubdir) +- grammar_base = os.path.basename(support.grammar_path) +- grammar_copy = os.path.join(tmpdir, grammar_base) +- grammar_sub_copy = os.path.join(tmpsubdir, grammar_base) +- shutil.copy(support.grammar_path, grammar_copy) +- shutil.copy(support.grammar_path, grammar_sub_copy) +- pickle_name = pgen2_driver._generate_pickle_name(grammar_copy) +- pickle_sub_name = pgen2_driver._generate_pickle_name( +- grammar_sub_copy) +- self.assertNotEqual(pickle_name, pickle_sub_name) +- +- # Generate a pickle file from this process. +- pgen2_driver.load_grammar(grammar_copy, save=True, force=True) +- self.assertTrue(os.path.exists(pickle_name)) +- +- # Generate a new pickle file in a subprocess with a most likely +- # different hash randomization seed. +- sub_env = dict(os.environ) +- sub_env['PYTHONHASHSEED'] = 'random' +- subprocess.check_call( +- [sys.executable, '-c', """ +-from lib2to3.pgen2 import driver as pgen2_driver +-pgen2_driver.load_grammar(%r, save=True, force=True) +- """ % (grammar_sub_copy,)], +- env=sub_env) +- self.assertTrue(os.path.exists(pickle_sub_name)) +- +- with open(pickle_name, 'rb') as pickle_f_1, \ +- open(pickle_sub_name, 'rb') as pickle_f_2: +- self.assertEqual( +- pickle_f_1.read(), pickle_f_2.read(), +- msg='Grammar caches generated using different hash seeds' +- ' were not identical.') +- finally: +- shutil.rmtree(tmpdir) +- +- +- + class GrammarTest(support.TestCase): + def validate(self, code): + support.parse_string(code) diff --git a/debian/patches/lib2to3-no-pickled-grammar.diff b/debian/patches/lib2to3-no-pickled-grammar.diff new file mode 100644 index 0000000..cc83155 --- /dev/null +++ b/debian/patches/lib2to3-no-pickled-grammar.diff @@ -0,0 +1,16 @@ +Index: b/Lib/lib2to3/pgen2/driver.py +=================================================================== +--- a/Lib/lib2to3/pgen2/driver.py ++++ b/Lib/lib2to3/pgen2/driver.py +@@ -122,7 +122,10 @@ def load_grammar(gt="Grammar.txt", gp=No + if force or not _newer(gp, gt): + logger.info("Generating grammar tables from %s", gt) + g = pgen.generate_grammar(gt) +- if save: ++ # the pickle files mismatch, when built on different architectures. ++ # don't save these for now. An alternative solution might be to ++ # include the multiarch triplet into the file name ++ if False: + logger.info("Writing grammar tables to %s", gp) + try: + g.dump(gp) diff --git a/debian/patches/link-opt.diff b/debian/patches/link-opt.diff new file mode 100644 index 0000000..c61ae2e --- /dev/null +++ b/debian/patches/link-opt.diff @@ -0,0 +1,26 @@ +# DP: Call the linker with -O1 -Bsymbolic-functions + +Index: b/configure.ac +=================================================================== +--- a/configure.ac ++++ b/configure.ac +@@ -2449,8 +2449,8 @@ then + fi + ;; + Linux*|GNU*|QNX*) +- LDSHARED='$(CC) -shared' +- LDCXXSHARED='$(CXX) -shared';; ++ LDSHARED='$(CC) -shared -Wl,-O1 -Wl,-Bsymbolic-functions' ++ LDCXXSHARED='$(CXX) -shared -Wl,-O1 -Wl,-Bsymbolic-functions';; + BSD/OS*/4*) + LDSHARED="gcc -shared" + LDCXXSHARED="g++ -shared";; +@@ -2548,7 +2548,7 @@ then + LINKFORSHARED="-Wl,-E -Wl,+s";; + # LINKFORSHARED="-Wl,-E -Wl,+s -Wl,+b\$(BINLIBDEST)/lib-dynload";; + BSD/OS/4*) LINKFORSHARED="-Xlinker -export-dynamic";; +- Linux*|GNU*) LINKFORSHARED="-Xlinker -export-dynamic";; ++ Linux*|GNU*) LINKFORSHARED="-Xlinker -export-dynamic -Wl,-O1 -Wl,-Bsymbolic-functions";; + # -u libsys_s pulls in all symbols in libsys + Darwin/*) + LINKFORSHARED="$extra_undefs -framework CoreFoundation" diff --git a/debian/patches/link-timemodule.diff b/debian/patches/link-timemodule.diff new file mode 100644 index 0000000..45e4153 --- /dev/null +++ b/debian/patches/link-timemodule.diff @@ -0,0 +1,13 @@ +Index: b/Modules/Setup.dist +=================================================================== +--- a/Modules/Setup.dist ++++ b/Modules/Setup.dist +@@ -118,7 +118,7 @@ _collections _collectionsmodule.c # Cont + itertools itertoolsmodule.c # Functions creating iterators for efficient looping + atexit atexitmodule.c # Register functions to be run at interpreter-shutdown + _stat _stat.c # stat.h interface +-time timemodule.c # -lm # time operations and variables ++time timemodule.c -lrt # -lm # time operations and variables + + # access to ISO C locale support + _locale _localemodule.c # -lintl diff --git a/debian/patches/local-blurb.diff b/debian/patches/local-blurb.diff new file mode 100644 index 0000000..a2e812c --- /dev/null +++ b/debian/patches/local-blurb.diff @@ -0,0 +1,13 @@ +Index: b/Doc/Makefile +=================================================================== +--- a/Doc/Makefile ++++ b/Doc/Makefile +@@ -6,7 +6,7 @@ + # You can set these variables from the command line. + PYTHON = python3 + SPHINXBUILD = sphinx-build +-BLURB = $(PYTHON) -m blurb ++BLURB = $(BLURB_ENV) $(PYTHON) -m blurb + PAPER = + SOURCES = + DISTVERSION = $(shell $(PYTHON) tools/extensions/patchlevel.py) diff --git a/debian/patches/locale-module.diff b/debian/patches/locale-module.diff new file mode 100644 index 0000000..1245cc4 --- /dev/null +++ b/debian/patches/locale-module.diff @@ -0,0 +1,19 @@ +# DP: * Lib/locale.py: +# DP: - Don't map 'utf8', 'utf-8' to 'utf', which is not a known encoding +# DP: for glibc. + +Index: b/Lib/locale.py +=================================================================== +--- a/Lib/locale.py ++++ b/Lib/locale.py +@@ -1335,8 +1335,8 @@ locale_alias = { + 'ug_cn': 'ug_CN.UTF-8', + 'uk': 'uk_UA.KOI8-U', + 'uk_ua': 'uk_UA.KOI8-U', +- 'univ': 'en_US.utf', +- 'universal': 'en_US.utf', ++ 'univ': 'en_US.UTF-8', ++ 'universal': 'en_US.UTF-8', + 'universal.utf8@ucs4': 'en_US.UTF-8', + 'unm_us': 'unm_US.UTF-8', + 'ur': 'ur_PK.CP1256', diff --git a/debian/patches/lto-link-flags.diff b/debian/patches/lto-link-flags.diff new file mode 100644 index 0000000..046e8d2 --- /dev/null +++ b/debian/patches/lto-link-flags.diff @@ -0,0 +1,22 @@ +Index: b/Makefile.pre.in +=================================================================== +--- a/Makefile.pre.in ++++ b/Makefile.pre.in +@@ -141,7 +141,7 @@ CONFINCLUDEPY= $(CONFINCLUDEDIR)/python$ + SHLIB_SUFFIX= @SHLIB_SUFFIX@ + EXT_SUFFIX= @EXT_SUFFIX@ + LDSHARED= @LDSHARED@ $(PY_LDFLAGS) +-BLDSHARED= @BLDSHARED@ $(PY_LDFLAGS) ++BLDSHARED= @BLDSHARED@ $(PY_LDFLAGS) $(PY_CFLAGS) + LDCXXSHARED= @LDCXXSHARED@ + DESTSHARED= $(BINLIBDEST)/lib-dynload + +@@ -548,7 +548,7 @@ clinic: $(BUILDPYTHON) + + # Build the interpreter + $(BUILDPYTHON): Programs/python.o $(LIBRARY) $(LDLIBRARY) $(PY3LIBRARY) +- $(LINKCC) $(PY_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/python.o $(BLDLIBRARY) $(LIBS) $(MODLIBS) $(SYSLIBS) $(LDLAST) ++ $(LINKCC) $(PY_LDFLAGS) $(PY_CFLAGS) $(LINKFORSHARED) -o $@ Programs/python.o $(BLDLIBRARY) $(LIBS) $(MODLIBS) $(SYSLIBS) $(LDLAST) + + platform: $(BUILDPYTHON) pybuilddir.txt + $(RUNSHARED) $(PYTHON_FOR_BUILD) -c 'import sys ; from sysconfig import get_platform ; print(get_platform()+"-"+sys.version[0:3])' >platform diff --git a/debian/patches/makesetup-bashism.diff b/debian/patches/makesetup-bashism.diff new file mode 100644 index 0000000..96fd289 --- /dev/null +++ b/debian/patches/makesetup-bashism.diff @@ -0,0 +1,15 @@ +# DP: Fix bashism in makesetup shell script + +Index: b/Modules/makesetup +=================================================================== +--- a/Modules/makesetup ++++ b/Modules/makesetup +@@ -279,7 +279,7 @@ sed -e 's/[ ]*#.*//' -e '/^[ ]*$/d' | + -) ;; + *) sedf="@sed.in.$$" + trap 'rm -f $sedf' 0 1 2 3 +- echo "1i\\" >$sedf ++ printf "1i\\" >$sedf + str="# Generated automatically from $makepre by makesetup." + echo "$str" >>$sedf + echo "s%_MODNAMES_%$NAMES%" >>$sedf diff --git a/debian/patches/mangle-fstack-protector.diff b/debian/patches/mangle-fstack-protector.diff new file mode 100644 index 0000000..19b27f7 --- /dev/null +++ b/debian/patches/mangle-fstack-protector.diff @@ -0,0 +1,26 @@ +# DP: When using GCC versions older than 4.9, automagically mangle +# DP: -fstack-protector-strong to -fstack-protector + +Index: b/Lib/distutils/sysconfig.py +=================================================================== +--- a/Lib/distutils/sysconfig.py ++++ b/Lib/distutils/sysconfig.py +@@ -13,6 +13,7 @@ import _imp + import os + import re + import sys ++import fnmatch + + from .errors import DistutilsPlatformError + +@@ -197,6 +198,10 @@ def customize_compiler(compiler): + cc = newcc + if 'CXX' in os.environ: + cxx = os.environ['CXX'] ++ if fnmatch.filter([cc, cxx], '*-4.[0-8]'): ++ configure_cflags = configure_cflags.replace('-fstack-protector-strong', '-fstack-protector') ++ ldshared = ldshared.replace('-fstack-protector-strong', '-fstack-protector') ++ cflags = cflags.replace('-fstack-protector-strong', '-fstack-protector') + if 'LDSHARED' in os.environ: + ldshared = os.environ['LDSHARED'] + if 'CPP' in os.environ: diff --git a/debian/patches/mpdecimal-version.diff b/debian/patches/mpdecimal-version.diff new file mode 100644 index 0000000..e31c90b --- /dev/null +++ b/debian/patches/mpdecimal-version.diff @@ -0,0 +1,17 @@ +# DP: Relax the mpdecimal version check + +Index: b/Modules/_decimal/_decimal.c +=================================================================== +--- a/Modules/_decimal/_decimal.c ++++ b/Modules/_decimal/_decimal.c +@@ -39,10 +39,6 @@ + #include "memory.h" + + +-#if !defined(MPD_VERSION_HEX) || MPD_VERSION_HEX < 0x02040100 +- #error "libmpdec version >= 2.4.1 required" +-#endif +- + + /* + * Type sizes with assertions in mpdecimal.h and pyport.h: diff --git a/debian/patches/multiarch-extname.diff b/debian/patches/multiarch-extname.diff new file mode 100644 index 0000000..61d1384 --- /dev/null +++ b/debian/patches/multiarch-extname.diff @@ -0,0 +1,92 @@ +# DP: Make sure to rename extensions to a tag including the MULTIARCH name + +this patch can be dropped for python3.5 final, if the upstream chage is kept. + +Index: b/Lib/distutils/dir_util.py +=================================================================== +--- a/Lib/distutils/dir_util.py ++++ b/Lib/distutils/dir_util.py +@@ -96,6 +96,9 @@ def create_tree(base_dir, files, mode=0o + for dir in sorted(need_dir): + mkpath(dir, mode, verbose=verbose, dry_run=dry_run) + ++import sysconfig ++_multiarch = None ++ + def copy_tree(src, dst, preserve_mode=1, preserve_times=1, + preserve_symlinks=0, update=0, verbose=1, dry_run=0): + """Copy an entire directory tree 'src' to a new location 'dst'. +@@ -131,6 +134,13 @@ def copy_tree(src, dst, preserve_mode=1, + raise DistutilsFileError( + "error listing files in '%s': %s" % (src, e.strerror)) + ++ ext_suffix = sysconfig.get_config_var ('EXT_SUFFIX') ++ _multiarch = sysconfig.get_config_var ('MULTIARCH') ++ if ext_suffix.endswith(_multiarch + ext_suffix[-3:]): ++ new_suffix = None ++ else: ++ new_suffix = "%s-%s%s" % (ext_suffix[:-3], _multiarch, ext_suffix[-3:]) ++ + if not dry_run: + mkpath(dst, verbose=verbose) + +@@ -139,6 +149,9 @@ def copy_tree(src, dst, preserve_mode=1, + for n in names: + src_name = os.path.join(src, n) + dst_name = os.path.join(dst, n) ++ if new_suffix and _multiarch and n.endswith(ext_suffix) and not n.endswith(new_suffix): ++ dst_name = os.path.join(dst, n.replace(ext_suffix, new_suffix)) ++ log.info("renaming extension %s -> %s", n, n.replace(ext_suffix, new_suffix)) + + if n.startswith('.nfs'): + # skip NFS rename files +Index: b/Lib/distutils/command/install_lib.py +=================================================================== +--- a/Lib/distutils/command/install_lib.py ++++ b/Lib/distutils/command/install_lib.py +@@ -56,6 +56,7 @@ class install_lib(Command): + self.compile = None + self.optimize = None + self.skip_build = None ++ self.multiarch = None # if we should rename the extensions + + def finalize_options(self): + # Get all the information we need to install pure Python modules +@@ -68,6 +69,7 @@ class install_lib(Command): + ('compile', 'compile'), + ('optimize', 'optimize'), + ('skip_build', 'skip_build'), ++ ('multiarch', 'multiarch'), + ) + + if self.compile is None: +@@ -108,6 +110,8 @@ class install_lib(Command): + + def install(self): + if os.path.isdir(self.build_dir): ++ import distutils.dir_util ++ distutils.dir_util._multiarch = self.multiarch + outfiles = self.copy_tree(self.build_dir, self.install_dir) + else: + self.warn("'%s' does not exist -- no Python modules to install" % +Index: b/Lib/distutils/command/install.py +=================================================================== +--- a/Lib/distutils/command/install.py ++++ b/Lib/distutils/command/install.py +@@ -192,6 +192,7 @@ class install(Command): + + # enable custom installation, known values: deb + self.install_layout = None ++ self.multiarch = None + + self.compile = None + self.optimize = None +@@ -449,6 +450,8 @@ class install(Command): + self.install_platbase = self.exec_prefix + if self.install_layout: + if self.install_layout.lower() in ['deb']: ++ import sysconfig ++ self.multiarch = sysconfig.get_config_var('MULTIARCH') + self.select_scheme("deb_system") + elif self.install_layout.lower() in ['unix']: + self.select_scheme("unix_prefix") diff --git a/debian/patches/multiarch.diff b/debian/patches/multiarch.diff new file mode 100644 index 0000000..d37f044 --- /dev/null +++ b/debian/patches/multiarch.diff @@ -0,0 +1,136 @@ +Index: b/Lib/sysconfig.py +=================================================================== +--- a/Lib/sysconfig.py ++++ b/Lib/sysconfig.py +@@ -337,6 +337,8 @@ def get_makefile_filename(): + config_dir_name = 'config-%s%s' % (_PY_VERSION_SHORT, sys.abiflags) + else: + config_dir_name = 'config' ++ if hasattr(sys.implementation, '_multiarch'): ++ config_dir_name += '-%s' % sys.implementation._multiarch + return os.path.join(get_path('stdlib'), config_dir_name, 'Makefile') + + def _generate_posix_vars(): +@@ -543,6 +545,12 @@ def get_config_vars(*args): + # the init-function. + _CONFIG_VARS['userbase'] = _getuserbase() + ++ multiarch = get_config_var('MULTIARCH') ++ if multiarch: ++ _CONFIG_VARS['multiarchsubdir'] = '/' + multiarch ++ else: ++ _CONFIG_VARS['multiarchsubdir'] = '' ++ + # Always convert srcdir to an absolute path + srcdir = _CONFIG_VARS.get('srcdir', _PROJECT_BASE) + if os.name == 'posix': +Index: b/Lib/distutils/sysconfig.py +=================================================================== +--- a/Lib/distutils/sysconfig.py ++++ b/Lib/distutils/sysconfig.py +@@ -99,6 +99,9 @@ def get_python_inc(plat_specific=0, pref + incdir = os.path.join(get_config_var('srcdir'), 'Include') + return os.path.normpath(incdir) + python_dir = 'python' + get_python_version() + build_flags ++ if not python_build and plat_specific: ++ import sysconfig ++ return sysconfig.get_path('platinclude') + return os.path.join(prefix, "include", python_dir) + elif os.name == "nt": + return os.path.join(prefix, "include") +@@ -257,6 +260,8 @@ def get_makefile_filename(): + return os.path.join(_sys_home or project_base, "Makefile") + lib_dir = get_python_lib(plat_specific=0, standard_lib=1) + config_file = 'config-{}{}'.format(get_python_version(), build_flags) ++ if hasattr(sys.implementation, '_multiarch'): ++ config_file += '-%s' % sys.implementation._multiarch + return os.path.join(lib_dir, config_file, 'Makefile') + + +Index: b/Makefile.pre.in +=================================================================== +--- a/Makefile.pre.in ++++ b/Makefile.pre.in +@@ -759,6 +759,7 @@ Modules/signalmodule.o: $(srcdir)/Module + + Python/dynload_shlib.o: $(srcdir)/Python/dynload_shlib.c Makefile + $(CC) -c $(PY_CORE_CFLAGS) \ ++ $(if $(MULTIARCH),-DMULTIARCH='"$(MULTIARCH)"') \ + -DSOABI='"$(SOABI)"' \ + -o $@ $(srcdir)/Python/dynload_shlib.c + +@@ -770,6 +771,7 @@ Python/dynload_hpux.o: $(srcdir)/Python/ + Python/sysmodule.o: $(srcdir)/Python/sysmodule.c Makefile + $(CC) -c $(PY_CORE_CFLAGS) \ + -DABIFLAGS='"$(ABIFLAGS)"' \ ++ -DMULTIARCH='"$(MULTIARCH)"' \ + -o $@ $(srcdir)/Python/sysmodule.c + + $(IO_OBJS): $(IO_H) +@@ -1336,6 +1338,10 @@ libinstall: build_all $(srcdir)/Lib/$(PL + $(srcdir)/Lib/$(PLATDIR): + mkdir $(srcdir)/Lib/$(PLATDIR) + cp $(srcdir)/Lib/plat-generic/regen $(srcdir)/Lib/$(PLATDIR)/regen ++ if [ -n "$(MULTIARCH)" ]; then \ ++ cp -p $(srcdir)/Lib/plat-linux/*.py $(srcdir)/Lib/$(PLATDIR)/.; \ ++ rm -f $(srcdir)/Lib/$(PLATDIR)/IN.py; \ ++ fi + export PATH; PATH="`pwd`:$$PATH"; \ + export PYTHONPATH; PYTHONPATH="`pwd`/Lib"; \ + export DYLD_FRAMEWORK_PATH; DYLD_FRAMEWORK_PATH="`pwd`"; \ +@@ -1386,7 +1392,7 @@ inclinstall: + LIBPL= @LIBPL@ + + # pkgconfig directory +-LIBPC= $(LIBDIR)/pkgconfig ++LIBPC= $(LIBDIR)/$(MULTIARCH)/pkgconfig + + libainstall: @DEF_MAKE_RULE@ python-config + @for i in $(LIBDIR) $(LIBPL) $(LIBPC); \ +Index: b/Python/sysmodule.c +=================================================================== +--- a/Python/sysmodule.c ++++ b/Python/sysmodule.c +@@ -1733,6 +1733,15 @@ make_impl_info(PyObject *version_info) + if (res < 0) + goto error; + ++ /* For Debian multiarch support. */ ++ value = PyUnicode_FromString(MULTIARCH); ++ if (value == NULL) ++ goto error; ++ res = PyDict_SetItemString(impl_info, "_multiarch", value); ++ Py_DECREF(value); ++ if (res < 0) ++ goto error; ++ + /* dict ready */ + + ns = _PyNamespace_New(impl_info); +Index: b/configure.ac +=================================================================== +--- a/configure.ac ++++ b/configure.ac +@@ -874,7 +874,12 @@ if test x$PLATFORM_TRIPLET != x && test + AC_MSG_ERROR([internal configure error for the platform triplet, please file a bug report]) + fi + fi +-PLATDIR=plat-$MACHDEP ++ ++if test x$PLATFORM_TRIPLET = x; then ++ PLATDIR=plat-$MACHDEP ++else ++ PLATDIR=plat-$PLATFORM_TRIPLET ++fi + AC_SUBST(PLATDIR) + AC_SUBST(PLATFORM_TRIPLET) + +@@ -4550,7 +4555,7 @@ AC_MSG_RESULT($LDVERSION) + + dnl define LIBPL after ABIFLAGS and LDVERSION is defined. + AC_SUBST(PY_ENABLE_SHARED) +-LIBPL='$(prefix)'"/lib/python${VERSION}/config-${LDVERSION}" ++LIBPL='${prefix}'"/lib/python${VERSION}/config-${LDVERSION}-${MULTIARCH}" + AC_SUBST(LIBPL) + + # Check whether right shifting a negative integer extends the sign bit diff --git a/debian/patches/no-large-file-support.diff b/debian/patches/no-large-file-support.diff new file mode 100644 index 0000000..78b9f12 --- /dev/null +++ b/debian/patches/no-large-file-support.diff @@ -0,0 +1,18 @@ +# DP: disable large file support for GNU/Hurd + +Index: b/configure.ac +=================================================================== +--- a/configure.ac ++++ b/configure.ac +@@ -2059,6 +2059,11 @@ if test "$sol_lfs_bug" = "yes"; then + use_lfs=no + fi + ++# Don't use largefile support for GNU/Hurd ++case $ac_sys_system in GNU*) ++ use_lfs=no ++esac ++ + if test "$use_lfs" = "yes"; then + # Two defines needed to enable largefile support on various platforms + # These may affect some typedefs diff --git a/debian/patches/platform-lsbrelease.diff b/debian/patches/platform-lsbrelease.diff new file mode 100644 index 0000000..3a3c2a4 --- /dev/null +++ b/debian/patches/platform-lsbrelease.diff @@ -0,0 +1,85 @@ +# DP: Use /etc/lsb-release to identify the platform. + +Index: b/Lib/platform.py +=================================================================== +--- a/Lib/platform.py ++++ b/Lib/platform.py +@@ -268,7 +268,7 @@ _release_version = re.compile(r'([^0-9]+ + _supported_dists = ( + 'SuSE', 'debian', 'fedora', 'redhat', 'centos', + 'mandrake', 'mandriva', 'rocks', 'slackware', 'yellowdog', 'gentoo', +- 'UnitedLinux', 'turbolinux', 'arch', 'mageia') ++ 'UnitedLinux', 'turbolinux', 'arch', 'mageia', 'Ubuntu') + + def _parse_release_file(firstline): + +@@ -297,6 +297,10 @@ def _parse_release_file(firstline): + id = l[1] + return '', version, id + ++_distributor_id_file_re = re.compile("(?:DISTRIB_ID\s*=)\s*(.*)", re.I) ++_release_file_re = re.compile("(?:DISTRIB_RELEASE\s*=)\s*(.*)", re.I) ++_codename_file_re = re.compile("(?:DISTRIB_CODENAME\s*=)\s*(.*)", re.I) ++ + def linux_distribution(distname='', version='', id='', + + supported_dists=_supported_dists, +@@ -329,6 +333,25 @@ def _linux_distribution(distname, versio + args given as parameters. + + """ ++ # check for the Debian/Ubuntu /etc/lsb-release file first, needed so ++ # that the distribution doesn't get identified as Debian. ++ try: ++ with open("/etc/lsb-release", "r") as etclsbrel: ++ for line in etclsbrel: ++ m = _distributor_id_file_re.search(line) ++ if m: ++ _u_distname = m.group(1).strip() ++ m = _release_file_re.search(line) ++ if m: ++ _u_version = m.group(1).strip() ++ m = _codename_file_re.search(line) ++ if m: ++ _u_id = m.group(1).strip() ++ if _u_distname and _u_version: ++ return (_u_distname, _u_version, _u_id) ++ except (EnvironmentError, UnboundLocalError): ++ pass ++ + try: + etc = os.listdir(_UNIXCONFDIR) + except OSError: +Index: b/Lib/test/test_platform.py +=================================================================== +--- a/Lib/test/test_platform.py ++++ b/Lib/test/test_platform.py +@@ -327,28 +327,6 @@ class PlatformTest(unittest.TestCase): + returncode = ret >> 8 + self.assertEqual(returncode, len(data)) + +- def test_linux_distribution_encoding(self): +- # Issue #17429 +- with tempfile.TemporaryDirectory() as tempdir: +- filename = os.path.join(tempdir, 'fedora-release') +- with open(filename, 'w', encoding='utf-8') as f: +- f.write('Fedora release 19 (Schr\xf6dinger\u2019s Cat)\n') +- +- with mock.patch('platform._UNIXCONFDIR', tempdir): +- with warnings.catch_warnings(): +- warnings.filterwarnings( +- 'ignore', +- 'dist\(\) and linux_distribution\(\) ' +- 'functions are deprecated .*', +- PendingDeprecationWarning, +- ) +- distname, version, distid = platform.linux_distribution() +- +- self.assertEqual(distname, 'Fedora') +- self.assertEqual(version, '19') +- self.assertEqual(distid, 'Schr\xf6dinger\u2019s Cat') +- +- + class DeprecationTest(unittest.TestCase): + + def test_dist_deprecation(self): diff --git a/debian/patches/profiled-build.diff b/debian/patches/profiled-build.diff new file mode 100644 index 0000000..508b30a --- /dev/null +++ b/debian/patches/profiled-build.diff @@ -0,0 +1,23 @@ +# DP: Ignore errors in the profile task. + +Index: b/Makefile.pre.in +=================================================================== +--- a/Makefile.pre.in ++++ b/Makefile.pre.in +@@ -479,6 +479,16 @@ build_all_generate_profile: + run_profile_task: + : # FIXME: can't run for a cross build + $(LLVM_PROF_FILE) $(RUNSHARED) ./$(BUILDPYTHON) $(PROFILE_TASK) || true ++ task="$(PROFILE_TASK)"; \ ++ case "$$task" in \ ++ *-s\ *) \ ++ $(LLVM_PROF_FILE) $(RUNSHARED) ./$(BUILDPYTHON) $$task; \ ++ while [ -f $(srcdir)/build/pynexttest ]; do \ ++ $(LLVM_PROF_FILE) $(RUNSHARED) ./$(BUILDPYTHON) $$task; \ ++ done;; \ ++ *) \ ++ $(LLVM_PROF_FILE) $(RUNSHARED) ./$(BUILDPYTHON) $$task; \ ++ esac + + build_all_merge_profile: + $(LLVM_PROF_MERGER) diff --git a/debian/patches/pydoc-use-pager.diff b/debian/patches/pydoc-use-pager.diff new file mode 100644 index 0000000..1a4c2af --- /dev/null +++ b/debian/patches/pydoc-use-pager.diff @@ -0,0 +1,15 @@ +# DP: pydoc: use the pager command if available. + +Index: b/Lib/pydoc.py +=================================================================== +--- a/Lib/pydoc.py ++++ b/Lib/pydoc.py +@@ -1439,6 +1439,8 @@ def getpager(): + return plainpager + if sys.platform == 'win32': + return lambda text: tempfilepager(plain(text), 'more <') ++ if hasattr(os, 'system') and os.system('(pager) 2>/dev/null') == 0: ++ return lambda text: pipepager(text, 'pager') + if hasattr(os, 'system') and os.system('(less) 2>/dev/null') == 0: + return lambda text: pipepager(text, 'less') + diff --git a/debian/patches/pyhash.diff b/debian/patches/pyhash.diff new file mode 100644 index 0000000..b9eec8b --- /dev/null +++ b/debian/patches/pyhash.diff @@ -0,0 +1,17 @@ +Index: b/Python/pyhash.c +=================================================================== +--- a/Python/pyhash.c ++++ b/Python/pyhash.c +@@ -384,7 +384,12 @@ siphash24(const void *src, Py_ssize_t sr + PY_UINT8_T *m; + + while (src_sz >= 8) { ++#if defined(__ARM_EABI__) && defined(__ARMEL__) ++ PY_UINT64_T mi; ++ memcpy(&mi, in, sizeof(PY_UINT64_T)); ++#else + PY_UINT64_T mi = _le64toh(*in); ++#endif + in += 1; + src_sz -= 8; + v3 ^= mi; diff --git a/debian/patches/reproducible-buildinfo.diff b/debian/patches/reproducible-buildinfo.diff new file mode 100644 index 0000000..a0b20f9 --- /dev/null +++ b/debian/patches/reproducible-buildinfo.diff @@ -0,0 +1,15 @@ +# DP: Build getbuildinfo.o with DATE/TIME values when defined + +Index: b/Makefile.pre.in +=================================================================== +--- a/Makefile.pre.in ++++ b/Makefile.pre.in +@@ -731,6 +731,8 @@ Modules/getbuildinfo.o: $(PARSER_OBJS) \ + -DGITVERSION="\"`LC_ALL=C $(GITVERSION)`\"" \ + -DGITTAG="\"`LC_ALL=C $(GITTAG)`\"" \ + -DGITBRANCH="\"`LC_ALL=C $(GITBRANCH)`\"" \ ++ $(if $(BUILD_DATE),-DDATE='"$(BUILD_DATE)"') \ ++ $(if $(BUILD_TIME),-DTIME='"$(BUILD_TIME)"') \ + -o $@ $(srcdir)/Modules/getbuildinfo.c + + Modules/getpath.o: $(srcdir)/Modules/getpath.c Makefile diff --git a/debian/patches/series b/debian/patches/series new file mode 100644 index 0000000..d9ee29d --- /dev/null +++ b/debian/patches/series @@ -0,0 +1,42 @@ +# git-updates.diff +deb-setup.diff +deb-locations.diff +distutils-install-layout.diff +locale-module.diff +distutils-link.diff +distutils-sysconfig.diff +tkinter-import.diff +gdbm-import.diff +link-opt.diff +setup-modules.diff +platform-lsbrelease.diff +bdist-wininst-notfound.diff +profiled-build.diff +makesetup-bashism.diff +hurd-disable-nonworking-constants.diff +langpack-gettext.diff +no-large-file-support.diff +disable-sem-check.diff +lib-argparse.diff +ctypes-arm.diff +# link-timemodule.diff +lto-link-flags.diff +multiarch.diff +lib2to3-no-pickled-grammar.diff +ext-no-libpython-link.diff +test-no-random-order.diff +multiarch-extname.diff +distutils-init.diff +tempfile-minimal.diff +disable-some-tests.diff +# issue21264.diff +ensurepip-wheels.diff +ensurepip-disabled.diff +mpdecimal-version.diff +mangle-fstack-protector.diff +reproducible-buildinfo.diff +pydoc-use-pager.diff +#asyncio366.diff +pyhash.diff +lib2to3-no-pgen-caching.diff +local-blurb.diff diff --git a/debian/patches/setup-modules.diff b/debian/patches/setup-modules.diff new file mode 100644 index 0000000..d3c5789 --- /dev/null +++ b/debian/patches/setup-modules.diff @@ -0,0 +1,52 @@ +# DP: Modules/Setup.dist: patches to build some extensions statically + +Index: b/Modules/Setup.dist +=================================================================== +--- a/Modules/Setup.dist ++++ b/Modules/Setup.dist +@@ -175,7 +175,7 @@ _symtable symtablemodule.c + #_weakref _weakref.c # basic weak reference support + #_testcapi _testcapimodule.c # Python C API test module + #_random _randommodule.c # Random number generator +-#_elementtree -I$(srcdir)/Modules/expat -DHAVE_EXPAT_CONFIG_H -DUSE_PYEXPAT_CAPI _elementtree.c # elementtree accelerator ++#_elementtree _elementtree.c -lexpat # elementtree accelerator + #_pickle _pickle.c # pickle accelerator + #_datetime _datetimemodule.c # datetime accelerator + #_bisect _bisectmodule.c # Bisection algorithms +@@ -204,10 +204,7 @@ _symtable symtablemodule.c + + # Socket module helper for SSL support; you must comment out the other + # socket line above, and possibly edit the SSL variable: +-#SSL=/usr/local/ssl +-#_ssl _ssl.c \ +-# -DUSE_SSL -I$(SSL)/include -I$(SSL)/include/openssl \ +-# -L$(SSL)/lib -lssl -lcrypto ++#_ssl _ssl.c -lssl -lcrypto + + # The crypt module is now disabled by default because it breaks builds + # on many systems (where -lcrypt is needed), e.g. Linux (I believe). +@@ -249,6 +246,7 @@ _symtable symtablemodule.c + #_sha256 sha256module.c + #_sha512 sha512module.c + ++#_hashlib _hashopenssl.c -lssl -lcrypto + + # The _tkinter module. + # +@@ -337,6 +335,7 @@ _symtable symtablemodule.c + # Fred Drake's interface to the Python parser + #parser parsermodule.c + ++#_ctypes _ctypes/_ctypes.c _ctypes/callbacks.c _ctypes/callproc.c _ctypes/stgdict.c _ctypes/cfield.c _ctypes/malloc_closure.c -lffi + + # Lee Busby's SIGFPE modules. + # The library to link fpectl with is platform specific. +@@ -371,7 +370,7 @@ _symtable symtablemodule.c + # + # More information on Expat can be found at www.libexpat.org. + # +-#pyexpat expat/xmlparse.c expat/xmlrole.c expat/xmltok.c pyexpat.c -I$(srcdir)/Modules/expat -DHAVE_EXPAT_CONFIG_H -DUSE_PYEXPAT_CAPI ++#pyexpat pyexpat.c -lexpat + + # Hye-Shik Chang's CJKCodecs + diff --git a/debian/patches/sysconfig-debian-schemes.diff b/debian/patches/sysconfig-debian-schemes.diff new file mode 100644 index 0000000..d26ee20 --- /dev/null +++ b/debian/patches/sysconfig-debian-schemes.diff @@ -0,0 +1,67 @@ +# DP: Add schemes 'deb_system' and 'posix_local', make the latter the default + +--- a/Lib/sysconfig.py ++++ b/Lib/sysconfig.py +@@ -32,6 +32,30 @@ + 'scripts': '{base}/bin', + 'data': '{base}', + }, ++ 'deb_system': { ++ 'stdlib': '{installed_base}/lib/python{py_version_short}', ++ 'platstdlib': '{platbase}/lib/python{py_version_short}', ++ 'purelib': '{base}/lib/python3/dist-packages', ++ 'platlib': '{platbase}/lib/python3/dist-packages', ++ 'include': ++ '{installed_base}/include/python{py_version_short}{abiflags}', ++ 'platinclude': ++ '{installed_platbase}/include/python{py_version_short}{abiflags}', ++ 'scripts': '{base}/bin', ++ 'data': '{base}', ++ }, ++ 'posix_local': { ++ 'stdlib': '{installed_base}/lib/python{py_version_short}', ++ 'platstdlib': '{platbase}/lib/python{py_version_short}', ++ 'purelib': '{base}/local/lib/python{py_version_short}/dist-packages', ++ 'platlib': '{platbase}/local/lib/python{py_version_short}/dist-packages', ++ 'include': ++ '{installed_base}/local/include/python{py_version_short}{abiflags}', ++ 'platinclude': ++ '{installed_platbase}/local/include/python{py_version_short}{abiflags}', ++ 'scripts': '{base}/local/bin', ++ 'data': '{base}', ++ }, + 'posix_home': { + 'stdlib': '{installed_base}/lib/python', + 'platstdlib': '{base}/lib/python', +@@ -162,7 +186,7 @@ + _PYTHON_BUILD = is_python_build(True) + + if _PYTHON_BUILD: +- for scheme in ('posix_prefix', 'posix_home'): ++ for scheme in ('posix_prefix', 'posix_home', 'posix_local', 'deb_system'): + _INSTALL_SCHEMES[scheme]['include'] = '{srcdir}/Include' + _INSTALL_SCHEMES[scheme]['platinclude'] = '{projectbase}/.' + +@@ -200,7 +224,12 @@ + def _get_default_scheme(): + if os.name == 'posix': + # the default scheme for posix is posix_prefix +- return 'posix_prefix' ++ if 'real_prefix' in sys.__dict__ or 'VIRTUAL_ENV' in os.environ: ++ # virtual environments ++ return 'posix_prefix' ++ else: ++ # Debian default ++ return 'posix_local' + return os.name + + +@@ -485,7 +514,7 @@ + else: + inc_dir = _sys_home or _PROJECT_BASE + else: +- inc_dir = get_path('platinclude') ++ inc_dir = get_path('platinclude', 'posix_prefix') + return os.path.join(inc_dir, 'pyconfig.h') + + diff --git a/debian/patches/sysconfigdata.diff b/debian/patches/sysconfigdata.diff new file mode 100644 index 0000000..5387689 --- /dev/null +++ b/debian/patches/sysconfigdata.diff @@ -0,0 +1,76 @@ +# DP: Issue #15298: Generate _sysconfigdata.py in the build dir, not the source dir. + +diff -r 2ecdda96f970 Lib/sysconfig.py +--- a/Lib/sysconfig.py Tue Jul 10 18:27:54 2012 +0200 ++++ b/Lib/sysconfig.py Tue Jul 10 22:06:43 2012 +0200 +@@ -390,7 +390,7 @@ + if _PYTHON_BUILD: + vars['LDSHARED'] = vars['BLDSHARED'] + +- destfile = os.path.join(os.path.dirname(__file__), '_sysconfigdata.py') ++ destfile = '_sysconfigdata.py' + with open(destfile, 'w', encoding='utf8') as f: + f.write('# system configuration generated and used by' + ' the sysconfig module\n') +diff -r 2ecdda96f970 Makefile.pre.in +--- a/Makefile.pre.in Tue Jul 10 18:27:54 2012 +0200 ++++ b/Makefile.pre.in Tue Jul 10 22:06:43 2012 +0200 +@@ -410,7 +410,7 @@ + Objects/unicodectype.o \ + Objects/weakrefobject.o + +-SYSCONFIGDATA=$(srcdir)/Lib/_sysconfigdata.py ++SYSCONFIGDATA=_sysconfigdata.py + + ########################################################################## + # objects that get linked into the Python library +@@ -472,6 +472,9 @@ + # Generate the sysconfig build-time data + $(SYSCONFIGDATA): $(BUILDPYTHON) + $(RUNSHARED) $(PYTHON_FOR_BUILD) -S -m sysconfig --generate-posix-vars ++ $(RUNSHARED) $(PYTHON_FOR_BUILD) -S -c 'import os,sys ; from distutils.util import get_platform ; d=os.path.join("build", "lib."+get_platform()+"-"+sys.version[0:3]+("-pydebug" if hasattr(sys, "gettotalrefcount") else "")); print(d, end="")' > pybuilddir.txt ++ mkdir -p `cat pybuilddir.txt` ++ cp $(SYSCONFIGDATA) `cat pybuilddir.txt`/. + + # Build the shared modules + sharedmods: $(BUILDPYTHON) $(SYSCONFIGDATA) +@@ -1036,7 +1039,7 @@ + else true; \ + fi; \ + done +- @for i in $(srcdir)/Lib/*.py ; \ ++ @for i in $(srcdir)/Lib/*.py $(SYSCONFIGDATA); \ + do \ + if test -x $$i; then \ + $(INSTALL_SCRIPT) $$i $(DESTDIR)$(LIBDEST); \ +diff -r 2ecdda96f970 setup.py +--- a/setup.py Tue Jul 10 18:27:54 2012 +0200 ++++ b/setup.py Tue Jul 10 22:06:43 2012 +0200 +@@ -33,10 +33,6 @@ + # This global variable is used to hold the list of modules to be disabled. + disabled_module_list = [] + +-# File which contains the directory for shared mods (for sys.path fixup +-# when running from the build dir, see Modules/getpath.c) +-_BUILDDIR_COOKIE = "pybuilddir.txt" +- + def add_dir_to_list(dirlist, dir): + """Add the directory 'dir' to the list 'dirlist' (after any relative + directories) if: +@@ -250,12 +246,9 @@ + args['compiler_so'] = compiler + ' ' + ccshared + ' ' + cflags + self.compiler.set_executables(**args) + +- # Not only do we write the builddir cookie, but we manually install +- # the shared modules directory if it isn't already in sys.path. +- # Otherwise trying to import the extensions after building them +- # will fail. +- with open(_BUILDDIR_COOKIE, "wb") as f: +- f.write(self.build_lib.encode('utf-8', 'surrogateescape')) ++ # We manually install the shared modules directory if it isn't ++ # already in sys.path. Otherwise trying to import the ++ # extensions after building them will fail. + abs_build_lib = os.path.join(os.getcwd(), self.build_lib) + if abs_build_lib not in sys.path: + sys.path.append(abs_build_lib) + diff --git a/debian/patches/tempfile-minimal.diff b/debian/patches/tempfile-minimal.diff new file mode 100644 index 0000000..5299715 --- /dev/null +++ b/debian/patches/tempfile-minimal.diff @@ -0,0 +1,169 @@ +# DP: Avoid shutil import when it is not available. + +Index: b/Lib/tempfile.py +=================================================================== +--- a/Lib/tempfile.py ++++ b/Lib/tempfile.py +@@ -40,7 +40,146 @@ import functools as _functools + import warnings as _warnings + import io as _io + import os as _os +-import shutil as _shutil ++try: ++ import shutil as _shutil ++ _rmtree = _shutil.rmtree ++except ImportError: ++ import sys as _sys ++ import stat as _stat ++ # version vulnerable to race conditions ++ def _rmtree_unsafe(path, onerror): ++ try: ++ if _os.path.islink(path): ++ # symlinks to directories are forbidden, see bug #1669 ++ raise OSError("Cannot call rmtree on a symbolic link") ++ except OSError: ++ onerror(_os.path.islink, path, _sys.exc_info()) ++ # can't continue even if onerror hook returns ++ return ++ names = [] ++ try: ++ names = _os.listdir(path) ++ except OSError: ++ onerror(_os.listdir, path, _sys.exc_info()) ++ for name in names: ++ fullname = _os.path.join(path, name) ++ try: ++ mode = _os.lstat(fullname).st_mode ++ except OSError: ++ mode = 0 ++ if _stat.S_ISDIR(mode): ++ _rmtree_unsafe(fullname, onerror) ++ else: ++ try: ++ _os.unlink(fullname) ++ except OSError: ++ onerror(_os.unlink, fullname, _sys.exc_info()) ++ try: ++ _os.rmdir(path) ++ except OSError: ++ onerror(_os.rmdir, path, _sys.exc_info()) ++ ++ # Version using fd-based APIs to protect against races ++ def _rmtree_safe_fd(topfd, path, onerror): ++ names = [] ++ try: ++ names = _os.listdir(topfd) ++ except OSError as err: ++ err.filename = path ++ onerror(_os.listdir, path, _sys.exc_info()) ++ for name in names: ++ fullname = _os.path.join(path, name) ++ try: ++ orig_st = _os.stat(name, dir_fd=topfd, follow_symlinks=False) ++ mode = orig_st.st_mode ++ except OSError: ++ mode = 0 ++ if _stat.S_ISDIR(mode): ++ try: ++ dirfd = _os.open(name, _os.O_RDONLY, dir_fd=topfd) ++ except OSError: ++ onerror(_os.open, fullname, _sys.exc_info()) ++ else: ++ try: ++ if _os.path.samestat(orig_st, _os.fstat(dirfd)): ++ _rmtree_safe_fd(dirfd, fullname, onerror) ++ try: ++ _os.rmdir(name, dir_fd=topfd) ++ except OSError: ++ onerror(_os.rmdir, fullname, _sys.exc_info()) ++ else: ++ try: ++ # This can only happen if someone replaces ++ # a directory with a symlink after the call to ++ # stat.S_ISDIR above. ++ raise OSError("Cannot call rmtree on a symbolic " ++ "link") ++ except OSError: ++ onerror(_os.path.islink, fullname, _sys.exc_info()) ++ finally: ++ _os.close(dirfd) ++ else: ++ try: ++ _os.unlink(name, dir_fd=topfd) ++ except OSError: ++ onerror(_os.unlink, fullname, _sys.exc_info()) ++ ++ _use_fd_functions = ({_os.open, _os.stat, _os.unlink, _os.rmdir} <= ++ _os.supports_dir_fd and ++ _os.listdir in _os.supports_fd and ++ _os.stat in _os.supports_follow_symlinks) ++ ++ def _rmtree(path, ignore_errors=False, onerror=None): ++ """Recursively delete a directory tree. ++ ++ If ignore_errors is set, errors are ignored; otherwise, if onerror ++ is set, it is called to handle the error with arguments (func, ++ path, exc_info) where func is platform and implementation dependent; ++ path is the argument to that function that caused it to fail; and ++ exc_info is a tuple returned by sys.exc_info(). If ignore_errors ++ is false and onerror is None, an exception is raised. ++ ++ """ ++ if ignore_errors: ++ def onerror(*args): ++ pass ++ elif onerror is None: ++ def onerror(*args): ++ raise ++ if _use_fd_functions: ++ # While the unsafe rmtree works fine on bytes, the fd based does not. ++ if isinstance(path, bytes): ++ path = _os.fsdecode(path) ++ # Note: To guard against symlink races, we use the standard ++ # lstat()/open()/fstat() trick. ++ try: ++ orig_st = _os.lstat(path) ++ except Exception: ++ onerror(_os.lstat, path, _sys.exc_info()) ++ return ++ try: ++ fd = _os.open(path, _os.O_RDONLY) ++ except Exception: ++ onerror(_os.lstat, path, _sys.exc_info()) ++ return ++ try: ++ if _os.path.samestat(orig_st, _os.fstat(fd)): ++ _rmtree_safe_fd(fd, path, onerror) ++ try: ++ _os.rmdir(path) ++ except OSError: ++ onerror(_os.rmdir, path, _sys.exc_info()) ++ else: ++ try: ++ # symlinks to directories are forbidden, see bug #1669 ++ raise OSError("Cannot call rmtree on a symbolic link") ++ except OSError: ++ onerror(_os.path.islink, path, _sys.exc_info()) ++ finally: ++ _os.close(fd) ++ else: ++ return _rmtree_unsafe(path, onerror) ++ + import errno as _errno + from random import Random as _Random + import weakref as _weakref +@@ -794,7 +933,7 @@ class TemporaryDirectory(object): + + @classmethod + def _cleanup(cls, name, warn_message): +- _shutil.rmtree(name) ++ _rmtree(name) + _warnings.warn(warn_message, ResourceWarning) + + +@@ -809,4 +948,4 @@ class TemporaryDirectory(object): + + def cleanup(self): + if self._finalizer.detach(): +- _shutil.rmtree(self.name) ++ _rmtree(self.name) diff --git a/debian/patches/test-no-random-order.diff b/debian/patches/test-no-random-order.diff new file mode 100644 index 0000000..1c98695 --- /dev/null +++ b/debian/patches/test-no-random-order.diff @@ -0,0 +1,14 @@ +# DP: Don't run the test suite in random order. + +Index: b/Tools/scripts/run_tests.py +=================================================================== +--- a/Tools/scripts/run_tests.py ++++ b/Tools/scripts/run_tests.py +@@ -37,7 +37,6 @@ def main(regrtest_args): + args.extend(['-W', 'error::BytesWarning']) + + args.extend(['-m', 'test', # Run the test suite +- '-r', # Randomize test order + '-w', # Re-run failed tests in verbose mode + ]) + if sys.platform == 'win32': diff --git a/debian/patches/tkinter-import.diff b/debian/patches/tkinter-import.diff new file mode 100644 index 0000000..81673e6 --- /dev/null +++ b/debian/patches/tkinter-import.diff @@ -0,0 +1,18 @@ +# DP: suggest installation of python-tk package on failing _tkinter import + +Index: b/Lib/tkinter/__init__.py +=================================================================== +--- a/Lib/tkinter/__init__.py ++++ b/Lib/tkinter/__init__.py +@@ -32,7 +32,10 @@ tk.mainloop() + + import sys + +-import _tkinter # If this fails your Python may not be configured for Tk ++try: ++ import _tkinter ++except ImportError as msg: ++ raise ImportError(str(msg) + ', please install the python3-tk package') + TclError = _tkinter.TclError + from tkinter.constants import * + import re |