summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app-vim/youcompleteme/Manifest6
-rw-r--r--app-vim/youcompleteme/files/remove-python2-support.patch5309
-rw-r--r--app-vim/youcompleteme/youcompleteme-20200203.ebuild109
3 files changed, 5424 insertions, 0 deletions
diff --git a/app-vim/youcompleteme/Manifest b/app-vim/youcompleteme/Manifest
index 99287ab..fa8b856 100644
--- a/app-vim/youcompleteme/Manifest
+++ b/app-vim/youcompleteme/Manifest
@@ -1,7 +1,13 @@
+AUX remove-python2-support.patch 199204 BLAKE2B 570bd01ae3e8a89c367194329f09738748a64b88b958dc085f91ff42a3a2f1751194905658c8b1d7ff5362869280952d9fec3ed022a0377a2399422e9b25fd70 SHA512 afccbce17d4f5a6605e11c4af65f2f3f89fd70aa689cfd9bfa4aceb08414f0b66ba822d362183499dda42f35996ebc15b478d8bdb348de21a1ad8ccb4651592d
DIST gocode-416643789f088aa5077f667cecde7f966131f6be.tar.gz 93109 BLAKE2B c40db19594e7876ab86ab3e67a51d985b9fbd7f38e578f7ccc1bfd4d23dc45d5875d4421aebeaa61851b49ba0cc1f522f18bcdf7438ffae841acddba51620163 SHA512 6dfbed0b47843b6251c86c13b3d6cd741c7745d32c39129cb4ba0e1aa8696d9ddb7bc60c4a1a20092764f89ff5fcde58327612486da8322930d5c0d2a7984765
+DIST gocode-5bee97b488366fd20b054d0861b89834ff5bbfb2.tar.gz 97152 BLAKE2B 4d5e4e6c2cc46348def3cc7700c47f73ac678ce31671d41d4ce37942477a66a65160a2c9d4fb4c6431d46ed2937d67290a6e6565e73b973862da431cbb281ba7 SHA512 9b1e81635a0b168d6e242afb69c47ec95ea9ccca3b039f54193591e9ea2bf09c914765bbf989adfa0ac55e3aab4007eae5fcee8ab28fe695ef87be71a884a4d6
DIST omnisharp-server-e1902915c6790bcec00b8d551199c8a3537d33c9.tar.gz 3005049 BLAKE2B 07c7a06c3d2b75a6df7b6bc6e2e26dc1dbab1af1e98018db14177a82304eb48160bb4dd25f0d6d1af5a50725e6d1b8365932a5ff575d2c08be87997f2ee58397 SHA512 47b6083016d9865708b3deaf44f3742f276d5aa13c3a310b9aa432650df18d35bed62668a90f1aba52fbe84fb7471dd885c34e351b859607d7221ae074290fde
DIST python-frozendict-b27053e4d11f5891319fd29eda561c130ba3112a.tar.gz 2192 BLAKE2B 58518ec7185c29d04448552e0dda147ad59c857c1e29f6384dab9a65c030678c6f350f7930f5878094e4fed03e4670df81be3bcb17f9233b55d8d51769621048 SHA512 f943e17e9ad5de31c1c8971e26a45cd61e972cbca31ce26e03285070728364e27edab4549532128a51bbcae38a5166ce8ea6e700069d242b0971f8004fb280f4
+DIST requests-futures-148451a06781d8196e5fb7e0e2bca7a765368ff1.tar.gz 7823 BLAKE2B 6ae2d74849f99a44a449caff70f065d8548d773224b39da661edb880a4eb84aa97da62bd72178b8713cb58ec5deb599fa7eb371fe90669fafc1e42611ada31d1 SHA512 396b02c202f0aa748df02afd8d04b72a2426932e55dde599a82fe134aa585b105267299899c12577f82d43479c56b95d257476010b8d7808817705c1e94a1781
DIST requests-futures-98712e7d0f6be2a090b6fda2a925f85e63656b58.tar.gz 4550 BLAKE2B 8ac1b79dd283ea99093eeaa693a10afddf5ce877263f848b6bb11f2ddd0984937b95f368007efb885ac5cd262aecba5c93c194a48d19283df14a0bdae90a9b46 SHA512 38f0c86ab8f74f4f48a6b4279c5d83580f5486a59b71d4d7009835e3f4e8fcbe07e188d0920de9b223d7c88592998c93e6c66f8aae19d22cf4a31705e066e54c
DIST ycmd-4dfa50eb5873c071b446f25b17cbad066164b339.tar.gz 4550802 BLAKE2B de11b0cbb7618971f013ad9bfc425db43f955e8489b8c75c08abbedcb93da8faeb75afbfcc29b84b6238e2da2ec5775198083d03a9867a79d76b0dcdaccba155 SHA512 6233940eacf7592f7164b833f93a784bc39dd8a467e4df08b1433a63bebc405b63b41a1e534dfefd24b5a06faf0e5cdc52467b4cb87d475fd24d66d15c12c770
+DIST ycmd-d3378ca3a3103535c14b104cb916dcbcdaf93eeb.tar.gz 4504015 BLAKE2B f79a3616c9ea2789cd10dd7e1d2b60d651c1f592b213cc0806f92ba12a115ddb3408ac7565e9e486c827ee87a8d4518c55a029f73d518749d3cfa0cd01cd5f00 SHA512 c4af6810995262a5c145ca8eeed9bf92c6ac204ac507f27571b68ed3258affae5734b5edfccdc6ae5f7fa310e1dbf9f74f2681d9c6d774685469ee314ada2573
+DIST youcompleteme-124661f218e80b96c1f9f3d124e99f9a2fd2d83b.tar.gz 317144 BLAKE2B 6fc1d421a41e13923dd677deeabda9b5350c526558acae53d1e38eba4f911ba8d99c28f9b60fcbadd36452fdf985e1c01d456d14ffdb6e86c4bd3fcf38567939 SHA512 7442ad4b72f7782f055ae9ed9e94221a579448f3aaf7054246218e50ab0b19a4f451239a0c67e3c59c27e6e9d8a005273d8524f1acc8f1f855eb14c1b0731078
DIST youcompleteme-25ebc0b9abb1b135c809ee850085a0305cbc5533.tar.gz 291574 BLAKE2B 99f03166cf6c5192e63c307b8c5558589ea877df74a595694f3169982b0ae2939d24fac469e80b476e23d99bf800ebc0b2e8795f1cf4b4a1c18bdd21f25e9bdb SHA512 6abf0696fcd9472d33071a890411bc0f19ba939a2c2a9d2a60480404759e19c3fd93d9eaa916dcd6c777bd8f8f0d02efac89efefca6219ea7b34c93555101115
EBUILD youcompleteme-20190321.ebuild 3688 BLAKE2B de7d0893d15b58aab2ce222b139157424d4cdad5058bd0cafb69c7588c25f0ea74def8601e8bc321edc35a27a61b42212055f96a13dabd01d51f2eb289a07fde SHA512 0a61dd4d3478630b79477f0934955b5bf0e80cdbc4407610b9758d05d4857c5b09129d051bce56472809a22d113f2bd9b9c58160e93c590103d65fec1f007437
+EBUILD youcompleteme-20200203.ebuild 3424 BLAKE2B f6987debc135539e7235ae2b30a96f09066d201c53cf6e7c9b56beb348d20fb1ae8ee74700b0ebc5d4ec8276374806192db21f150b4068d841ace2e5e6fca29c SHA512 440de9a627d2a13372e1dff37cd62196e5ebae7eae49a822554ffb08f32c991cead0fa7f7bb020ed3f7a7d2b10232500fca942cb44e8bbf78f91d2b6c32d3253
diff --git a/app-vim/youcompleteme/files/remove-python2-support.patch b/app-vim/youcompleteme/files/remove-python2-support.patch
new file mode 100644
index 0000000..08cbb74
--- /dev/null
+++ b/app-vim/youcompleteme/files/remove-python2-support.patch
@@ -0,0 +1,5309 @@
+From db57ade716d366e92c604b6d385c96c4b8db8a65 Mon Sep 17 00:00:00 2001
+From: Boris Staletic <boris.staletic@gmail.com>
+Date: Thu, 21 Nov 2019 12:58:02 +0100
+Subject: [PATCH] Remove python2 support
+
+---
+ README.md | 90 +--
+ autoload/youcompleteme.vim | 194 ++---
+ azure-pipelines.yml | 13 -
+ install.sh | 11 +-
+ plugin/youcompleteme.vim | 8 +-
+ python/ycm/base.py | 7 -
+ python/ycm/buffer.py | 9 +-
+ python/ycm/client/base_request.py | 20 +-
+ python/ycm/client/command_request.py | 7 -
+ .../ycm/client/completer_available_request.py | 7 -
+ python/ycm/client/completion_request.py | 7 -
+ python/ycm/client/debug_info_request.py | 7 -
+ python/ycm/client/event_notification.py | 7 -
+ python/ycm/client/messages_request.py | 7 -
+ python/ycm/client/omni_completion_request.py | 7 -
+ python/ycm/client/shutdown_request.py | 7 -
+ python/ycm/client/signature_help_request.py | 7 -
+ python/ycm/client/ycmd_keepalive.py | 9 +-
+ python/ycm/diagnostic_filter.py | 16 +-
+ python/ycm/diagnostic_interface.py | 18 +-
+ python/ycm/omni_completer.py | 7 -
+ python/ycm/paths.py | 28 +-
+ python/ycm/signature_help.py | 9 +-
+ python/ycm/syntax_parse.py | 12 +-
+ python/ycm/tests/__init__.py | 7 -
+ python/ycm/tests/base_test.py | 11 +-
+ python/ycm/tests/client/base_request_test.py | 7 -
+ .../ycm/tests/client/command_request_test.py | 11 +-
+ .../tests/client/completion_request_test.py | 9 +-
+ .../tests/client/debug_info_request_test.py | 7 -
+ .../ycm/tests/client/messages_request_test.py | 8 -
+ .../client/omni_completion_request_tests.py | 7 -
+ python/ycm/tests/command_test.py | 7 -
+ python/ycm/tests/completion_test.py | 9 -
+ python/ycm/tests/diagnostic_filter_test.py | 7 -
+ python/ycm/tests/event_notification_test.py | 9 -
+ python/ycm/tests/mock_utils.py | 11 +-
+ python/ycm/tests/paths_test.py | 23 +-
+ python/ycm/tests/postcomplete_test.py | 7 -
+ python/ycm/tests/signature_help_test.py | 9 -
+ python/ycm/tests/syntax_parse_test.py | 7 -
+ python/ycm/tests/test_utils.py | 48 +-
+ python/ycm/tests/vimsupport_test.py | 12 -
+ python/ycm/tests/youcompleteme_test.py | 33 +-
+ python/ycm/unsafe_thread_pool_executor.py | 3 +-
+ python/ycm/vimsupport.py | 16 +-
+ python/ycm/youcompleteme.py | 34 +-
+ test/README.md | 8 +-
+ test/docker/ci/push | 1 -
+ test/docker/ci/rebuild | 3 -
+ test/docker/manual/push | 1 -
+ test/docker/manual/rebuild | 3 -
+ third_party/pythonfutures/CHANGES | 44 --
+ third_party/pythonfutures/LICENSE | 21 -
+ .../pythonfutures/concurrent/__init__.py | 3 -
+ .../concurrent/futures/__init__.py | 18 -
+ .../pythonfutures/concurrent/futures/_base.py | 574 --------------
+ .../concurrent/futures/_compat.py | 101 ---
+ .../concurrent/futures/process.py | 363 ---------
+ .../concurrent/futures/thread.py | 138 ----
+ third_party/pythonfutures/crawl.py | 74 --
+ third_party/pythonfutures/docs/conf.py | 194 -----
+ third_party/pythonfutures/docs/index.rst | 345 ---------
+ third_party/pythonfutures/docs/make.bat | 112 ---
+ third_party/pythonfutures/futures/__init__.py | 24 -
+ third_party/pythonfutures/futures/process.py | 1 -
+ third_party/pythonfutures/futures/thread.py | 1 -
+ third_party/pythonfutures/primes.py | 50 --
+ third_party/pythonfutures/setup.cfg | 6 -
+ third_party/pythonfutures/setup.py | 33 -
+ third_party/pythonfutures/test_futures.py | 723 ------------------
+ third_party/pythonfutures/tox.ini | 8 -
+ 75 files changed, 183 insertions(+), 3552 deletions(-)
+ delete mode 100755 third_party/pythonfutures/CHANGES
+ delete mode 100755 third_party/pythonfutures/LICENSE
+ delete mode 100755 third_party/pythonfutures/concurrent/__init__.py
+ delete mode 100755 third_party/pythonfutures/concurrent/futures/__init__.py
+ delete mode 100755 third_party/pythonfutures/concurrent/futures/_base.py
+ delete mode 100755 third_party/pythonfutures/concurrent/futures/_compat.py
+ delete mode 100755 third_party/pythonfutures/concurrent/futures/process.py
+ delete mode 100755 third_party/pythonfutures/concurrent/futures/thread.py
+ delete mode 100755 third_party/pythonfutures/crawl.py
+ delete mode 100755 third_party/pythonfutures/docs/conf.py
+ delete mode 100755 third_party/pythonfutures/docs/index.rst
+ delete mode 100755 third_party/pythonfutures/docs/make.bat
+ delete mode 100755 third_party/pythonfutures/futures/__init__.py
+ delete mode 100755 third_party/pythonfutures/futures/process.py
+ delete mode 100755 third_party/pythonfutures/futures/thread.py
+ delete mode 100755 third_party/pythonfutures/primes.py
+ delete mode 100755 third_party/pythonfutures/setup.cfg
+ delete mode 100755 third_party/pythonfutures/setup.py
+ delete mode 100755 third_party/pythonfutures/test_futures.py
+ delete mode 100755 third_party/pythonfutures/tox.ini
+
+diff --git a/README.md b/README.md
+index d296b5ba3..0705acdb2 100644
+--- a/README.md
++++ b/README.md
+@@ -281,7 +281,7 @@ YouCompleteMe, however they may not work for everyone. If the following
+ instructions don't work for you, check out the [full installation
+ guide](#full-installation-guide).
+
+-Make sure you have Vim 7.4.1578 with Python 2 or Python 3 support. The Vim
++Make sure you have Vim 7.4.1578 with Python 3 support. The Vim
+ package on Fedora 27 and later and the pre-installed Vim on Ubuntu 16.04 and
+ later are recent enough. You can see the version of Vim installed by running
+ `vim --version`. If the version is too old, you may need to [compile Vim from
+@@ -376,13 +376,13 @@ guide](#full-installation-guide).
+ **Important:** we assume that you are using the `cmd.exe` command prompt and
+ that you know how to add an executable to the PATH environment variable.
+
+-Make sure you have at least Vim 7.4.1578 with Python 2 or Python 3 support. You
++Make sure you have at least Vim 7.4.1578 with Python 3 support. You
+ can check the version and which Python is supported by typing `:version` inside
+-Vim. Look at the features included: `+python/dyn` for Python 2 and
+-`+python3/dyn` for Python 3. Take note of the Vim architecture, i.e. 32 or
++Vim. Look at the features included: `+python3/dyn` for Python 3.
++Take note of the Vim architecture, i.e. 32 or
+ 64-bit. It will be important when choosing the Python installer. We recommend
+ using a 64-bit client. [Daily updated installers of 32-bit and 64-bit Vim with
+-Python 2 and Python 3 support][vim-win-download] are available.
++Python 3 support][vim-win-download] are available.
+
+ **NOTE**: For all features, such as signature help, use Vim 8.1.1875 or later.
+
+@@ -403,17 +403,15 @@ process.
+
+ Download and install the following software:
+
+-- [Python 2 or Python 3][python-win-download]. Be sure to pick the version
++- [Python 3][python-win-download]. Be sure to pick the version
+ corresponding to your Vim architecture. It is _Windows x86_ for a 32-bit Vim
+ and _Windows x86-64_ for a 64-bit Vim. We recommend installing Python 3.
+ Additionally, the version of Python you install must match up exactly with
+ the version of Python that Vim is looking for. Type `:version` and look at the
+ bottom of the page at the list of compiler flags. Look for flags that look
+- similar to `-DDYNAMIC_PYTHON_DLL=\"python27.dll\"` and
+- `-DDYNAMIC_PYTHON3_DLL=\"python35.dll\"`. The former indicates that Vim is
+- looking for Python 2.7 and the latter indicates that Vim is looking for
+- Python 3.5. You'll need one or the other installed, matching the version
+- number exactly.
++ similar to `-DDYNAMIC_PYTHON3_DLL=\"python35.dll\"`. This indicates
++ that Vim is looking for Python 3.5. You'll need one or the other installed,
++ matching the version number exactly.
+ - [CMake][cmake-download]. Add CMake executable to the PATH environment
+ variable.
+ - [Visual Studio Build Tools 2017][visual-studio-download]. During setup,
+@@ -488,7 +486,7 @@ guide](#full-installation-guide).
+
+ **NOTE:** OpenBSD / FreeBSD are not officially supported platforms by YCM.
+
+-Make sure you have Vim 7.4.1578 with Python 2 or Python 3 support.
++Make sure you have Vim 7.4.1578 with Python 3 support.
+
+ **NOTE**: For all features, such as signature help, use Vim 8.1.1875 or later.
+
+@@ -588,7 +586,7 @@ process.
+ **Please follow the instructions carefully. Read EVERY WORD.**
+
+ 1. **Ensure that your version of Vim is _at least_ 7.4.1578 _and_ that it has
+- support for Python 2 or Python 3 scripting**.
++ support for Python 3 scripting**.
+
+ Inside Vim, type `:version`. Look at the first two to three lines of output;
+ it should say `Vi IMproved X.Y`, where X.Y is the major version of vim. If
+@@ -654,7 +652,7 @@ process.
+ Debian-like Linux distro, this would be `sudo apt-get install python-dev
+ python3-dev`. On macOS they should already be present.
+
+- On Windows, you need to download and install [Python 2 or
++ On Windows, you need to download and install [Python
+ Python 3][python-win-download]. Pick the version corresponding to your Vim
+ architecture. You will also need Microsoft Visual C++ (MSVC) to build YCM.
+ You can obtain it by installing [Visual Studio Build
+@@ -1500,8 +1498,6 @@ should work out of the box with no additional configuration (provided that you
+ built YCM with the `--rust-completer` flag; see the [*Installation*
+ section](#installation) for details). The install script takes care of
+ installing [the Rust source code][rust-src], so no configuration is necessary.
+-In case you are running Python 2.7.8 and older, you will need to manually
+-install [rustup][].
+
+ To [configure RLS](#lsp-configuration) look up [rls configuration options][
+ rls-preferences]
+@@ -3252,34 +3248,6 @@ But fear not, you should be able to tweak your extra conf files to continue
+ working by using the `g:ycm_extra_conf_vim_data` option. See the docs on that
+ option for details.
+
+-### I get `ImportError` exceptions that mention `PyInit_ycm_core` or `initycm_core`
+-
+-These errors are caused by building the YCM native libraries for Python 2 and
+-trying to load them into a Python 3 process (or the other way around).
+-
+-For instance, if building for Python 2 but loading in Python 3:
+-
+-```
+-ImportError: dynamic module does not define init function (PyInit_ycm_core)
+-```
+-
+-If building for Python 3 but loading in Python 2:
+-
+-```
+-ImportError: dynamic module does not define init function (initycm_core)
+-```
+-
+-Setting the `g:ycm_server_python_interpreter` option to force the use of a
+-specific Python interpreter for `ycmd` is usually the easiest way to solve the
+-problem. Common values for that option are `/usr/bin/python` and
+-`/usr/bin/python3`.
+-
+-### I get a linker warning regarding `libpython` on macOS when compiling YCM
+-
+-If the warning is `ld: warning: path '/usr/lib/libpython2.7.dylib' following -L
+-not a directory`, then feel free to ignore it; it's caused by a limitation of
+-CMake and is not an issue. Everything should still work fine.
+-
+ ### I get a weird window at the top of my file when I use the semantic engine
+
+ This is Vim's `preview` window. Vim uses it to show you extra information about
+@@ -3357,20 +3325,20 @@ Look at the output of your CMake call. There should be a line in it like the
+ following (with `.dylib` in place of `.so` on macOS):
+
+ ```
+--- Found PythonLibs: /usr/lib/libpython2.7.so (Required is at least version "2.5")
++-- Found PythonLibs: /usr/lib/libpython3.6.so (Required is at least version "3.5")
+ ```
+
+ That would be the **correct** output. An example of **incorrect** output would
+ be the following:
+
+ ```
+--- Found PythonLibs: /usr/lib/libpython2.7.so (found suitable version "2.5.1", minimum required is "2.5")
++-- Found PythonLibs: /usr/lib/libpython3.6.so (found suitable version "3.5.1", minimum required is "3.5")
+ ```
+
+ Notice how there's an extra bit of output there, the `found suitable version
+ "<version>"` part, where `<version>` is not the same as the version of the
+-dynamic library. In the example shown, the library is version 2.7 but the second
+-string is version `2.5.1`.
++dynamic library. In the example shown, the library is version 3.6 but the second
++string is version `3.5.1`.
+
+ This means that CMake found one version of Python headers and a different
+ version for the library. This is wrong. It can happen when you have multiple
+@@ -3380,7 +3348,7 @@ You should probably add the following flags to your cmake call (again, `dylib`
+ instead of `so` on macOS):
+
+ ```
+--DPYTHON_INCLUDE_DIR=/usr/include/python2.7 -DPYTHON_LIBRARY=/usr/lib/libpython2.7.so
++-DPYTHON_INCLUDE_DIR=/usr/include/python3.6 -DPYTHON_LIBRARY=/usr/lib/libpython3.6.so
+ ```
+
+ This will force the paths to the Python include directory and the Python library
+@@ -3388,22 +3356,22 @@ to use. You may need to set these flags to something else, but you need to make
+ sure you use the same version of Python that your Vim binary is built against,
+ which is highly likely to be the system's default Python.
+
+-### I get `libpython2.7.a [...] relocation R_X86_64_32` when compiling
++### I get `libpython3.5.a [...] relocation R_X86_64_32` when compiling
+
+ The error is usually encountered when compiling YCM on Centos or RHEL. The full
+ error looks something like the following:
+
+ ```
+-/usr/bin/ld: /usr/local/lib/libpython2.7.a(abstract.o): relocation R_X86_64_32 against `a local symbol' can not be used when making a shared object; recompile with -fPIC
++/usr/bin/ld: /usr/local/lib/libpython3.5.a(abstract.o): relocation R_X86_64_32 against `a local symbol' can not be used when making a shared object; recompile with -fPIC
+ ```
+
+ It's possible to get a slightly different error that's similar to the one above.
+ Here's the problem and how you solve it:
+
+-Your `libpython2.7.a` was not compiled with `-fPIC` so it can't be linked into
++Your `libpython3.5.a` was not compiled with `-fPIC` so it can't be linked into
+ `ycm_core.so`. Use the `-DPYTHON_LIBRARY=` CMake flag to point it to a `.so`
+ version of libpython on your machine (for instance,
+-`-DPYTHON_LIBRARY=/usr/lib/libpython2.7.so`). Naturally, this means you'll have
++`-DPYTHON_LIBRARY=/usr/lib/libpython3.5.so`). Naturally, this means you'll have
+ to go through the full installation guide by hand.
+
+ ### I see `undefined symbol: clang_getCompletionFixIt` in the server logs.
+@@ -3640,20 +3608,6 @@ os.environ['PATH'] = ';'.join(path)
+ EOF
+ ```
+
+-### I hear that YCM only supports Python 2, is that true?
+-
+-**No.** Both the Vim client and the [ycmd server][ycmd] run on Python 2 or 3. If
+-you are talking about code completion in a project, you can configure the Python
+-used for your project through a `.ycm_extra_conf.py` file. See [the Python
+-Semantic Completion section](#python-semantic-completion) for more details.
+-
+-### On Windows I get `E887: Sorry, this command is disabled, the Python's site module could not be loaded`
+-
+-If you are running vim on Windows with Python 2.7.11, this is likely caused by a
+-[bug][vim_win-python2.7.11-bug]. Follow this
+-[workaround][vim_win-python2.7.11-bug_workaround] or use a different version
+-(Python 2.7.12 does not suffer from the bug).
+-
+ ### I can't complete Python packages in a virtual environment.
+
+ This means that the Python used to run [Jedi][] is not the Python of the virtual
+@@ -3823,8 +3777,6 @@ This software is licensed under the [GPL v3 license][gpl].
+ [add-msbuild-to-path]: http://stackoverflow.com/questions/6319274/how-do-i-run-msbuild-from-the-command-line-using-windows-sdk-7-1
+ [identify-R6034-cause]: http://stackoverflow.com/questions/14552348/runtime-error-r6034-in-embedded-python-application/34696022
+ [ccoc]: https://github.com/Valloric/YouCompleteMe/blob/master/CODE_OF_CONDUCT.md
+-[vim_win-python2.7.11-bug]: https://github.com/vim/vim/issues/717
+-[vim_win-python2.7.11-bug_workaround]: https://github.com/vim/vim-win32-installer/blob/a27bbdba9bb87fa0e44c8a00d33d46be936822dd/appveyor.bat#L86-L88
+ [gitter]: https://gitter.im/Valloric/YouCompleteMe
+ [ninja-compdb]: https://ninja-build.org/manual.html
+ [++enc]: http://vimdoc.sourceforge.net/htmldoc/editing.html#++enc
+diff --git a/autoload/youcompleteme.vim b/autoload/youcompleteme.vim
+index d8f4703e5..8e07aa8e7 100644
+--- a/autoload/youcompleteme.vim
++++ b/autoload/youcompleteme.vim
+@@ -58,32 +58,6 @@ let s:buftype_blacklist = {
+ \ }
+
+
+-" When both versions are available, we prefer Python 3 over Python 2:
+-" - faster startup (no monkey-patching from python-future);
+-" - better Windows support (e.g. temporary paths are not returned in all
+-" lowercase);
+-" - Python 2 support will eventually be dropped.
+-function! s:UsingPython3()
+- if has('python3')
+- return 1
+- endif
+- return 0
+-endfunction
+-
+-
+-let s:using_python3 = s:UsingPython3()
+-let s:python_until_eof = s:using_python3 ? "python3 << EOF" : "python << EOF"
+-let s:python_command = s:using_python3 ? "py3 " : "py "
+-
+-
+-function! s:Pyeval( eval_string )
+- if s:using_python3
+- return py3eval( a:eval_string )
+- endif
+- return pyeval( a:eval_string )
+-endfunction
+-
+-
+ function! s:StartMessagePoll()
+ if s:pollers.receive_messages.id < 0
+ let s:pollers.receive_messages.id = timer_start(
+@@ -96,10 +70,9 @@ endfunction
+ function! s:ReceiveMessages( timer_id )
+ let poll_again = v:false
+ if s:AllowedToCompleteInCurrentBuffer()
+- let poll_again = s:Pyeval( 'ycm_state.OnPeriodicTick()' )
++ let poll_again = py3eval( 'ycm_state.OnPeriodicTick()' )
+ endif
+
+-
+ if poll_again
+ let s:pollers.receive_messages.id = timer_start(
+ \ s:pollers.receive_messages.wait_milliseconds,
+@@ -164,7 +137,7 @@ function! youcompleteme#Enable()
+ \ s:pollers.server_ready.wait_milliseconds,
+ \ function( 's:PollServerReady' ) )
+
+- let s:default_completion = s:Pyeval( 'vimsupport.NO_COMPLETIONS' )
++ let s:default_completion = py3eval( 'vimsupport.NO_COMPLETIONS' )
+ let s:completion = s:default_completion
+
+ if exists( '*prop_type_add' ) && exists( '*prop_type_delete' )
+@@ -198,50 +171,24 @@ endfunction
+
+
+ function! youcompleteme#GetErrorCount()
+- return s:Pyeval( 'ycm_state.GetErrorCount()' )
++ return py3eval( 'ycm_state.GetErrorCount()' )
+ endfunction
+
+
+ function! youcompleteme#GetWarningCount()
+- return s:Pyeval( 'ycm_state.GetWarningCount()' )
++ return py3eval( 'ycm_state.GetWarningCount()' )
+ endfunction
+
+
+ function! s:SetUpPython() abort
+- exec s:python_until_eof
+-from __future__ import unicode_literals
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-
++ py3 << EOF
+ import os.path as p
+-import re
+ import sys
+ import traceback
+ import vim
+
+ root_folder = p.normpath( p.join( vim.eval( 's:script_folder_path' ), '..' ) )
+ third_party_folder = p.join( root_folder, 'third_party' )
+-python_stdlib_zip_regex = re.compile( 'python[23][0-9]\\.zip' )
+-
+-
+-def IsStandardLibraryFolder( path ):
+- return ( ( p.isfile( path )
+- and python_stdlib_zip_regex.match( p.basename( path ) ) )
+- or p.isfile( p.join( path, 'os.py' ) ) )
+-
+-
+-def IsVirtualEnvLibraryFolder( path ):
+- return p.isfile( p.join( path, 'orig-prefix.txt' ) )
+-
+-
+-def GetStandardLibraryIndexInSysPath():
+- for index, path in enumerate( sys.path ):
+- if ( IsStandardLibraryFolder( path ) and
+- not IsVirtualEnvLibraryFolder( path ) ):
+- return index
+- raise RuntimeError( 'Could not find standard library path in Python path.' )
+-
+
+ # Add dependencies to Python path.
+ dependencies = [ p.join( root_folder, 'python' ),
+@@ -256,18 +203,10 @@ dependencies = [ p.join( root_folder, 'python' ),
+ p.join( third_party_folder, 'requests_deps', 'certifi' ),
+ p.join( third_party_folder, 'requests_deps', 'requests' ) ]
+
+-# The concurrent.futures module is part of the standard library on Python 3.
+-if sys.version_info[ 0 ] == 2:
+- dependencies.append( p.join( third_party_folder, 'pythonfutures' ) )
+-
+ sys.path[ 0:0 ] = dependencies
+
+ # We enclose this code in a try/except block to avoid backtraces in Vim.
+ try:
+- # The python-future module must be inserted after the standard library path.
+- sys.path.insert( GetStandardLibraryIndexInSysPath() + 1,
+- p.join( third_party_folder, 'python-future', 'src' ) )
+-
+ # Import the modules used in this file.
+ from ycm import base, vimsupport, youcompleteme
+
+@@ -315,14 +254,12 @@ function! s:SetUpKeyMappings()
+ " With this command, when the completion window is visible, the tab key
+ " (default) will select the next candidate in the window. In vim, this also
+ " changes the typed-in text to that of the candidate completion.
+- exe 'inoremap <expr>' . key .
+- \ ' pumvisible() ? "\<C-n>" : "\' . key .'"'
++ exe 'inoremap <expr>' . key . ' pumvisible() ? "\<C-n>" : "\' . key .'"'
+ endfor
+
+ for key in g:ycm_key_list_previous_completion
+ " This selects the previous candidate for shift-tab (default)
+- exe 'inoremap <expr>' . key .
+- \ ' pumvisible() ? "\<C-p>" : "\' . key .'"'
++ exe 'inoremap <expr>' . key . ' pumvisible() ? "\<C-p>" : "\' . key .'"'
+ endfor
+
+ for key in g:ycm_key_list_stop_completion
+@@ -387,10 +324,12 @@ function! s:SetUpSigns()
+ highlight link YcmWarningLine SyntasticWarningLine
+ endif
+
+- exe 'sign define YcmError text=' . g:ycm_error_symbol .
+- \ ' texthl=YcmErrorSign linehl=YcmErrorLine'
+- exe 'sign define YcmWarning text=' . g:ycm_warning_symbol .
+- \ ' texthl=YcmWarningSign linehl=YcmWarningLine'
++ call sign_define( 'YcmError', { 'text': g:ycm_error_symbol,
++ \ 'texthl': 'YcmErrorSign',
++ \ 'linehl': 'YcmErrorLine' } )
++ call sign_define( 'YcmWarning', { 'text': g:ycm_error_symbol,
++ \ 'texthl': 'YcmWarningSign',
++ \ 'linehl': 'YcmWarningLine' } )
+ endfunction
+
+
+@@ -451,9 +390,8 @@ function! s:DisableOnLargeFile( buffer )
+ let b:ycm_largefile =
+ \ threshold > 0 && getfsize( expand( a:buffer ) ) > threshold
+ if b:ycm_largefile
+- exec s:python_command "vimsupport.PostVimMessage(" .
+- \ "'YouCompleteMe is disabled in this buffer; " .
+- \ "the file exceeded the max size (see YCM options).' )"
++ py3 vimsupport.PostVimMessage( 'YouCompleteMe is disabled in this buffer;' .
++ \ ' the file exceeded the max size (see YCM options).' )
+ endif
+ return b:ycm_largefile
+ endfunction
+@@ -560,7 +498,7 @@ function! s:OnVimLeave()
+ for poller in values( s:pollers )
+ call s:StopPoller( poller )
+ endfor
+- exec s:python_command "ycm_state.OnVimLeave()"
++ py3 ycm_state.OnVimLeave()
+ endfunction
+
+
+@@ -569,7 +507,7 @@ function! s:OnCompleteDone()
+ return
+ endif
+
+- exec s:python_command "ycm_state.OnCompleteDone()"
++ py3 ycm_state.OnCompleteDone()
+ call s:UpdateSignatureHelp()
+ endfunction
+
+@@ -599,7 +537,7 @@ function! s:OnFileTypeSet()
+ call s:SetCompleteFunc()
+ call s:StartMessagePoll()
+
+- exec s:python_command "ycm_state.OnFileTypeSet()"
++ py3 ycm_state.OnFileTypeSet()
+ call s:OnFileReadyToParse( 1 )
+ endfunction
+
+@@ -613,7 +551,7 @@ function! s:OnBufferEnter()
+ call s:SetUpCompleteopt()
+ call s:SetCompleteFunc()
+
+- exec s:python_command "ycm_state.OnBufferVisit()"
++ py3 ycm_state.OnBufferVisit()
+ " Last parse may be outdated because of changes from other buffers. Force a
+ " new parse.
+ call s:OnFileReadyToParse( 1 )
+@@ -628,23 +566,23 @@ function! s:OnBufferUnload()
+ return
+ endif
+
+- exec s:python_command "ycm_state.OnBufferUnload( " . buffer_number . " )"
++ py3 ycm_state.OnBufferUnload( vimsupport.GetIntValue( 'buffer_number' ) )
+ endfunction
+
+
+ function! s:UpdateMatches()
+- exec s:python_command "ycm_state.UpdateMatches()"
++ py3 ycm_state.UpdateMatches()
+ endfunction
+
+
+ function! s:PollServerReady( timer_id )
+- if !s:Pyeval( 'ycm_state.IsServerAlive()' )
+- exec s:python_command "ycm_state.NotifyUserIfServerCrashed()"
++ if !py3eval( 'ycm_state.IsServerAlive()' )
++ py3 ycm_state.NotifyUserIfServerCrashed()
+ " Server crashed. Don't poll it again.
+ return
+ endif
+
+- if !s:Pyeval( 'ycm_state.CheckIfServerIsReady()' )
++ if !py3eval( 'ycm_state.CheckIfServerIsReady()' )
+ let s:pollers.server_ready.id = timer_start(
+ \ s:pollers.server_ready.wait_milliseconds,
+ \ function( 's:PollServerReady' ) )
+@@ -663,11 +601,11 @@ function! s:OnFileReadyToParse( ... )
+
+ " We only want to send a new FileReadyToParse event notification if the buffer
+ " has changed since the last time we sent one, or if forced.
+- if force_parsing || s:Pyeval( "ycm_state.NeedsReparse()" )
++ if force_parsing || py3eval( "ycm_state.NeedsReparse()" )
+ " We switched buffers or somethuing, so claer.
+ " FIXME: sig hekp should be buffer local?
+ call s:ClearSignatureHelp()
+- exec s:python_command "ycm_state.OnFileReadyToParse()"
++ py3 ycm_state.OnFileReadyToParse()
+
+ call s:StopPoller( s:pollers.file_parse_response )
+ let s:pollers.file_parse_response.id = timer_start(
+@@ -678,15 +616,15 @@ endfunction
+
+
+ function! s:PollFileParseResponse( ... )
+- if !s:Pyeval( "ycm_state.FileParseRequestReady()" )
++ if !py3eval( "ycm_state.FileParseRequestReady()" )
+ let s:pollers.file_parse_response.id = timer_start(
+ \ s:pollers.file_parse_response.wait_milliseconds,
+ \ function( 's:PollFileParseResponse' ) )
+ return
+ endif
+
+- exec s:python_command "ycm_state.HandleFileParseRequest()"
+- if s:Pyeval( "ycm_state.ShouldResendFileParseRequest()" )
++ py3 ycm_state.HandleFileParseRequest()
++ if py3eval( "ycm_state.ShouldResendFileParseRequest()" )
+ call s:OnFileReadyToParse( 1 )
+ endif
+ endfunction
+@@ -755,7 +693,7 @@ function! s:OnCursorMovedNormalMode()
+ return
+ endif
+
+- exec s:python_command "ycm_state.OnCursorMoved()"
++ py3 ycm_state.OnCursorMoved()
+ endfunction
+
+
+@@ -784,7 +722,7 @@ function! s:OnTextChangedInsertMode()
+ " We have to make sure we correctly leave semantic mode even when the user
+ " inserts something like a "operator[]" candidate string which fails
+ " CurrentIdentifierFinished check.
+- if s:force_semantic && !s:Pyeval( 'base.LastEnteredCharIsIdentifierChar()' )
++ if s:force_semantic && !py3eval( 'base.LastEnteredCharIsIdentifierChar()' )
+ let s:force_semantic = 0
+ endif
+
+@@ -800,7 +738,7 @@ function! s:OnTextChangedInsertMode()
+ call s:RequestSignatureHelp()
+ endif
+
+- exec s:python_command "ycm_state.OnCursorMoved()"
++ py3 ycm_state.OnCursorMoved()
+
+ if g:ycm_autoclose_preview_window_after_completion
+ call s:ClosePreviewWindowIfNeeded()
+@@ -818,7 +756,7 @@ function! s:OnInsertLeave()
+ let s:completion = s:default_completion
+
+ call s:OnFileReadyToParse()
+- exec s:python_command "ycm_state.OnInsertLeave()"
++ py3 ycm_state.OnInsertLeave()
+ if g:ycm_autoclose_preview_window_after_completion ||
+ \ g:ycm_autoclose_preview_window_after_insertion
+ call s:ClosePreviewWindowIfNeeded()
+@@ -845,10 +783,10 @@ endfunction
+
+
+ function! s:IdentifierFinishedOperations()
+- if !s:Pyeval( 'base.CurrentIdentifierFinished()' )
++ if !py3eval( 'base.CurrentIdentifierFinished()' )
+ return
+ endif
+- exec s:python_command "ycm_state.OnCurrentIdentifierFinished()"
++ py3 ycm_state.OnCurrentIdentifierFinished()
+ let s:force_semantic = 0
+ let s:completion = s:default_completion
+ endfunction
+@@ -888,13 +826,13 @@ endfunction
+
+
+ function! s:OnBlankLine()
+- return s:Pyeval( 'not vim.current.line or vim.current.line.isspace()' )
++ return py3eval( 'not vim.current.line or vim.current.line.isspace()' )
+ endfunction
+
+
+ function! s:RequestCompletion()
+- exec s:python_command "ycm_state.SendCompletionRequest(" .
+- \ "vimsupport.GetBoolValue( 's:force_semantic' ) )"
++ py3 ycm_state.SendCompletionRequest(
++ \ vimsupport.GetBoolValue( 's:force_semantic' ) )
+
+ call s:PollCompletion()
+ endfunction
+@@ -903,7 +841,7 @@ endfunction
+ function! s:RequestSemanticCompletion()
+ if &completefunc == "youcompleteme#CompleteFunc"
+ let s:force_semantic = 1
+- exec s:python_command "ycm_state.SendCompletionRequest( True )"
++ py3 ycm_state.SendCompletionRequest( True )
+
+ call s:PollCompletion()
+ endif
+@@ -916,20 +854,20 @@ endfunction
+
+
+ function! s:PollCompletion( ... )
+- if !s:Pyeval( 'ycm_state.CompletionRequestReady()' )
++ if !py3eval( 'ycm_state.CompletionRequestReady()' )
+ let s:pollers.completion.id = timer_start(
+ \ s:pollers.completion.wait_milliseconds,
+ \ function( 's:PollCompletion' ) )
+ return
+ endif
+
+- let s:completion = s:Pyeval( 'ycm_state.GetCompletionResponse()' )
++ let s:completion = py3eval( 'ycm_state.GetCompletionResponse()' )
+ call s:Complete()
+ endfunction
+
+
+ function! s:ShouldUseSignatureHelp()
+- return s:Pyeval( 'vimsupport.VimSupportsPopupWindows()' )
++ return py3eval( 'vimsupport.VimSupportsPopupWindows()' )
+ endfunction
+
+
+@@ -943,7 +881,7 @@ function! s:RequestSignatureHelp()
+ return
+ endif
+
+- if s:Pyeval( 'ycm_state.SendSignatureHelpRequest()' )
++ if py3eval( 'ycm_state.SendSignatureHelpRequest()' )
+ call s:PollSignatureHelp()
+ endif
+ endfunction
+@@ -960,14 +898,14 @@ function! s:PollSignatureHelp( ... )
+ return
+ endif
+
+- if !s:Pyeval( 'ycm_state.SignatureHelpRequestReady()' )
++ if !py3eval( 'ycm_state.SignatureHelpRequestReady()' )
+ let s:pollers.signature_help.id = timer_start(
+ \ s:pollers.signature_help.wait_milliseconds,
+ \ function( 's:PollSignatureHelp' ) )
+ return
+ endif
+
+- let s:signature_help = s:Pyeval( 'ycm_state.GetSignatureHelpResponse()' )
++ let s:signature_help = py3eval( 'ycm_state.GetSignatureHelpResponse()' )
+ call s:UpdateSignatureHelp()
+ endfunction
+
+@@ -1023,7 +961,7 @@ function! s:UpdateSignatureHelp()
+ return
+ endif
+
+- call s:Pyeval(
++ call py3eval(
+ \ 'ycm_state.UpdateSignatureHelp( vim.eval( "s:signature_help" ) )' )
+ endfunction
+
+@@ -1035,12 +973,12 @@ function! s:ClearSignatureHelp()
+
+ call s:StopPoller( s:pollers.signature_help )
+ let s:signature_help = s:default_signature_help
+- call s:Pyeval( 'ycm_state.ClearSignatureHelp()' )
++ call py3eval( 'ycm_state.ClearSignatureHelp()' )
+ endfunction
+
+
+ function! youcompleteme#ServerPid()
+- return s:Pyeval( 'ycm_state.ServerPid()' )
++ return py3eval( 'ycm_state.ServerPid()' )
+ endfunction
+
+
+@@ -1049,7 +987,7 @@ function! s:SetUpCommands()
+ command! YcmDebugInfo call s:DebugInfo()
+ command! -nargs=* -complete=custom,youcompleteme#LogsComplete
+ \ YcmToggleLogs call s:ToggleLogs(<f-args>)
+- if s:Pyeval( 'vimsupport.VimVersionAtLeast( "7.4.1898" )' )
++ if py3eval( 'vimsupport.VimVersionAtLeast( "7.4.1898" )' )
+ command! -nargs=* -complete=custom,youcompleteme#SubCommandsComplete -range
+ \ YcmCompleter call s:CompleterCommand(<q-mods>,
+ \ <count>,
+@@ -1073,7 +1011,7 @@ endfunction
+ function! s:RestartServer()
+ call s:SetUpOptions()
+
+- exec s:python_command "ycm_state.RestartServer()"
++ py3 ycm_state.RestartServer()
+
+ call s:StopPoller( s:pollers.receive_messages )
+ call s:ClearSignatureHelp()
+@@ -1087,7 +1025,7 @@ endfunction
+
+ function! s:DebugInfo()
+ echom "Printing YouCompleteMe debug information..."
+- let debug_info = s:Pyeval( 'ycm_state.DebugInfo()' )
++ let debug_info = py3eval( 'ycm_state.DebugInfo()' )
+ for line in split( debug_info, "\n" )
+ echom '-- ' . line
+ endfor
+@@ -1095,50 +1033,50 @@ endfunction
+
+
+ function! s:ToggleLogs(...)
+- exec s:python_command "ycm_state.ToggleLogs( *vim.eval( 'a:000' ) )"
++ py3 ycm_state.ToggleLogs( *vim.eval( 'a:000' ) )
+ endfunction
+
+
+ function! youcompleteme#LogsComplete( arglead, cmdline, cursorpos )
+- return join( s:Pyeval( 'list( ycm_state.GetLogfiles() )' ), "\n" )
++ return join( py3eval( 'list( ycm_state.GetLogfiles() )' ), "\n" )
+ endfunction
+
+
+ function! s:CompleterCommand( mods, count, line1, line2, ... )
+- exec s:python_command "ycm_state.SendCommandRequest(" .
+- \ "vim.eval( 'a:000' )," .
+- \ "vim.eval( 'a:mods' )," .
+- \ "vimsupport.GetBoolValue( 'a:count != -1' )," .
+- \ "vimsupport.GetIntValue( 'a:line1' )," .
+- \ "vimsupport.GetIntValue( 'a:line2' ) )"
++ py3 ycm_state.SendCommandRequest(
++ \ vim.eval( 'a:000' ),
++ \ vim.eval( 'a:mods' ),
++ \ vimsupport.GetBoolValue( 'a:count != -1' ),
++ \ vimsupport.GetIntValue( 'a:line1' ),
++ \ vimsupport.GetIntValue( 'a:line2' ) )
+ endfunction
+
+
+ function! youcompleteme#SubCommandsComplete( arglead, cmdline, cursorpos )
+- return join( s:Pyeval( 'ycm_state.GetDefinedSubcommands()' ), "\n" )
++ return join( py3eval( 'ycm_state.GetDefinedSubcommands()' ), "\n" )
+ endfunction
+
+
+ function! youcompleteme#OpenGoToList()
+- exec s:python_command "vimsupport.PostVimMessage(" .
++ py3 vimsupport.PostVimMessage(
+ \ "'WARNING: youcompleteme#OpenGoToList function is deprecated. " .
+- \ "Do NOT use it.' )"
+- exec s:python_command "vimsupport.OpenQuickFixList( True, True )"
++ \ "Do NOT use it.'" )
++ py3 vimsupport.OpenQuickFixList( True, True )
+ endfunction
+
+
+ function! s:ShowDiagnostics()
+- exec s:python_command "ycm_state.ShowDiagnostics()"
++ py3 ycm_state.ShowDiagnostics()
+ endfunction
+
+
+ function! s:ShowDetailedDiagnostic()
+- exec s:python_command "ycm_state.ShowDetailedDiagnostic()"
++ py3 ycm_state.ShowDetailedDiagnostic()
+ endfunction
+
+
+ function! s:ForceCompileAndDiagnostics()
+- exec s:python_command "ycm_state.ForceCompileAndDiagnostics()"
++ py3 ycm_state.ForceCompileAndDiagnostics()
+ endfunction
+
+
+diff --git a/azure-pipelines.yml b/azure-pipelines.yml
+index c23f2a62d..3b43a69c4 100644
+--- a/azure-pipelines.yml
++++ b/azure-pipelines.yml
+@@ -18,10 +18,6 @@ jobs:
+ vmImage: 'ubuntu-16.04'
+ strategy:
+ matrix:
+- 'Python 2.7':
+- # Tests are failing on Python 2.7.0 with the exception
+- # "TypeError: argument can't be <type 'unicode'>"
+- YCM_PYTHON_VERSION: '2.7.13'
+ 'Python 3.5':
+ YCM_PYTHON_VERSION: '3.5.3'
+ maxParallel: 2
+@@ -49,9 +45,6 @@ jobs:
+ vmImage: 'ubuntu-16.04'
+ strategy:
+ matrix:
+- 'Python 2.7':
+- IMAGE: ycm-vim-py2
+- PIP: pip
+ 'Python 3.5':
+ IMAGE: ycm-vim-py3
+ PIP: pip3
+@@ -82,8 +75,6 @@ jobs:
+ vmImage: 'macOS-10.13'
+ strategy:
+ matrix:
+- 'Python 2.7':
+- YCM_PYTHON_VERSION: '2.7.13'
+ 'Python 3.5':
+ YCM_PYTHON_VERSION: '3.5.3'
+ maxParallel: 2
+@@ -109,10 +100,6 @@ jobs:
+ vmImage: 'windows-2019'
+ strategy:
+ matrix:
+- # We only test Python 2.7 on 64-bit.
+- 'Python 2.7 64-bit':
+- YCM_PYTHON_VERSION: '2.7'
+- YCM_ARCH: x64
+ 'Python 3.7 32-bit':
+ YCM_PYTHON_VERSION: '3.7'
+ YCM_ARCH: x86
+diff --git a/install.sh b/install.sh
+index 8c9c2fd1b..82ecf88c1 100755
+--- a/install.sh
++++ b/install.sh
+@@ -5,13 +5,4 @@ echo "WARNING: this script is deprecated. Use the install.py script instead." 1>
+
+ SCRIPT_DIR=$(dirname $0 || exit $?)
+
+-command_exists() {
+- command -v "$1" >/dev/null 2>&1 ;
+-}
+-
+-PYTHON_BINARY=python
+-if command_exists python2; then
+- PYTHON_BINARY=python2
+-fi
+-
+-$PYTHON_BINARY "$SCRIPT_DIR/install.py" "$@" || exit $?
++python3 "$SCRIPT_DIR/install.py" "$@" || exit $?
+diff --git a/plugin/youcompleteme.vim b/plugin/youcompleteme.vim
+index d2994c25a..bfe77516a 100644
+--- a/plugin/youcompleteme.vim
++++ b/plugin/youcompleteme.vim
+@@ -55,17 +55,17 @@ elseif !has( 'timers' )
+ call s:restore_cpo()
+ finish
+ elseif ( v:version > 800 || ( v:version == 800 && has( 'patch1436' ) ) ) &&
+- \ !has( 'python_compiled' ) && !has( 'python3_compiled' )
++ \ !has( 'python3_compiled' )
+ echohl WarningMsg |
+ \ echomsg "YouCompleteMe unavailable: requires Vim compiled with " .
+- \ "Python (2.7.1+ or 3.5.1+) support." |
++ \ "Python (3.5.1+) support." |
+ \ echohl None
+ call s:restore_cpo()
+ finish
+-" These calls try to load the Python 2 and Python 3 libraries when Vim is
++" These calls try to load the Python 3 libraries when Vim is
+ " compiled dynamically against them. Since only one can be loaded at a time on
+ " some platforms, we first check if Python 3 is available.
+-elseif !has( 'python3' ) && !has( 'python' )
++elseif !has( 'python3' )
+ echohl WarningMsg |
+ \ echomsg "YouCompleteMe unavailable: unable to load Python." |
+ \ echohl None
+diff --git a/python/ycm/base.py b/python/ycm/base.py
+index 7013dfed1..2d331c618 100644
+--- a/python/ycm/base.py
++++ b/python/ycm/base.py
+@@ -15,13 +15,6 @@
+ # You should have received a copy of the GNU General Public License
+ # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+
+-from __future__ import unicode_literals
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-# Not installing aliases from python-future; it's unreliable and slow.
+-from builtins import * # noqa
+-
+ from ycm import vimsupport
+ from ycmd import identifier_utils
+
+diff --git a/python/ycm/buffer.py b/python/ycm/buffer.py
+index b95c0da32..848adc898 100644
+--- a/python/ycm/buffer.py
++++ b/python/ycm/buffer.py
+@@ -15,13 +15,6 @@
+ # You should have received a copy of the GNU General Public License
+ # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+
+-from __future__ import unicode_literals
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-# Not installing aliases from python-future; it's unreliable and slow.
+-from builtins import * # noqa
+-
+ from ycm import vimsupport
+ from ycm.client.event_notification import EventNotification
+ from ycm.diagnostic_interface import DiagnosticInterface
+@@ -34,7 +27,7 @@
+ # Used to store buffer related information like diagnostics, latest parse
+ # request. Stores buffer change tick at the parse request moment, allowing
+ # to effectively determine whether reparse is needed for the buffer.
+-class Buffer( object ):
++class Buffer:
+
+ def __init__( self, bufnr, user_options, filetypes ):
+ self._number = bufnr
+diff --git a/python/ycm/client/base_request.py b/python/ycm/client/base_request.py
+index ec7d5ecea..e474fc54f 100644
+--- a/python/ycm/client/base_request.py
++++ b/python/ycm/client/base_request.py
+@@ -15,21 +15,15 @@
+ # You should have received a copy of the GNU General Public License
+ # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+
+-from __future__ import unicode_literals
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-# Not installing aliases from python-future; it's unreliable and slow.
+-from builtins import * # noqa
+-
+ import logging
+ import json
+ import vim
+-from future.utils import native
+ from base64 import b64decode, b64encode
++from hmac import compare_digest
++from urllib.parse import urljoin, urlparse
+ from ycm import vimsupport
+-from ycmd.utils import ToBytes, urljoin, urlparse, GetCurrentDirectory
+-from ycmd.hmac_utils import CreateRequestHmac, CreateHmac, SecureBytesEqual
++from ycmd.utils import ToBytes, GetCurrentDirectory
++from ycmd.hmac_utils import CreateRequestHmac, CreateHmac
+ from ycmd.responses import ServerError, UnknownExtraConf
+
+ _HEADERS = { 'content-type': 'application/json' }
+@@ -40,7 +34,7 @@
+ _logger = logging.getLogger( __name__ )
+
+
+-class BaseRequest( object ):
++class BaseRequest:
+
+ def __init__( self ):
+ self._should_resend = False
+@@ -298,13 +292,13 @@ def _ToUtf8Json( data ):
+ def _ValidateResponseObject( response ):
+ our_hmac = CreateHmac( response.content, BaseRequest.hmac_secret )
+ their_hmac = ToBytes( b64decode( response.headers[ _HMAC_HEADER ] ) )
+- if not SecureBytesEqual( our_hmac, their_hmac ):
++ if not compare_digest( our_hmac, their_hmac ):
+ raise RuntimeError( 'Received invalid HMAC for response!' )
+ return True
+
+
+ def _BuildUri( handler ):
+- return native( ToBytes( urljoin( BaseRequest.server_location, handler ) ) )
++ return ToBytes( urljoin( BaseRequest.server_location, handler ) )
+
+
+ def MakeServerException( data ):
+diff --git a/python/ycm/client/command_request.py b/python/ycm/client/command_request.py
+index 168af18dd..ac25304e9 100644
+--- a/python/ycm/client/command_request.py
++++ b/python/ycm/client/command_request.py
+@@ -15,13 +15,6 @@
+ # You should have received a copy of the GNU General Public License
+ # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+
+-from __future__ import unicode_literals
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-# Not installing aliases from python-future; it's unreliable and slow.
+-from builtins import * # noqa
+-
+ from ycm.client.base_request import BaseRequest, BuildRequestData
+ from ycm import vimsupport
+ from ycmd.utils import ToUnicode
+diff --git a/python/ycm/client/completer_available_request.py b/python/ycm/client/completer_available_request.py
+index 9ee9dd3f8..7822876fc 100644
+--- a/python/ycm/client/completer_available_request.py
++++ b/python/ycm/client/completer_available_request.py
+@@ -15,13 +15,6 @@
+ # You should have received a copy of the GNU General Public License
+ # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+
+-from __future__ import unicode_literals
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-# Not installing aliases from python-future; it's unreliable and slow.
+-from builtins import * # noqa
+-
+ from ycm.client.base_request import BaseRequest, BuildRequestData
+
+
+diff --git a/python/ycm/client/completion_request.py b/python/ycm/client/completion_request.py
+index fa7cd223a..8e0b72771 100644
+--- a/python/ycm/client/completion_request.py
++++ b/python/ycm/client/completion_request.py
+@@ -15,13 +15,6 @@
+ # You should have received a copy of the GNU General Public License
+ # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+
+-from __future__ import unicode_literals
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-# Not installing aliases from python-future; it's unreliable and slow.
+-from builtins import * # noqa
+-
+ import logging
+ from ycmd.utils import ToUnicode
+ from ycm.client.base_request import ( BaseRequest, DisplayServerException,
+diff --git a/python/ycm/client/debug_info_request.py b/python/ycm/client/debug_info_request.py
+index 32d7bd3df..95949086b 100644
+--- a/python/ycm/client/debug_info_request.py
++++ b/python/ycm/client/debug_info_request.py
+@@ -15,13 +15,6 @@
+ # You should have received a copy of the GNU General Public License
+ # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+
+-from __future__ import unicode_literals
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-# Not installing aliases from python-future; it's unreliable and slow.
+-from builtins import * # noqa
+-
+ from ycm.client.base_request import BaseRequest, BuildRequestData
+
+
+diff --git a/python/ycm/client/event_notification.py b/python/ycm/client/event_notification.py
+index bdbe07e34..eee90eefe 100644
+--- a/python/ycm/client/event_notification.py
++++ b/python/ycm/client/event_notification.py
+@@ -15,13 +15,6 @@
+ # You should have received a copy of the GNU General Public License
+ # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+
+-from __future__ import unicode_literals
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-# Not installing aliases from python-future; it's unreliable and slow.
+-from builtins import * # noqa
+-
+ from ycm.client.base_request import BaseRequest, BuildRequestData
+
+
+diff --git a/python/ycm/client/messages_request.py b/python/ycm/client/messages_request.py
+index 0f1bd2aab..d382d1445 100644
+--- a/python/ycm/client/messages_request.py
++++ b/python/ycm/client/messages_request.py
+@@ -15,13 +15,6 @@
+ # You should have received a copy of the GNU General Public License
+ # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+
+-from __future__ import unicode_literals
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-# Not installing aliases from python-future; it's unreliable and slow.
+-from builtins import * # noqa
+-
+ from ycm.client.base_request import BaseRequest, BuildRequestData
+ from ycm.vimsupport import PostVimMessage
+
+diff --git a/python/ycm/client/omni_completion_request.py b/python/ycm/client/omni_completion_request.py
+index 397ba2128..3733603b1 100644
+--- a/python/ycm/client/omni_completion_request.py
++++ b/python/ycm/client/omni_completion_request.py
+@@ -15,13 +15,6 @@
+ # You should have received a copy of the GNU General Public License
+ # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+
+-from __future__ import unicode_literals
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-# Not installing aliases from python-future; it's unreliable and slow.
+-from builtins import * # noqa
+-
+ from ycm.client.completion_request import CompletionRequest
+
+
+diff --git a/python/ycm/client/shutdown_request.py b/python/ycm/client/shutdown_request.py
+index ae42734fb..159814315 100644
+--- a/python/ycm/client/shutdown_request.py
++++ b/python/ycm/client/shutdown_request.py
+@@ -15,13 +15,6 @@
+ # You should have received a copy of the GNU General Public License
+ # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+
+-from __future__ import unicode_literals
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-# Not installing aliases from python-future; it's unreliable and slow.
+-from builtins import * # noqa
+-
+ from ycm.client.base_request import BaseRequest
+
+ TIMEOUT_SECONDS = 0.1
+diff --git a/python/ycm/client/signature_help_request.py b/python/ycm/client/signature_help_request.py
+index 292b3407c..f65c47d39 100644
+--- a/python/ycm/client/signature_help_request.py
++++ b/python/ycm/client/signature_help_request.py
+@@ -15,13 +15,6 @@
+ # You should have received a copy of the GNU General Public License
+ # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+
+-from __future__ import unicode_literals
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-# Not installing aliases from python-future; it's unreliable and slow.
+-from builtins import * # noqa
+-
+ import logging
+ from ycm.client.base_request import ( BaseRequest, DisplayServerException,
+ MakeServerException )
+diff --git a/python/ycm/client/ycmd_keepalive.py b/python/ycm/client/ycmd_keepalive.py
+index 278ef86ea..a13727f73 100644
+--- a/python/ycm/client/ycmd_keepalive.py
++++ b/python/ycm/client/ycmd_keepalive.py
+@@ -15,13 +15,6 @@
+ # You should have received a copy of the GNU General Public License
+ # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+
+-from __future__ import unicode_literals
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-# Not installing aliases from python-future; it's unreliable and slow.
+-from builtins import * # noqa
+-
+ import time
+ from threading import Thread
+ from ycm.client.base_request import BaseRequest
+@@ -30,7 +23,7 @@
+ # This class can be used to keep the ycmd server alive for the duration of the
+ # life of the client. By default, ycmd shuts down if it doesn't see a request in
+ # a while.
+-class YcmdKeepalive( object ):
++class YcmdKeepalive:
+ def __init__( self, ping_interval_seconds = 60 * 10 ):
+ self._keepalive_thread = Thread( target = self._ThreadMain )
+ self._keepalive_thread.daemon = True
+diff --git a/python/ycm/diagnostic_filter.py b/python/ycm/diagnostic_filter.py
+index 7448f2c0a..eb651df48 100644
+--- a/python/ycm/diagnostic_filter.py
++++ b/python/ycm/diagnostic_filter.py
+@@ -15,19 +15,11 @@
+ # You should have received a copy of the GNU General Public License
+ # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+
+-from __future__ import unicode_literals
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-# Not installing aliases from python-future; it's unreliable and slow.
+-from builtins import * # noqa
+-
+-from future.utils import iterkeys, iteritems
+ from ycm import vimsupport
+ import re
+
+
+-class DiagnosticFilter( object ):
++class DiagnosticFilter:
+ def __init__( self, config_or_filters ):
+ if isinstance( config_or_filters, list ):
+ self._filters = config_or_filters
+@@ -55,7 +47,7 @@ def SubsetForTypes( self, filetypes ):
+ def CreateFromOptions( user_options ):
+ all_filters = user_options[ 'filter_diagnostics' ]
+ compiled_by_type = {}
+- for type_spec, filter_value in iteritems( all_filters ):
++ for type_spec, filter_value in all_filters.items():
+ filetypes = [ type_spec ]
+ if type_spec.find( ',' ) != -1:
+ filetypes = type_spec.split( ',' )
+@@ -65,7 +57,7 @@ def CreateFromOptions( user_options ):
+ return _MasterDiagnosticFilter( compiled_by_type )
+
+
+-class _MasterDiagnosticFilter( object ):
++class _MasterDiagnosticFilter:
+
+ def __init__( self, all_filters ):
+ self._all_filters = all_filters
+@@ -139,7 +131,7 @@ def _CompileFilters( config ):
+ """Given a filter config dictionary, return a list of compiled filters"""
+ filters = []
+
+- for filter_type in iterkeys( config ):
++ for filter_type in config.keys():
+ compiler = FILTER_COMPILERS.get( filter_type )
+
+ if compiler is not None:
+diff --git a/python/ycm/diagnostic_interface.py b/python/ycm/diagnostic_interface.py
+index 5475d961a..3e4d8f99b 100644
+--- a/python/ycm/diagnostic_interface.py
++++ b/python/ycm/diagnostic_interface.py
+@@ -15,20 +15,12 @@
+ # You should have received a copy of the GNU General Public License
+ # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+
+-from __future__ import unicode_literals
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-# Not installing aliases from python-future; it's unreliable and slow.
+-from builtins import * # noqa
+-
+-from future.utils import itervalues, iteritems
+ from collections import defaultdict
+ from ycm import vimsupport
+ from ycm.diagnostic_filter import DiagnosticFilter, CompileLevel
+
+
+-class DiagnosticInterface( object ):
++class DiagnosticInterface:
+ def __init__( self, bufnr, user_options ):
+ self._bufnr = bufnr
+ self._user_options = user_options
+@@ -114,7 +106,7 @@ def _EchoDiagnosticForLine( self, line_num ):
+
+ def _DiagnosticsCount( self, predicate ):
+ count = 0
+- for diags in itervalues( self._line_to_diags ):
++ for diags in self._line_to_diags.values():
+ count += sum( 1 for d in diags if predicate( d ) )
+ return count
+
+@@ -136,7 +128,7 @@ def UpdateMatches( self ):
+
+ matches_to_remove = vimsupport.GetDiagnosticMatchesInCurrentWindow()
+
+- for diags in itervalues( self._line_to_diags ):
++ for diags in self._line_to_diags.values():
+ # Insert squiggles in reverse order so that errors overlap warnings.
+ for diag in reversed( diags ):
+ group = ( 'YcmErrorSection' if _DiagnosticIsError( diag ) else
+@@ -157,7 +149,7 @@ def UpdateMatches( self ):
+ def _UpdateSigns( self ):
+ signs_to_unplace = vimsupport.GetSignsInBuffer( self._bufnr )
+
+- for line, diags in iteritems( self._line_to_diags ):
++ for line, diags in self._line_to_diags.items():
+ if not diags:
+ continue
+
+@@ -185,7 +177,7 @@ def _ConvertDiagListToDict( self ):
+ line_number = location[ 'line_num' ]
+ self._line_to_diags[ line_number ].append( diag )
+
+- for diags in itervalues( self._line_to_diags ):
++ for diags in self._line_to_diags.values():
+ # We also want errors to be listed before warnings so that errors aren't
+ # hidden by the warnings; Vim won't place a sign over an existing one.
+ diags.sort( key = lambda diag: ( diag[ 'kind' ],
+diff --git a/python/ycm/omni_completer.py b/python/ycm/omni_completer.py
+index a62a968b3..8c9f6a0fc 100644
+--- a/python/ycm/omni_completer.py
++++ b/python/ycm/omni_completer.py
+@@ -15,13 +15,6 @@
+ # You should have received a copy of the GNU General Public License
+ # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+
+-from __future__ import unicode_literals
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-# Not installing aliases from python-future; it's unreliable and slow.
+-from builtins import * # noqa
+-
+ import vim
+ from ycm import vimsupport
+ from ycmd import utils
+diff --git a/python/ycm/paths.py b/python/ycm/paths.py
+index 013a66e30..055c9d90a 100644
+--- a/python/ycm/paths.py
++++ b/python/ycm/paths.py
+@@ -15,13 +15,6 @@
+ # You should have received a copy of the GNU General Public License
+ # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+
+-from __future__ import unicode_literals
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-# Not installing aliases from python-future; it's unreliable and slow.
+-from builtins import * # noqa
+-
+ import os
+ import sys
+ import vim
+@@ -33,7 +26,7 @@
+ 'ycmd' )
+ WIN_PYTHON_PATH = os.path.join( sys.exec_prefix, 'python.exe' )
+ PYTHON_BINARY_REGEX = re.compile(
+- r'python((2(\.7)?)|(3(\.[5-9])?))?(.exe)?$', re.IGNORECASE )
++ r'python(3(\.[5-9])?)?(.exe)?$', re.IGNORECASE )
+
+
+ # Not caching the result of this function; users shouldn't have to restart Vim
+@@ -51,7 +44,7 @@ def PathToPythonInterpreter():
+ return python_interpreter
+
+ raise RuntimeError( "Path in 'g:ycm_server_python_interpreter' option "
+- "does not point to a valid Python 2.7 or 3.5+." )
++ "does not point to a valid Python 3.5+." )
+
+ python_interpreter = _PathToPythonUsedDuringBuild()
+ if python_interpreter and utils.GetExecutable( python_interpreter ):
+@@ -66,18 +59,12 @@ def PathToPythonInterpreter():
+ if _EndsWithPython( python_interpreter ):
+ return python_interpreter
+
+- # As a last resort, we search python in the PATH. We prefer Python 2 over 3
+- # for the sake of backwards compatibility with ycm_extra_conf.py files out
+- # there; few people wrote theirs to work on py3.
+- # So we check 'python2' before 'python' because on some distributions (Arch
+- # Linux for example), python refers to python3.
+- python_interpreter = utils.PathToFirstExistingExecutable( [ 'python2',
+- 'python',
+- 'python3' ] )
++ python_interpreter = utils.PathToFirstExistingExecutable( [ 'python3',
++ 'python' ] )
+ if python_interpreter:
+ return python_interpreter
+
+- raise RuntimeError( "Cannot find Python 2.7 or 3.5+. "
++ raise RuntimeError( "Cannot find Python 3.5+. "
+ "Set the 'g:ycm_server_python_interpreter' option "
+ "to a Python interpreter path." )
+
+@@ -88,13 +75,12 @@ def _PathToPythonUsedDuringBuild():
+ try:
+ filepath = os.path.join( DIR_OF_YCMD, 'PYTHON_USED_DURING_BUILDING' )
+ return utils.ReadFile( filepath ).strip()
+- # We need to check for IOError for Python2 and OSError for Python3
+- except ( IOError, OSError ):
++ except OSError:
+ return None
+
+
+ def _EndsWithPython( path ):
+- """Check if given path ends with a python 2.7 or 3.5+ name."""
++ """Check if given path ends with a python 3.5+ name."""
+ return path and PYTHON_BINARY_REGEX.search( path ) is not None
+
+
+diff --git a/python/ycm/signature_help.py b/python/ycm/signature_help.py
+index 821aa4350..0fd6dd3a7 100644
+--- a/python/ycm/signature_help.py
++++ b/python/ycm/signature_help.py
+@@ -15,13 +15,6 @@
+ # You should have received a copy of the GNU General Public License
+ # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+
+-from __future__ import unicode_literals
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-# Not installing aliases from python-future; it's unreliable and slow.
+-from builtins import * # noqa
+-
+ import vim
+ import json
+ from ycm import vimsupport
+@@ -29,7 +22,7 @@
+ from ycm.vimsupport import memoize, GetIntValue
+
+
+-class SignatureHelpState( object ):
++class SignatureHelpState:
+ ACTIVE = 'ACTIVE'
+ INACTIVE = 'INACTIVE'
+
+diff --git a/python/ycm/syntax_parse.py b/python/ycm/syntax_parse.py
+index 0fbdad9d0..aa44eb5e0 100644
+--- a/python/ycm/syntax_parse.py
++++ b/python/ycm/syntax_parse.py
+@@ -15,14 +15,6 @@
+ # You should have received a copy of the GNU General Public License
+ # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+
+-from __future__ import unicode_literals
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-# Not installing aliases from python-future; it's unreliable and slow.
+-from builtins import * # noqa
+-
+-from future.utils import itervalues
+ import re
+ from ycm import vimsupport
+
+@@ -61,7 +53,7 @@
+ }
+
+
+-class SyntaxGroup( object ):
++class SyntaxGroup:
+ def __init__( self, name, lines = None ):
+ self.name = name
+ self.lines = lines if lines else []
+@@ -169,7 +161,7 @@ def GetParentNames( group ):
+ parent_names.append( line[ len( links_to ): ] )
+ return parent_names
+
+- for group in itervalues( group_name_to_group ):
++ for group in group_name_to_group.values():
+ parent_names = GetParentNames( group )
+
+ for parent_name in parent_names:
+diff --git a/python/ycm/tests/__init__.py b/python/ycm/tests/__init__.py
+index 5ee0f8c8a..ad542f791 100644
+--- a/python/ycm/tests/__init__.py
++++ b/python/ycm/tests/__init__.py
+@@ -15,13 +15,6 @@
+ # You should have received a copy of the GNU General Public License
+ # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+
+-from __future__ import unicode_literals
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-# Not installing aliases from python-future; it's unreliable and slow.
+-from builtins import * # noqa
+-
+ from ycm.tests.test_utils import MockVimModule
+ MockVimModule()
+
+diff --git a/python/ycm/tests/base_test.py b/python/ycm/tests/base_test.py
+index 15e51b2ce..53473165c 100644
+--- a/python/ycm/tests/base_test.py
++++ b/python/ycm/tests/base_test.py
+@@ -1,7 +1,5 @@
+-# coding: utf-8
+-#
+ # Copyright (C) 2013 Google Inc.
+-# 2016 YouCompleteMe contributors
++# 2020 YouCompleteMe contributors
+ #
+ # This file is part of YouCompleteMe.
+ #
+@@ -18,13 +16,6 @@
+ # You should have received a copy of the GNU General Public License
+ # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+
+-from __future__ import unicode_literals
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-# Not installing aliases from python-future; it's unreliable and slow.
+-from builtins import * # noqa
+-
+ import contextlib
+ from nose.tools import eq_, ok_
+ from mock import patch
+diff --git a/python/ycm/tests/client/base_request_test.py b/python/ycm/tests/client/base_request_test.py
+index 997313a0c..29ddc8bf0 100644
+--- a/python/ycm/tests/client/base_request_test.py
++++ b/python/ycm/tests/client/base_request_test.py
+@@ -15,13 +15,6 @@
+ # You should have received a copy of the GNU General Public License
+ # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+
+-from __future__ import unicode_literals
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-# Not installing aliases from python-future; it's unreliable and slow.
+-from builtins import * # noqa
+-
+ from ycm.tests.test_utils import MockVimBuffers, MockVimModule, VimBuffer
+ MockVimModule()
+
+diff --git a/python/ycm/tests/client/command_request_test.py b/python/ycm/tests/client/command_request_test.py
+index ac5f83ee6..fc4b3f4ce 100644
+--- a/python/ycm/tests/client/command_request_test.py
++++ b/python/ycm/tests/client/command_request_test.py
+@@ -15,13 +15,6 @@
+ # You should have received a copy of the GNU General Public License
+ # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+
+-from __future__ import unicode_literals
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-# Not installing aliases from python-future; it's unreliable and slow.
+-from builtins import * # noqa
+-
+ from ycm.tests.test_utils import ExtendedMock, MockVimModule
+ MockVimModule()
+
+@@ -31,7 +24,7 @@
+ from ycm.client.command_request import CommandRequest
+
+
+-class GoToResponse_QuickFix_test( object ):
++class GoToResponse_QuickFix_test:
+ """This class tests the generation of QuickFix lists for GoTo responses which
+ return multiple locations, such as the Python completer and JavaScript
+ completer. It mostly proves that we use 1-based indexing for the column
+@@ -118,7 +111,7 @@ def _CheckGoToList( self,
+ set_fitting_height.assert_called_once_with()
+
+
+-class Response_Detection_test( object ):
++class Response_Detection_test:
+
+ def BasicResponse_test( self ):
+ def _BasicResponseTest( command, response ):
+diff --git a/python/ycm/tests/client/completion_request_test.py b/python/ycm/tests/client/completion_request_test.py
+index a39e4499f..ef7cdd3c2 100644
+--- a/python/ycm/tests/client/completion_request_test.py
++++ b/python/ycm/tests/client/completion_request_test.py
+@@ -15,13 +15,6 @@
+ # You should have received a copy of the GNU General Public License
+ # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+
+-from __future__ import unicode_literals
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-# Not installing aliases from python-future; it's unreliable and slow.
+-from builtins import * # noqa
+-
+ from nose.tools import eq_
+ from ycm.tests.test_utils import MockVimModule
+ vim_mock = MockVimModule()
+@@ -29,7 +22,7 @@
+ from ycm.client import completion_request
+
+
+-class ConvertCompletionResponseToVimDatas_test( object ):
++class ConvertCompletionResponseToVimDatas_test:
+ """ This class tests the
+ completion_request._ConvertCompletionResponseToVimDatas method """
+
+diff --git a/python/ycm/tests/client/debug_info_request_test.py b/python/ycm/tests/client/debug_info_request_test.py
+index 9ce2a4331..93f7ac393 100644
+--- a/python/ycm/tests/client/debug_info_request_test.py
++++ b/python/ycm/tests/client/debug_info_request_test.py
+@@ -15,13 +15,6 @@
+ # You should have received a copy of the GNU General Public License
+ # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+
+-from __future__ import unicode_literals
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-# Not installing aliases from python-future; it's unreliable and slow.
+-from builtins import * # noqa
+-
+ from copy import deepcopy
+ from hamcrest import assert_that, contains_string, equal_to
+
+diff --git a/python/ycm/tests/client/messages_request_test.py b/python/ycm/tests/client/messages_request_test.py
+index 1f89f7170..2504ca074 100644
+--- a/python/ycm/tests/client/messages_request_test.py
++++ b/python/ycm/tests/client/messages_request_test.py
+@@ -15,14 +15,6 @@
+ # You should have received a copy of the GNU General Public License
+ # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+
+-
+-from __future__ import unicode_literals
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-# Not installing aliases from python-future; it's unreliable and slow.
+-from builtins import * # noqa
+-
+ from ycm.tests.test_utils import MockVimModule
+ MockVimModule()
+
+diff --git a/python/ycm/tests/client/omni_completion_request_tests.py b/python/ycm/tests/client/omni_completion_request_tests.py
+index 39c267b4b..0028ee3f7 100644
+--- a/python/ycm/tests/client/omni_completion_request_tests.py
++++ b/python/ycm/tests/client/omni_completion_request_tests.py
+@@ -15,13 +15,6 @@
+ # You should have received a copy of the GNU General Public License
+ # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+
+-from __future__ import unicode_literals
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-# Not installing aliases from python-future; it's unreliable and slow.
+-from builtins import * # noqa
+-
+ from mock import MagicMock
+ from nose.tools import eq_
+
+diff --git a/python/ycm/tests/command_test.py b/python/ycm/tests/command_test.py
+index 0652207b8..f4e909db1 100644
+--- a/python/ycm/tests/command_test.py
++++ b/python/ycm/tests/command_test.py
+@@ -15,13 +15,6 @@
+ # You should have received a copy of the GNU General Public License
+ # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+
+-from __future__ import unicode_literals
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-# Not installing aliases from python-future; it's unreliable and slow.
+-from builtins import * # noqa
+-
+ from ycm.tests.test_utils import MockVimModule, MockVimBuffers, VimBuffer
+ MockVimModule()
+
+diff --git a/python/ycm/tests/completion_test.py b/python/ycm/tests/completion_test.py
+index 00ac7cf38..db9c972da 100644
+--- a/python/ycm/tests/completion_test.py
++++ b/python/ycm/tests/completion_test.py
+@@ -1,5 +1,3 @@
+-# coding: utf-8
+-#
+ # Copyright (C) 2016 YouCompleteMe contributors
+ #
+ # This file is part of YouCompleteMe.
+@@ -17,13 +15,6 @@
+ # You should have received a copy of the GNU General Public License
+ # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+
+-from __future__ import unicode_literals
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-# Not installing aliases from python-future; it's unreliable and slow.
+-from builtins import * # noqa
+-
+ from ycm.tests.test_utils import ( CurrentWorkingDirectory, ExtendedMock,
+ MockVimModule, MockVimBuffers, VimBuffer )
+ MockVimModule()
+diff --git a/python/ycm/tests/diagnostic_filter_test.py b/python/ycm/tests/diagnostic_filter_test.py
+index e4a74b227..3591feb93 100644
+--- a/python/ycm/tests/diagnostic_filter_test.py
++++ b/python/ycm/tests/diagnostic_filter_test.py
+@@ -15,13 +15,6 @@
+ # You should have received a copy of the GNU General Public License
+ # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+
+-from __future__ import unicode_literals
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-# Not installing aliases from python-future; it's unreliable and slow.
+-from builtins import * # noqa
+-
+ from ycm.tests.test_utils import MockVimModule
+ MockVimModule()
+
+diff --git a/python/ycm/tests/event_notification_test.py b/python/ycm/tests/event_notification_test.py
+index ce1450ae0..85e73dcb4 100644
+--- a/python/ycm/tests/event_notification_test.py
++++ b/python/ycm/tests/event_notification_test.py
+@@ -1,5 +1,3 @@
+-# coding: utf-8
+-#
+ # Copyright (C) 2015-2018 YouCompleteMe contributors
+ #
+ # This file is part of YouCompleteMe.
+@@ -17,13 +15,6 @@
+ # You should have received a copy of the GNU General Public License
+ # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+
+-from __future__ import unicode_literals
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-# Not installing aliases from python-future; it's unreliable and slow.
+-from builtins import * # noqa
+-
+ from ycm.tests.test_utils import ( CurrentWorkingDirectory, ExtendedMock,
+ MockVimBuffers, MockVimModule, VimBuffer,
+ VimSign )
+diff --git a/python/ycm/tests/mock_utils.py b/python/ycm/tests/mock_utils.py
+index 3770202de..36faa10a0 100644
+--- a/python/ycm/tests/mock_utils.py
++++ b/python/ycm/tests/mock_utils.py
+@@ -14,18 +14,11 @@
+ #
+ # You should have received a copy of the GNU General Public License
+ # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+-from __future__ import unicode_literals
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-# Not installing aliases from python-future; it's unreliable and slow.
+-from builtins import * # noqa
+-
+ import mock
+ import requests
+
+
+-class FakeResponse( object ):
++class FakeResponse:
+ """A fake version of a requests response object, just about suitable for
+ mocking a server response. Not usually used directly. See
+ MockServerResponse* methods"""
+@@ -46,7 +39,7 @@ def raise_for_status( self ):
+ raise self._exception
+
+
+-class FakeFuture( object ):
++class FakeFuture:
+ """A fake version of a future response object, just about suitable for
+ mocking a server response as generated by PostDataToHandlerAsync.
+ Not usually used directly. See MockAsyncServerResponse* methods"""
+diff --git a/python/ycm/tests/paths_test.py b/python/ycm/tests/paths_test.py
+index 1bd4390ed..86c7c8069 100644
+--- a/python/ycm/tests/paths_test.py
++++ b/python/ycm/tests/paths_test.py
+@@ -15,13 +15,6 @@
+ # You should have received a copy of the GNU General Public License
+ # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+
+-from __future__ import unicode_literals
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-# Not installing aliases from python-future; it's unreliable and slow.
+-from builtins import * # noqa
+-
+ from ycm.tests.test_utils import MockVimModule
+ MockVimModule()
+
+@@ -39,20 +32,6 @@ def EndsWithPython_Bad( path ):
+ 'Path {0} does end with a Python name.'.format( path ) )
+
+
+-def EndsWithPython_Python2Paths_test():
+- python_paths = [
+- 'python',
+- 'python2',
+- '/usr/bin/python2.7',
+- '/home/user/.pyenv/shims/python2.7',
+- r'C:\Python27\python.exe',
+- '/Contents/MacOS/Python'
+- ]
+-
+- for path in python_paths:
+- yield EndsWithPython_Good, path
+-
+-
+ def EndsWithPython_Python3Paths_test():
+ python_paths = [
+ 'python3',
+@@ -71,7 +50,7 @@ def EndsWithPython_BadPaths_test():
+ '',
+ '/opt/local/bin/vim',
+ r'C:\Program Files\Vim\vim74\gvim.exe',
+- '/usr/bin/python2.5',
++ '/usr/bin/python2.7',
+ '/home/user/.pyenv/shims/python3.2',
+ ]
+
+diff --git a/python/ycm/tests/postcomplete_test.py b/python/ycm/tests/postcomplete_test.py
+index e1ee26230..bccf95739 100644
+--- a/python/ycm/tests/postcomplete_test.py
++++ b/python/ycm/tests/postcomplete_test.py
+@@ -17,13 +17,6 @@
+ # You should have received a copy of the GNU General Public License
+ # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+
+-from __future__ import unicode_literals
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-# Not installing aliases from python-future; it's unreliable and slow.
+-from builtins import * # noqa
+-
+ from ycm.tests.test_utils import MockVimModule
+ MockVimModule()
+
+diff --git a/python/ycm/tests/signature_help_test.py b/python/ycm/tests/signature_help_test.py
+index d46c5fc16..f993376ab 100644
+--- a/python/ycm/tests/signature_help_test.py
++++ b/python/ycm/tests/signature_help_test.py
+@@ -1,5 +1,3 @@
+-# coding: utf-8
+-#
+ # Copyright (C) 2019 YouCompleteMe contributors
+ #
+ # This file is part of YouCompleteMe.
+@@ -17,13 +15,6 @@
+ # You should have received a copy of the GNU General Public License
+ # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+
+-# Intentionally not importing unicode_literals!
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-# Not installing aliases from python-future; it's unreliable and slow.
+-from builtins import * # noqa
+-
+ from hamcrest import ( assert_that,
+ empty )
+ from ycm import signature_help as sh
+diff --git a/python/ycm/tests/syntax_parse_test.py b/python/ycm/tests/syntax_parse_test.py
+index 7c6566339..a807cd7ad 100644
+--- a/python/ycm/tests/syntax_parse_test.py
++++ b/python/ycm/tests/syntax_parse_test.py
+@@ -16,13 +16,6 @@
+ # You should have received a copy of the GNU General Public License
+ # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+
+-from __future__ import unicode_literals
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-# Not installing aliases from python-future; it's unreliable and slow.
+-from builtins import * # noqa
+-
+ from ycm.tests.test_utils import MockVimModule
+ MockVimModule()
+
+diff --git a/python/ycm/tests/test_utils.py b/python/ycm/tests/test_utils.py
+index 1f076d05d..fc4349fe2 100644
+--- a/python/ycm/tests/test_utils.py
++++ b/python/ycm/tests/test_utils.py
+@@ -15,15 +15,7 @@
+ # You should have received a copy of the GNU General Public License
+ # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+
+-from __future__ import unicode_literals
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-# Not installing aliases from python-future; it's unreliable and slow.
+-from builtins import * # noqa
+-
+ from collections import defaultdict, namedtuple
+-from future.utils import iteritems, PY2
+ from mock import DEFAULT, MagicMock, patch
+ from hamcrest import assert_that, equal_to
+ import contextlib
+@@ -39,7 +31,7 @@
+ except ImportError:
+ from unittest2 import skipIf
+
+-from ycmd.utils import GetCurrentDirectory, OnMac, OnWindows, ToBytes, ToUnicode
++from ycmd.utils import GetCurrentDirectory, OnMac, OnWindows, ToUnicode
+
+
+ BUFNR_REGEX = re.compile( '^bufnr\\(\'(?P<buffer_filename>.+)\', ([01])\\)$' )
+@@ -180,8 +172,7 @@ def _MockVimBufferEval( value ):
+ if match:
+ findstart = int( match.group( 'findstart' ) )
+ base = match.group( 'base' )
+- value = current_buffer.omnifunc( findstart, base )
+- return value if findstart else ToBytesOnPY2( value )
++ return current_buffer.omnifunc( findstart, base )
+
+ return None
+
+@@ -201,7 +192,7 @@ def _MockVimOptionsEval( value ):
+
+ if value == 'keys( g: )':
+ global_options = {}
+- for key, value in iteritems( VIM_OPTIONS ):
++ for key, value in VIM_OPTIONS.items():
+ if key.startswith( 'g:' ):
+ global_options[ key[ 2: ] ] = value
+ return global_options
+@@ -382,7 +373,7 @@ def _MockVimCommand( command ):
+ return DEFAULT
+
+
+-class VimBuffer( object ):
++class VimBuffer:
+ """An object that looks like a vim.buffer object:
+ - |name| : full path of the buffer with symbolic links resolved;
+ - |number| : buffer number;
+@@ -457,7 +448,7 @@ def __repr__( self ):
+ self.number )
+
+
+-class VimBuffers( object ):
++class VimBuffers:
+ """An object that looks like a vim.buffers object."""
+
+ def __init__( self, buffers ):
+@@ -482,7 +473,7 @@ def pop( self, index ):
+ return self._buffers.pop( index )
+
+
+-class VimWindow( object ):
++class VimWindow:
+ """An object that looks like a vim.window object:
+ - |number|: number of the window;
+ - |buffer_object|: a VimBuffer object representing the buffer inside the
+@@ -503,7 +494,7 @@ def __repr__( self ):
+ self.cursor )
+
+
+-class VimWindows( object ):
++class VimWindows:
+ """An object that looks like a vim.windows object."""
+
+ def __init__( self, buffers, cursor ):
+@@ -530,7 +521,7 @@ def __iter__( self ):
+ return iter( self._windows )
+
+
+-class VimCurrent( object ):
++class VimCurrent:
+ """An object that looks like a vim.current object. |current_window| must be a
+ VimWindow object."""
+
+@@ -540,7 +531,7 @@ def __init__( self, current_window ):
+ self.line = self.buffer.contents[ current_window.cursor[ 0 ] - 1 ]
+
+
+-class VimMatch( object ):
++class VimMatch:
+
+ def __init__( self, group, pattern ):
+ current_window = VIM_MOCK.current.window.number
+@@ -565,7 +556,7 @@ def __getitem__( self, key ):
+ return self.id
+
+
+-class VimSign( object ):
++class VimSign:
+
+ def __init__( self, sign_id, line, name, bufnr ):
+ self.id = sign_id
+@@ -717,22 +708,3 @@ def Wrapper( *args, **kwargs ):
+ return Wrapper
+
+ return decorator
+-
+-
+-def ToBytesOnPY2( data ):
+- # To test the omnifunc, etc. returning strings, which can be of different
+- # types depending on python version, we use ToBytes on PY2 and just the native
+- # str on python3. This roughly matches what happens between py2 and py3
+- # versions within Vim.
+- if not PY2:
+- return data
+-
+- if isinstance( data, int ):
+- return data
+- if isinstance( data, list ):
+- return [ ToBytesOnPY2( item ) for item in data ]
+- if isinstance( data, dict ):
+- for item in data:
+- data[ item ] = ToBytesOnPY2( data[ item ] )
+- return data
+- return ToBytes( data )
+diff --git a/python/ycm/tests/vimsupport_test.py b/python/ycm/tests/vimsupport_test.py
+index ea9e006c5..bb79aef26 100644
+--- a/python/ycm/tests/vimsupport_test.py
++++ b/python/ycm/tests/vimsupport_test.py
+@@ -1,5 +1,3 @@
+-# coding: utf-8
+-#
+ # Copyright (C) 2015-2018 YouCompleteMe contributors
+ #
+ # This file is part of YouCompleteMe.
+@@ -16,14 +14,6 @@
+ #
+ # You should have received a copy of the GNU General Public License
+ # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+-
+-# Intentionally not importing unicode_literals!
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-# Not installing aliases from python-future; it's unreliable and slow.
+-from builtins import * # noqa
+-
+ from ycm.tests import PathToTestFile
+ from ycm.tests.test_utils import ( CurrentWorkingDirectory, ExtendedMock,
+ MockVimBuffers, MockVimModule, Version,
+@@ -1705,8 +1695,6 @@ def InsertNamespace_append_test( vim_current, *args ):
+
+ @patch( 'vim.command', new_callable = ExtendedMock )
+ def JumpToLocation_SameFile_SameBuffer_NoSwapFile_test( vim_command ):
+- # No 'u' prefix for the current buffer name string to simulate Vim returning
+- # bytes on Python 2 but unicode on Python 3.
+ current_buffer = VimBuffer( 'uni¢𐍈d€' )
+ with MockVimBuffers( [ current_buffer ], [ current_buffer ] ) as vim:
+ vimsupport.JumpToLocation( os.path.realpath( u'uni¢𐍈d€' ),
+diff --git a/python/ycm/tests/youcompleteme_test.py b/python/ycm/tests/youcompleteme_test.py
+index 069d4f30f..42a436093 100644
+--- a/python/ycm/tests/youcompleteme_test.py
++++ b/python/ycm/tests/youcompleteme_test.py
+@@ -15,13 +15,6 @@
+ # You should have received a copy of the GNU General Public License
+ # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+
+-from __future__ import unicode_literals
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-# Not installing aliases from python-future; it's unreliable and slow.
+-from builtins import * # noqa
+-
+ from ycm.client.messages_request import MessagesPoll
+ from ycm.tests.test_utils import ( ExtendedMock,
+ MockVimBuffers,
+@@ -71,7 +64,7 @@ def YouCompleteMe_InvalidPythonInterpreterPath_test( post_vim_message ):
+ post_vim_message.assert_called_once_with(
+ "Unable to start the ycmd server. "
+ "Path in 'g:ycm_server_python_interpreter' option does not point "
+- "to a valid Python 2.7 or 3.5+. "
++ "to a valid Python 3.5+. "
+ "Correct the error then restart the server with ':YcmRestartServer'." )
+
+ post_vim_message.reset_mock()
+@@ -98,7 +91,7 @@ def YouCompleteMe_NoPythonInterpreterFound_test( post_vim_message, *args ):
+
+ assert_that( ycm.IsServerAlive(), equal_to( False ) )
+ post_vim_message.assert_called_once_with(
+- "Unable to start the ycmd server. Cannot find Python 2.7 or 3.5+. "
++ "Unable to start the ycmd server. Cannot find Python 3.5+. "
+ "Set the 'g:ycm_server_python_interpreter' option to a Python "
+ "interpreter path. "
+ "Correct the error then restart the server with ':YcmRestartServer'." )
+@@ -154,28 +147,6 @@ def YouCompleteMe_NotifyUserIfServerCrashed_MissingCore_test():
+ } )
+
+
+-def YouCompleteMe_NotifyUserIfServerCrashed_Python2Core_test():
+- message = ( "The ycmd server SHUT DOWN (restart with ':YcmRestartServer'). "
+- "YCM core library compiled for Python 2 but loaded in Python 3. "
+- "Set the 'g:ycm_server_python_interpreter' option to a Python 2 "
+- "interpreter path." )
+- RunNotifyUserIfServerCrashed( {
+- 'return_code': 5,
+- 'expected_message': equal_to( message )
+- } )
+-
+-
+-def YouCompleteMe_NotifyUserIfServerCrashed_Python3Core_test():
+- message = ( "The ycmd server SHUT DOWN (restart with ':YcmRestartServer'). "
+- "YCM core library compiled for Python 3 but loaded in Python 2. "
+- "Set the 'g:ycm_server_python_interpreter' option to a Python 3 "
+- "interpreter path." )
+- RunNotifyUserIfServerCrashed( {
+- 'return_code': 6,
+- 'expected_message': equal_to( message )
+- } )
+-
+-
+ def YouCompleteMe_NotifyUserIfServerCrashed_OutdatedCore_test():
+ message = ( "The ycmd server SHUT DOWN (restart with ':YcmRestartServer'). "
+ "YCM core library too old; PLEASE RECOMPILE by running the "
+diff --git a/python/ycm/unsafe_thread_pool_executor.py b/python/ycm/unsafe_thread_pool_executor.py
+index e55dc018c..5baa11f2d 100644
+--- a/python/ycm/unsafe_thread_pool_executor.py
++++ b/python/ycm/unsafe_thread_pool_executor.py
+@@ -6,7 +6,6 @@
+ # (the Python Software Foundation License).
+
+
+-from __future__ import with_statement
+ import threading
+ import weakref
+ import sys
+@@ -28,7 +27,7 @@
+ # only send network requests). The YCM workload is one of those workloads where
+ # it's safe (the aforementioned network requests case).
+
+-class _WorkItem( object ):
++class _WorkItem:
+ def __init__( self, future, fn, args, kwargs ):
+ self.future = future
+ self.fn = fn
+diff --git a/python/ycm/vimsupport.py b/python/ycm/vimsupport.py
+index 81ab12219..93d5b676b 100644
+--- a/python/ycm/vimsupport.py
++++ b/python/ycm/vimsupport.py
+@@ -15,14 +15,6 @@
+ # You should have received a copy of the GNU General Public License
+ # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+
+-from __future__ import unicode_literals
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-# Not installing aliases from python-future; it's unreliable and slow.
+-from builtins import * # noqa
+-
+-from future.utils import iterkeys
+ import vim
+ import os
+ import json
+@@ -321,8 +313,7 @@ def GetDiagnosticMatchPattern( line_num,
+ def LineAndColumnNumbersClamped( line_num, column_num ):
+ line_num = max( min( line_num, len( vim.current.buffer ) ), 1 )
+
+- # Vim buffers are a list of byte objects on Python 2 but Unicode objects on
+- # Python 3.
++ # Vim buffers are a list of Unicode objects on Python 3.
+ max_column = len( ToBytes( vim.current.buffer[ line_num - 1 ] ) )
+
+ return line_num, min( column_num, max_column )
+@@ -876,7 +867,7 @@ def ReplaceChunks( chunks, silent=False ):
+ chunks_by_file = _SortChunksByFile( chunks )
+
+ # We sort the file list simply to enable repeatable testing.
+- sorted_file_list = sorted( iterkeys( chunks_by_file ) )
++ sorted_file_list = sorted( chunks_by_file.keys() )
+
+ if not silent:
+ # Make sure the user is prepared to have her screen mutilated by the new
+@@ -997,8 +988,7 @@ def ReplaceChunk( start, end, replacement_text, vim_buffer ):
+ # so we convert to bytes
+ replacement_lines = SplitLines( ToBytes( replacement_text ) )
+
+- # NOTE: Vim buffers are a list of byte objects on Python 2 but unicode
+- # objects on Python 3.
++ # NOTE: Vim buffers are a list of unicode objects on Python 3.
+ start_existing_text = ToBytes( vim_buffer[ start_line ] )[ : start_column ]
+ end_line_text = ToBytes( vim_buffer[ end_line ] )
+ end_existing_text = end_line_text[ end_column : ]
+diff --git a/python/ycm/youcompleteme.py b/python/ycm/youcompleteme.py
+index 05da81c8d..8a8cf5ff7 100644
+--- a/python/ycm/youcompleteme.py
++++ b/python/ycm/youcompleteme.py
+@@ -15,14 +15,6 @@
+ # You should have received a copy of the GNU General Public License
+ # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
+
+-from __future__ import unicode_literals
+-from __future__ import print_function
+-from __future__ import division
+-from __future__ import absolute_import
+-# Not installing aliases from python-future; it's unreliable and slow.
+-from builtins import * # noqa
+-
+-from future.utils import iteritems, itervalues
+ import base64
+ import json
+ import logging
+@@ -85,17 +77,12 @@ def PatchNoProxy():
+ CORE_MISSING_MESSAGE = (
+ 'YCM core library not detected; you need to compile YCM before using it. '
+ 'Follow the instructions in the documentation.' )
+-CORE_PYTHON2_MESSAGE = (
+- "YCM core library compiled for Python 2 but loaded in Python 3. "
+- "Set the 'g:ycm_server_python_interpreter' option to a Python 2 "
+- "interpreter path." )
+-CORE_PYTHON3_MESSAGE = (
+- "YCM core library compiled for Python 3 but loaded in Python 2. "
+- "Set the 'g:ycm_server_python_interpreter' option to a Python 3 "
+- "interpreter path." )
+ CORE_OUTDATED_MESSAGE = (
+ 'YCM core library too old; PLEASE RECOMPILE by running the install.py '
+ 'script. See the documentation for more details.' )
++NO_PYTHON2_SUPPORT_MESSAGE = (
++ 'YCM has dropped support for python2. '
++ 'You need to recompile it with python3 instead.' )
+ SERVER_IDLE_SUICIDE_SECONDS = 1800 # 30 minutes
+ CLIENT_LOGFILE_FORMAT = 'ycm_'
+ SERVER_LOGFILE_FORMAT = 'ycmd_{port}_{std}_'
+@@ -105,7 +92,7 @@ def PatchNoProxy():
+ HANDLE_FLAG_INHERIT = 0x00000001
+
+
+-class YouCompleteMe( object ):
++class YouCompleteMe:
+ def __init__( self ):
+ self._available_completers = {}
+ self._user_options = None
+@@ -261,17 +248,16 @@ def NotifyUserIfServerCrashed( self ):
+ error_message = CORE_UNEXPECTED_MESSAGE.format( logfile = logfile )
+ elif return_code == 4:
+ error_message = CORE_MISSING_MESSAGE
+- elif return_code == 5:
+- error_message = CORE_PYTHON2_MESSAGE
+- elif return_code == 6:
+- error_message = CORE_PYTHON3_MESSAGE
+ elif return_code == 7:
+ error_message = CORE_OUTDATED_MESSAGE
++ elif return_code == 8:
++ error_message = NO_PYTHON2_SUPPORT_MESSAGE
+ else:
+ error_message = EXIT_CODE_UNEXPECTED_MESSAGE.format( code = return_code,
+ logfile = logfile )
+
+- error_message = SERVER_SHUTDOWN_MESSAGE + ' ' + error_message
++ if return_code != 8:
++ error_message = SERVER_SHUTDOWN_MESSAGE + ' ' + error_message
+ self._logger.error( error_message )
+ vimsupport.PostVimMessage( error_message )
+
+@@ -502,7 +488,7 @@ def OnPeriodicTick( self ):
+ not self._message_poll_requests[ filetype ].Poll( self ) ):
+ self._message_poll_requests[ filetype ] = None
+
+- return any( itervalues( self._message_poll_requests ) )
++ return any( self._message_poll_requests.values() )
+
+
+ def OnFileReadyToParse( self ):
+@@ -819,5 +805,5 @@ def _AddUltiSnipsDataIfNeeded( self, extra_data ):
+ extra_data[ 'ultisnips_snippets' ] = [
+ { 'trigger': trigger,
+ 'description': snippet[ 'description' ] }
+- for trigger, snippet in iteritems( snippets )
++ for trigger, snippet in snippets.items()
+ ]
+diff --git a/test/README.md b/test/README.md
+index 0edc0a43a..e109b4fc3 100644
+--- a/test/README.md
++++ b/test/README.md
+@@ -8,8 +8,6 @@ your machine.
+
+ * Make sure you have docker installed (duh)
+ * Run `./docker/manual/run`.
+- * This will use a Vim build with python 3
+- * If you want python2, run `./docker/manual/run --py 2`
+ * You should now be in the container. Your YCM checkout is now mounted in
+ `$HOME/YouCompleteMe`
+ * Run the following setup:
+@@ -248,15 +246,13 @@ To get a local summary:
+
+ # Docker
+
+-We generate and push 4 containers:
++We generate and push 2 containers:
+
+ * `youcompleteme/ycm-vim-py3:test` and `youcompleteme/ycm-vim-py3:manual`
+-* `youcompleteme/ycm-vim-py2:test` and `youcompleteme/ycm-vim-py2:manual`
+
+ The `:test` tags are the containers that are used by Azure pipelines to run the
+ tests and contains essentially Ubuntu LTS + the YCM dependencies and a build of
+-Vim at a specific version built with either python3 (`-py3`) or python 2
+-(`-py2`) support..
++Vim at a specific version built with python3 (`-py3`) support.
+
+ The `:manual` tags extend the `:test` tags with a user account that largely
+ matches the one created by Azure to run our tests. It also installs a basic
+diff --git a/test/docker/ci/push b/test/docker/ci/push
+index e08178b72..31a8481d9 100755
+--- a/test/docker/ci/push
++++ b/test/docker/ci/push
+@@ -2,5 +2,4 @@
+
+ set -e
+
+-docker push youcompleteme/ycm-vim-py2:test
+ docker push youcompleteme/ycm-vim-py3:test
+diff --git a/test/docker/ci/rebuild b/test/docker/ci/rebuild
+index 1363e3e25..6b384ad04 100755
+--- a/test/docker/ci/rebuild
++++ b/test/docker/ci/rebuild
+@@ -8,9 +8,6 @@ else
+ OPTS="--no-cache"
+ fi
+
+-docker build ${OPTS} -t youcompleteme/ycm-vim-py2:test \
+- --build-arg YCM_VIM_PYTHON=python \
+- image/
+ docker build ${OPTS} -t youcompleteme/ycm-vim-py3:test \
+ --build-arg YCM_VIM_PYTHON=python3 \
+ image/
+diff --git a/test/docker/manual/push b/test/docker/manual/push
+index bfe60c2d3..ee32addfc 100755
+--- a/test/docker/manual/push
++++ b/test/docker/manual/push
+@@ -2,5 +2,4 @@
+
+ set -e
+
+-docker push youcompleteme/ycm-vim-py2:manual
+ docker push youcompleteme/ycm-vim-py3:manual
+diff --git a/test/docker/manual/rebuild b/test/docker/manual/rebuild
+index c956d657d..9c7f39166 100755
+--- a/test/docker/manual/rebuild
++++ b/test/docker/manual/rebuild
+@@ -8,9 +8,6 @@ else
+ OPTS="--no-cache"
+ fi
+
+-docker build ${OPTS} -t youcompleteme/ycm-vim-py2:manual \
+- --build-arg YCM_PYTHON=py2 \
+- image/
+ docker build ${OPTS} -t youcompleteme/ycm-vim-py3:manual \
+ --build-arg YCM_PYTHON=py3 \
+ image/
+diff --git a/third_party/pythonfutures/CHANGES b/third_party/pythonfutures/CHANGES
+deleted file mode 100755
+index 81df636f2..000000000
+--- a/third_party/pythonfutures/CHANGES
++++ /dev/null
+@@ -1,44 +0,0 @@
+-2.1.4
+-=====
+-
+-- Ported the library again from Python 3.2.5 to get the latest bug fixes
+-
+-
+-2.1.3
+-=====
+-
+-- Fixed race condition in wait(return_when=ALL_COMPLETED)
+- (http://bugs.python.org/issue14406) -- thanks Ralf Schmitt
+-- Added missing setUp() methods to several test classes
+-
+-
+-2.1.2
+-=====
+-
+-- Fixed installation problem on Python 3.1
+-
+-
+-2.1.1
+-=====
+-
+-- Fixed missing 'concurrent' package declaration in setup.py
+-
+-
+-2.1
+-===
+-
+-- Moved the code from the 'futures' package to 'concurrent.futures' to provide
+- a drop in backport that matches the code in Python 3.2 standard library
+-- Deprecated the old 'futures' package
+-
+-
+-2.0
+-===
+-
+-- Changed implementation to match PEP 3148
+-
+-
+-1.0
+-===
+-
+-Initial release.
+diff --git a/third_party/pythonfutures/LICENSE b/third_party/pythonfutures/LICENSE
+deleted file mode 100755
+index c430db0f1..000000000
+--- a/third_party/pythonfutures/LICENSE
++++ /dev/null
+@@ -1,21 +0,0 @@
+-Copyright 2009 Brian Quinlan. All rights reserved.
+-
+-Redistribution and use in source and binary forms, with or without modification,
+-are permitted provided that the following conditions are met:
+-
+- 1. Redistributions of source code must retain the above copyright notice,
+- this list of conditions and the following disclaimer.
+- 2. Redistributions in binary form must reproduce the above copyright notice,
+- this list of conditions and the following disclaimer in the documentation
+- and/or other materials provided with the distribution.
+-
+-THIS SOFTWARE IS PROVIDED BY BRIAN QUINLAN "AS IS" AND ANY EXPRESS OR IMPLIED
+-WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+-HALL THE FREEBSD PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+-PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+-LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+-OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+-ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+\ No newline at end of file
+diff --git a/third_party/pythonfutures/concurrent/__init__.py b/third_party/pythonfutures/concurrent/__init__.py
+deleted file mode 100755
+index b36383a61..000000000
+--- a/third_party/pythonfutures/concurrent/__init__.py
++++ /dev/null
+@@ -1,3 +0,0 @@
+-from pkgutil import extend_path
+-
+-__path__ = extend_path(__path__, __name__)
+diff --git a/third_party/pythonfutures/concurrent/futures/__init__.py b/third_party/pythonfutures/concurrent/futures/__init__.py
+deleted file mode 100755
+index b5231f8aa..000000000
+--- a/third_party/pythonfutures/concurrent/futures/__init__.py
++++ /dev/null
+@@ -1,18 +0,0 @@
+-# Copyright 2009 Brian Quinlan. All Rights Reserved.
+-# Licensed to PSF under a Contributor Agreement.
+-
+-"""Execute computations asynchronously using threads or processes."""
+-
+-__author__ = 'Brian Quinlan (brian@sweetapp.com)'
+-
+-from concurrent.futures._base import (FIRST_COMPLETED,
+- FIRST_EXCEPTION,
+- ALL_COMPLETED,
+- CancelledError,
+- TimeoutError,
+- Future,
+- Executor,
+- wait,
+- as_completed)
+-from concurrent.futures.process import ProcessPoolExecutor
+-from concurrent.futures.thread import ThreadPoolExecutor
+diff --git a/third_party/pythonfutures/concurrent/futures/_base.py b/third_party/pythonfutures/concurrent/futures/_base.py
+deleted file mode 100755
+index 8ed69b7d9..000000000
+--- a/third_party/pythonfutures/concurrent/futures/_base.py
++++ /dev/null
+@@ -1,574 +0,0 @@
+-# Copyright 2009 Brian Quinlan. All Rights Reserved.
+-# Licensed to PSF under a Contributor Agreement.
+-
+-from __future__ import with_statement
+-import logging
+-import threading
+-import time
+-
+-try:
+- from collections import namedtuple
+-except ImportError:
+- from concurrent.futures._compat import namedtuple
+-
+-__author__ = 'Brian Quinlan (brian@sweetapp.com)'
+-
+-FIRST_COMPLETED = 'FIRST_COMPLETED'
+-FIRST_EXCEPTION = 'FIRST_EXCEPTION'
+-ALL_COMPLETED = 'ALL_COMPLETED'
+-_AS_COMPLETED = '_AS_COMPLETED'
+-
+-# Possible future states (for internal use by the futures package).
+-PENDING = 'PENDING'
+-RUNNING = 'RUNNING'
+-# The future was cancelled by the user...
+-CANCELLED = 'CANCELLED'
+-# ...and _Waiter.add_cancelled() was called by a worker.
+-CANCELLED_AND_NOTIFIED = 'CANCELLED_AND_NOTIFIED'
+-FINISHED = 'FINISHED'
+-
+-_FUTURE_STATES = [
+- PENDING,
+- RUNNING,
+- CANCELLED,
+- CANCELLED_AND_NOTIFIED,
+- FINISHED
+-]
+-
+-_STATE_TO_DESCRIPTION_MAP = {
+- PENDING: "pending",
+- RUNNING: "running",
+- CANCELLED: "cancelled",
+- CANCELLED_AND_NOTIFIED: "cancelled",
+- FINISHED: "finished"
+-}
+-
+-# Logger for internal use by the futures package.
+-LOGGER = logging.getLogger("concurrent.futures")
+-
+-class Error(Exception):
+- """Base class for all future-related exceptions."""
+- pass
+-
+-class CancelledError(Error):
+- """The Future was cancelled."""
+- pass
+-
+-class TimeoutError(Error):
+- """The operation exceeded the given deadline."""
+- pass
+-
+-class _Waiter(object):
+- """Provides the event that wait() and as_completed() block on."""
+- def __init__(self):
+- self.event = threading.Event()
+- self.finished_futures = []
+-
+- def add_result(self, future):
+- self.finished_futures.append(future)
+-
+- def add_exception(self, future):
+- self.finished_futures.append(future)
+-
+- def add_cancelled(self, future):
+- self.finished_futures.append(future)
+-
+-class _AsCompletedWaiter(_Waiter):
+- """Used by as_completed()."""
+-
+- def __init__(self):
+- super(_AsCompletedWaiter, self).__init__()
+- self.lock = threading.Lock()
+-
+- def add_result(self, future):
+- with self.lock:
+- super(_AsCompletedWaiter, self).add_result(future)
+- self.event.set()
+-
+- def add_exception(self, future):
+- with self.lock:
+- super(_AsCompletedWaiter, self).add_exception(future)
+- self.event.set()
+-
+- def add_cancelled(self, future):
+- with self.lock:
+- super(_AsCompletedWaiter, self).add_cancelled(future)
+- self.event.set()
+-
+-class _FirstCompletedWaiter(_Waiter):
+- """Used by wait(return_when=FIRST_COMPLETED)."""
+-
+- def add_result(self, future):
+- super(_FirstCompletedWaiter, self).add_result(future)
+- self.event.set()
+-
+- def add_exception(self, future):
+- super(_FirstCompletedWaiter, self).add_exception(future)
+- self.event.set()
+-
+- def add_cancelled(self, future):
+- super(_FirstCompletedWaiter, self).add_cancelled(future)
+- self.event.set()
+-
+-class _AllCompletedWaiter(_Waiter):
+- """Used by wait(return_when=FIRST_EXCEPTION and ALL_COMPLETED)."""
+-
+- def __init__(self, num_pending_calls, stop_on_exception):
+- self.num_pending_calls = num_pending_calls
+- self.stop_on_exception = stop_on_exception
+- self.lock = threading.Lock()
+- super(_AllCompletedWaiter, self).__init__()
+-
+- def _decrement_pending_calls(self):
+- with self.lock:
+- self.num_pending_calls -= 1
+- if not self.num_pending_calls:
+- self.event.set()
+-
+- def add_result(self, future):
+- super(_AllCompletedWaiter, self).add_result(future)
+- self._decrement_pending_calls()
+-
+- def add_exception(self, future):
+- super(_AllCompletedWaiter, self).add_exception(future)
+- if self.stop_on_exception:
+- self.event.set()
+- else:
+- self._decrement_pending_calls()
+-
+- def add_cancelled(self, future):
+- super(_AllCompletedWaiter, self).add_cancelled(future)
+- self._decrement_pending_calls()
+-
+-class _AcquireFutures(object):
+- """A context manager that does an ordered acquire of Future conditions."""
+-
+- def __init__(self, futures):
+- self.futures = sorted(futures, key=id)
+-
+- def __enter__(self):
+- for future in self.futures:
+- future._condition.acquire()
+-
+- def __exit__(self, *args):
+- for future in self.futures:
+- future._condition.release()
+-
+-def _create_and_install_waiters(fs, return_when):
+- if return_when == _AS_COMPLETED:
+- waiter = _AsCompletedWaiter()
+- elif return_when == FIRST_COMPLETED:
+- waiter = _FirstCompletedWaiter()
+- else:
+- pending_count = sum(
+- f._state not in [CANCELLED_AND_NOTIFIED, FINISHED] for f in fs)
+-
+- if return_when == FIRST_EXCEPTION:
+- waiter = _AllCompletedWaiter(pending_count, stop_on_exception=True)
+- elif return_when == ALL_COMPLETED:
+- waiter = _AllCompletedWaiter(pending_count, stop_on_exception=False)
+- else:
+- raise ValueError("Invalid return condition: %r" % return_when)
+-
+- for f in fs:
+- f._waiters.append(waiter)
+-
+- return waiter
+-
+-def as_completed(fs, timeout=None):
+- """An iterator over the given futures that yields each as it completes.
+-
+- Args:
+- fs: The sequence of Futures (possibly created by different Executors) to
+- iterate over.
+- timeout: The maximum number of seconds to wait. If None, then there
+- is no limit on the wait time.
+-
+- Returns:
+- An iterator that yields the given Futures as they complete (finished or
+- cancelled).
+-
+- Raises:
+- TimeoutError: If the entire result iterator could not be generated
+- before the given timeout.
+- """
+- if timeout is not None:
+- end_time = timeout + time.time()
+-
+- with _AcquireFutures(fs):
+- finished = set(
+- f for f in fs
+- if f._state in [CANCELLED_AND_NOTIFIED, FINISHED])
+- pending = set(fs) - finished
+- waiter = _create_and_install_waiters(fs, _AS_COMPLETED)
+-
+- try:
+- for future in finished:
+- yield future
+-
+- while pending:
+- if timeout is None:
+- wait_timeout = None
+- else:
+- wait_timeout = end_time - time.time()
+- if wait_timeout < 0:
+- raise TimeoutError(
+- '%d (of %d) futures unfinished' % (
+- len(pending), len(fs)))
+-
+- waiter.event.wait(wait_timeout)
+-
+- with waiter.lock:
+- finished = waiter.finished_futures
+- waiter.finished_futures = []
+- waiter.event.clear()
+-
+- for future in finished:
+- yield future
+- pending.remove(future)
+-
+- finally:
+- for f in fs:
+- f._waiters.remove(waiter)
+-
+-DoneAndNotDoneFutures = namedtuple(
+- 'DoneAndNotDoneFutures', 'done not_done')
+-def wait(fs, timeout=None, return_when=ALL_COMPLETED):
+- """Wait for the futures in the given sequence to complete.
+-
+- Args:
+- fs: The sequence of Futures (possibly created by different Executors) to
+- wait upon.
+- timeout: The maximum number of seconds to wait. If None, then there
+- is no limit on the wait time.
+- return_when: Indicates when this function should return. The options
+- are:
+-
+- FIRST_COMPLETED - Return when any future finishes or is
+- cancelled.
+- FIRST_EXCEPTION - Return when any future finishes by raising an
+- exception. If no future raises an exception
+- then it is equivalent to ALL_COMPLETED.
+- ALL_COMPLETED - Return when all futures finish or are cancelled.
+-
+- Returns:
+- A named 2-tuple of sets. The first set, named 'done', contains the
+- futures that completed (is finished or cancelled) before the wait
+- completed. The second set, named 'not_done', contains uncompleted
+- futures.
+- """
+- with _AcquireFutures(fs):
+- done = set(f for f in fs
+- if f._state in [CANCELLED_AND_NOTIFIED, FINISHED])
+- not_done = set(fs) - done
+-
+- if (return_when == FIRST_COMPLETED) and done:
+- return DoneAndNotDoneFutures(done, not_done)
+- elif (return_when == FIRST_EXCEPTION) and done:
+- if any(f for f in done
+- if not f.cancelled() and f.exception() is not None):
+- return DoneAndNotDoneFutures(done, not_done)
+-
+- if len(done) == len(fs):
+- return DoneAndNotDoneFutures(done, not_done)
+-
+- waiter = _create_and_install_waiters(fs, return_when)
+-
+- waiter.event.wait(timeout)
+- for f in fs:
+- f._waiters.remove(waiter)
+-
+- done.update(waiter.finished_futures)
+- return DoneAndNotDoneFutures(done, set(fs) - done)
+-
+-class Future(object):
+- """Represents the result of an asynchronous computation."""
+-
+- def __init__(self):
+- """Initializes the future. Should not be called by clients."""
+- self._condition = threading.Condition()
+- self._state = PENDING
+- self._result = None
+- self._exception = None
+- self._waiters = []
+- self._done_callbacks = []
+-
+- def _invoke_callbacks(self):
+- for callback in self._done_callbacks:
+- try:
+- callback(self)
+- except Exception:
+- LOGGER.exception('exception calling callback for %r', self)
+-
+- def __repr__(self):
+- with self._condition:
+- if self._state == FINISHED:
+- if self._exception:
+- return '<Future at %s state=%s raised %s>' % (
+- hex(id(self)),
+- _STATE_TO_DESCRIPTION_MAP[self._state],
+- self._exception.__class__.__name__)
+- else:
+- return '<Future at %s state=%s returned %s>' % (
+- hex(id(self)),
+- _STATE_TO_DESCRIPTION_MAP[self._state],
+- self._result.__class__.__name__)
+- return '<Future at %s state=%s>' % (
+- hex(id(self)),
+- _STATE_TO_DESCRIPTION_MAP[self._state])
+-
+- def cancel(self):
+- """Cancel the future if possible.
+-
+- Returns True if the future was cancelled, False otherwise. A future
+- cannot be cancelled if it is running or has already completed.
+- """
+- with self._condition:
+- if self._state in [RUNNING, FINISHED]:
+- return False
+-
+- if self._state in [CANCELLED, CANCELLED_AND_NOTIFIED]:
+- return True
+-
+- self._state = CANCELLED
+- self._condition.notify_all()
+-
+- self._invoke_callbacks()
+- return True
+-
+- def cancelled(self):
+- """Return True if the future has cancelled."""
+- with self._condition:
+- return self._state in [CANCELLED, CANCELLED_AND_NOTIFIED]
+-
+- def running(self):
+- """Return True if the future is currently executing."""
+- with self._condition:
+- return self._state == RUNNING
+-
+- def done(self):
+- """Return True of the future was cancelled or finished executing."""
+- with self._condition:
+- return self._state in [CANCELLED, CANCELLED_AND_NOTIFIED, FINISHED]
+-
+- def __get_result(self):
+- if self._exception:
+- raise self._exception
+- else:
+- return self._result
+-
+- def add_done_callback(self, fn):
+- """Attaches a callable that will be called when the future finishes.
+-
+- Args:
+- fn: A callable that will be called with this future as its only
+- argument when the future completes or is cancelled. The callable
+- will always be called by a thread in the same process in which
+- it was added. If the future has already completed or been
+- cancelled then the callable will be called immediately. These
+- callables are called in the order that they were added.
+- """
+- with self._condition:
+- if self._state not in [CANCELLED, CANCELLED_AND_NOTIFIED, FINISHED]:
+- self._done_callbacks.append(fn)
+- return
+- fn(self)
+-
+- def result(self, timeout=None):
+- """Return the result of the call that the future represents.
+-
+- Args:
+- timeout: The number of seconds to wait for the result if the future
+- isn't done. If None, then there is no limit on the wait time.
+-
+- Returns:
+- The result of the call that the future represents.
+-
+- Raises:
+- CancelledError: If the future was cancelled.
+- TimeoutError: If the future didn't finish executing before the given
+- timeout.
+- Exception: If the call raised then that exception will be raised.
+- """
+- with self._condition:
+- if self._state in [CANCELLED, CANCELLED_AND_NOTIFIED]:
+- raise CancelledError()
+- elif self._state == FINISHED:
+- return self.__get_result()
+-
+- self._condition.wait(timeout)
+-
+- if self._state in [CANCELLED, CANCELLED_AND_NOTIFIED]:
+- raise CancelledError()
+- elif self._state == FINISHED:
+- return self.__get_result()
+- else:
+- raise TimeoutError()
+-
+- def exception(self, timeout=None):
+- """Return the exception raised by the call that the future represents.
+-
+- Args:
+- timeout: The number of seconds to wait for the exception if the
+- future isn't done. If None, then there is no limit on the wait
+- time.
+-
+- Returns:
+- The exception raised by the call that the future represents or None
+- if the call completed without raising.
+-
+- Raises:
+- CancelledError: If the future was cancelled.
+- TimeoutError: If the future didn't finish executing before the given
+- timeout.
+- """
+-
+- with self._condition:
+- if self._state in [CANCELLED, CANCELLED_AND_NOTIFIED]:
+- raise CancelledError()
+- elif self._state == FINISHED:
+- return self._exception
+-
+- self._condition.wait(timeout)
+-
+- if self._state in [CANCELLED, CANCELLED_AND_NOTIFIED]:
+- raise CancelledError()
+- elif self._state == FINISHED:
+- return self._exception
+- else:
+- raise TimeoutError()
+-
+- # The following methods should only be used by Executors and in tests.
+- def set_running_or_notify_cancel(self):
+- """Mark the future as running or process any cancel notifications.
+-
+- Should only be used by Executor implementations and unit tests.
+-
+- If the future has been cancelled (cancel() was called and returned
+- True) then any threads waiting on the future completing (though calls
+- to as_completed() or wait()) are notified and False is returned.
+-
+- If the future was not cancelled then it is put in the running state
+- (future calls to running() will return True) and True is returned.
+-
+- This method should be called by Executor implementations before
+- executing the work associated with this future. If this method returns
+- False then the work should not be executed.
+-
+- Returns:
+- False if the Future was cancelled, True otherwise.
+-
+- Raises:
+- RuntimeError: if this method was already called or if set_result()
+- or set_exception() was called.
+- """
+- with self._condition:
+- if self._state == CANCELLED:
+- self._state = CANCELLED_AND_NOTIFIED
+- for waiter in self._waiters:
+- waiter.add_cancelled(self)
+- # self._condition.notify_all() is not necessary because
+- # self.cancel() triggers a notification.
+- return False
+- elif self._state == PENDING:
+- self._state = RUNNING
+- return True
+- else:
+- LOGGER.critical('Future %s in unexpected state: %s',
+- id(self.future),
+- self.future._state)
+- raise RuntimeError('Future in unexpected state')
+-
+- def set_result(self, result):
+- """Sets the return value of work associated with the future.
+-
+- Should only be used by Executor implementations and unit tests.
+- """
+- with self._condition:
+- self._result = result
+- self._state = FINISHED
+- for waiter in self._waiters:
+- waiter.add_result(self)
+- self._condition.notify_all()
+- self._invoke_callbacks()
+-
+- def set_exception(self, exception):
+- """Sets the result of the future as being the given exception.
+-
+- Should only be used by Executor implementations and unit tests.
+- """
+- with self._condition:
+- self._exception = exception
+- self._state = FINISHED
+- for waiter in self._waiters:
+- waiter.add_exception(self)
+- self._condition.notify_all()
+- self._invoke_callbacks()
+-
+-class Executor(object):
+- """This is an abstract base class for concrete asynchronous executors."""
+-
+- def submit(self, fn, *args, **kwargs):
+- """Submits a callable to be executed with the given arguments.
+-
+- Schedules the callable to be executed as fn(*args, **kwargs) and returns
+- a Future instance representing the execution of the callable.
+-
+- Returns:
+- A Future representing the given call.
+- """
+- raise NotImplementedError()
+-
+- def map(self, fn, *iterables, **kwargs):
+- """Returns a iterator equivalent to map(fn, iter).
+-
+- Args:
+- fn: A callable that will take as many arguments as there are
+- passed iterables.
+- timeout: The maximum number of seconds to wait. If None, then there
+- is no limit on the wait time.
+-
+- Returns:
+- An iterator equivalent to: map(func, *iterables) but the calls may
+- be evaluated out-of-order.
+-
+- Raises:
+- TimeoutError: If the entire result iterator could not be generated
+- before the given timeout.
+- Exception: If fn(*args) raises for any values.
+- """
+- timeout = kwargs.get('timeout')
+- if timeout is not None:
+- end_time = timeout + time.time()
+-
+- fs = [self.submit(fn, *args) for args in zip(*iterables)]
+-
+- try:
+- for future in fs:
+- if timeout is None:
+- yield future.result()
+- else:
+- yield future.result(end_time - time.time())
+- finally:
+- for future in fs:
+- future.cancel()
+-
+- def shutdown(self, wait=True):
+- """Clean-up the resources associated with the Executor.
+-
+- It is safe to call this method several times. Otherwise, no other
+- methods can be called after this one.
+-
+- Args:
+- wait: If True then shutdown will not return until all running
+- futures have finished executing and the resources used by the
+- executor have been reclaimed.
+- """
+- pass
+-
+- def __enter__(self):
+- return self
+-
+- def __exit__(self, exc_type, exc_val, exc_tb):
+- self.shutdown(wait=True)
+- return False
+diff --git a/third_party/pythonfutures/concurrent/futures/_compat.py b/third_party/pythonfutures/concurrent/futures/_compat.py
+deleted file mode 100755
+index 11462326b..000000000
+--- a/third_party/pythonfutures/concurrent/futures/_compat.py
++++ /dev/null
+@@ -1,101 +0,0 @@
+-from keyword import iskeyword as _iskeyword
+-from operator import itemgetter as _itemgetter
+-import sys as _sys
+-
+-
+-def namedtuple(typename, field_names):
+- """Returns a new subclass of tuple with named fields.
+-
+- >>> Point = namedtuple('Point', 'x y')
+- >>> Point.__doc__ # docstring for the new class
+- 'Point(x, y)'
+- >>> p = Point(11, y=22) # instantiate with positional args or keywords
+- >>> p[0] + p[1] # indexable like a plain tuple
+- 33
+- >>> x, y = p # unpack like a regular tuple
+- >>> x, y
+- (11, 22)
+- >>> p.x + p.y # fields also accessable by name
+- 33
+- >>> d = p._asdict() # convert to a dictionary
+- >>> d['x']
+- 11
+- >>> Point(**d) # convert from a dictionary
+- Point(x=11, y=22)
+- >>> p._replace(x=100) # _replace() is like str.replace() but targets named fields
+- Point(x=100, y=22)
+-
+- """
+-
+- # Parse and validate the field names. Validation serves two purposes,
+- # generating informative error messages and preventing template injection attacks.
+- if isinstance(field_names, basestring):
+- field_names = field_names.replace(',', ' ').split() # names separated by whitespace and/or commas
+- field_names = tuple(map(str, field_names))
+- for name in (typename,) + field_names:
+- if not all(c.isalnum() or c=='_' for c in name):
+- raise ValueError('Type names and field names can only contain alphanumeric characters and underscores: %r' % name)
+- if _iskeyword(name):
+- raise ValueError('Type names and field names cannot be a keyword: %r' % name)
+- if name[0].isdigit():
+- raise ValueError('Type names and field names cannot start with a number: %r' % name)
+- seen_names = set()
+- for name in field_names:
+- if name.startswith('_'):
+- raise ValueError('Field names cannot start with an underscore: %r' % name)
+- if name in seen_names:
+- raise ValueError('Encountered duplicate field name: %r' % name)
+- seen_names.add(name)
+-
+- # Create and fill-in the class template
+- numfields = len(field_names)
+- argtxt = repr(field_names).replace("'", "")[1:-1] # tuple repr without parens or quotes
+- reprtxt = ', '.join('%s=%%r' % name for name in field_names)
+- dicttxt = ', '.join('%r: t[%d]' % (name, pos) for pos, name in enumerate(field_names))
+- template = '''class %(typename)s(tuple):
+- '%(typename)s(%(argtxt)s)' \n
+- __slots__ = () \n
+- _fields = %(field_names)r \n
+- def __new__(_cls, %(argtxt)s):
+- return _tuple.__new__(_cls, (%(argtxt)s)) \n
+- @classmethod
+- def _make(cls, iterable, new=tuple.__new__, len=len):
+- 'Make a new %(typename)s object from a sequence or iterable'
+- result = new(cls, iterable)
+- if len(result) != %(numfields)d:
+- raise TypeError('Expected %(numfields)d arguments, got %%d' %% len(result))
+- return result \n
+- def __repr__(self):
+- return '%(typename)s(%(reprtxt)s)' %% self \n
+- def _asdict(t):
+- 'Return a new dict which maps field names to their values'
+- return {%(dicttxt)s} \n
+- def _replace(_self, **kwds):
+- 'Return a new %(typename)s object replacing specified fields with new values'
+- result = _self._make(map(kwds.pop, %(field_names)r, _self))
+- if kwds:
+- raise ValueError('Got unexpected field names: %%r' %% kwds.keys())
+- return result \n
+- def __getnewargs__(self):
+- return tuple(self) \n\n''' % locals()
+- for i, name in enumerate(field_names):
+- template += ' %s = _property(_itemgetter(%d))\n' % (name, i)
+-
+- # Execute the template string in a temporary namespace and
+- # support tracing utilities by setting a value for frame.f_globals['__name__']
+- namespace = dict(_itemgetter=_itemgetter, __name__='namedtuple_%s' % typename,
+- _property=property, _tuple=tuple)
+- try:
+- exec(template, namespace)
+- except SyntaxError:
+- e = _sys.exc_info()[1]
+- raise SyntaxError(e.message + ':\n' + template)
+- result = namespace[typename]
+-
+- # For pickling to work, the __module__ variable needs to be set to the frame
+- # where the named tuple is created. Bypass this step in enviroments where
+- # sys._getframe is not defined (Jython for example).
+- if hasattr(_sys, '_getframe'):
+- result.__module__ = _sys._getframe(1).f_globals.get('__name__', '__main__')
+-
+- return result
+diff --git a/third_party/pythonfutures/concurrent/futures/process.py b/third_party/pythonfutures/concurrent/futures/process.py
+deleted file mode 100755
+index 98684f8e8..000000000
+--- a/third_party/pythonfutures/concurrent/futures/process.py
++++ /dev/null
+@@ -1,363 +0,0 @@
+-# Copyright 2009 Brian Quinlan. All Rights Reserved.
+-# Licensed to PSF under a Contributor Agreement.
+-
+-"""Implements ProcessPoolExecutor.
+-
+-The follow diagram and text describe the data-flow through the system:
+-
+-|======================= In-process =====================|== Out-of-process ==|
+-
+-+----------+ +----------+ +--------+ +-----------+ +---------+
+-| | => | Work Ids | => | | => | Call Q | => | |
+-| | +----------+ | | +-----------+ | |
+-| | | ... | | | | ... | | |
+-| | | 6 | | | | 5, call() | | |
+-| | | 7 | | | | ... | | |
+-| Process | | ... | | Local | +-----------+ | Process |
+-| Pool | +----------+ | Worker | | #1..n |
+-| Executor | | Thread | | |
+-| | +----------- + | | +-----------+ | |
+-| | <=> | Work Items | <=> | | <= | Result Q | <= | |
+-| | +------------+ | | +-----------+ | |
+-| | | 6: call() | | | | ... | | |
+-| | | future | | | | 4, result | | |
+-| | | ... | | | | 3, except | | |
+-+----------+ +------------+ +--------+ +-----------+ +---------+
+-
+-Executor.submit() called:
+-- creates a uniquely numbered _WorkItem and adds it to the "Work Items" dict
+-- adds the id of the _WorkItem to the "Work Ids" queue
+-
+-Local worker thread:
+-- reads work ids from the "Work Ids" queue and looks up the corresponding
+- WorkItem from the "Work Items" dict: if the work item has been cancelled then
+- it is simply removed from the dict, otherwise it is repackaged as a
+- _CallItem and put in the "Call Q". New _CallItems are put in the "Call Q"
+- until "Call Q" is full. NOTE: the size of the "Call Q" is kept small because
+- calls placed in the "Call Q" can no longer be cancelled with Future.cancel().
+-- reads _ResultItems from "Result Q", updates the future stored in the
+- "Work Items" dict and deletes the dict entry
+-
+-Process #1..n:
+-- reads _CallItems from "Call Q", executes the calls, and puts the resulting
+- _ResultItems in "Request Q"
+-"""
+-
+-from __future__ import with_statement
+-import atexit
+-import multiprocessing
+-import threading
+-import weakref
+-import sys
+-
+-from concurrent.futures import _base
+-
+-try:
+- import queue
+-except ImportError:
+- import Queue as queue
+-
+-__author__ = 'Brian Quinlan (brian@sweetapp.com)'
+-
+-# Workers are created as daemon threads and processes. This is done to allow the
+-# interpreter to exit when there are still idle processes in a
+-# ProcessPoolExecutor's process pool (i.e. shutdown() was not called). However,
+-# allowing workers to die with the interpreter has two undesirable properties:
+-# - The workers would still be running during interpretor shutdown,
+-# meaning that they would fail in unpredictable ways.
+-# - The workers could be killed while evaluating a work item, which could
+-# be bad if the callable being evaluated has external side-effects e.g.
+-# writing to a file.
+-#
+-# To work around this problem, an exit handler is installed which tells the
+-# workers to exit when their work queues are empty and then waits until the
+-# threads/processes finish.
+-
+-_threads_queues = weakref.WeakKeyDictionary()
+-_shutdown = False
+-
+-def _python_exit():
+- global _shutdown
+- _shutdown = True
+- items = list(_threads_queues.items())
+- for t, q in items:
+- q.put(None)
+- for t, q in items:
+- t.join()
+-
+-# Controls how many more calls than processes will be queued in the call queue.
+-# A smaller number will mean that processes spend more time idle waiting for
+-# work while a larger number will make Future.cancel() succeed less frequently
+-# (Futures in the call queue cannot be cancelled).
+-EXTRA_QUEUED_CALLS = 1
+-
+-class _WorkItem(object):
+- def __init__(self, future, fn, args, kwargs):
+- self.future = future
+- self.fn = fn
+- self.args = args
+- self.kwargs = kwargs
+-
+-class _ResultItem(object):
+- def __init__(self, work_id, exception=None, result=None):
+- self.work_id = work_id
+- self.exception = exception
+- self.result = result
+-
+-class _CallItem(object):
+- def __init__(self, work_id, fn, args, kwargs):
+- self.work_id = work_id
+- self.fn = fn
+- self.args = args
+- self.kwargs = kwargs
+-
+-def _process_worker(call_queue, result_queue):
+- """Evaluates calls from call_queue and places the results in result_queue.
+-
+- This worker is run in a separate process.
+-
+- Args:
+- call_queue: A multiprocessing.Queue of _CallItems that will be read and
+- evaluated by the worker.
+- result_queue: A multiprocessing.Queue of _ResultItems that will written
+- to by the worker.
+- shutdown: A multiprocessing.Event that will be set as a signal to the
+- worker that it should exit when call_queue is empty.
+- """
+- while True:
+- call_item = call_queue.get(block=True)
+- if call_item is None:
+- # Wake up queue management thread
+- result_queue.put(None)
+- return
+- try:
+- r = call_item.fn(*call_item.args, **call_item.kwargs)
+- except BaseException:
+- e = sys.exc_info()[1]
+- result_queue.put(_ResultItem(call_item.work_id,
+- exception=e))
+- else:
+- result_queue.put(_ResultItem(call_item.work_id,
+- result=r))
+-
+-def _add_call_item_to_queue(pending_work_items,
+- work_ids,
+- call_queue):
+- """Fills call_queue with _WorkItems from pending_work_items.
+-
+- This function never blocks.
+-
+- Args:
+- pending_work_items: A dict mapping work ids to _WorkItems e.g.
+- {5: <_WorkItem...>, 6: <_WorkItem...>, ...}
+- work_ids: A queue.Queue of work ids e.g. Queue([5, 6, ...]). Work ids
+- are consumed and the corresponding _WorkItems from
+- pending_work_items are transformed into _CallItems and put in
+- call_queue.
+- call_queue: A multiprocessing.Queue that will be filled with _CallItems
+- derived from _WorkItems.
+- """
+- while True:
+- if call_queue.full():
+- return
+- try:
+- work_id = work_ids.get(block=False)
+- except queue.Empty:
+- return
+- else:
+- work_item = pending_work_items[work_id]
+-
+- if work_item.future.set_running_or_notify_cancel():
+- call_queue.put(_CallItem(work_id,
+- work_item.fn,
+- work_item.args,
+- work_item.kwargs),
+- block=True)
+- else:
+- del pending_work_items[work_id]
+- continue
+-
+-def _queue_management_worker(executor_reference,
+- processes,
+- pending_work_items,
+- work_ids_queue,
+- call_queue,
+- result_queue):
+- """Manages the communication between this process and the worker processes.
+-
+- This function is run in a local thread.
+-
+- Args:
+- executor_reference: A weakref.ref to the ProcessPoolExecutor that owns
+- this thread. Used to determine if the ProcessPoolExecutor has been
+- garbage collected and that this function can exit.
+- process: A list of the multiprocessing.Process instances used as
+- workers.
+- pending_work_items: A dict mapping work ids to _WorkItems e.g.
+- {5: <_WorkItem...>, 6: <_WorkItem...>, ...}
+- work_ids_queue: A queue.Queue of work ids e.g. Queue([5, 6, ...]).
+- call_queue: A multiprocessing.Queue that will be filled with _CallItems
+- derived from _WorkItems for processing by the process workers.
+- result_queue: A multiprocessing.Queue of _ResultItems generated by the
+- process workers.
+- """
+- nb_shutdown_processes = [0]
+- def shutdown_one_process():
+- """Tell a worker to terminate, which will in turn wake us again"""
+- call_queue.put(None)
+- nb_shutdown_processes[0] += 1
+- while True:
+- _add_call_item_to_queue(pending_work_items,
+- work_ids_queue,
+- call_queue)
+-
+- result_item = result_queue.get(block=True)
+- if result_item is not None:
+- work_item = pending_work_items[result_item.work_id]
+- del pending_work_items[result_item.work_id]
+-
+- if result_item.exception:
+- work_item.future.set_exception(result_item.exception)
+- else:
+- work_item.future.set_result(result_item.result)
+- # Check whether we should start shutting down.
+- executor = executor_reference()
+- # No more work items can be added if:
+- # - The interpreter is shutting down OR
+- # - The executor that owns this worker has been collected OR
+- # - The executor that owns this worker has been shutdown.
+- if _shutdown or executor is None or executor._shutdown_thread:
+- # Since no new work items can be added, it is safe to shutdown
+- # this thread if there are no pending work items.
+- if not pending_work_items:
+- while nb_shutdown_processes[0] < len(processes):
+- shutdown_one_process()
+- # If .join() is not called on the created processes then
+- # some multiprocessing.Queue methods may deadlock on Mac OS
+- # X.
+- for p in processes:
+- p.join()
+- call_queue.close()
+- return
+- del executor
+-
+-_system_limits_checked = False
+-_system_limited = None
+-def _check_system_limits():
+- global _system_limits_checked, _system_limited
+- if _system_limits_checked:
+- if _system_limited:
+- raise NotImplementedError(_system_limited)
+- _system_limits_checked = True
+- try:
+- import os
+- nsems_max = os.sysconf("SC_SEM_NSEMS_MAX")
+- except (AttributeError, ValueError):
+- # sysconf not available or setting not available
+- return
+- if nsems_max == -1:
+- # indetermine limit, assume that limit is determined
+- # by available memory only
+- return
+- if nsems_max >= 256:
+- # minimum number of semaphores available
+- # according to POSIX
+- return
+- _system_limited = "system provides too few semaphores (%d available, 256 necessary)" % nsems_max
+- raise NotImplementedError(_system_limited)
+-
+-class ProcessPoolExecutor(_base.Executor):
+- def __init__(self, max_workers=None):
+- """Initializes a new ProcessPoolExecutor instance.
+-
+- Args:
+- max_workers: The maximum number of processes that can be used to
+- execute the given calls. If None or not given then as many
+- worker processes will be created as the machine has processors.
+- """
+- _check_system_limits()
+-
+- if max_workers is None:
+- self._max_workers = multiprocessing.cpu_count()
+- else:
+- self._max_workers = max_workers
+-
+- # Make the call queue slightly larger than the number of processes to
+- # prevent the worker processes from idling. But don't make it too big
+- # because futures in the call queue cannot be cancelled.
+- self._call_queue = multiprocessing.Queue(self._max_workers +
+- EXTRA_QUEUED_CALLS)
+- self._result_queue = multiprocessing.Queue()
+- self._work_ids = queue.Queue()
+- self._queue_management_thread = None
+- self._processes = set()
+-
+- # Shutdown is a two-step process.
+- self._shutdown_thread = False
+- self._shutdown_lock = threading.Lock()
+- self._queue_count = 0
+- self._pending_work_items = {}
+-
+- def _start_queue_management_thread(self):
+- # When the executor gets lost, the weakref callback will wake up
+- # the queue management thread.
+- def weakref_cb(_, q=self._result_queue):
+- q.put(None)
+- if self._queue_management_thread is None:
+- self._queue_management_thread = threading.Thread(
+- target=_queue_management_worker,
+- args=(weakref.ref(self, weakref_cb),
+- self._processes,
+- self._pending_work_items,
+- self._work_ids,
+- self._call_queue,
+- self._result_queue))
+- self._queue_management_thread.daemon = True
+- self._queue_management_thread.start()
+- _threads_queues[self._queue_management_thread] = self._result_queue
+-
+- def _adjust_process_count(self):
+- for _ in range(len(self._processes), self._max_workers):
+- p = multiprocessing.Process(
+- target=_process_worker,
+- args=(self._call_queue,
+- self._result_queue))
+- p.start()
+- self._processes.add(p)
+-
+- def submit(self, fn, *args, **kwargs):
+- with self._shutdown_lock:
+- if self._shutdown_thread:
+- raise RuntimeError('cannot schedule new futures after shutdown')
+-
+- f = _base.Future()
+- w = _WorkItem(f, fn, args, kwargs)
+-
+- self._pending_work_items[self._queue_count] = w
+- self._work_ids.put(self._queue_count)
+- self._queue_count += 1
+- # Wake up queue management thread
+- self._result_queue.put(None)
+-
+- self._start_queue_management_thread()
+- self._adjust_process_count()
+- return f
+- submit.__doc__ = _base.Executor.submit.__doc__
+-
+- def shutdown(self, wait=True):
+- with self._shutdown_lock:
+- self._shutdown_thread = True
+- if self._queue_management_thread:
+- # Wake up queue management thread
+- self._result_queue.put(None)
+- if wait:
+- self._queue_management_thread.join()
+- # To reduce the risk of openning too many files, remove references to
+- # objects that use file descriptors.
+- self._queue_management_thread = None
+- self._call_queue = None
+- self._result_queue = None
+- self._processes = None
+- shutdown.__doc__ = _base.Executor.shutdown.__doc__
+-
+-atexit.register(_python_exit)
+diff --git a/third_party/pythonfutures/concurrent/futures/thread.py b/third_party/pythonfutures/concurrent/futures/thread.py
+deleted file mode 100755
+index a45959d3b..000000000
+--- a/third_party/pythonfutures/concurrent/futures/thread.py
++++ /dev/null
+@@ -1,138 +0,0 @@
+-# Copyright 2009 Brian Quinlan. All Rights Reserved.
+-# Licensed to PSF under a Contributor Agreement.
+-
+-"""Implements ThreadPoolExecutor."""
+-
+-from __future__ import with_statement
+-import atexit
+-import threading
+-import weakref
+-import sys
+-
+-from concurrent.futures import _base
+-
+-try:
+- import queue
+-except ImportError:
+- import Queue as queue
+-
+-__author__ = 'Brian Quinlan (brian@sweetapp.com)'
+-
+-# Workers are created as daemon threads. This is done to allow the interpreter
+-# to exit when there are still idle threads in a ThreadPoolExecutor's thread
+-# pool (i.e. shutdown() was not called). However, allowing workers to die with
+-# the interpreter has two undesirable properties:
+-# - The workers would still be running during interpretor shutdown,
+-# meaning that they would fail in unpredictable ways.
+-# - The workers could be killed while evaluating a work item, which could
+-# be bad if the callable being evaluated has external side-effects e.g.
+-# writing to a file.
+-#
+-# To work around this problem, an exit handler is installed which tells the
+-# workers to exit when their work queues are empty and then waits until the
+-# threads finish.
+-
+-_threads_queues = weakref.WeakKeyDictionary()
+-_shutdown = False
+-
+-def _python_exit():
+- global _shutdown
+- _shutdown = True
+- items = list(_threads_queues.items())
+- for t, q in items:
+- q.put(None)
+- for t, q in items:
+- t.join()
+-
+-atexit.register(_python_exit)
+-
+-class _WorkItem(object):
+- def __init__(self, future, fn, args, kwargs):
+- self.future = future
+- self.fn = fn
+- self.args = args
+- self.kwargs = kwargs
+-
+- def run(self):
+- if not self.future.set_running_or_notify_cancel():
+- return
+-
+- try:
+- result = self.fn(*self.args, **self.kwargs)
+- except BaseException:
+- e = sys.exc_info()[1]
+- self.future.set_exception(e)
+- else:
+- self.future.set_result(result)
+-
+-def _worker(executor_reference, work_queue):
+- try:
+- while True:
+- work_item = work_queue.get(block=True)
+- if work_item is not None:
+- work_item.run()
+- continue
+- executor = executor_reference()
+- # Exit if:
+- # - The interpreter is shutting down OR
+- # - The executor that owns the worker has been collected OR
+- # - The executor that owns the worker has been shutdown.
+- if _shutdown or executor is None or executor._shutdown:
+- # Notice other workers
+- work_queue.put(None)
+- return
+- del executor
+- except BaseException:
+- _base.LOGGER.critical('Exception in worker', exc_info=True)
+-
+-class ThreadPoolExecutor(_base.Executor):
+- def __init__(self, max_workers):
+- """Initializes a new ThreadPoolExecutor instance.
+-
+- Args:
+- max_workers: The maximum number of threads that can be used to
+- execute the given calls.
+- """
+- self._max_workers = max_workers
+- self._work_queue = queue.Queue()
+- self._threads = set()
+- self._shutdown = False
+- self._shutdown_lock = threading.Lock()
+-
+- def submit(self, fn, *args, **kwargs):
+- with self._shutdown_lock:
+- if self._shutdown:
+- raise RuntimeError('cannot schedule new futures after shutdown')
+-
+- f = _base.Future()
+- w = _WorkItem(f, fn, args, kwargs)
+-
+- self._work_queue.put(w)
+- self._adjust_thread_count()
+- return f
+- submit.__doc__ = _base.Executor.submit.__doc__
+-
+- def _adjust_thread_count(self):
+- # When the executor gets lost, the weakref callback will wake up
+- # the worker threads.
+- def weakref_cb(_, q=self._work_queue):
+- q.put(None)
+- # TODO(bquinlan): Should avoid creating new threads if there are more
+- # idle threads than items in the work queue.
+- if len(self._threads) < self._max_workers:
+- t = threading.Thread(target=_worker,
+- args=(weakref.ref(self, weakref_cb),
+- self._work_queue))
+- t.daemon = True
+- t.start()
+- self._threads.add(t)
+- _threads_queues[t] = self._work_queue
+-
+- def shutdown(self, wait=True):
+- with self._shutdown_lock:
+- self._shutdown = True
+- self._work_queue.put(None)
+- if wait:
+- for t in self._threads:
+- t.join()
+- shutdown.__doc__ = _base.Executor.shutdown.__doc__
+diff --git a/third_party/pythonfutures/crawl.py b/third_party/pythonfutures/crawl.py
+deleted file mode 100755
+index 86e0af7fe..000000000
+--- a/third_party/pythonfutures/crawl.py
++++ /dev/null
+@@ -1,74 +0,0 @@
+-"""Compare the speed of downloading URLs sequentially vs. using futures."""
+-
+-import functools
+-import time
+-import timeit
+-import sys
+-
+-try:
+- from urllib2 import urlopen
+-except ImportError:
+- from urllib.request import urlopen
+-
+-from concurrent.futures import (as_completed, ThreadPoolExecutor,
+- ProcessPoolExecutor)
+-
+-URLS = ['http://www.google.com/',
+- 'http://www.apple.com/',
+- 'http://www.ibm.com',
+- 'http://www.thisurlprobablydoesnotexist.com',
+- 'http://www.slashdot.org/',
+- 'http://www.python.org/',
+- 'http://www.bing.com/',
+- 'http://www.facebook.com/',
+- 'http://www.yahoo.com/',
+- 'http://www.youtube.com/',
+- 'http://www.blogger.com/']
+-
+-def load_url(url, timeout):
+- kwargs = {'timeout': timeout} if sys.version_info >= (2, 6) else {}
+- return urlopen(url, **kwargs).read()
+-
+-def download_urls_sequential(urls, timeout=60):
+- url_to_content = {}
+- for url in urls:
+- try:
+- url_to_content[url] = load_url(url, timeout=timeout)
+- except:
+- pass
+- return url_to_content
+-
+-def download_urls_with_executor(urls, executor, timeout=60):
+- try:
+- url_to_content = {}
+- future_to_url = dict((executor.submit(load_url, url, timeout), url)
+- for url in urls)
+-
+- for future in as_completed(future_to_url):
+- try:
+- url_to_content[future_to_url[future]] = future.result()
+- except:
+- pass
+- return url_to_content
+- finally:
+- executor.shutdown()
+-
+-def main():
+- for name, fn in [('sequential',
+- functools.partial(download_urls_sequential, URLS)),
+- ('processes',
+- functools.partial(download_urls_with_executor,
+- URLS,
+- ProcessPoolExecutor(10))),
+- ('threads',
+- functools.partial(download_urls_with_executor,
+- URLS,
+- ThreadPoolExecutor(10)))]:
+- sys.stdout.write('%s: ' % name.ljust(12))
+- start = time.time()
+- url_map = fn()
+- sys.stdout.write('%.2f seconds (%d of %d downloaded)\n' %
+- (time.time() - start, len(url_map), len(URLS)))
+-
+-if __name__ == '__main__':
+- main()
+diff --git a/third_party/pythonfutures/docs/conf.py b/third_party/pythonfutures/docs/conf.py
+deleted file mode 100755
+index 124cd5183..000000000
+--- a/third_party/pythonfutures/docs/conf.py
++++ /dev/null
+@@ -1,194 +0,0 @@
+-# -*- coding: utf-8 -*-
+-#
+-# futures documentation build configuration file, created by
+-# sphinx-quickstart on Wed Jun 3 19:35:34 2009.
+-#
+-# This file is execfile()d with the current directory set to its containing dir.
+-#
+-# Note that not all possible configuration values are present in this
+-# autogenerated file.
+-#
+-# All configuration values have a default; values that are commented out
+-# serve to show the default.
+-
+-import sys, os
+-
+-# If extensions (or modules to document with autodoc) are in another directory,
+-# add these directories to sys.path here. If the directory is relative to the
+-# documentation root, use os.path.abspath to make it absolute, like shown here.
+-#sys.path.append(os.path.abspath('.'))
+-
+-# -- General configuration -----------------------------------------------------
+-
+-# Add any Sphinx extension module names here, as strings. They can be extensions
+-# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+-extensions = []
+-
+-# Add any paths that contain templates here, relative to this directory.
+-templates_path = ['_templates']
+-
+-# The suffix of source filenames.
+-source_suffix = '.rst'
+-
+-# The encoding of source files.
+-#source_encoding = 'utf-8'
+-
+-# The master toctree document.
+-master_doc = 'index'
+-
+-# General information about the project.
+-project = u'futures'
+-copyright = u'2009-2011, Brian Quinlan'
+-
+-# The version info for the project you're documenting, acts as replacement for
+-# |version| and |release|, also used in various other places throughout the
+-# built documents.
+-#
+-# The short X.Y version.
+-version = '2.1.3'
+-# The full version, including alpha/beta/rc tags.
+-release = '2.1.3'
+-
+-# The language for content autogenerated by Sphinx. Refer to documentation
+-# for a list of supported languages.
+-#language = None
+-
+-# There are two options for replacing |today|: either, you set today to some
+-# non-false value, then it is used:
+-#today = ''
+-# Else, today_fmt is used as the format for a strftime call.
+-#today_fmt = '%B %d, %Y'
+-
+-# List of documents that shouldn't be included in the build.
+-#unused_docs = []
+-
+-# List of directories, relative to source directory, that shouldn't be searched
+-# for source files.
+-exclude_trees = ['_build']
+-
+-# The reST default role (used for this markup: `text`) to use for all documents.
+-#default_role = None
+-
+-# If true, '()' will be appended to :func: etc. cross-reference text.
+-#add_function_parentheses = True
+-
+-# If true, the current module name will be prepended to all description
+-# unit titles (such as .. function::).
+-#add_module_names = True
+-
+-# If true, sectionauthor and moduleauthor directives will be shown in the
+-# output. They are ignored by default.
+-#show_authors = False
+-
+-# The name of the Pygments (syntax highlighting) style to use.
+-pygments_style = 'sphinx'
+-
+-# A list of ignored prefixes for module index sorting.
+-#modindex_common_prefix = []
+-
+-
+-# -- Options for HTML output ---------------------------------------------------
+-
+-# The theme to use for HTML and HTML Help pages. Major themes that come with
+-# Sphinx are currently 'default' and 'sphinxdoc'.
+-html_theme = 'default'
+-
+-# Theme options are theme-specific and customize the look and feel of a theme
+-# further. For a list of options available for each theme, see the
+-# documentation.
+-#html_theme_options = {}
+-
+-# Add any paths that contain custom themes here, relative to this directory.
+-#html_theme_path = []
+-
+-# The name for this set of Sphinx documents. If None, it defaults to
+-# "<project> v<release> documentation".
+-#html_title = None
+-
+-# A shorter title for the navigation bar. Default is the same as html_title.
+-#html_short_title = None
+-
+-# The name of an image file (relative to this directory) to place at the top
+-# of the sidebar.
+-#html_logo = None
+-
+-# The name of an image file (within the static path) to use as favicon of the
+-# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+-# pixels large.
+-#html_favicon = None
+-
+-# Add any paths that contain custom static files (such as style sheets) here,
+-# relative to this directory. They are copied after the builtin static files,
+-# so a file named "default.css" will overwrite the builtin "default.css".
+-html_static_path = ['_static']
+-
+-# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+-# using the given strftime format.
+-#html_last_updated_fmt = '%b %d, %Y'
+-
+-# If true, SmartyPants will be used to convert quotes and dashes to
+-# typographically correct entities.
+-#html_use_smartypants = True
+-
+-# Custom sidebar templates, maps document names to template names.
+-#html_sidebars = {}
+-
+-# Additional templates that should be rendered to pages, maps page names to
+-# template names.
+-#html_additional_pages = {}
+-
+-# If false, no module index is generated.
+-#html_use_modindex = True
+-
+-# If false, no index is generated.
+-#html_use_index = True
+-
+-# If true, the index is split into individual pages for each letter.
+-#html_split_index = False
+-
+-# If true, links to the reST sources are added to the pages.
+-#html_show_sourcelink = True
+-
+-# If true, an OpenSearch description file will be output, and all pages will
+-# contain a <link> tag referring to it. The value of this option must be the
+-# base URL from which the finished HTML is served.
+-#html_use_opensearch = ''
+-
+-# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
+-#html_file_suffix = ''
+-
+-# Output file base name for HTML help builder.
+-htmlhelp_basename = 'futuresdoc'
+-
+-
+-# -- Options for LaTeX output --------------------------------------------------
+-
+-# The paper size ('letter' or 'a4').
+-#latex_paper_size = 'letter'
+-
+-# The font size ('10pt', '11pt' or '12pt').
+-#latex_font_size = '10pt'
+-
+-# Grouping the document tree into LaTeX files. List of tuples
+-# (source start file, target name, title, author, documentclass [howto/manual]).
+-latex_documents = [
+- ('index', 'futures.tex', u'futures Documentation',
+- u'Brian Quinlan', 'manual'),
+-]
+-
+-# The name of an image file (relative to this directory) to place at the top of
+-# the title page.
+-#latex_logo = None
+-
+-# For "manual" documents, if this is true, then toplevel headings are parts,
+-# not chapters.
+-#latex_use_parts = False
+-
+-# Additional stuff for the LaTeX preamble.
+-#latex_preamble = ''
+-
+-# Documents to append as an appendix to all manuals.
+-#latex_appendices = []
+-
+-# If false, no module index is generated.
+-#latex_use_modindex = True
+diff --git a/third_party/pythonfutures/docs/index.rst b/third_party/pythonfutures/docs/index.rst
+deleted file mode 100755
+index 525ce6ab3..000000000
+--- a/third_party/pythonfutures/docs/index.rst
++++ /dev/null
+@@ -1,345 +0,0 @@
+-:mod:`concurrent.futures` --- Asynchronous computation
+-======================================================
+-
+-.. module:: concurrent.futures
+- :synopsis: Execute computations asynchronously using threads or processes.
+-
+-The :mod:`concurrent.futures` module provides a high-level interface for
+-asynchronously executing callables.
+-
+-The asynchronous execution can be be performed by threads using
+-:class:`ThreadPoolExecutor` or seperate processes using
+-:class:`ProcessPoolExecutor`. Both implement the same interface, which is
+-defined by the abstract :class:`Executor` class.
+-
+-Executor Objects
+-----------------
+-
+-:class:`Executor` is an abstract class that provides methods to execute calls
+-asynchronously. It should not be used directly, but through its two
+-subclasses: :class:`ThreadPoolExecutor` and :class:`ProcessPoolExecutor`.
+-
+-.. method:: Executor.submit(fn, *args, **kwargs)
+-
+- Schedules the callable to be executed as *fn*(*\*args*, *\*\*kwargs*) and
+- returns a :class:`Future` representing the execution of the callable.
+-
+-::
+-
+- with ThreadPoolExecutor(max_workers=1) as executor:
+- future = executor.submit(pow, 323, 1235)
+- print(future.result())
+-
+-.. method:: Executor.map(func, *iterables, timeout=None)
+-
+- Equivalent to map(*func*, *\*iterables*) but func is executed asynchronously
+- and several calls to *func* may be made concurrently. The returned iterator
+- raises a :exc:`TimeoutError` if :meth:`__next__()` is called and the result
+- isn't available after *timeout* seconds from the original call to
+- :meth:`map()`. *timeout* can be an int or float. If *timeout* is not
+- specified or ``None`` then there is no limit to the wait time. If a call
+- raises an exception then that exception will be raised when its value is
+- retrieved from the iterator.
+-
+-.. method:: Executor.shutdown(wait=True)
+-
+- Signal the executor that it should free any resources that it is using when
+- the currently pending futures are done executing. Calls to
+- :meth:`Executor.submit` and :meth:`Executor.map` made after shutdown will
+- raise :exc:`RuntimeError`.
+-
+- If *wait* is `True` then this method will not return until all the pending
+- futures are done executing and the resources associated with the executor
+- have been freed. If *wait* is `False` then this method will return
+- immediately and the resources associated with the executor will be freed
+- when all pending futures are done executing. Regardless of the value of
+- *wait*, the entire Python program will not exit until all pending futures
+- are done executing.
+-
+- You can avoid having to call this method explicitly if you use the `with`
+- statement, which will shutdown the `Executor` (waiting as if
+- `Executor.shutdown` were called with *wait* set to `True`):
+-
+-::
+-
+- import shutil
+- with ThreadPoolExecutor(max_workers=4) as e:
+- e.submit(shutil.copy, 'src1.txt', 'dest1.txt')
+- e.submit(shutil.copy, 'src2.txt', 'dest2.txt')
+- e.submit(shutil.copy, 'src3.txt', 'dest3.txt')
+- e.submit(shutil.copy, 'src3.txt', 'dest4.txt')
+-
+-
+-ThreadPoolExecutor Objects
+---------------------------
+-
+-The :class:`ThreadPoolExecutor` class is an :class:`Executor` subclass that uses
+-a pool of threads to execute calls asynchronously.
+-
+-Deadlock can occur when the callable associated with a :class:`Future` waits on
+-the results of another :class:`Future`. For example:
+-
+-::
+-
+- import time
+- def wait_on_b():
+- time.sleep(5)
+- print(b.result()) # b will never complete because it is waiting on a.
+- return 5
+-
+- def wait_on_a():
+- time.sleep(5)
+- print(a.result()) # a will never complete because it is waiting on b.
+- return 6
+-
+-
+- executor = ThreadPoolExecutor(max_workers=2)
+- a = executor.submit(wait_on_b)
+- b = executor.submit(wait_on_a)
+-
+-And:
+-
+-::
+-
+- def wait_on_future():
+- f = executor.submit(pow, 5, 2)
+- # This will never complete because there is only one worker thread and
+- # it is executing this function.
+- print(f.result())
+-
+- executor = ThreadPoolExecutor(max_workers=1)
+- executor.submit(wait_on_future)
+-
+-.. class:: ThreadPoolExecutor(max_workers)
+-
+- Executes calls asynchronously using at pool of at most *max_workers* threads.
+-
+-.. _threadpoolexecutor-example:
+-
+-ThreadPoolExecutor Example
+-^^^^^^^^^^^^^^^^^^^^^^^^^^
+-::
+-
+- from concurrent import futures
+- import urllib.request
+-
+- URLS = ['http://www.foxnews.com/',
+- 'http://www.cnn.com/',
+- 'http://europe.wsj.com/',
+- 'http://www.bbc.co.uk/',
+- 'http://some-made-up-domain.com/']
+-
+- def load_url(url, timeout):
+- return urllib.request.urlopen(url, timeout=timeout).read()
+-
+- with futures.ThreadPoolExecutor(max_workers=5) as executor:
+- future_to_url = dict((executor.submit(load_url, url, 60), url)
+- for url in URLS)
+-
+- for future in futures.as_completed(future_to_url):
+- url = future_to_url[future]
+- if future.exception() is not None:
+- print('%r generated an exception: %s' % (url,
+- future.exception()))
+- else:
+- print('%r page is %d bytes' % (url, len(future.result())))
+-
+-ProcessPoolExecutor Objects
+----------------------------
+-
+-The :class:`ProcessPoolExecutor` class is an :class:`Executor` subclass that
+-uses a pool of processes to execute calls asynchronously.
+-:class:`ProcessPoolExecutor` uses the :mod:`multiprocessing` module, which
+-allows it to side-step the :term:`Global Interpreter Lock` but also means that
+-only picklable objects can be executed and returned.
+-
+-Calling :class:`Executor` or :class:`Future` methods from a callable submitted
+-to a :class:`ProcessPoolExecutor` will result in deadlock.
+-
+-.. class:: ProcessPoolExecutor(max_workers=None)
+-
+- Executes calls asynchronously using a pool of at most *max_workers*
+- processes. If *max_workers* is ``None`` or not given then as many worker
+- processes will be created as the machine has processors.
+-
+-.. _processpoolexecutor-example:
+-
+-ProcessPoolExecutor Example
+-^^^^^^^^^^^^^^^^^^^^^^^^^^^
+-::
+-
+- import math
+-
+- PRIMES = [
+- 112272535095293,
+- 112582705942171,
+- 112272535095293,
+- 115280095190773,
+- 115797848077099,
+- 1099726899285419]
+-
+- def is_prime(n):
+- if n % 2 == 0:
+- return False
+-
+- sqrt_n = int(math.floor(math.sqrt(n)))
+- for i in range(3, sqrt_n + 1, 2):
+- if n % i == 0:
+- return False
+- return True
+-
+- def main():
+- with futures.ProcessPoolExecutor() as executor:
+- for number, prime in zip(PRIMES, executor.map(is_prime, PRIMES)):
+- print('%d is prime: %s' % (number, prime))
+-
+- if __name__ == '__main__':
+- main()
+-
+-Future Objects
+---------------
+-
+-The :class:`Future` class encapulates the asynchronous execution of a callable.
+-:class:`Future` instances are created by :meth:`Executor.submit`.
+-
+-.. method:: Future.cancel()
+-
+- Attempt to cancel the call. If the call is currently being executed then
+- it cannot be cancelled and the method will return `False`, otherwise the call
+- will be cancelled and the method will return `True`.
+-
+-.. method:: Future.cancelled()
+-
+- Return `True` if the call was successfully cancelled.
+-
+-.. method:: Future.running()
+-
+- Return `True` if the call is currently being executed and cannot be
+- cancelled.
+-
+-.. method:: Future.done()
+-
+- Return `True` if the call was successfully cancelled or finished running.
+-
+-.. method:: Future.result(timeout=None)
+-
+- Return the value returned by the call. If the call hasn't yet completed then
+- this method will wait up to *timeout* seconds. If the call hasn't completed
+- in *timeout* seconds then a :exc:`TimeoutError` will be raised. *timeout* can
+- be an int or float.If *timeout* is not specified or ``None`` then there is no
+- limit to the wait time.
+-
+- If the future is cancelled before completing then :exc:`CancelledError` will
+- be raised.
+-
+- If the call raised then this method will raise the same exception.
+-
+-.. method:: Future.exception(timeout=None)
+-
+- Return the exception raised by the call. If the call hasn't yet completed
+- then this method will wait up to *timeout* seconds. If the call hasn't
+- completed in *timeout* seconds then a :exc:`TimeoutError` will be raised.
+- *timeout* can be an int or float. If *timeout* is not specified or ``None``
+- then there is no limit to the wait time.
+-
+- If the future is cancelled before completing then :exc:`CancelledError` will
+- be raised.
+-
+- If the call completed without raising then ``None`` is returned.
+-
+-.. method:: Future.add_done_callback(fn)
+-
+- Attaches the callable *fn* to the future. *fn* will be called, with the
+- future as its only argument, when the future is cancelled or finishes
+- running.
+-
+- Added callables are called in the order that they were added and are always
+- called in a thread belonging to the process that added them. If the callable
+- raises an :exc:`Exception` then it will be logged and ignored. If the
+- callable raises another :exc:`BaseException` then the behavior is not
+- defined.
+-
+- If the future has already completed or been cancelled then *fn* will be
+- called immediately.
+-
+-Internal Future Methods
+-^^^^^^^^^^^^^^^^^^^^^^^
+-
+-The following :class:`Future` methods are meant for use in unit tests and
+-:class:`Executor` implementations.
+-
+-.. method:: Future.set_running_or_notify_cancel()
+-
+- This method should only be called by :class:`Executor` implementations before
+- executing the work associated with the :class:`Future` and by unit tests.
+-
+- If the method returns `False` then the :class:`Future` was cancelled i.e.
+- :meth:`Future.cancel` was called and returned `True`. Any threads waiting
+- on the :class:`Future` completing (i.e. through :func:`as_completed` or
+- :func:`wait`) will be woken up.
+-
+- If the method returns `True` then the :class:`Future` was not cancelled
+- and has been put in the running state i.e. calls to
+- :meth:`Future.running` will return `True`.
+-
+- This method can only be called once and cannot be called after
+- :meth:`Future.set_result` or :meth:`Future.set_exception` have been
+- called.
+-
+-.. method:: Future.set_result(result)
+-
+- Sets the result of the work associated with the :class:`Future` to *result*.
+-
+- This method should only be used by Executor implementations and unit tests.
+-
+-.. method:: Future.set_exception(exception)
+-
+- Sets the result of the work associated with the :class:`Future` to the
+- :class:`Exception` *exception*.
+-
+- This method should only be used by Executor implementations and unit tests.
+-
+-Module Functions
+-----------------
+-
+-.. function:: wait(fs, timeout=None, return_when=ALL_COMPLETED)
+-
+- Wait for the :class:`Future` instances (possibly created by different
+- :class:`Executor` instances) given by *fs* to complete. Returns a named
+- 2-tuple of sets. The first set, named "done", contains the futures that
+- completed (finished or were cancelled) before the wait completed. The second
+- set, named "not_done", contains uncompleted futures.
+-
+- *timeout* can be used to control the maximum number of seconds to wait before
+- returning. *timeout* can be an int or float. If *timeout* is not specified or
+- ``None`` then there is no limit to the wait time.
+-
+- *return_when* indicates when this function should return. It must be one of
+- the following constants:
+-
+- +-----------------------------+----------------------------------------+
+- | Constant | Description |
+- +=============================+========================================+
+- | :const:`FIRST_COMPLETED` | The function will return when any |
+- | | future finishes or is cancelled. |
+- +-----------------------------+----------------------------------------+
+- | :const:`FIRST_EXCEPTION` | The function will return when any |
+- | | future finishes by raising an |
+- | | exception. If no future raises an |
+- | | exception then it is equivalent to |
+- | | `ALL_COMPLETED`. |
+- +-----------------------------+----------------------------------------+
+- | :const:`ALL_COMPLETED` | The function will return when all |
+- | | futures finish or are cancelled. |
+- +-----------------------------+----------------------------------------+
+-
+-.. function:: as_completed(fs, timeout=None)
+-
+- Returns an iterator over the :class:`Future` instances (possibly created
+- by different :class:`Executor` instances) given by *fs* that yields futures
+- as they complete (finished or were cancelled). Any futures that completed
+- before :func:`as_completed()` was called will be yielded first. The returned
+- iterator raises a :exc:`TimeoutError` if :meth:`__next__()` is called and
+- the result isn't available after *timeout* seconds from the original call
+- to :func:`as_completed()`. *timeout* can be an int or float. If *timeout*
+- is not specified or ``None`` then there is no limit to the wait time.
+diff --git a/third_party/pythonfutures/docs/make.bat b/third_party/pythonfutures/docs/make.bat
+deleted file mode 100755
+index 3e8021b56..000000000
+--- a/third_party/pythonfutures/docs/make.bat
++++ /dev/null
+@@ -1,112 +0,0 @@
+-@ECHO OFF
+-
+-REM Command file for Sphinx documentation
+-
+-set SPHINXBUILD=sphinx-build
+-set ALLSPHINXOPTS=-d _build/doctrees %SPHINXOPTS% .
+-if NOT "%PAPER%" == "" (
+- set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
+-)
+-
+-if "%1" == "" goto help
+-
+-if "%1" == "help" (
+- :help
+- echo.Please use `make ^<target^>` where ^<target^> is one of
+- echo. html to make standalone HTML files
+- echo. dirhtml to make HTML files named index.html in directories
+- echo. pickle to make pickle files
+- echo. json to make JSON files
+- echo. htmlhelp to make HTML files and a HTML help project
+- echo. qthelp to make HTML files and a qthelp project
+- echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
+- echo. changes to make an overview over all changed/added/deprecated items
+- echo. linkcheck to check all external links for integrity
+- echo. doctest to run all doctests embedded in the documentation if enabled
+- goto end
+-)
+-
+-if "%1" == "clean" (
+- for /d %%i in (_build\*) do rmdir /q /s %%i
+- del /q /s _build\*
+- goto end
+-)
+-
+-if "%1" == "html" (
+- %SPHINXBUILD% -b html %ALLSPHINXOPTS% _build/html
+- echo.
+- echo.Build finished. The HTML pages are in _build/html.
+- goto end
+-)
+-
+-if "%1" == "dirhtml" (
+- %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% _build/dirhtml
+- echo.
+- echo.Build finished. The HTML pages are in _build/dirhtml.
+- goto end
+-)
+-
+-if "%1" == "pickle" (
+- %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% _build/pickle
+- echo.
+- echo.Build finished; now you can process the pickle files.
+- goto end
+-)
+-
+-if "%1" == "json" (
+- %SPHINXBUILD% -b json %ALLSPHINXOPTS% _build/json
+- echo.
+- echo.Build finished; now you can process the JSON files.
+- goto end
+-)
+-
+-if "%1" == "htmlhelp" (
+- %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% _build/htmlhelp
+- echo.
+- echo.Build finished; now you can run HTML Help Workshop with the ^
+-.hhp project file in _build/htmlhelp.
+- goto end
+-)
+-
+-if "%1" == "qthelp" (
+- %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% _build/qthelp
+- echo.
+- echo.Build finished; now you can run "qcollectiongenerator" with the ^
+-.qhcp project file in _build/qthelp, like this:
+- echo.^> qcollectiongenerator _build\qthelp\futures.qhcp
+- echo.To view the help file:
+- echo.^> assistant -collectionFile _build\qthelp\futures.ghc
+- goto end
+-)
+-
+-if "%1" == "latex" (
+- %SPHINXBUILD% -b latex %ALLSPHINXOPTS% _build/latex
+- echo.
+- echo.Build finished; the LaTeX files are in _build/latex.
+- goto end
+-)
+-
+-if "%1" == "changes" (
+- %SPHINXBUILD% -b changes %ALLSPHINXOPTS% _build/changes
+- echo.
+- echo.The overview file is in _build/changes.
+- goto end
+-)
+-
+-if "%1" == "linkcheck" (
+- %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% _build/linkcheck
+- echo.
+- echo.Link check complete; look for any errors in the above output ^
+-or in _build/linkcheck/output.txt.
+- goto end
+-)
+-
+-if "%1" == "doctest" (
+- %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% _build/doctest
+- echo.
+- echo.Testing of doctests in the sources finished, look at the ^
+-results in _build/doctest/output.txt.
+- goto end
+-)
+-
+-:end
+diff --git a/third_party/pythonfutures/futures/__init__.py b/third_party/pythonfutures/futures/__init__.py
+deleted file mode 100755
+index 8f8b23481..000000000
+--- a/third_party/pythonfutures/futures/__init__.py
++++ /dev/null
+@@ -1,24 +0,0 @@
+-# Copyright 2009 Brian Quinlan. All Rights Reserved.
+-# Licensed to PSF under a Contributor Agreement.
+-
+-"""Execute computations asynchronously using threads or processes."""
+-
+-import warnings
+-
+-from concurrent.futures import (FIRST_COMPLETED,
+- FIRST_EXCEPTION,
+- ALL_COMPLETED,
+- CancelledError,
+- TimeoutError,
+- Future,
+- Executor,
+- wait,
+- as_completed,
+- ProcessPoolExecutor,
+- ThreadPoolExecutor)
+-
+-__author__ = 'Brian Quinlan (brian@sweetapp.com)'
+-
+-warnings.warn('The futures package has been deprecated. '
+- 'Use the concurrent.futures package instead.',
+- DeprecationWarning)
+diff --git a/third_party/pythonfutures/futures/process.py b/third_party/pythonfutures/futures/process.py
+deleted file mode 100755
+index e9d37b16c..000000000
+--- a/third_party/pythonfutures/futures/process.py
++++ /dev/null
+@@ -1 +0,0 @@
+-from concurrent.futures import ProcessPoolExecutor
+diff --git a/third_party/pythonfutures/futures/thread.py b/third_party/pythonfutures/futures/thread.py
+deleted file mode 100755
+index f6bd05de6..000000000
+--- a/third_party/pythonfutures/futures/thread.py
++++ /dev/null
+@@ -1 +0,0 @@
+-from concurrent.futures import ThreadPoolExecutor
+diff --git a/third_party/pythonfutures/primes.py b/third_party/pythonfutures/primes.py
+deleted file mode 100755
+index 0da2b3e64..000000000
+--- a/third_party/pythonfutures/primes.py
++++ /dev/null
+@@ -1,50 +0,0 @@
+-from __future__ import with_statement
+-import math
+-import time
+-import sys
+-
+-from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
+-
+-PRIMES = [
+- 112272535095293,
+- 112582705942171,
+- 112272535095293,
+- 115280095190773,
+- 115797848077099,
+- 117450548693743,
+- 993960000099397]
+-
+-def is_prime(n):
+- if n % 2 == 0:
+- return False
+-
+- sqrt_n = int(math.floor(math.sqrt(n)))
+- for i in range(3, sqrt_n + 1, 2):
+- if n % i == 0:
+- return False
+- return True
+-
+-def sequential():
+- return list(map(is_prime, PRIMES))
+-
+-def with_process_pool_executor():
+- with ProcessPoolExecutor(10) as executor:
+- return list(executor.map(is_prime, PRIMES))
+-
+-def with_thread_pool_executor():
+- with ThreadPoolExecutor(10) as executor:
+- return list(executor.map(is_prime, PRIMES))
+-
+-def main():
+- for name, fn in [('sequential', sequential),
+- ('processes', with_process_pool_executor),
+- ('threads', with_thread_pool_executor)]:
+- sys.stdout.write('%s: ' % name.ljust(12))
+- start = time.time()
+- if fn() != [True] * len(PRIMES):
+- sys.stdout.write('failed\n')
+- else:
+- sys.stdout.write('%.2f seconds\n' % (time.time() - start))
+-
+-if __name__ == '__main__':
+- main()
+diff --git a/third_party/pythonfutures/setup.cfg b/third_party/pythonfutures/setup.cfg
+deleted file mode 100755
+index 0a9f4f521..000000000
+--- a/third_party/pythonfutures/setup.cfg
++++ /dev/null
+@@ -1,6 +0,0 @@
+-[build_sphinx]
+-source-dir = docs
+-build-dir = build/sphinx
+-
+-[upload_docs]
+-upload-dir = build/sphinx/html
+diff --git a/third_party/pythonfutures/setup.py b/third_party/pythonfutures/setup.py
+deleted file mode 100755
+index c08461eda..000000000
+--- a/third_party/pythonfutures/setup.py
++++ /dev/null
+@@ -1,33 +0,0 @@
+-#!/usr/bin/env python
+-import sys
+-
+-extras = {}
+-try:
+- from setuptools import setup
+- extras['zip_safe'] = False
+- if sys.version_info < (2, 6):
+- extras['install_requires'] = ['multiprocessing']
+-except ImportError:
+- from distutils.core import setup
+-
+-setup(name='futures',
+- version='2.1.4',
+- description='Backport of the concurrent.futures package from Python 3.2',
+- author='Brian Quinlan',
+- author_email='brian@sweetapp.com',
+- maintainer='Alex Gronholm',
+- maintainer_email='alex.gronholm+pypi@nextday.fi',
+- url='http://code.google.com/p/pythonfutures',
+- download_url='http://pypi.python.org/pypi/futures/',
+- packages=['futures', 'concurrent', 'concurrent.futures'],
+- license='BSD',
+- classifiers=['License :: OSI Approved :: BSD License',
+- 'Development Status :: 5 - Production/Stable',
+- 'Intended Audience :: Developers',
+- 'Programming Language :: Python :: 2.5',
+- 'Programming Language :: Python :: 2.6',
+- 'Programming Language :: Python :: 2.7',
+- 'Programming Language :: Python :: 3',
+- 'Programming Language :: Python :: 3.1'],
+- **extras
+- )
+diff --git a/third_party/pythonfutures/test_futures.py b/third_party/pythonfutures/test_futures.py
+deleted file mode 100755
+index dd7fd3e69..000000000
+--- a/third_party/pythonfutures/test_futures.py
++++ /dev/null
+@@ -1,723 +0,0 @@
+-from __future__ import with_statement
+-import os
+-import subprocess
+-import sys
+-import threading
+-import functools
+-import contextlib
+-import logging
+-import re
+-import time
+-
+-from concurrent import futures
+-from concurrent.futures._base import (
+- PENDING, RUNNING, CANCELLED, CANCELLED_AND_NOTIFIED, FINISHED, Future)
+-
+-try:
+- import unittest2 as unittest
+-except ImportError:
+- import unittest
+-
+-try:
+- from StringIO import StringIO
+-except ImportError:
+- from io import StringIO
+-
+-try:
+- from test import test_support
+-except ImportError:
+- from test import support as test_support
+-
+-try:
+- next
+-except NameError:
+- next = lambda x: x.next()
+-
+-
+-def reap_threads(func):
+- """Use this function when threads are being used. This will
+- ensure that the threads are cleaned up even when the test fails.
+- If threading is unavailable this function does nothing.
+- """
+- @functools.wraps(func)
+- def decorator(*args):
+- key = test_support.threading_setup()
+- try:
+- return func(*args)
+- finally:
+- test_support.threading_cleanup(*key)
+- return decorator
+-
+-
+-# Executing the interpreter in a subprocess
+-def _assert_python(expected_success, *args, **env_vars):
+- cmd_line = [sys.executable]
+- if not env_vars:
+- cmd_line.append('-E')
+- # Need to preserve the original environment, for in-place testing of
+- # shared library builds.
+- env = os.environ.copy()
+- # But a special flag that can be set to override -- in this case, the
+- # caller is responsible to pass the full environment.
+- if env_vars.pop('__cleanenv', None):
+- env = {}
+- env.update(env_vars)
+- cmd_line.extend(args)
+- p = subprocess.Popen(cmd_line, stdin=subprocess.PIPE,
+- stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+- env=env)
+- try:
+- out, err = p.communicate()
+- finally:
+- subprocess._cleanup()
+- p.stdout.close()
+- p.stderr.close()
+- rc = p.returncode
+- err = strip_python_stderr(err)
+- if (rc and expected_success) or (not rc and not expected_success):
+- raise AssertionError(
+- "Process return code is %d, "
+- "stderr follows:\n%s" % (rc, err.decode('ascii', 'ignore')))
+- return rc, out, err
+-
+-
+-def assert_python_ok(*args, **env_vars):
+- """
+- Assert that running the interpreter with `args` and optional environment
+- variables `env_vars` is ok and return a (return code, stdout, stderr) tuple.
+- """
+- return _assert_python(True, *args, **env_vars)
+-
+-
+-def strip_python_stderr(stderr):
+- """Strip the stderr of a Python process from potential debug output
+- emitted by the interpreter.
+-
+- This will typically be run on the result of the communicate() method
+- of a subprocess.Popen object.
+- """
+- stderr = re.sub(r"\[\d+ refs\]\r?\n?$".encode(), "".encode(), stderr).strip()
+- return stderr
+-
+-
+-@contextlib.contextmanager
+-def captured_stderr():
+- """Return a context manager used by captured_stdout/stdin/stderr
+- that temporarily replaces the sys stream *stream_name* with a StringIO."""
+- logging_stream = StringIO()
+- handler = logging.StreamHandler(logging_stream)
+- logging.root.addHandler(handler)
+-
+- try:
+- yield logging_stream
+- finally:
+- logging.root.removeHandler(handler)
+-
+-
+-def create_future(state=PENDING, exception=None, result=None):
+- f = Future()
+- f._state = state
+- f._exception = exception
+- f._result = result
+- return f
+-
+-
+-PENDING_FUTURE = create_future(state=PENDING)
+-RUNNING_FUTURE = create_future(state=RUNNING)
+-CANCELLED_FUTURE = create_future(state=CANCELLED)
+-CANCELLED_AND_NOTIFIED_FUTURE = create_future(state=CANCELLED_AND_NOTIFIED)
+-EXCEPTION_FUTURE = create_future(state=FINISHED, exception=IOError())
+-SUCCESSFUL_FUTURE = create_future(state=FINISHED, result=42)
+-
+-
+-def mul(x, y):
+- return x * y
+-
+-
+-def sleep_and_raise(t):
+- time.sleep(t)
+- raise Exception('this is an exception')
+-
+-def sleep_and_print(t, msg):
+- time.sleep(t)
+- print(msg)
+- sys.stdout.flush()
+-
+-
+-class ExecutorMixin:
+- worker_count = 5
+-
+- def setUp(self):
+- self.t1 = time.time()
+- try:
+- self.executor = self.executor_type(max_workers=self.worker_count)
+- except NotImplementedError:
+- e = sys.exc_info()[1]
+- self.skipTest(str(e))
+- self._prime_executor()
+-
+- def tearDown(self):
+- self.executor.shutdown(wait=True)
+- dt = time.time() - self.t1
+- if test_support.verbose:
+- print("%.2fs" % dt)
+- self.assertLess(dt, 60, "synchronization issue: test lasted too long")
+-
+- def _prime_executor(self):
+- # Make sure that the executor is ready to do work before running the
+- # tests. This should reduce the probability of timeouts in the tests.
+- futures = [self.executor.submit(time.sleep, 0.1)
+- for _ in range(self.worker_count)]
+-
+- for f in futures:
+- f.result()
+-
+-
+-class ThreadPoolMixin(ExecutorMixin):
+- executor_type = futures.ThreadPoolExecutor
+-
+-
+-class ProcessPoolMixin(ExecutorMixin):
+- executor_type = futures.ProcessPoolExecutor
+-
+-
+-class ExecutorShutdownTest(unittest.TestCase):
+- def test_run_after_shutdown(self):
+- self.executor.shutdown()
+- self.assertRaises(RuntimeError,
+- self.executor.submit,
+- pow, 2, 5)
+-
+- def test_interpreter_shutdown(self):
+- # Test the atexit hook for shutdown of worker threads and processes
+- rc, out, err = assert_python_ok('-c', """if 1:
+- from concurrent.futures import %s
+- from time import sleep
+- from test_futures import sleep_and_print
+- t = %s(5)
+- t.submit(sleep_and_print, 1.0, "apple")
+- """ % (self.executor_type.__name__, self.executor_type.__name__))
+- # Errors in atexit hooks don't change the process exit code, check
+- # stderr manually.
+- self.assertFalse(err)
+- self.assertEqual(out.strip(), "apple".encode())
+-
+- def test_hang_issue12364(self):
+- fs = [self.executor.submit(time.sleep, 0.1) for _ in range(50)]
+- self.executor.shutdown()
+- for f in fs:
+- f.result()
+-
+-
+-class ThreadPoolShutdownTest(ThreadPoolMixin, ExecutorShutdownTest):
+- def _prime_executor(self):
+- pass
+-
+- def test_threads_terminate(self):
+- self.executor.submit(mul, 21, 2)
+- self.executor.submit(mul, 6, 7)
+- self.executor.submit(mul, 3, 14)
+- self.assertEqual(len(self.executor._threads), 3)
+- self.executor.shutdown()
+- for t in self.executor._threads:
+- t.join()
+-
+- def test_context_manager_shutdown(self):
+- with futures.ThreadPoolExecutor(max_workers=5) as e:
+- executor = e
+- self.assertEqual(list(e.map(abs, range(-5, 5))),
+- [5, 4, 3, 2, 1, 0, 1, 2, 3, 4])
+-
+- for t in executor._threads:
+- t.join()
+-
+- def test_del_shutdown(self):
+- executor = futures.ThreadPoolExecutor(max_workers=5)
+- executor.map(abs, range(-5, 5))
+- threads = executor._threads
+- del executor
+-
+- for t in threads:
+- t.join()
+-
+-
+-class ProcessPoolShutdownTest(ProcessPoolMixin, ExecutorShutdownTest):
+- def _prime_executor(self):
+- pass
+-
+- def test_processes_terminate(self):
+- self.executor.submit(mul, 21, 2)
+- self.executor.submit(mul, 6, 7)
+- self.executor.submit(mul, 3, 14)
+- self.assertEqual(len(self.executor._processes), 5)
+- processes = self.executor._processes
+- self.executor.shutdown()
+-
+- for p in processes:
+- p.join()
+-
+- def test_context_manager_shutdown(self):
+- with futures.ProcessPoolExecutor(max_workers=5) as e:
+- processes = e._processes
+- self.assertEqual(list(e.map(abs, range(-5, 5))),
+- [5, 4, 3, 2, 1, 0, 1, 2, 3, 4])
+-
+- for p in processes:
+- p.join()
+-
+- def test_del_shutdown(self):
+- executor = futures.ProcessPoolExecutor(max_workers=5)
+- list(executor.map(abs, range(-5, 5)))
+- queue_management_thread = executor._queue_management_thread
+- processes = executor._processes
+- del executor
+-
+- queue_management_thread.join()
+- for p in processes:
+- p.join()
+-
+-
+-class WaitTests(unittest.TestCase):
+-
+- def test_first_completed(self):
+- future1 = self.executor.submit(mul, 21, 2)
+- future2 = self.executor.submit(time.sleep, 1.5)
+-
+- done, not_done = futures.wait(
+- [CANCELLED_FUTURE, future1, future2],
+- return_when=futures.FIRST_COMPLETED)
+-
+- self.assertEqual(set([future1]), done)
+- self.assertEqual(set([CANCELLED_FUTURE, future2]), not_done)
+-
+- def test_first_completed_some_already_completed(self):
+- future1 = self.executor.submit(time.sleep, 1.5)
+-
+- finished, pending = futures.wait(
+- [CANCELLED_AND_NOTIFIED_FUTURE, SUCCESSFUL_FUTURE, future1],
+- return_when=futures.FIRST_COMPLETED)
+-
+- self.assertEqual(
+- set([CANCELLED_AND_NOTIFIED_FUTURE, SUCCESSFUL_FUTURE]),
+- finished)
+- self.assertEqual(set([future1]), pending)
+-
+- def test_first_exception(self):
+- future1 = self.executor.submit(mul, 2, 21)
+- future2 = self.executor.submit(sleep_and_raise, 1.5)
+- future3 = self.executor.submit(time.sleep, 3)
+-
+- finished, pending = futures.wait(
+- [future1, future2, future3],
+- return_when=futures.FIRST_EXCEPTION)
+-
+- self.assertEqual(set([future1, future2]), finished)
+- self.assertEqual(set([future3]), pending)
+-
+- def test_first_exception_some_already_complete(self):
+- future1 = self.executor.submit(divmod, 21, 0)
+- future2 = self.executor.submit(time.sleep, 1.5)
+-
+- finished, pending = futures.wait(
+- [SUCCESSFUL_FUTURE,
+- CANCELLED_FUTURE,
+- CANCELLED_AND_NOTIFIED_FUTURE,
+- future1, future2],
+- return_when=futures.FIRST_EXCEPTION)
+-
+- self.assertEqual(set([SUCCESSFUL_FUTURE,
+- CANCELLED_AND_NOTIFIED_FUTURE,
+- future1]), finished)
+- self.assertEqual(set([CANCELLED_FUTURE, future2]), pending)
+-
+- def test_first_exception_one_already_failed(self):
+- future1 = self.executor.submit(time.sleep, 2)
+-
+- finished, pending = futures.wait(
+- [EXCEPTION_FUTURE, future1],
+- return_when=futures.FIRST_EXCEPTION)
+-
+- self.assertEqual(set([EXCEPTION_FUTURE]), finished)
+- self.assertEqual(set([future1]), pending)
+-
+- def test_all_completed(self):
+- future1 = self.executor.submit(divmod, 2, 0)
+- future2 = self.executor.submit(mul, 2, 21)
+-
+- finished, pending = futures.wait(
+- [SUCCESSFUL_FUTURE,
+- CANCELLED_AND_NOTIFIED_FUTURE,
+- EXCEPTION_FUTURE,
+- future1,
+- future2],
+- return_when=futures.ALL_COMPLETED)
+-
+- self.assertEqual(set([SUCCESSFUL_FUTURE,
+- CANCELLED_AND_NOTIFIED_FUTURE,
+- EXCEPTION_FUTURE,
+- future1,
+- future2]), finished)
+- self.assertEqual(set(), pending)
+-
+- def test_timeout(self):
+- future1 = self.executor.submit(mul, 6, 7)
+- future2 = self.executor.submit(time.sleep, 3)
+-
+- finished, pending = futures.wait(
+- [CANCELLED_AND_NOTIFIED_FUTURE,
+- EXCEPTION_FUTURE,
+- SUCCESSFUL_FUTURE,
+- future1, future2],
+- timeout=1.5,
+- return_when=futures.ALL_COMPLETED)
+-
+- self.assertEqual(set([CANCELLED_AND_NOTIFIED_FUTURE,
+- EXCEPTION_FUTURE,
+- SUCCESSFUL_FUTURE,
+- future1]), finished)
+- self.assertEqual(set([future2]), pending)
+-
+-
+-class ThreadPoolWaitTests(ThreadPoolMixin, WaitTests):
+-
+- def test_pending_calls_race(self):
+- # Issue #14406: multi-threaded race condition when waiting on all
+- # futures.
+- event = threading.Event()
+- def future_func():
+- event.wait()
+- oldswitchinterval = sys.getcheckinterval()
+- sys.setcheckinterval(1)
+- try:
+- fs = set(self.executor.submit(future_func) for i in range(100))
+- event.set()
+- futures.wait(fs, return_when=futures.ALL_COMPLETED)
+- finally:
+- sys.setcheckinterval(oldswitchinterval)
+-
+-
+-class ProcessPoolWaitTests(ProcessPoolMixin, WaitTests):
+- pass
+-
+-
+-class AsCompletedTests(unittest.TestCase):
+- # TODO(brian@sweetapp.com): Should have a test with a non-zero timeout.
+- def test_no_timeout(self):
+- future1 = self.executor.submit(mul, 2, 21)
+- future2 = self.executor.submit(mul, 7, 6)
+-
+- completed = set(futures.as_completed(
+- [CANCELLED_AND_NOTIFIED_FUTURE,
+- EXCEPTION_FUTURE,
+- SUCCESSFUL_FUTURE,
+- future1, future2]))
+- self.assertEqual(set(
+- [CANCELLED_AND_NOTIFIED_FUTURE,
+- EXCEPTION_FUTURE,
+- SUCCESSFUL_FUTURE,
+- future1, future2]),
+- completed)
+-
+- def test_zero_timeout(self):
+- future1 = self.executor.submit(time.sleep, 2)
+- completed_futures = set()
+- try:
+- for future in futures.as_completed(
+- [CANCELLED_AND_NOTIFIED_FUTURE,
+- EXCEPTION_FUTURE,
+- SUCCESSFUL_FUTURE,
+- future1],
+- timeout=0):
+- completed_futures.add(future)
+- except futures.TimeoutError:
+- pass
+-
+- self.assertEqual(set([CANCELLED_AND_NOTIFIED_FUTURE,
+- EXCEPTION_FUTURE,
+- SUCCESSFUL_FUTURE]),
+- completed_futures)
+-
+-
+-class ThreadPoolAsCompletedTests(ThreadPoolMixin, AsCompletedTests):
+- pass
+-
+-
+-class ProcessPoolAsCompletedTests(ProcessPoolMixin, AsCompletedTests):
+- pass
+-
+-
+-class ExecutorTest(unittest.TestCase):
+- # Executor.shutdown() and context manager usage is tested by
+- # ExecutorShutdownTest.
+- def test_submit(self):
+- future = self.executor.submit(pow, 2, 8)
+- self.assertEqual(256, future.result())
+-
+- def test_submit_keyword(self):
+- future = self.executor.submit(mul, 2, y=8)
+- self.assertEqual(16, future.result())
+-
+- def test_map(self):
+- self.assertEqual(
+- list(self.executor.map(pow, range(10), range(10))),
+- list(map(pow, range(10), range(10))))
+-
+- def test_map_exception(self):
+- i = self.executor.map(divmod, [1, 1, 1, 1], [2, 3, 0, 5])
+- self.assertEqual(next(i), (0, 1))
+- self.assertEqual(next(i), (0, 1))
+- self.assertRaises(ZeroDivisionError, next, i)
+-
+- def test_map_timeout(self):
+- results = []
+- try:
+- for i in self.executor.map(time.sleep,
+- [0, 0, 3],
+- timeout=1.5):
+- results.append(i)
+- except futures.TimeoutError:
+- pass
+- else:
+- self.fail('expected TimeoutError')
+-
+- self.assertEqual([None, None], results)
+-
+-
+-class ThreadPoolExecutorTest(ThreadPoolMixin, ExecutorTest):
+- pass
+-
+-
+-class ProcessPoolExecutorTest(ProcessPoolMixin, ExecutorTest):
+- pass
+-
+-
+-class FutureTests(unittest.TestCase):
+- def test_done_callback_with_result(self):
+- callback_result = [None]
+- def fn(callback_future):
+- callback_result[0] = callback_future.result()
+-
+- f = Future()
+- f.add_done_callback(fn)
+- f.set_result(5)
+- self.assertEqual(5, callback_result[0])
+-
+- def test_done_callback_with_exception(self):
+- callback_exception = [None]
+- def fn(callback_future):
+- callback_exception[0] = callback_future.exception()
+-
+- f = Future()
+- f.add_done_callback(fn)
+- f.set_exception(Exception('test'))
+- self.assertEqual(('test',), callback_exception[0].args)
+-
+- def test_done_callback_with_cancel(self):
+- was_cancelled = [None]
+- def fn(callback_future):
+- was_cancelled[0] = callback_future.cancelled()
+-
+- f = Future()
+- f.add_done_callback(fn)
+- self.assertTrue(f.cancel())
+- self.assertTrue(was_cancelled[0])
+-
+- def test_done_callback_raises(self):
+- with captured_stderr() as stderr:
+- raising_was_called = [False]
+- fn_was_called = [False]
+-
+- def raising_fn(callback_future):
+- raising_was_called[0] = True
+- raise Exception('doh!')
+-
+- def fn(callback_future):
+- fn_was_called[0] = True
+-
+- f = Future()
+- f.add_done_callback(raising_fn)
+- f.add_done_callback(fn)
+- f.set_result(5)
+- self.assertTrue(raising_was_called)
+- self.assertTrue(fn_was_called)
+- self.assertIn('Exception: doh!', stderr.getvalue())
+-
+- def test_done_callback_already_successful(self):
+- callback_result = [None]
+- def fn(callback_future):
+- callback_result[0] = callback_future.result()
+-
+- f = Future()
+- f.set_result(5)
+- f.add_done_callback(fn)
+- self.assertEqual(5, callback_result[0])
+-
+- def test_done_callback_already_failed(self):
+- callback_exception = [None]
+- def fn(callback_future):
+- callback_exception[0] = callback_future.exception()
+-
+- f = Future()
+- f.set_exception(Exception('test'))
+- f.add_done_callback(fn)
+- self.assertEqual(('test',), callback_exception[0].args)
+-
+- def test_done_callback_already_cancelled(self):
+- was_cancelled = [None]
+- def fn(callback_future):
+- was_cancelled[0] = callback_future.cancelled()
+-
+- f = Future()
+- self.assertTrue(f.cancel())
+- f.add_done_callback(fn)
+- self.assertTrue(was_cancelled[0])
+-
+- def test_repr(self):
+- self.assertRegexpMatches(repr(PENDING_FUTURE),
+- '<Future at 0x[0-9a-f]+ state=pending>')
+- self.assertRegexpMatches(repr(RUNNING_FUTURE),
+- '<Future at 0x[0-9a-f]+ state=running>')
+- self.assertRegexpMatches(repr(CANCELLED_FUTURE),
+- '<Future at 0x[0-9a-f]+ state=cancelled>')
+- self.assertRegexpMatches(repr(CANCELLED_AND_NOTIFIED_FUTURE),
+- '<Future at 0x[0-9a-f]+ state=cancelled>')
+- self.assertRegexpMatches(
+- repr(EXCEPTION_FUTURE),
+- '<Future at 0x[0-9a-f]+ state=finished raised IOError>')
+- self.assertRegexpMatches(
+- repr(SUCCESSFUL_FUTURE),
+- '<Future at 0x[0-9a-f]+ state=finished returned int>')
+-
+- def test_cancel(self):
+- f1 = create_future(state=PENDING)
+- f2 = create_future(state=RUNNING)
+- f3 = create_future(state=CANCELLED)
+- f4 = create_future(state=CANCELLED_AND_NOTIFIED)
+- f5 = create_future(state=FINISHED, exception=IOError())
+- f6 = create_future(state=FINISHED, result=5)
+-
+- self.assertTrue(f1.cancel())
+- self.assertEqual(f1._state, CANCELLED)
+-
+- self.assertFalse(f2.cancel())
+- self.assertEqual(f2._state, RUNNING)
+-
+- self.assertTrue(f3.cancel())
+- self.assertEqual(f3._state, CANCELLED)
+-
+- self.assertTrue(f4.cancel())
+- self.assertEqual(f4._state, CANCELLED_AND_NOTIFIED)
+-
+- self.assertFalse(f5.cancel())
+- self.assertEqual(f5._state, FINISHED)
+-
+- self.assertFalse(f6.cancel())
+- self.assertEqual(f6._state, FINISHED)
+-
+- def test_cancelled(self):
+- self.assertFalse(PENDING_FUTURE.cancelled())
+- self.assertFalse(RUNNING_FUTURE.cancelled())
+- self.assertTrue(CANCELLED_FUTURE.cancelled())
+- self.assertTrue(CANCELLED_AND_NOTIFIED_FUTURE.cancelled())
+- self.assertFalse(EXCEPTION_FUTURE.cancelled())
+- self.assertFalse(SUCCESSFUL_FUTURE.cancelled())
+-
+- def test_done(self):
+- self.assertFalse(PENDING_FUTURE.done())
+- self.assertFalse(RUNNING_FUTURE.done())
+- self.assertTrue(CANCELLED_FUTURE.done())
+- self.assertTrue(CANCELLED_AND_NOTIFIED_FUTURE.done())
+- self.assertTrue(EXCEPTION_FUTURE.done())
+- self.assertTrue(SUCCESSFUL_FUTURE.done())
+-
+- def test_running(self):
+- self.assertFalse(PENDING_FUTURE.running())
+- self.assertTrue(RUNNING_FUTURE.running())
+- self.assertFalse(CANCELLED_FUTURE.running())
+- self.assertFalse(CANCELLED_AND_NOTIFIED_FUTURE.running())
+- self.assertFalse(EXCEPTION_FUTURE.running())
+- self.assertFalse(SUCCESSFUL_FUTURE.running())
+-
+- def test_result_with_timeout(self):
+- self.assertRaises(futures.TimeoutError,
+- PENDING_FUTURE.result, timeout=0)
+- self.assertRaises(futures.TimeoutError,
+- RUNNING_FUTURE.result, timeout=0)
+- self.assertRaises(futures.CancelledError,
+- CANCELLED_FUTURE.result, timeout=0)
+- self.assertRaises(futures.CancelledError,
+- CANCELLED_AND_NOTIFIED_FUTURE.result, timeout=0)
+- self.assertRaises(IOError, EXCEPTION_FUTURE.result, timeout=0)
+- self.assertEqual(SUCCESSFUL_FUTURE.result(timeout=0), 42)
+-
+- def test_result_with_success(self):
+- # TODO(brian@sweetapp.com): This test is timing dependant.
+- def notification():
+- # Wait until the main thread is waiting for the result.
+- time.sleep(1)
+- f1.set_result(42)
+-
+- f1 = create_future(state=PENDING)
+- t = threading.Thread(target=notification)
+- t.start()
+-
+- self.assertEqual(f1.result(timeout=5), 42)
+-
+- def test_result_with_cancel(self):
+- # TODO(brian@sweetapp.com): This test is timing dependant.
+- def notification():
+- # Wait until the main thread is waiting for the result.
+- time.sleep(1)
+- f1.cancel()
+-
+- f1 = create_future(state=PENDING)
+- t = threading.Thread(target=notification)
+- t.start()
+-
+- self.assertRaises(futures.CancelledError, f1.result, timeout=5)
+-
+- def test_exception_with_timeout(self):
+- self.assertRaises(futures.TimeoutError,
+- PENDING_FUTURE.exception, timeout=0)
+- self.assertRaises(futures.TimeoutError,
+- RUNNING_FUTURE.exception, timeout=0)
+- self.assertRaises(futures.CancelledError,
+- CANCELLED_FUTURE.exception, timeout=0)
+- self.assertRaises(futures.CancelledError,
+- CANCELLED_AND_NOTIFIED_FUTURE.exception, timeout=0)
+- self.assertTrue(isinstance(EXCEPTION_FUTURE.exception(timeout=0),
+- IOError))
+- self.assertEqual(SUCCESSFUL_FUTURE.exception(timeout=0), None)
+-
+- def test_exception_with_success(self):
+- def notification():
+- # Wait until the main thread is waiting for the exception.
+- time.sleep(1)
+- with f1._condition:
+- f1._state = FINISHED
+- f1._exception = IOError()
+- f1._condition.notify_all()
+-
+- f1 = create_future(state=PENDING)
+- t = threading.Thread(target=notification)
+- t.start()
+-
+- self.assertTrue(isinstance(f1.exception(timeout=5), IOError))
+-
+-@reap_threads
+-def test_main():
+- try:
+- test_support.run_unittest(ProcessPoolExecutorTest,
+- ThreadPoolExecutorTest,
+- ProcessPoolWaitTests,
+- ThreadPoolWaitTests,
+- ProcessPoolAsCompletedTests,
+- ThreadPoolAsCompletedTests,
+- FutureTests,
+- ProcessPoolShutdownTest,
+- ThreadPoolShutdownTest)
+- finally:
+- test_support.reap_children()
+-
+-if __name__ == "__main__":
+- test_main()
+diff --git a/third_party/pythonfutures/tox.ini b/third_party/pythonfutures/tox.ini
+deleted file mode 100755
+index c1ff2f13f..000000000
+--- a/third_party/pythonfutures/tox.ini
++++ /dev/null
+@@ -1,8 +0,0 @@
+-[tox]
+-envlist = py26,py27,py31
+-
+-[testenv]
+-commands={envpython} test_futures.py []
+-
+-[testenv:py26]
+-deps=unittest2
diff --git a/app-vim/youcompleteme/youcompleteme-20200203.ebuild b/app-vim/youcompleteme/youcompleteme-20200203.ebuild
new file mode 100644
index 0000000..5fccb53
--- /dev/null
+++ b/app-vim/youcompleteme/youcompleteme-20200203.ebuild
@@ -0,0 +1,109 @@
+EAPI="7"
+PYTHON_COMPAT=( python3_{4,5,6,7,8} )
+inherit multilib python-single-r1 cmake-utils vim-plugin
+
+youcompletemev="124661f218e80b96c1f9f3d124e99f9a2fd2d83b"
+ycmdv="d3378ca3a3103535c14b104cb916dcbcdaf93eeb"
+reqfuv="148451a06781d8196e5fb7e0e2bca7a765368ff1"
+ossv="e1902915c6790bcec00b8d551199c8a3537d33c9"
+gocodev="5bee97b488366fd20b054d0861b89834ff5bbfb2"
+
+KEYWORDS="~amd64 ~x86"
+SRC_URI="
+ https://github.com/Valloric/YouCompleteMe/archive/$youcompletemev.tar.gz -> youcompleteme-$youcompletemev.tar.gz
+ https://github.com/Valloric/ycmd/archive/$ycmdv.tar.gz -> ycmd-$ycmdv.tar.gz
+ https://github.com/ross/requests-futures/archive/$reqfuv.tar.gz -> requests-futures-$reqfuv.tar.gz
+ csharp? ( https://github.com/OmniSharp/omnisharp-server/archive/$ossv.tar.gz -> omnisharp-server-$ossv.tar.gz )
+ go? ( https://github.com/nsf/gocode/archive/$gocodev.tar.gz -> gocode-$gocodev.tar.gz )
+"
+
+DESCRIPTION="vim plugin: a code-completion engine for Vim"
+HOMEPAGE="http://valloric.github.io/YouCompleteMe/"
+
+LICENSE="GPL-3"
+IUSE="+clang test go csharp"
+REQUIRED_USE="${PYTHON_REQUIRED_USE}"
+
+COMMON_DEPEND="
+ ${PYTHON_DEPS}
+ clang? ( sys-devel/clang:9 )
+ >=dev-libs/boost-1.65:=[python,threads,${PYTHON_USEDEP}]
+ || (
+ app-editors/vim[python,${PYTHON_USEDEP}]
+ app-editors/gvim[python,${PYTHON_USEDEP}]
+ )
+"
+RDEPEND="
+ ${COMMON_DEPEND}
+ dev-python/bottle[${PYTHON_USEDEP}]
+ dev-python/future[${PYTHON_USEDEP}]
+ dev-python/jedi[${PYTHON_USEDEP}]
+ dev-python/requests[${PYTHON_USEDEP}]
+ dev-python/sh[${PYTHON_USEDEP}]
+ dev-python/waitress[${PYTHON_USEDEP}]
+"
+DEPEND="
+ ${COMMON_DEPEND}
+ test? (
+ >=dev-python/mock-1.0.1[${PYTHON_USEDEP}]
+ >=dev-python/nose-1.3.0[${PYTHON_USEDEP}]
+ dev-cpp/gmock
+ dev-cpp/gtest
+ )
+"
+
+S="${WORKDIR}/YouCompleteMe-$youcompletemev"
+CMAKE_IN_SOURCE_BUILD=1
+CMAKE_USE_DIR=${S}/third_party/ycmd/cpp
+
+VIM_PLUGIN_HELPFILES="${PN}"
+
+src_prepare() {
+ eapply ${FILESDIR}/remove-python2-support.patch
+ for third_party_module in ycmd requests-futures; do
+ rm -r "${S}"/third_party/${third_party_module} || die "Failed to remove third party module ${third_party_module}"
+ done
+ mv ${WORKDIR}/ycmd-$ycmdv ${S}/third_party/ycmd
+ use csharp && mv ${WORKDIR}/omnisharp-server-$ossv ${S}/third_party/ycmd/third_party/omnisharp-server
+ use go && mv ${WORKDIR}/gocode-$gocodev ${S}/third_party/ycmd/third_party/gocode
+ mv ${WORKDIR}/requests-futures-$reqfuv ${S}/third_party/requests-futures
+ cmake-utils_src_prepare
+ default
+}
+
+src_configure() {
+ local mycmakeargs=(
+ -DUSE_CLANG_COMPLETER=$(usex clang)
+ -DUSE_SYSTEM_LIBCLANG=$(usex clang)
+ -DPATH_TO_LLVM_ROOT=/usr/lib/llvm/9
+ -DUSE_SYSTEM_BOOST=ON
+ )
+ cmake-utils_src_configure
+}
+
+src_test() {
+ cd "${S}/third_party/ycmd/cpp/ycm/tests"
+ LD_LIBRARY_PATH="${EROOT}"/usr/$(get_libdir)/llvm \
+ ./ycm_core_tests || die
+
+ cd "${S}"/python/ycm
+
+ local dirs=( "${S}"/third_party/*/ "${S}"/third_party/ycmd/third_party/*/ )
+ local -x PYTHONPATH=${PYTHONPATH}:$(IFS=:; echo "${dirs[*]}")
+
+ nosetests || die
+}
+
+src_install() {
+ dodoc *.md third_party/ycmd/*.md
+ rm -r *.md *.sh COPYING.txt third_party/ycmd/cpp || die
+ rm -r third_party/ycmd/{*.md,*.sh} || die
+ find python third_party/ycmd -depth -name '*test*' -exec rm -r {} + || die
+ find python third_party/ycmd -depth -name '*examples*' -exec rm -r {} + || die
+ rm third_party/ycmd/third_party/clang/lib/libclang.so.* || die
+
+ vim-plugin_src_install
+
+ python_optimize "${ED}"
+ python_fix_shebang "${ED}"
+}