PYTHON-2133 Drop install support for Python 2
This commit is contained in:
parent
fb4c20adfa
commit
dea4b90193
@ -349,16 +349,6 @@ functions:
|
||||
${PREPARE_SHELL}
|
||||
PYTHON_BINARY=${PYTHON_BINARY} PROJECT_DIRECTORY=${PROJECT_DIRECTORY} sh ${PROJECT_DIRECTORY}/.evergreen/run-mockupdb-tests.sh
|
||||
|
||||
"run cdecimal tests":
|
||||
- command: shell.exec
|
||||
type: test
|
||||
params:
|
||||
working_dir: "src"
|
||||
script: |
|
||||
set -o xtrace
|
||||
${PREPARE_SHELL}
|
||||
PYTHON_BINARY=${PYTHON_BINARY} sh ${PROJECT_DIRECTORY}/.evergreen/run-cdecimal-tests.sh
|
||||
|
||||
"run doctests":
|
||||
- command: shell.exec
|
||||
type: test
|
||||
@ -1179,15 +1169,6 @@ tasks:
|
||||
TOPOLOGY: "replica_set"
|
||||
- func: "run mod_wsgi tests"
|
||||
|
||||
- name: "cdecimal"
|
||||
tags: ["cdecimal"]
|
||||
commands:
|
||||
- func: "bootstrap mongo-orchestration"
|
||||
vars:
|
||||
VERSION: "latest"
|
||||
TOPOLOGY: "server"
|
||||
- func: "run cdecimal tests"
|
||||
|
||||
- name: "no-server"
|
||||
tags: ["no-server"]
|
||||
commands:
|
||||
|
||||
@ -1,13 +1,11 @@
|
||||
language: python
|
||||
|
||||
python:
|
||||
- 2.7
|
||||
- 3.4
|
||||
- 3.5
|
||||
- 3.6
|
||||
- 3.7
|
||||
- 3.8
|
||||
- pypy
|
||||
- pypy3.5
|
||||
|
||||
services:
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
include README.rst
|
||||
include LICENSE
|
||||
include THIRD-PARTY-NOTICES
|
||||
include ez_setup.py
|
||||
recursive-include doc *.rst
|
||||
recursive-include doc *.py
|
||||
recursive-include doc *.conf
|
||||
|
||||
15
README.rst
15
README.rst
@ -89,7 +89,7 @@ is incompatible with PyMongo.
|
||||
Dependencies
|
||||
============
|
||||
|
||||
PyMongo supports CPython 2.7, 3.4+, PyPy, and PyPy3.5+.
|
||||
PyMongo supports CPython 3.4+ and PyPy3.5+.
|
||||
|
||||
Optional dependencies:
|
||||
|
||||
@ -119,9 +119,6 @@ PyMongo::
|
||||
|
||||
$ python -m pip install pymongo[tls]
|
||||
|
||||
.. note:: Users of Python versions older than 2.7.9 will also
|
||||
receive the dependencies for OCSP when using the tls extra.
|
||||
|
||||
OCSP (Online Certificate Status Protocol) requires `PyOpenSSL
|
||||
<https://pypi.org/project/pyOpenSSL/>`_, `requests
|
||||
<https://pypi.org/project/requests/>`_ and `service_identity
|
||||
@ -149,16 +146,6 @@ command::
|
||||
|
||||
$ python -m pip install pymongo[gssapi,aws,ocsp,snappy,srv,tls,zstd,encryption]
|
||||
|
||||
Other optional packages:
|
||||
|
||||
- `backports.pbkdf2 <https://pypi.python.org/pypi/backports.pbkdf2/>`_,
|
||||
improves authentication performance with SCRAM-SHA-1 and SCRAM-SHA-256.
|
||||
It especially improves performance on Python versions older than 2.7.8.
|
||||
- `monotonic <https://pypi.python.org/pypi/monotonic>`_ adds support for
|
||||
a monotonic clock, which improves reliability in environments
|
||||
where clock adjustments are frequent. Not needed in Python 3.
|
||||
|
||||
|
||||
Additional dependencies are:
|
||||
|
||||
- (to generate documentation) sphinx_
|
||||
|
||||
16
RELEASE.rst
16
RELEASE.rst
@ -33,10 +33,10 @@ Doing a Release
|
||||
|
||||
1. PyMongo is tested on Evergreen. Ensure the latest commit are passing CI
|
||||
as expected: https://evergreen.mongodb.com/waterfall/mongo-python-driver.
|
||||
To test locally, ``python setup.py test`` will build the C extensions and
|
||||
test. ``python tools/clean.py`` will remove the extensions,
|
||||
and then ``python setup.py --no_ext test`` will run the tests without
|
||||
them. You can also run the doctests: ``python setup.py doc -t``.
|
||||
To test locally, ``python3 setup.py test`` will build the C extensions and
|
||||
test. ``python3 tools/clean.py`` will remove the extensions,
|
||||
and then ``python3 setup.py --no_ext test`` will run the tests without
|
||||
them. You can also run the doctests: ``python3 setup.py doc -t``.
|
||||
|
||||
2. Check Jira to ensure all the tickets in this version have been completed.
|
||||
|
||||
@ -63,17 +63,11 @@ Doing a Release
|
||||
the next steps let's assume we unpacked these files into the following paths::
|
||||
|
||||
$ ls path/to/manylinux
|
||||
pymongo-<version>-cp27-cp27m-manylinux1_i686.whl
|
||||
...
|
||||
pymongo-<version>-cp38-cp38-manylinux2014_x86_64.whl
|
||||
$ ls path/to/mac/
|
||||
pymongo-<version>-cp27-cp27m-macosx_10_14_intel.whl
|
||||
...
|
||||
pymongo-<version>-py2.7-macosx-10.14-intel.egg
|
||||
$ ls path/to/windows/
|
||||
pymongo-<version>-cp27-cp27m-win32.whl
|
||||
...
|
||||
pymongo-<version>-cp38-cp38-win_amd64.whl
|
||||
...
|
||||
|
||||
10. Build the source distribution::
|
||||
|
||||
|
||||
@ -1,66 +0,0 @@
|
||||
# Copyright 2017 MongoDB, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Test PyMongo with cdecimal monkey-patched over stdlib decimal."""
|
||||
|
||||
import getopt
|
||||
import sys
|
||||
|
||||
try:
|
||||
import cdecimal
|
||||
_HAVE_CDECIMAL = True
|
||||
except ImportError:
|
||||
_HAVE_CDECIMAL = False
|
||||
|
||||
|
||||
def run(args):
|
||||
"""Run tests with cdecimal monkey-patched over stdlib decimal."""
|
||||
# Monkey-patch.
|
||||
sys.modules['decimal'] = cdecimal
|
||||
|
||||
# Run the tests.
|
||||
sys.argv[:] = ['setup.py', 'test'] + list(args)
|
||||
import setup
|
||||
|
||||
|
||||
def main():
|
||||
"""Parse options and run tests."""
|
||||
usage = """python %s
|
||||
|
||||
Test PyMongo with cdecimal monkey-patched over decimal.""" % (sys.argv[0],)
|
||||
|
||||
try:
|
||||
opts, args = getopt.getopt(
|
||||
sys.argv[1:], "h", ["help"])
|
||||
except getopt.GetoptError as err:
|
||||
print(str(err))
|
||||
print(usage)
|
||||
sys.exit(2)
|
||||
|
||||
for option_name, _ in opts:
|
||||
if option_name in ("-h", "--help"):
|
||||
print(usage)
|
||||
sys.exit()
|
||||
else:
|
||||
assert False, "unhandled option"
|
||||
|
||||
if not _HAVE_CDECIMAL:
|
||||
print("The cdecimal package is not installed.")
|
||||
sys.exit(1)
|
||||
|
||||
run(args) # Command line args to setup.py, like what test to run.
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@ -7,32 +7,7 @@ Atlas to :class:`~pymongo.mongo_client.MongoClient`::
|
||||
|
||||
client = pymongo.MongoClient(<Atlas connection string>)
|
||||
|
||||
Connections to Atlas require TLS/SSL. For connections using TLS/SSL, PyMongo
|
||||
may require third party dependencies as determined by your version of Python.
|
||||
With PyMongo 3.3+, you can install PyMongo 3.3+ and any TLS/SSL-related
|
||||
dependencies using the following pip command::
|
||||
|
||||
$ python -m pip install pymongo[tls]
|
||||
|
||||
Starting with PyMongo 3.11 this installs `PyOpenSSL
|
||||
<https://pypi.org/project/pyOpenSSL/>`_, `requests`_
|
||||
and `service_identity
|
||||
<https://pypi.org/project/service_identity/>`_
|
||||
for users of Python versions older than 2.7.9. PyOpenSSL supports SNI for these
|
||||
old Python versions, allowing applictions to connect to Altas free and shared
|
||||
tier instances.
|
||||
|
||||
Earlier versions of PyMongo require you to manually install the dependencies.
|
||||
For a list of TLS/SSL-related dependencies, see :doc:`examples/tls`.
|
||||
|
||||
.. note:: Connecting to Atlas "Free Tier" or "Shared Cluster" instances
|
||||
requires Server Name Indication (SNI) support. SNI support requires CPython
|
||||
2.7.9 / PyPy 2.5.1 or newer or PyMongo 3.11+ with PyOpenSSL.
|
||||
To check if your version of Python supports SNI run the following command::
|
||||
|
||||
$ python -c "import ssl; print(getattr(ssl, 'HAS_SNI', False))"
|
||||
|
||||
You should see "True".
|
||||
Connections to Atlas require TLS/SSL.
|
||||
|
||||
.. warning:: Industry best practices recommend, and some regulations require,
|
||||
the use of TLS 1.1 or newer. Though no application changes are required for
|
||||
|
||||
@ -18,9 +18,9 @@ effort to release at least one minor version that *deprecates* it. We add
|
||||
`DeprecationWarning`_. You can ensure your code is future-proof by running
|
||||
your code with the latest PyMongo release and looking for DeprecationWarnings.
|
||||
|
||||
Starting with Python 2.7, the interpreter silences DeprecationWarnings by
|
||||
default. For example, the following code uses the deprecated ``insert``
|
||||
method but does not raise any warning:
|
||||
The interpreter silences DeprecationWarnings by default. For example, the
|
||||
following code uses the deprecated ``insert`` method but does not raise any
|
||||
warning:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@ -32,13 +32,13 @@ method but does not raise any warning:
|
||||
|
||||
To print deprecation warnings to stderr, run python with "-Wd"::
|
||||
|
||||
$ python -Wd insert.py
|
||||
$ python3 -Wd insert.py
|
||||
insert.py:4: DeprecationWarning: insert is deprecated. Use insert_one or insert_many instead.
|
||||
client.test.test.insert({})
|
||||
|
||||
You can turn warnings into exceptions with "python -We"::
|
||||
|
||||
$ python -We insert.py
|
||||
$ python3 -We insert.py
|
||||
Traceback (most recent call last):
|
||||
File "insert.py", line 4, in <module>
|
||||
client.test.test.insert({})
|
||||
@ -55,8 +55,8 @@ deprecated PyMongo features.
|
||||
.. _semantic versioning: http://semver.org/
|
||||
|
||||
.. _DeprecationWarning:
|
||||
https://docs.python.org/2/library/exceptions.html#exceptions.DeprecationWarning
|
||||
https://docs.python.org/3/library/exceptions.html#DeprecationWarning
|
||||
|
||||
.. _the warnings module: https://docs.python.org/2/library/warnings.html
|
||||
.. _the warnings module: https://docs.python.org/3/library/warnings.html
|
||||
|
||||
.. _the -W command line option: https://docs.python.org/2/using/cmdline.html#cmdoption-W
|
||||
.. _the -W command line option: https://docs.python.org/3/using/cmdline.html#cmdoption-w
|
||||
|
||||
@ -15,37 +15,20 @@ Installing with pip
|
||||
We recommend using `pip <http://pypi.python.org/pypi/pip>`_
|
||||
to install pymongo on all platforms::
|
||||
|
||||
$ python -m pip install pymongo
|
||||
$ python3 -m pip install pymongo
|
||||
|
||||
To get a specific version of pymongo::
|
||||
|
||||
$ python -m pip install pymongo==3.5.1
|
||||
$ python3 -m pip install pymongo==3.5.1
|
||||
|
||||
To upgrade using pip::
|
||||
|
||||
$ python -m pip install --upgrade pymongo
|
||||
|
||||
.. note::
|
||||
pip does not support installing python packages in .egg format. If you would
|
||||
like to install PyMongo from a .egg provided on pypi use easy_install
|
||||
instead.
|
||||
|
||||
Installing with easy_install
|
||||
----------------------------
|
||||
|
||||
To use ``easy_install`` from
|
||||
`setuptools <http://pypi.python.org/pypi/setuptools>`_ do::
|
||||
|
||||
$ python -m easy_install pymongo
|
||||
|
||||
To upgrade do::
|
||||
|
||||
$ python -m easy_install -U pymongo
|
||||
$ python3 -m pip install --upgrade pymongo
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
PyMongo supports CPython 2.7, 3.4+, PyPy, and PyPy3.5+.
|
||||
PyMongo supports CPython 3.4+ and PyPy3.5+.
|
||||
|
||||
Optional dependencies:
|
||||
|
||||
@ -54,66 +37,44 @@ GSSAPI authentication requires `pykerberos
|
||||
<https://pypi.python.org/pypi/winkerberos>`_ on Windows. The correct
|
||||
dependency can be installed automatically along with PyMongo::
|
||||
|
||||
$ python -m pip install pymongo[gssapi]
|
||||
$ python3 -m pip install pymongo[gssapi]
|
||||
|
||||
:ref:`MONGODB-AWS` authentication requires `pymongo-auth-aws
|
||||
<https://pypi.org/project/pymongo-auth-aws/>`_::
|
||||
|
||||
$ python -m pip install pymongo[aws]
|
||||
$ python3 -m pip install pymongo[aws]
|
||||
|
||||
Support for mongodb+srv:// URIs requires `dnspython
|
||||
<https://pypi.python.org/pypi/dnspython>`_::
|
||||
|
||||
$ python -m pip install pymongo[srv]
|
||||
|
||||
TLS / SSL support may require `ipaddress
|
||||
<https://pypi.python.org/pypi/ipaddress>`_ and `certifi
|
||||
<https://pypi.python.org/pypi/certifi>`_ or `wincertstore
|
||||
<https://pypi.python.org/pypi/wincertstore>`_ depending on the Python
|
||||
version in use. The necessary dependencies can be installed along with
|
||||
PyMongo::
|
||||
|
||||
$ python -m pip install pymongo[tls]
|
||||
|
||||
.. note:: Users of Python versions older than 2.7.9 will also
|
||||
receive the dependencies for OCSP when using the tls extra.
|
||||
$ python3 -m pip install pymongo[srv]
|
||||
|
||||
:ref:`OCSP` requires `PyOpenSSL
|
||||
<https://pypi.org/project/pyOpenSSL/>`_, `requests
|
||||
<https://pypi.org/project/requests/>`_ and `service_identity
|
||||
<https://pypi.org/project/service_identity/>`_::
|
||||
|
||||
$ python -m pip install pymongo[ocsp]
|
||||
$ python3 -m pip install pymongo[ocsp]
|
||||
|
||||
Wire protocol compression with snappy requires `python-snappy
|
||||
<https://pypi.org/project/python-snappy>`_::
|
||||
|
||||
$ python -m pip install pymongo[snappy]
|
||||
$ python3 -m pip install pymongo[snappy]
|
||||
|
||||
Wire protocol compression with zstandard requires `zstandard
|
||||
<https://pypi.org/project/zstandard>`_::
|
||||
|
||||
$ python -m pip install pymongo[zstd]
|
||||
$ python3 -m pip install pymongo[zstd]
|
||||
|
||||
:ref:`Client-Side Field Level Encryption` requires `pymongocrypt
|
||||
<https://pypi.org/project/pymongocrypt/>`_::
|
||||
|
||||
$ python -m pip install pymongo[encryption]
|
||||
$ python3 -m pip install pymongo[encryption]
|
||||
|
||||
You can install all dependencies automatically with the following
|
||||
command::
|
||||
|
||||
$ python -m pip install pymongo[gssapi,aws,ocsp,snappy,srv,tls,zstd,encryption]
|
||||
|
||||
Other optional packages:
|
||||
|
||||
- `backports.pbkdf2 <https://pypi.python.org/pypi/backports.pbkdf2/>`_,
|
||||
improves authentication performance with SCRAM-SHA-1 and SCRAM-SHA-256.
|
||||
It especially improves performance on Python versions older than 2.7.8.
|
||||
- `monotonic <https://pypi.python.org/pypi/monotonic>`_ adds support for
|
||||
a monotonic clock, which improves reliability in environments
|
||||
where clock adjustments are frequent. Not needed in Python 3.
|
||||
|
||||
$ python3 -m pip install pymongo[gssapi,aws,ocsp,snappy,srv,zstd,encryption]
|
||||
|
||||
Installing from source
|
||||
----------------------
|
||||
@ -124,7 +85,7 @@ latest source from GitHub and install the driver from the resulting tree::
|
||||
|
||||
$ git clone git://github.com/mongodb/mongo-python-driver.git pymongo
|
||||
$ cd pymongo/
|
||||
$ python setup.py install
|
||||
$ python3 setup.py install
|
||||
|
||||
Installing from source on Unix
|
||||
..............................
|
||||
@ -172,9 +133,8 @@ See `http://bugs.python.org/issue11623 <http://bugs.python.org/issue11623>`_
|
||||
for a more detailed explanation.
|
||||
|
||||
**Lion (10.7) and newer** - PyMongo's C extensions can be built against
|
||||
versions of Python 2.7 >= 2.7.4 or Python 3.4+ downloaded from
|
||||
python.org. In all cases Xcode must be installed with 'UNIX Development
|
||||
Support'.
|
||||
versions of Python 3.4+ downloaded from python.org. In all cases Xcode must be
|
||||
installed with 'UNIX Development Support'.
|
||||
|
||||
**Xcode 5.1**: Starting with version 5.1 the version of clang that ships with
|
||||
Xcode throws an error when it encounters compiler flags it doesn't recognize.
|
||||
@ -209,8 +169,7 @@ requirements apply to both CPython and ActiveState's ActivePython:
|
||||
For Python 3.5 and newer install Visual Studio 2015. For Python 3.4
|
||||
install Visual Studio 2010. You must use the full version of Visual Studio
|
||||
2010 as Visual C++ Express does not provide 64-bit compilers. Make sure that
|
||||
you check the "x64 Compilers and Tools" option under Visual C++. For Python 2.7
|
||||
install the `Microsoft Visual C++ Compiler for Python 2.7`_.
|
||||
you check the "x64 Compilers and Tools" option under Visual C++.
|
||||
|
||||
32-bit Windows
|
||||
~~~~~~~~~~~~~~
|
||||
@ -219,10 +178,6 @@ For Python 3.5 and newer install Visual Studio 2015.
|
||||
|
||||
For Python 3.4 install Visual C++ 2010 Express.
|
||||
|
||||
For Python 2.7 install the `Microsoft Visual C++ Compiler for Python 2.7`_
|
||||
|
||||
.. _`Microsoft Visual C++ Compiler for Python 2.7`: https://www.microsoft.com/en-us/download/details.aspx?id=44266
|
||||
|
||||
.. _install-no-c:
|
||||
|
||||
Installing Without C Extensions
|
||||
@ -237,34 +192,7 @@ If you wish to install PyMongo without the C extensions, even if the
|
||||
extensions build properly, it can be done using a command line option to
|
||||
*setup.py*::
|
||||
|
||||
$ python setup.py --no_ext install
|
||||
|
||||
Building PyMongo egg Packages
|
||||
-----------------------------
|
||||
|
||||
Some organizations do not allow compilers and other build tools on production
|
||||
systems. To install PyMongo on these systems with C extensions you may need to
|
||||
build custom egg packages. Make sure that you have installed the dependencies
|
||||
listed above for your operating system then run the following command in the
|
||||
PyMongo source directory::
|
||||
|
||||
$ python setup.py bdist_egg
|
||||
|
||||
The egg package can be found in the dist/ subdirectory. The file name will
|
||||
resemble “pymongo-3.6-py2.7-linux-x86_64.egg” but may have a different name
|
||||
depending on your platform and the version of python you use to compile.
|
||||
|
||||
.. warning::
|
||||
|
||||
These “binary distributions,” will only work on systems that resemble the
|
||||
environment on which you built the package. In other words, ensure that
|
||||
operating systems and versions of Python and architecture (i.e. “32” or “64”
|
||||
bit) match.
|
||||
|
||||
Copy this file to the target system and issue the following command to install the
|
||||
package::
|
||||
|
||||
$ sudo python -m easy_install pymongo-3.6-py2.7-linux-x86_64.egg
|
||||
$ python3 setup.py --no_ext install
|
||||
|
||||
Installing a beta or release candidate
|
||||
--------------------------------------
|
||||
@ -275,4 +203,4 @@ but can be found on the
|
||||
`GitHub tags page <https://github.com/mongodb/mongo-python-driver/tags>`_.
|
||||
They can be installed by passing the full URL for the tag to pip::
|
||||
|
||||
$ python -m pip install https://github.com/mongodb/mongo-python-driver/archive/3.11.0rc0.tar.gz
|
||||
$ python3 -m pip install https://github.com/mongodb/mongo-python-driver/archive/3.11.0rc0.tar.gz
|
||||
|
||||
414
ez_setup.py
414
ez_setup.py
@ -1,414 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Setuptools bootstrapping installer.
|
||||
|
||||
Maintained at https://github.com/pypa/setuptools/tree/bootstrap.
|
||||
|
||||
Run this script to install or upgrade setuptools.
|
||||
|
||||
This method is DEPRECATED. Check https://github.com/pypa/setuptools/issues/581 for more details.
|
||||
"""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
import zipfile
|
||||
import optparse
|
||||
import subprocess
|
||||
import platform
|
||||
import textwrap
|
||||
import contextlib
|
||||
|
||||
from distutils import log
|
||||
|
||||
try:
|
||||
from urllib.request import urlopen
|
||||
except ImportError:
|
||||
from urllib2 import urlopen
|
||||
|
||||
try:
|
||||
from site import USER_SITE
|
||||
except ImportError:
|
||||
USER_SITE = None
|
||||
|
||||
# 33.1.1 is the last version that supports setuptools self upgrade/installation.
|
||||
DEFAULT_VERSION = "33.1.1"
|
||||
DEFAULT_URL = "https://pypi.io/packages/source/s/setuptools/"
|
||||
DEFAULT_SAVE_DIR = os.curdir
|
||||
DEFAULT_DEPRECATION_MESSAGE = "ez_setup.py is deprecated and when using it setuptools will be pinned to {0} since it's the last version that supports setuptools self upgrade/installation, check https://github.com/pypa/setuptools/issues/581 for more info; use pip to install setuptools"
|
||||
|
||||
MEANINGFUL_INVALID_ZIP_ERR_MSG = 'Maybe {0} is corrupted, delete it and try again.'
|
||||
|
||||
log.warn(DEFAULT_DEPRECATION_MESSAGE.format(DEFAULT_VERSION))
|
||||
|
||||
|
||||
def _python_cmd(*args):
|
||||
"""
|
||||
Execute a command.
|
||||
|
||||
Return True if the command succeeded.
|
||||
"""
|
||||
args = (sys.executable,) + args
|
||||
return subprocess.call(args) == 0
|
||||
|
||||
|
||||
def _install(archive_filename, install_args=()):
|
||||
"""Install Setuptools."""
|
||||
with archive_context(archive_filename):
|
||||
# installing
|
||||
log.warn('Installing Setuptools')
|
||||
if not _python_cmd('setup.py', 'install', *install_args):
|
||||
log.warn('Something went wrong during the installation.')
|
||||
log.warn('See the error message above.')
|
||||
# exitcode will be 2
|
||||
return 2
|
||||
|
||||
|
||||
def _build_egg(egg, archive_filename, to_dir):
|
||||
"""Build Setuptools egg."""
|
||||
with archive_context(archive_filename):
|
||||
# building an egg
|
||||
log.warn('Building a Setuptools egg in %s', to_dir)
|
||||
_python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir)
|
||||
# returning the result
|
||||
log.warn(egg)
|
||||
if not os.path.exists(egg):
|
||||
raise IOError('Could not build the egg.')
|
||||
|
||||
|
||||
class ContextualZipFile(zipfile.ZipFile):
|
||||
|
||||
"""Supplement ZipFile class to support context manager for Python 2.6."""
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
self.close()
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
"""Construct a ZipFile or ContextualZipFile as appropriate."""
|
||||
if hasattr(zipfile.ZipFile, '__exit__'):
|
||||
return zipfile.ZipFile(*args, **kwargs)
|
||||
return super(ContextualZipFile, cls).__new__(cls)
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def archive_context(filename):
|
||||
"""
|
||||
Unzip filename to a temporary directory, set to the cwd.
|
||||
|
||||
The unzipped target is cleaned up after.
|
||||
"""
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
log.warn('Extracting in %s', tmpdir)
|
||||
old_wd = os.getcwd()
|
||||
try:
|
||||
os.chdir(tmpdir)
|
||||
try:
|
||||
with ContextualZipFile(filename) as archive:
|
||||
archive.extractall()
|
||||
except zipfile.BadZipfile as err:
|
||||
if not err.args:
|
||||
err.args = ('', )
|
||||
err.args = err.args + (
|
||||
MEANINGFUL_INVALID_ZIP_ERR_MSG.format(filename),
|
||||
)
|
||||
raise
|
||||
|
||||
# going in the directory
|
||||
subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
|
||||
os.chdir(subdir)
|
||||
log.warn('Now working in %s', subdir)
|
||||
yield
|
||||
|
||||
finally:
|
||||
os.chdir(old_wd)
|
||||
shutil.rmtree(tmpdir)
|
||||
|
||||
|
||||
def _do_download(version, download_base, to_dir, download_delay):
|
||||
"""Download Setuptools."""
|
||||
py_desig = 'py{sys.version_info[0]}.{sys.version_info[1]}'.format(sys=sys)
|
||||
tp = 'setuptools-{version}-{py_desig}.egg'
|
||||
egg = os.path.join(to_dir, tp.format(**locals()))
|
||||
if not os.path.exists(egg):
|
||||
archive = download_setuptools(version, download_base,
|
||||
to_dir, download_delay)
|
||||
_build_egg(egg, archive, to_dir)
|
||||
sys.path.insert(0, egg)
|
||||
|
||||
# Remove previously-imported pkg_resources if present (see
|
||||
# https://bitbucket.org/pypa/setuptools/pull-request/7/ for details).
|
||||
if 'pkg_resources' in sys.modules:
|
||||
_unload_pkg_resources()
|
||||
|
||||
import setuptools
|
||||
setuptools.bootstrap_install_from = egg
|
||||
|
||||
|
||||
def use_setuptools(
|
||||
version=DEFAULT_VERSION, download_base=DEFAULT_URL,
|
||||
to_dir=DEFAULT_SAVE_DIR, download_delay=15):
|
||||
"""
|
||||
Ensure that a setuptools version is installed.
|
||||
|
||||
Return None. Raise SystemExit if the requested version
|
||||
or later cannot be installed.
|
||||
"""
|
||||
to_dir = os.path.abspath(to_dir)
|
||||
|
||||
# prior to importing, capture the module state for
|
||||
# representative modules.
|
||||
rep_modules = 'pkg_resources', 'setuptools'
|
||||
imported = set(sys.modules).intersection(rep_modules)
|
||||
|
||||
try:
|
||||
import pkg_resources
|
||||
pkg_resources.require("setuptools>=" + version)
|
||||
# a suitable version is already installed
|
||||
return
|
||||
except ImportError:
|
||||
# pkg_resources not available; setuptools is not installed; download
|
||||
pass
|
||||
except pkg_resources.DistributionNotFound:
|
||||
# no version of setuptools was found; allow download
|
||||
pass
|
||||
except pkg_resources.VersionConflict as VC_err:
|
||||
if imported:
|
||||
_conflict_bail(VC_err, version)
|
||||
|
||||
# otherwise, unload pkg_resources to allow the downloaded version to
|
||||
# take precedence.
|
||||
del pkg_resources
|
||||
_unload_pkg_resources()
|
||||
|
||||
return _do_download(version, download_base, to_dir, download_delay)
|
||||
|
||||
|
||||
def _conflict_bail(VC_err, version):
|
||||
"""
|
||||
Setuptools was imported prior to invocation, so it is
|
||||
unsafe to unload it. Bail out.
|
||||
"""
|
||||
conflict_tmpl = textwrap.dedent("""
|
||||
The required version of setuptools (>={version}) is not available,
|
||||
and can't be installed while this script is running. Please
|
||||
install a more recent version first, using
|
||||
'easy_install -U setuptools'.
|
||||
|
||||
(Currently using {VC_err.args[0]!r})
|
||||
""")
|
||||
msg = conflict_tmpl.format(**locals())
|
||||
sys.stderr.write(msg)
|
||||
sys.exit(2)
|
||||
|
||||
|
||||
def _unload_pkg_resources():
|
||||
sys.meta_path = [
|
||||
importer
|
||||
for importer in sys.meta_path
|
||||
if importer.__class__.__module__ != 'pkg_resources.extern'
|
||||
]
|
||||
del_modules = [
|
||||
name for name in sys.modules
|
||||
if name.startswith('pkg_resources')
|
||||
]
|
||||
for mod_name in del_modules:
|
||||
del sys.modules[mod_name]
|
||||
|
||||
|
||||
def _clean_check(cmd, target):
|
||||
"""
|
||||
Run the command to download target.
|
||||
|
||||
If the command fails, clean up before re-raising the error.
|
||||
"""
|
||||
try:
|
||||
subprocess.check_call(cmd)
|
||||
except subprocess.CalledProcessError:
|
||||
if os.access(target, os.F_OK):
|
||||
os.unlink(target)
|
||||
raise
|
||||
|
||||
|
||||
def download_file_powershell(url, target):
|
||||
"""
|
||||
Download the file at url to target using Powershell.
|
||||
|
||||
Powershell will validate trust.
|
||||
Raise an exception if the command cannot complete.
|
||||
"""
|
||||
target = os.path.abspath(target)
|
||||
ps_cmd = (
|
||||
"[System.Net.WebRequest]::DefaultWebProxy.Credentials = "
|
||||
"[System.Net.CredentialCache]::DefaultCredentials; "
|
||||
'(new-object System.Net.WebClient).DownloadFile("%(url)s", "%(target)s")'
|
||||
% locals()
|
||||
)
|
||||
cmd = [
|
||||
'powershell',
|
||||
'-Command',
|
||||
ps_cmd,
|
||||
]
|
||||
_clean_check(cmd, target)
|
||||
|
||||
|
||||
def has_powershell():
|
||||
"""Determine if Powershell is available."""
|
||||
if platform.system() != 'Windows':
|
||||
return False
|
||||
cmd = ['powershell', '-Command', 'echo test']
|
||||
with open(os.path.devnull, 'wb') as devnull:
|
||||
try:
|
||||
subprocess.check_call(cmd, stdout=devnull, stderr=devnull)
|
||||
except Exception:
|
||||
return False
|
||||
return True
|
||||
download_file_powershell.viable = has_powershell
|
||||
|
||||
|
||||
def download_file_curl(url, target):
|
||||
cmd = ['curl', url, '--location', '--silent', '--output', target]
|
||||
_clean_check(cmd, target)
|
||||
|
||||
|
||||
def has_curl():
|
||||
cmd = ['curl', '--version']
|
||||
with open(os.path.devnull, 'wb') as devnull:
|
||||
try:
|
||||
subprocess.check_call(cmd, stdout=devnull, stderr=devnull)
|
||||
except Exception:
|
||||
return False
|
||||
return True
|
||||
download_file_curl.viable = has_curl
|
||||
|
||||
|
||||
def download_file_wget(url, target):
|
||||
cmd = ['wget', url, '--quiet', '--output-document', target]
|
||||
_clean_check(cmd, target)
|
||||
|
||||
|
||||
def has_wget():
|
||||
cmd = ['wget', '--version']
|
||||
with open(os.path.devnull, 'wb') as devnull:
|
||||
try:
|
||||
subprocess.check_call(cmd, stdout=devnull, stderr=devnull)
|
||||
except Exception:
|
||||
return False
|
||||
return True
|
||||
download_file_wget.viable = has_wget
|
||||
|
||||
|
||||
def download_file_insecure(url, target):
|
||||
"""Use Python to download the file, without connection authentication."""
|
||||
src = urlopen(url)
|
||||
try:
|
||||
# Read all the data in one block.
|
||||
data = src.read()
|
||||
finally:
|
||||
src.close()
|
||||
|
||||
# Write all the data in one block to avoid creating a partial file.
|
||||
with open(target, "wb") as dst:
|
||||
dst.write(data)
|
||||
download_file_insecure.viable = lambda: True
|
||||
|
||||
|
||||
def get_best_downloader():
|
||||
downloaders = (
|
||||
download_file_powershell,
|
||||
download_file_curl,
|
||||
download_file_wget,
|
||||
download_file_insecure,
|
||||
)
|
||||
viable_downloaders = (dl for dl in downloaders if dl.viable())
|
||||
return next(viable_downloaders, None)
|
||||
|
||||
|
||||
def download_setuptools(
|
||||
version=DEFAULT_VERSION, download_base=DEFAULT_URL,
|
||||
to_dir=DEFAULT_SAVE_DIR, delay=15,
|
||||
downloader_factory=get_best_downloader):
|
||||
"""
|
||||
Download setuptools from a specified location and return its filename.
|
||||
|
||||
`version` should be a valid setuptools version number that is available
|
||||
as an sdist for download under the `download_base` URL (which should end
|
||||
with a '/'). `to_dir` is the directory where the egg will be downloaded.
|
||||
`delay` is the number of seconds to pause before an actual download
|
||||
attempt.
|
||||
|
||||
``downloader_factory`` should be a function taking no arguments and
|
||||
returning a function for downloading a URL to a target.
|
||||
"""
|
||||
# making sure we use the absolute path
|
||||
to_dir = os.path.abspath(to_dir)
|
||||
zip_name = "setuptools-%s.zip" % version
|
||||
url = download_base + zip_name
|
||||
saveto = os.path.join(to_dir, zip_name)
|
||||
if not os.path.exists(saveto): # Avoid repeated downloads
|
||||
log.warn("Downloading %s", url)
|
||||
downloader = downloader_factory()
|
||||
downloader(url, saveto)
|
||||
return os.path.realpath(saveto)
|
||||
|
||||
|
||||
def _build_install_args(options):
|
||||
"""
|
||||
Build the arguments to 'python setup.py install' on the setuptools package.
|
||||
|
||||
Returns list of command line arguments.
|
||||
"""
|
||||
return ['--user'] if options.user_install else []
|
||||
|
||||
|
||||
def _parse_args():
|
||||
"""Parse the command line for options."""
|
||||
parser = optparse.OptionParser()
|
||||
parser.add_option(
|
||||
'--user', dest='user_install', action='store_true', default=False,
|
||||
help='install in user site package')
|
||||
parser.add_option(
|
||||
'--download-base', dest='download_base', metavar="URL",
|
||||
default=DEFAULT_URL,
|
||||
help='alternative URL from where to download the setuptools package')
|
||||
parser.add_option(
|
||||
'--insecure', dest='downloader_factory', action='store_const',
|
||||
const=lambda: download_file_insecure, default=get_best_downloader,
|
||||
help='Use internal, non-validating downloader'
|
||||
)
|
||||
parser.add_option(
|
||||
'--version', help="Specify which version to download",
|
||||
default=DEFAULT_VERSION,
|
||||
)
|
||||
parser.add_option(
|
||||
'--to-dir',
|
||||
help="Directory to save (and re-use) package",
|
||||
default=DEFAULT_SAVE_DIR,
|
||||
)
|
||||
options, args = parser.parse_args()
|
||||
# positional arguments are ignored
|
||||
return options
|
||||
|
||||
|
||||
def _download_args(options):
|
||||
"""Return args for download_setuptools function from cmdline args."""
|
||||
return dict(
|
||||
version=options.version,
|
||||
download_base=options.download_base,
|
||||
downloader_factory=options.downloader_factory,
|
||||
to_dir=options.to_dir,
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
"""Install or upgrade setuptools and EasyInstall."""
|
||||
options = _parse_args()
|
||||
archive = download_setuptools(**_download_args(options))
|
||||
return _install(archive, _build_install_args(options))
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
122
setup.py
122
setup.py
@ -5,8 +5,8 @@ import sys
|
||||
import warnings
|
||||
|
||||
|
||||
if sys.version_info[:2] < (2, 7):
|
||||
raise RuntimeError("Python version >= 2.7 required.")
|
||||
if sys.version_info[:2] < (3, 4):
|
||||
raise RuntimeError("Python version >= 3.4 required.")
|
||||
|
||||
|
||||
# Hack to silence atexit traceback in some Python versions
|
||||
@ -15,14 +15,7 @@ try:
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
# Don't force people to install setuptools unless
|
||||
# we have to.
|
||||
try:
|
||||
from setuptools import setup, __version__ as _setuptools_version
|
||||
except ImportError:
|
||||
from ez_setup import use_setuptools
|
||||
use_setuptools()
|
||||
from setuptools import setup, __version__ as _setuptools_version
|
||||
from setuptools import setup, __version__ as _setuptools_version
|
||||
|
||||
from distutils.cmd import Command
|
||||
from distutils.command.build_ext import build_ext
|
||||
@ -152,45 +145,45 @@ class doc(Command):
|
||||
raise RuntimeError(
|
||||
"You must install Sphinx to build or test the documentation.")
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
import doctest
|
||||
from doctest import OutputChecker as _OutputChecker
|
||||
# TODO: Convert all the docs to Python 3 and delete all this.
|
||||
import doctest
|
||||
from doctest import OutputChecker as _OutputChecker
|
||||
|
||||
# Match u or U (possibly followed by r or R), removing it.
|
||||
# r/R can follow u/U but not precede it. Don't match the
|
||||
# single character string 'u' or 'U'.
|
||||
_u_literal_re = re.compile(
|
||||
r"(\W|^)(?<![\'\"])[uU]([rR]?[\'\"])", re.UNICODE)
|
||||
# Match b or B (possibly followed by r or R), removing.
|
||||
# r/R can follow b/B but not precede it. Don't match the
|
||||
# single character string 'b' or 'B'.
|
||||
_b_literal_re = re.compile(
|
||||
r"(\W|^)(?<![\'\"])[bB]([rR]?[\'\"])", re.UNICODE)
|
||||
# Match u or U (possibly followed by r or R), removing it.
|
||||
# r/R can follow u/U but not precede it. Don't match the
|
||||
# single character string 'u' or 'U'.
|
||||
_u_literal_re = re.compile(
|
||||
r"(\W|^)(?<![\'\"])[uU]([rR]?[\'\"])", re.UNICODE)
|
||||
# Match b or B (possibly followed by r or R), removing.
|
||||
# r/R can follow b/B but not precede it. Don't match the
|
||||
# single character string 'b' or 'B'.
|
||||
_b_literal_re = re.compile(
|
||||
r"(\W|^)(?<![\'\"])[bB]([rR]?[\'\"])", re.UNICODE)
|
||||
|
||||
class _StringPrefixFixer(_OutputChecker):
|
||||
class _StringPrefixFixer(_OutputChecker):
|
||||
|
||||
def check_output(self, want, got, optionflags):
|
||||
# The docstrings are written with python 2.x in mind.
|
||||
# To make the doctests pass in python 3 we have to
|
||||
# strip the 'u' prefix from the expected results. The
|
||||
# actual results won't have that prefix.
|
||||
want = re.sub(_u_literal_re, r'\1\2', want)
|
||||
# We also have to strip the 'b' prefix from the actual
|
||||
# results since python 2.x expected results won't have
|
||||
# that prefix.
|
||||
got = re.sub(_b_literal_re, r'\1\2', got)
|
||||
return super(
|
||||
_StringPrefixFixer, self).check_output(
|
||||
want, got, optionflags)
|
||||
def check_output(self, want, got, optionflags):
|
||||
# The docstrings are written with python 2.x in mind.
|
||||
# To make the doctests pass in python 3 we have to
|
||||
# strip the 'u' prefix from the expected results. The
|
||||
# actual results won't have that prefix.
|
||||
want = re.sub(_u_literal_re, r'\1\2', want)
|
||||
# We also have to strip the 'b' prefix from the actual
|
||||
# results since python 2.x expected results won't have
|
||||
# that prefix.
|
||||
got = re.sub(_b_literal_re, r'\1\2', got)
|
||||
return super(
|
||||
_StringPrefixFixer, self).check_output(
|
||||
want, got, optionflags)
|
||||
|
||||
def output_difference(self, example, got, optionflags):
|
||||
example.want = re.sub(_u_literal_re, r'\1\2', example.want)
|
||||
got = re.sub(_b_literal_re, r'\1\2', got)
|
||||
return super(
|
||||
_StringPrefixFixer, self).output_difference(
|
||||
example, got, optionflags)
|
||||
def output_difference(self, example, got, optionflags):
|
||||
example.want = re.sub(_u_literal_re, r'\1\2', example.want)
|
||||
got = re.sub(_b_literal_re, r'\1\2', got)
|
||||
return super(
|
||||
_StringPrefixFixer, self).output_difference(
|
||||
example, got, optionflags)
|
||||
|
||||
doctest.OutputChecker = _StringPrefixFixer
|
||||
doctest.OutputChecker = _StringPrefixFixer
|
||||
|
||||
if self.test:
|
||||
path = os.path.join(
|
||||
@ -327,46 +320,11 @@ extras_require = {
|
||||
'encryption': ['pymongocrypt<2.0.0'],
|
||||
'ocsp': pyopenssl_reqs,
|
||||
'snappy': ['python-snappy'],
|
||||
'tls': [],
|
||||
'zstd': ['zstandard'],
|
||||
'aws': ['pymongo-auth-aws<2.0.0'],
|
||||
'srv': ["dnspython>=1.16.0,<2.0.0"],
|
||||
}
|
||||
|
||||
# https://jira.mongodb.org/browse/PYTHON-2117
|
||||
# Environment marker support didn't settle down until version 20.10
|
||||
# https://setuptools.readthedocs.io/en/latest/history.html#v20-10-0
|
||||
_use_env_markers = tuple(map(int, _setuptools_version.split('.')[:2])) > (20, 9)
|
||||
|
||||
# TLS and DNS extras
|
||||
# We install PyOpenSSL and service_identity for Python < 2.7.9 to
|
||||
# get support for SNI, which is required to connection to Altas
|
||||
# free and shared tier.
|
||||
if sys.version_info[0] == 2:
|
||||
if _use_env_markers:
|
||||
# For building wheels on Python versions >= 2.7.9
|
||||
for req in pyopenssl_reqs:
|
||||
extras_require['tls'].append(
|
||||
"%s ; python_full_version < '2.7.9'" % (req,))
|
||||
if sys.platform == 'win32':
|
||||
extras_require['tls'].append(
|
||||
"wincertstore>=0.2 ; python_full_version < '2.7.9'")
|
||||
else:
|
||||
extras_require['tls'].append(
|
||||
"certifi ; python_full_version < '2.7.9'")
|
||||
elif sys.version_info < (2, 7, 9):
|
||||
# For installing from source or egg files on Python versions
|
||||
# older than 2.7.9, or systems that have setuptools versions
|
||||
# older than 20.10.
|
||||
extras_require['tls'].extend(pyopenssl_reqs)
|
||||
if sys.platform == 'win32':
|
||||
extras_require['tls'].append("wincertstore>=0.2")
|
||||
else:
|
||||
extras_require['tls'].append("certifi")
|
||||
extras_require.update({'srv': ["dnspython>=1.16.0,<1.17.0"]})
|
||||
extras_require.update({'tls': ["ipaddress"]})
|
||||
else:
|
||||
extras_require.update({'srv': ["dnspython>=1.16.0,<2.0.0"]})
|
||||
|
||||
# GSSAPI extras
|
||||
if sys.platform == 'win32':
|
||||
extras_require['gssapi'] = ["winkerberos>=0.5.0"]
|
||||
@ -404,7 +362,7 @@ setup(
|
||||
keywords=["mongo", "mongodb", "pymongo", "gridfs", "bson"],
|
||||
install_requires=[],
|
||||
license="Apache License, Version 2.0",
|
||||
python_requires=">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*",
|
||||
python_requires=">=3.4",
|
||||
classifiers=[
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
"Intended Audience :: Developers",
|
||||
@ -412,8 +370,6 @@ setup(
|
||||
"Operating System :: MacOS :: MacOS X",
|
||||
"Operating System :: Microsoft :: Windows",
|
||||
"Operating System :: POSIX",
|
||||
"Programming Language :: Python :: 2",
|
||||
"Programming Language :: Python :: 2.7",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.4",
|
||||
"Programming Language :: Python :: 3.5",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user