1
0
mirror of https://github.com/certbot/certbot.git synced 2025-08-06 16:42:41 +03:00

Add Python 3.10 support and tests (#9077)

Fixes https://github.com/certbot/certbot/issues/9058.

The changes to the CI config are equivalent to the ones made in https://github.com/certbot/certbot/pull/8460.

Other than ignoring some warnings raised by botocore, the main additional work that had to be done here was switching away from using `distutils.version.LooseVersion` since the entire `distutils` module was deprecated in Python 3.10. To do that, I took a few different approaches:

* If the version strings being parsed are from Python packages such as Certbot or setuptools, I switched to using [pkg_resources.parse_version](https://setuptools.pypa.io/en/latest/pkg_resources.html#parsing-utilities) from `setuptools`. This functionality has been available since [setuptools 8.0 from 2014](https://setuptools.pypa.io/en/latest/history.html#id865).
* If the version strings being parsed are not from Python packages, I added code equivalent to `distutils.version.LooseVersion` in `certbot.util.parse_loose_version`.
* The code for `CERTBOT_PIP_NO_BINARY` can be completely removed since that variable isn't used or referenced anywhere in this repo.

* add python 3.10 support

* make some version changes

* don't use looseversion in setup.py

* switch to pkg_resources

* deprecate get_strict_version

* fix route53 tests

* remove unused CERTBOT_PIP_NO_BINARY code

* stop using distutils in letstest

* add unit tests

* more changelog entries
This commit is contained in:
Brad Warren
2021-11-08 15:55:32 -08:00
committed by GitHub
parent b1edda8a65
commit e8265dbf9c
35 changed files with 158 additions and 61 deletions

View File

@@ -4,7 +4,7 @@ jobs:
- name: IMAGE_NAME
value: ubuntu-18.04
- name: PYTHON_VERSION
value: 3.9
value: 3.10
- group: certbot-common
strategy:
matrix:
@@ -17,6 +17,9 @@ jobs:
linux-py38:
PYTHON_VERSION: 3.8
TOXENV: py38
linux-py39:
PYTHON_VERSION: 3.9
TOXENV: py39
linux-py37-nopin:
PYTHON_VERSION: 3.7
TOXENV: py37
@@ -71,6 +74,14 @@ jobs:
PYTHON_VERSION: 3.9
TOXENV: integration
ACME_SERVER: boulder-v2
linux-boulder-v1-py310-integration:
PYTHON_VERSION: 3.10
TOXENV: integration
ACME_SERVER: boulder-v1
linux-boulder-v2-py310-integration:
PYTHON_VERSION: 3.10
TOXENV: integration
ACME_SERVER: boulder-v2
nginx-compat:
TOXENV: nginx_compat
linux-integration-rfc2136:

View File

@@ -1,16 +1,16 @@
jobs:
- job: test
variables:
PYTHON_VERSION: 3.9
PYTHON_VERSION: 3.10
strategy:
matrix:
macos-py36:
IMAGE_NAME: macOS-10.15
PYTHON_VERSION: 3.6
TOXENV: py36
macos-py39:
macos-py310:
IMAGE_NAME: macOS-10.15
PYTHON_VERSION: 3.9
PYTHON_VERSION: 3.10
TOXENV: py39
windows-py36:
IMAGE_NAME: vs2017-win2016
@@ -36,17 +36,17 @@ jobs:
IMAGE_NAME: ubuntu-18.04
PYTHON_VERSION: 3.6
TOXENV: py36
linux-py39-cover:
linux-py310-cover:
IMAGE_NAME: ubuntu-18.04
PYTHON_VERSION: 3.9
TOXENV: py39-cover
linux-py39-lint:
PYTHON_VERSION: 3.10
TOXENV: py310-cover
linux-py310-lint:
IMAGE_NAME: ubuntu-18.04
PYTHON_VERSION: 3.9
PYTHON_VERSION: 3.10
TOXENV: lint-posix
linux-py39-mypy:
linux-py310-mypy:
IMAGE_NAME: ubuntu-18.04
PYTHON_VERSION: 3.9
PYTHON_VERSION: 3.10
TOXENV: mypy-posix
linux-integration:
IMAGE_NAME: ubuntu-18.04

View File

@@ -45,6 +45,7 @@ setup(
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Security',
],

View File

@@ -2,7 +2,6 @@
# pylint: disable=too-many-lines
from collections import defaultdict
import copy
from distutils.version import LooseVersion
import fnmatch
import logging
import re
@@ -154,9 +153,10 @@ class ApacheConfigurator(common.Installer, interfaces.Authenticator):
"""
# Disabling TLS session tickets is supported by Apache 2.4.11+ and OpenSSL 1.0.2l+.
# So for old versions of Apache we pick a configuration without this option.
min_openssl_version = util.parse_loose_version('1.0.2l')
openssl_version = self.openssl_version(warn_on_no_mod_ssl)
if self.version < (2, 4, 11) or not openssl_version or\
LooseVersion(openssl_version) < LooseVersion('1.0.2l'):
util.parse_loose_version(openssl_version) < min_openssl_version:
return apache_util.find_ssl_apache_conf("old")
return apache_util.find_ssl_apache_conf("current")

View File

@@ -1,6 +1,4 @@
""" Entry point for Apache Plugin """
from distutils.version import LooseVersion
from certbot import util
from certbot_apache._internal import configurator
from certbot_apache._internal import override_arch
@@ -47,7 +45,8 @@ def get_configurator():
override_class = None
# Special case for older Fedora versions
if os_name == 'fedora' and LooseVersion(os_version) < LooseVersion('29'):
min_version = util.parse_loose_version('29')
if os_name == 'fedora' and util.parse_loose_version(os_version) < min_version:
os_name = 'fedora_old'
try:

View File

@@ -38,6 +38,7 @@ setup(
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Security',
'Topic :: System :: Installation/Setup',

View File

@@ -1,8 +1,8 @@
#!/usr/bin/env python
"""Module to call certbot in test mode"""
from distutils.version import LooseVersion
import os
import pkg_resources
import subprocess
import sys
@@ -85,7 +85,7 @@ def _compute_additional_args(workspace, environ, force_renew):
cwd=workspace, env=environ)
# Typical response is: output = 'certbot 0.31.0.dev0'
version_str = output.split(' ')[1].strip()
if LooseVersion(version_str) >= LooseVersion('0.30.0'):
if pkg_resources.parse_version(version_str) >= pkg_resources.parse_version('0.30.0'):
additional_args.append('--no-random-sleep-on-renew')
if force_renew:

View File

@@ -1,5 +1,4 @@
from distutils.version import LooseVersion
from pkg_resources import parse_version
from setuptools import __version__ as setuptools_version
from setuptools import find_packages
from setuptools import setup
@@ -10,7 +9,7 @@ version = '0.32.0.dev0'
min_setuptools_version='36.2'
# This conditional isn't necessary, but it provides better error messages to
# people who try to install this package with older versions of setuptools.
if LooseVersion(setuptools_version) < LooseVersion(min_setuptools_version):
if parse_version(setuptools_version) < parse_version(min_setuptools_version):
raise RuntimeError(f'setuptools {min_setuptools_version}+ is required')
install_requires = [
@@ -29,6 +28,7 @@ install_requires = [
'pywin32>=300 ; sys_platform == "win32"',
'pyyaml',
'requests',
'setuptools',
'types-python-dateutil'
]
@@ -51,6 +51,7 @@ setup(
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Security',
],

View File

@@ -29,6 +29,7 @@ setup(
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Security',
],

View File

@@ -51,6 +51,7 @@ setup(
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Security',
'Topic :: System :: Installation/Setup',

View File

@@ -51,6 +51,7 @@ setup(
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Security',
'Topic :: System :: Installation/Setup',

View File

@@ -51,6 +51,7 @@ setup(
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Security',
'Topic :: System :: Installation/Setup',

View File

@@ -53,6 +53,7 @@ setup(
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Security',
'Topic :: System :: Installation/Setup',

View File

@@ -51,6 +51,7 @@ setup(
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Security',
'Topic :: System :: Installation/Setup',

View File

@@ -51,6 +51,7 @@ setup(
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Security',
'Topic :: System :: Installation/Setup',

View File

@@ -54,6 +54,7 @@ setup(
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Security',
'Topic :: System :: Installation/Setup',

View File

@@ -51,6 +51,7 @@ setup(
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Security',
'Topic :: System :: Installation/Setup',

View File

@@ -51,6 +51,7 @@ setup(
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Security',
'Topic :: System :: Installation/Setup',

View File

@@ -51,6 +51,7 @@ setup(
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Security',
'Topic :: System :: Installation/Setup',

View File

@@ -51,6 +51,7 @@ setup(
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Security',
'Topic :: System :: Installation/Setup',

View File

@@ -51,6 +51,7 @@ setup(
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Security',
'Topic :: System :: Installation/Setup',

View File

@@ -51,6 +51,7 @@ setup(
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Security',
'Topic :: System :: Installation/Setup',

View File

@@ -51,6 +51,7 @@ setup(
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Security',
'Topic :: System :: Installation/Setup',

View File

@@ -1,6 +1,5 @@
# pylint: disable=too-many-lines
"""Nginx Configuration"""
from distutils.version import LooseVersion
import logging
import re
import socket
@@ -142,8 +141,9 @@ class NginxConfigurator(common.Installer, interfaces.Authenticator):
# For a complete history, check out https://github.com/certbot/certbot/issues/7322
use_tls13 = self.version >= (1, 13, 0)
min_openssl_version = util.parse_loose_version('1.0.2l')
session_tix_off = self.version >= (1, 5, 9) and self.openssl_version and\
LooseVersion(self.openssl_version) >= LooseVersion('1.0.2l')
util.parse_loose_version(self.openssl_version) >= min_openssl_version
if use_tls13:
if session_tix_off:

View File

@@ -35,6 +35,7 @@ setup(
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Security',
'Topic :: System :: Installation/Setup',

View File

@@ -6,11 +6,15 @@ Certbot adheres to [Semantic Versioning](https://semver.org/).
### Added
*
* Support for Python 3.10 was added to Certbot and all of its components.
* The function certbot.util.parse_loose_version was added to parse version
strings in the same way as the now deprecated distutils.version.LooseVersion
class from the Python standard library.
### Changed
*
* The function certbot.util.get_strict_version was deprecated and will be
removed in a future release.
### Fixed

View File

@@ -12,6 +12,7 @@ from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey
from cryptography.hazmat.primitives.serialization import load_pem_private_key
import parsedatetime
import pkg_resources
import pytz
import certbot
@@ -33,7 +34,7 @@ logger = logging.getLogger(__name__)
ALL_FOUR = ("cert", "privkey", "chain", "fullchain")
README = "README"
CURRENT_VERSION = util.get_strict_version(certbot.__version__)
CURRENT_VERSION = pkg_resources.parse_version(certbot.__version__)
BASE_PRIVKEY_MODE = 0o600
@@ -457,7 +458,7 @@ class RenewableCert(interfaces.RenewableCert):
conf_version = self.configuration.get("version")
if (conf_version is not None and
util.get_strict_version(conf_version) > CURRENT_VERSION):
pkg_resources.parse_version(conf_version) > CURRENT_VERSION):
logger.info(
"Attempting to parse the version %s renewal configuration "
"file found at %s with version %s of Certbot. This might not "

View File

@@ -1,10 +1,7 @@
"""Utilities for all Certbot."""
# distutils.version under virtualenv confuses pylint
# For more info, see: https://github.com/PyCQA/pylint/issues/73
import argparse
import atexit
import collections
import distutils.version
import errno
import logging
import platform
@@ -14,6 +11,7 @@ import subprocess
import sys
from typing import Dict
from typing import IO
from typing import List
from typing import Text
from typing import Tuple
from typing import Union
@@ -61,7 +59,7 @@ _INITIAL_PID = os.getpid()
# program exits before the lock is cleaned up, it is automatically
# released, but the file isn't deleted.
_LOCKS: Dict[str, lock.LockFile] = {}
_VERSION_COMPONENT_RE = re.compile(r'(\d+ | [a-z]+ | \.)', re.VERBOSE)
def env_no_snap_for_external_calls():
"""
@@ -612,6 +610,11 @@ def get_strict_version(normalized):
:rtype: distutils.version.StrictVersion
"""
warnings.warn("certbot.util.get_strict_version is deprecated and will be "
"removed in a future release.", DeprecationWarning)
with warnings.catch_warnings():
warnings.simplefilter("ignore", DeprecationWarning)
import distutils.version
# strict version ending with "a" and a number designates a pre-release
return distutils.version.StrictVersion(normalized.replace(".dev", "a"))
@@ -639,6 +642,32 @@ def atexit_register(func, *args, **kwargs):
atexit.register(_atexit_call, func, *args, **kwargs)
def parse_loose_version(version_string):
"""Parses a version string into its components.
This code and the returned tuple is based on the now deprecated
distutils.version.LooseVersion class from the Python standard library.
Two LooseVersion classes and two lists as returned by this function should
compare in the same way. See
https://github.com/python/cpython/blob/v3.10.0/Lib/distutils/version.py#L205-L347.
:param str version_string: version string
:returns: list of parsed version string components
:rtype: list
"""
components: List[Union[int, str]]
components = [x for x in _VERSION_COMPONENT_RE.split(version_string)
if x and x != '.']
for i, obj in enumerate(components):
try:
components[i] = int(obj)
except ValueError:
pass
return components
def _atexit_call(func, *args, **kwargs):
if _INITIAL_PID == os.getpid():
func(*args, **kwargs)

View File

@@ -1,9 +1,9 @@
import codecs
from distutils.version import LooseVersion
import os
import re
import sys
from pkg_resources import parse_version
from setuptools import __version__ as setuptools_version
from setuptools import find_packages
from setuptools import setup
@@ -11,7 +11,7 @@ from setuptools import setup
min_setuptools_version='39.0.1'
# This conditional isn't necessary, but it provides better error messages to
# people who try to install this package with older versions of setuptools.
if LooseVersion(setuptools_version) < LooseVersion(min_setuptools_version):
if parse_version(setuptools_version) < parse_version(min_setuptools_version):
raise RuntimeError(f'setuptools {min_setuptools_version}+ is required')
# Workaround for https://bugs.python.org/issue8876, see
@@ -132,6 +132,7 @@ setup(
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Security',
'Topic :: System :: Installation/Setup',

View File

@@ -592,6 +592,19 @@ class OsInfoTest(unittest.TestCase):
self.assertEqual(cbutil.get_python_os_info(), ("testdist", "42"))
class GetStrictVersionTest(unittest.TestCase):
"""Test for certbot.util.get_strict_version."""
@classmethod
def _call(cls, *args, **kwargs):
from certbot.util import get_strict_version
return get_strict_version(*args, **kwargs)
def test_it(self):
with self.assertWarnsRegex(DeprecationWarning, "get_strict_version"):
self._call("1.2.3")
class AtexitRegisterTest(unittest.TestCase):
"""Tests for certbot.util.atexit_register."""
def setUp(self):
@@ -624,5 +637,38 @@ class AtexitRegisterTest(unittest.TestCase):
atexit_func(*args[1:], **kwargs)
class ParseLooseVersionTest(unittest.TestCase):
"""Test for certbot.util.parse_loose_version.
These tests are based on the original tests for
distutils.version.LooseVersion at
https://github.com/python/cpython/blob/v3.10.0/Lib/distutils/tests/test_version.py#L58-L81.
"""
@classmethod
def _call(cls, *args, **kwargs):
from certbot.util import parse_loose_version
return parse_loose_version(*args, **kwargs)
def test_less_than(self):
comparisons = (('1.5.1', '1.5.2b2'),
('3.4j', '1996.07.12'),
('2g6', '11g'),
('0.960923', '2.2beta29'),
('1.13++', '5.5.kw'))
for v1, v2 in comparisons:
self.assertLess(self._call(v1), self._call(v2))
def test_equal(self):
self.assertEqual(self._call('8.02'), self._call('8.02'))
def test_greater_than(self):
comparisons = (('161', '3.10a'),
('3.2.pl0', '3.1.1.6'))
for v1, v2 in comparisons:
self.assertGreater(self._call(v1), self._call(v2))
if __name__ == "__main__":
unittest.main() # pragma: no cover

View File

@@ -1,8 +1,9 @@
#!/usr/bin/env python
# Test script for OpenSSL version checking
from distutils.version import LooseVersion
import sys
from certbot import util
def main(openssl_version, apache_version):
if not openssl_version.strip():
@@ -12,8 +13,8 @@ def main(openssl_version, apache_version):
conf_file_location = "/etc/letsencrypt/options-ssl-apache.conf"
with open(conf_file_location) as f:
contents = f.read()
if LooseVersion(apache_version.strip()) < LooseVersion('2.4.11') or \
LooseVersion(openssl_version.strip()) < LooseVersion('1.0.2l'):
if util.parse_loose_version(apache_version.strip()) < util.parse_loose_version('2.4.11') or \
util.parse_loose_version(openssl_version.strip()) < util.parse_loose_version('1.0.2l'):
# should be old version
# assert SSLSessionTickets not in conf file
if "SSLSessionTickets" in contents:

View File

@@ -20,6 +20,7 @@ setup(
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Security',
],

View File

@@ -22,6 +22,11 @@
# the certbot.display.util module.
# 5) A deprecation warning is raised in dnspython==1.15.0 in the oldest tests for
# certbot-dns-rfc2136.
# 6) The vendored version of six in botocore causes ImportWarnings in Python
# 3.10+. See https://github.com/boto/botocore/issues/2548.
# 7) botocore's default TLS settings raise deprecation warnings in Python
# 3.10+, but their values are sane from a security perspective. See
# https://github.com/boto/botocore/issues/2550.
filterwarnings =
error
ignore:The external mock module:PendingDeprecationWarning
@@ -29,3 +34,5 @@ filterwarnings =
ignore:.*attribute in certbot.interfaces module is deprecated:DeprecationWarning
ignore:.*attribute in certbot.display.util module is deprecated:DeprecationWarning
ignore:decodestring\(\) is a deprecated alias:DeprecationWarning:dns
ignore:_SixMetaPathImporter.:ImportWarning
ignore:ssl.PROTOCOL_TLS:DeprecationWarning:botocore

View File

@@ -14,7 +14,6 @@ variable VENV_NAME.
from __future__ import print_function
from distutils.version import LooseVersion
import glob
import os
import re
@@ -200,31 +199,9 @@ def install_packages(venv_name, pip_args):
# Using the python executable from venv, we ensure to execute following commands in this venv.
py_venv = get_venv_python_path(venv_name)
subprocess_with_print([py_venv, os.path.abspath('tools/pipstrap.py')])
# We only use this value during pip install because:
# 1) We're really only adding it for installing cryptography, which happens here, and
# 2) There are issues with calling it along with VIRTUALENV_NO_DOWNLOAD, which applies at the
# steps above, not during pip install.
env_pip_no_binary = os.environ.get('CERTBOT_PIP_NO_BINARY')
if env_pip_no_binary:
# Check OpenSSL version. If it's too low, don't apply the env variable.
openssl_version_string = str(subprocess_output_with_print(['openssl', 'version']))
matches = re.findall(r'OpenSSL ([^ ]+) ', openssl_version_string)
if not matches:
print('Could not find OpenSSL version, not setting PIP_NO_BINARY.')
else:
openssl_version = matches[0]
if LooseVersion(openssl_version) >= LooseVersion('1.0.2'):
print('Setting PIP_NO_BINARY to {0}'
' as specified in CERTBOT_PIP_NO_BINARY'.format(env_pip_no_binary))
os.environ['PIP_NO_BINARY'] = env_pip_no_binary
else:
print('Not setting PIP_NO_BINARY, as OpenSSL version is too old.')
command = [py_venv, os.path.abspath('tools/pip_install.py')]
command.extend(pip_args)
subprocess_with_print(command)
if 'PIP_NO_BINARY' in os.environ:
del os.environ['PIP_NO_BINARY']
if os.path.isdir(os.path.join(venv_name, 'bin')):
# Linux/OSX specific

View File

@@ -22,6 +22,7 @@ setup(
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Operating System :: Microsoft :: Windows',
'Topic :: Software Development :: Build Tools',
],