PYTHON-2133 Drop install support for Python 2

This commit is contained in:
Bernie Hackett 2021-01-13 20:04:16 -08:00
parent fb4c20adfa
commit dea4b90193
11 changed files with 72 additions and 734 deletions

View File

@ -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:

View File

@ -1,13 +1,11 @@
language: python
python:
- 2.7
- 3.4
- 3.5
- 3.6
- 3.7
- 3.8
- pypy
- pypy3.5
services:

View File

@ -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

View File

@ -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_

View File

@ -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::

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
View File

@ -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",