Skip to content

Add support for Python3.10 on testing Docker image #8328

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Aug 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 14 additions & 9 deletions docs/config-file/v2.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Below is an example YAML file which shows the most common configuration options:

# Optionally set the version of Python and requirements required to build your docs
python:
version: 3.7
version: "3.7"
install:
- requirements: docs/requirements.txt

Expand All @@ -51,7 +51,7 @@ Below is an example YAML file which shows the most common configuration options:

# Optionally set the version of Python and requirements required to build your docs
python:
version: 3.7
version: "3.7"
install:
- requirements: docs/requirements.txt

Expand Down Expand Up @@ -138,7 +138,7 @@ Configuration of the Python environment to be used.
version: 2

python:
version: 3.7
version: "3.7"
install:
- requirements: docs/requirements.txt
- method: pip
Expand All @@ -154,13 +154,18 @@ python.version

The Python version (this depends on :ref:`config-file/v2:build.image`).

:Type: ``number``
:Type: ``string``
:Default: ``3``

.. note::

Make sure to use quotes (``"``) to make it a string.
We previously supported using numbers here,
but that approach is deprecated.
.. warning::

If you are using a :ref:`Conda <config-file/v2:conda>` environment to manage
the build, this setting will not have any effect, as the Python version is managed by Conda.
If you are using a :ref:`Conda <config-file/v2:conda>` environment to manage
the build, this setting will not have any effect, as the Python version is managed by Conda.

python.install
``````````````
Expand Down Expand Up @@ -189,7 +194,7 @@ Example:
version: 2

python:
version: 3.7
version: "3.7"
install:
- requirements: docs/requirements.txt
- requirements: requirements.txt
Expand Down Expand Up @@ -237,7 +242,7 @@ Example:
version: 2

python:
version: 3.7
version: "3.7"
install:
- method: pip
path: .
Expand Down Expand Up @@ -308,7 +313,7 @@ Configuration for the documentation build process.
- cmake

python:
version: 3.7
version: "3.7"


build.image
Expand Down
49 changes: 18 additions & 31 deletions readthedocs/config/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,23 +254,22 @@ def validate(self):

@property
def python_interpreter(self):
ver = self.python_full_version
if not ver or isinstance(ver, (int, float)):
return 'python{}'.format(ver)

# Allow to specify ``pypy3.5`` as Python interpreter
return ver
version = self.python_full_version
if version.startswith('pypy'):
# Allow to specify ``pypy3.5`` as Python interpreter
return version
return f'python{version}'

@property
def python_full_version(self):
ver = self.python.version
if ver in [2, 3]:
version = self.python.version
if version in ['2', '3']:
# use default Python version if user only set '2', or '3'
return self.get_default_python_version_for_image(
self.build.image,
ver,
version,
)
return ver
return version

@property
def valid_build_images(self):
Expand Down Expand Up @@ -454,7 +453,7 @@ def validate_python(self):
"""Validates the ``python`` key, set default values it's necessary."""
install_project = self.defaults.get('install_project', False)
use_system_packages = self.defaults.get('use_system_packages', False)
version = self.defaults.get('python_version', 2)
version = self.defaults.get('python_version', '2')
python = {
'use_system_site_packages': use_system_packages,
'install_with_pip': False,
Expand Down Expand Up @@ -513,17 +512,7 @@ def validate_python(self):

if 'version' in raw_python:
with self.catch_validation_error('python.version'):
# Try to convert strings to an int first, to catch '2', then
# a float, to catch '2.7'
version = raw_python['version']
if isinstance(version, str):
try:
version = int(version)
except ValueError:
try:
version = float(version)
except ValueError:
pass
version = str(raw_python['version'])
python['version'] = validate_choice(
version,
self.get_valid_python_versions(),
Expand Down Expand Up @@ -839,15 +828,13 @@ def validate_python(self):

python = {}
with self.catch_validation_error('python.version'):
version = self.pop_config('python.version', 3)
if isinstance(version, str):
try:
version = int(version)
except ValueError:
try:
version = float(version)
except ValueError:
pass
version = self.pop_config('python.version', '3')
if version == 3.1:
# Special case for ``python.version: 3.10``,
# yaml will transform this to the numeric value of `3.1`.
# Save some frustration to users.
version = '3.10'
version = str(version)
python['version'] = validate_choice(
version,
self.get_valid_python_versions(),
Expand Down
101 changes: 54 additions & 47 deletions readthedocs/config/tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -341,18 +341,18 @@ class TestValidatePythonVersion:
def test_it_defaults_to_a_valid_version(self):
build = get_build_config({'python': {}})
build.validate()
assert build.python.version == 2
assert build.python.version == '2'
assert build.python_interpreter == 'python2.7'
assert build.python_full_version == 2.7
assert build.python_full_version == '2.7'

def test_it_supports_other_versions(self):
build = get_build_config(
{'python': {'version': 3.7}},
)
build.validate()
assert build.python.version == 3.7
assert build.python.version == '3.7'
assert build.python_interpreter == 'python3.7'
assert build.python_full_version == 3.7
assert build.python_full_version == '3.7'

def test_it_supports_string_versions(self):
build = get_build_config(
Expand Down Expand Up @@ -381,28 +381,11 @@ def test_it_validates_wrong_type(self):
assert excinfo.value.key == 'python.version'
assert excinfo.value.code == INVALID_CHOICE

def test_it_validates_wrong_type_right_value(self):
build = get_build_config(
{'python': {'version': '3.6'}},
)
build.validate()
assert build.python.version == 3.6
assert build.python_interpreter == 'python3.6'
assert build.python_full_version == 3.6

build = get_build_config(
{'python': {'version': '3'}},
)
build.validate()
assert build.python.version == 3
assert build.python_interpreter == 'python3.7'
assert build.python_full_version == 3.7

def test_it_validates_env_supported_versions(self):
build = get_build_config(
{'python': {'version': 3.6}},
{'python': {'version': '3.6'}},
env_config={
'python': {'supported_versions': [3.5]},
'python': {'supported_versions': ['3.5']},
'build': {'image': 'custom'},
},
)
Expand All @@ -412,18 +395,18 @@ def test_it_validates_env_supported_versions(self):
assert excinfo.value.code == INVALID_CHOICE

build = get_build_config(
{'python': {'version': 3.6}},
{'python': {'version': '3.6'}},
env_config={
'python': {'supported_versions': [3.5, 3.6]},
'python': {'supported_versions': ['3.5', '3.6']},
'build': {'image': 'custom'},
},
)
build.validate()
assert build.python.version == 3.6
assert build.python.version == '3.6'
assert build.python_interpreter == 'python3.6'
assert build.python_full_version == 3.6
assert build.python_full_version == '3.6'

@pytest.mark.parametrize('value', [2, 3])
@pytest.mark.parametrize('value', ['2', '3'])
def test_it_respects_default_value(self, value):
defaults = {
'python_version': value,
Expand Down Expand Up @@ -741,7 +724,7 @@ def test_as_dict(tmpdir):
'version': '1',
'formats': ['pdf'],
'python': {
'version': 3.7,
'version': '3.7',
'install': [{
'requirements': 'requirements.txt',
}],
Expand Down Expand Up @@ -1003,8 +986,8 @@ def test_python_check_invalid_types(self, value):
@pytest.mark.parametrize(
'image,versions',
[
('latest', [2, 2.7, 3, 3.5, 3.6, 3.7, 'pypy3.5']),
('stable', [2, 2.7, 3, 3.5, 3.6, 3.7]),
('latest', ['2', '2.7', '3', '3.5', '3.6', '3.7', 'pypy3.5']),
('stable', ['2', '2.7', '3', '3.5', '3.6', '3.7']),
],
)
def test_python_version(self, image, versions):
Expand All @@ -1030,7 +1013,31 @@ def test_python_version_accepts_string(self):
},
})
build.validate()
assert build.python.version == 3.6
assert build.python.version == '3.6'

def test_python_version_accepts_number(self):
build = self.get_build_config({
'build': {
'image': 'latest',
},
'python': {
'version': 3.6,
},
})
build.validate()
assert build.python.version == '3.6'

def test_python_version_310_as_number(self):
build = self.get_build_config({
'build': {
'image': 'testing',
},
'python': {
'version': 3.10,
},
})
build.validate()
assert build.python.version == '3.10'

@pytest.mark.parametrize(
'image,versions',
Expand All @@ -1056,27 +1063,27 @@ def test_python_version_invalid(self, image, versions):
def test_python_version_default(self):
build = self.get_build_config({})
build.validate()
assert build.python.version == 3
assert build.python.version == '3'

@pytest.mark.parametrize(
'image,default_version',
'image, default_version, full_version',
[
('2.0', 3.5),
('4.0', 3.7),
('5.0', 3.7),
('latest', 3.7),
('stable', 3.7),
('2.0', '3', '3.5'),
('4.0', '3', '3.7'),
('5.0', '3', '3.7'),
('latest', '3', '3.7'),
('stable', '3', '3.7'),
],
)
def test_python_version_default_from_image(self, image, default_version):
def test_python_version_default_from_image(self, image, default_version, full_version):
build = self.get_build_config({
'build': {
'image': image,
},
})
build.validate()
assert build.python.version == int(default_version) # 2 or 3
assert build.python_full_version == default_version
assert build.python.version == default_version
assert build.python_full_version == full_version

@pytest.mark.parametrize('value', [2, 3])
def test_python_version_overrides_default(self, value):
Expand All @@ -1085,13 +1092,13 @@ def test_python_version_overrides_default(self, value):
{'defaults': {'python_version': value}},
)
build.validate()
assert build.python.version == 3
assert build.python.version == '3'

@pytest.mark.parametrize('value', [2, 3, 3.6])
@pytest.mark.parametrize('value', ['2', '3', '3.6'])
def test_python_version_priority_over_default(self, value):
build = self.get_build_config(
{'python': {'version': value}},
{'defaults': {'python_version': 3}},
{'defaults': {'python_version': '3'}},
)
build.validate()
assert build.python.version == value
Expand Down Expand Up @@ -2109,7 +2116,7 @@ def test_as_dict(self, tmpdir):
'version': 2,
'formats': ['pdf'],
'python': {
'version': 3.6,
'version': '3.6',
'install': [{
'requirements': 'requirements.txt',
}],
Expand All @@ -2122,7 +2129,7 @@ def test_as_dict(self, tmpdir):
'version': '2',
'formats': ['pdf'],
'python': {
'version': 3.6,
'version': '3.6',
'install': [{
'requirements': 'requirements.txt',
}],
Expand Down
2 changes: 1 addition & 1 deletion readthedocs/doc_builder/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def load_yaml_config(version):
# can be rejected at validation

img_name = project.container_image or DOCKER_IMAGE
python_version = 3 if project.python_interpreter == 'python3' else 2
python_version = '3' if project.python_interpreter == 'python3' else '2'
try:
sphinx_configuration = path.join(
version.get_conf_py_path(),
Expand Down
Loading