Skip to content

Commit dced32b

Browse files
authored
Pass build env python limits to config object (#2627)
* Pass build env python limits to config object * Pass in build env python limitations to config * Exposes parsing error to user on build output * Typo on config method * Alter build image names * Bump release of readthedocs-build * Don't test BuildConfig, test our implementation Tunes up tests, fixes a bug with logic on python version detection * Fix tests that were depending on config wrapper tests
1 parent 5b5ffb5 commit dced32b

File tree

7 files changed

+237
-104
lines changed

7 files changed

+237
-104
lines changed

readthedocs/doc_builder/config.py

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
load as load_config)
33

44

5+
from .constants import BUILD_IMAGES, DOCKER_IMAGE
56
from readthedocs.projects.exceptions import ProjectImportError
67

78

@@ -49,25 +50,27 @@ def extra_requirements(self):
4950

5051
@property
5152
def python_interpreter(self):
52-
if 'version' in self._yaml_config.get('python', {}):
53-
ver = self._yaml_config['python']['version']
54-
if str(ver).startswith('2'):
55-
return 'python'
56-
else:
57-
return 'python3'
58-
else:
59-
return self._project.python_interpreter
53+
ver = self.python_version
54+
if ver in [2, 3]:
55+
# Get the highest version of the major series version if user only
56+
# gave us a version of '2', or '3'
57+
ver = max(filter(
58+
lambda x: x < ver + 1,
59+
self._yaml_config.get_valid_python_versions(),
60+
))
61+
return 'python{0}'.format(ver)
6062

6163
@property
6264
def python_version(self):
65+
# There should always be a version in the YAML config. If the config
66+
# version is the default response of `2`, then assume we can use the
67+
# Python.python_interpreter version to infer this value instead.
68+
version = 2
6369
if 'version' in self._yaml_config.get('python', {}):
64-
ver = self._yaml_config['python']['version']
65-
return ver
66-
else:
67-
if self._project.python_interpreter == 'python':
68-
return 2
69-
else:
70-
return 3
70+
version = self._yaml_config['python']['version']
71+
if version == 2 and self._project.python_interpreter == 'python3':
72+
version = 3
73+
return version
7174

7275
@property
7376
def use_system_site_packages(self):
@@ -125,20 +128,36 @@ def load_yaml_config(version):
125128
"""
126129

127130
checkout_path = version.project.checkout_path(version.slug)
131+
env_config = {}
132+
133+
# Get build image to set up the python version validation. Pass in the
134+
# build image python limitations to the loaded config so that the versions
135+
# can be rejected at validation
136+
build_image = BUILD_IMAGES.get(
137+
version.project.container_image,
138+
BUILD_IMAGES.get(DOCKER_IMAGE, None),
139+
)
140+
if build_image:
141+
env_config = {
142+
'python': build_image['python'],
143+
}
144+
128145
try:
146+
sphinx_env_config = env_config.copy()
147+
sphinx_env_config.update({
148+
'output_base': '',
149+
'type': 'sphinx',
150+
'name': version.slug,
151+
})
129152
config = load_config(
130153
path=checkout_path,
131-
env_config={
132-
'output_base': '',
133-
'type': 'sphinx',
134-
'name': version.slug,
135-
},
154+
env_config=sphinx_env_config,
136155
)[0]
137156
except InvalidConfig: # This is a subclass of ConfigError, so has to come first
138157
raise
139158
except ConfigError:
140159
config = BuildConfig(
141-
env_config={},
160+
env_config=env_config,
142161
raw_config={},
143162
source_file='empty',
144163
source_position=0,

readthedocs/doc_builder/constants.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,27 @@
1515

1616
PDF_RE = re.compile('Output written on (.*?)')
1717

18+
# Docker
1819
DOCKER_SOCKET = getattr(settings, 'DOCKER_SOCKET', 'unix:///var/run/docker.sock')
1920
DOCKER_VERSION = getattr(settings, 'DOCKER_VERSION', 'auto')
20-
DOCKER_IMAGE = getattr(settings, 'DOCKER_IMAGE', 'rtfd-build')
21+
DOCKER_IMAGE = getattr(settings, 'DOCKER_IMAGE', 'readthedocs/build:2.0')
2122
DOCKER_LIMITS = {'memory': '200m', 'time': 600}
2223
DOCKER_LIMITS.update(getattr(settings, 'DOCKER_LIMITS', {}))
2324

2425
DOCKER_TIMEOUT_EXIT_CODE = 42
2526
DOCKER_OOM_EXIT_CODE = 137
2627

2728
DOCKER_HOSTNAME_MAX_LEN = 64
29+
30+
# Build images
31+
BUILD_IMAGES = {
32+
'readthedocs/build:1.0': {
33+
'python': {'supported_versions': [2, 2.7, 3, 3.3]},
34+
},
35+
'readthedocs/build:2.0': {
36+
'python': {'supported_versions': [2, 2.7, 3, 3.5]},
37+
},
38+
'readthedocs/build:latest': {
39+
'python': {'supported_versions': [2, 2.7, 3, 3.3, 3.4, 3.5, 3.6]},
40+
},
41+
}

readthedocs/projects/tasks.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from django.conf import settings
1919
from django.core.urlresolvers import reverse
2020
from django.utils.translation import ugettext_lazy as _
21+
from readthedocs_build.config import ConfigError
2122

2223
from readthedocs.builds.constants import (LATEST,
2324
BUILD_STATE_CLONING,
@@ -132,7 +133,12 @@ def run(self, pk, version_pk=None, build_pk=None, record=True, docker=False,
132133
status_code=423
133134
)
134135

135-
self.config = load_yaml_config(version=self.version)
136+
try:
137+
self.config = load_yaml_config(version=self.version)
138+
except ConfigError as e:
139+
raise BuildEnvironmentError(
140+
'Problem parsing YAML configuration. {0}'.format(str(e))
141+
)
136142

137143
if self.setup_env.failure or self.config is None:
138144
self._log('Failing build because of setup failure: %s' % self.setup_env.failure)

readthedocs/rtd_tests/tests/test_builds.py

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from readthedocs.doc_builder.python_environments import Virtualenv
1111
from readthedocs.doc_builder.loader import get_builder_class
1212
from readthedocs.projects.tasks import UpdateDocsTask
13-
from readthedocs.rtd_tests.tests.test_config_wrapper import get_build_config
13+
from readthedocs.rtd_tests.tests.test_config_wrapper import create_load
1414

1515
from ..mocks.environment import EnvironmentMockGroup
1616

@@ -40,8 +40,7 @@ def test_build(self):
4040

4141
build_env = LocalEnvironment(project=project, version=version, build={})
4242
python_env = Virtualenv(version=version, build_env=build_env)
43-
yaml_config = get_build_config({})
44-
config = ConfigWrapper(version=version, yaml_config=yaml_config)
43+
config = ConfigWrapper(version=version, yaml_config=create_load()()[0])
4544
task = UpdateDocsTask(build_env=build_env, project=project, python_env=python_env,
4645
version=version, search=False, localmedia=False, config=config)
4746
task.build_docs()
@@ -65,8 +64,7 @@ def test_build_respects_pdf_flag(self):
6564

6665
build_env = LocalEnvironment(project=project, version=version, build={})
6766
python_env = Virtualenv(version=version, build_env=build_env)
68-
yaml_config = get_build_config({})
69-
config = ConfigWrapper(version=version, yaml_config=yaml_config)
67+
config = ConfigWrapper(version=version, yaml_config=create_load()()[0])
7068
task = UpdateDocsTask(build_env=build_env, project=project, python_env=python_env,
7169
version=version, search=False, localmedia=False, config=config)
7270

@@ -91,8 +89,7 @@ def test_build_respects_epub_flag(self):
9189

9290
build_env = LocalEnvironment(project=project, version=version, build={})
9391
python_env = Virtualenv(version=version, build_env=build_env)
94-
yaml_config = get_build_config({})
95-
config = ConfigWrapper(version=version, yaml_config=yaml_config)
92+
config = ConfigWrapper(version=version, yaml_config=create_load()()[0])
9693
task = UpdateDocsTask(build_env=build_env, project=project, python_env=python_env,
9794
version=version, search=False, localmedia=False, config=config)
9895
task.build_docs()
@@ -116,8 +113,9 @@ def test_build_respects_yaml(self):
116113

117114
build_env = LocalEnvironment(project=project, version=version, build={})
118115
python_env = Virtualenv(version=version, build_env=build_env)
119-
yaml_config = get_build_config({'formats': ['epub']})
120-
config = ConfigWrapper(version=version, yaml_config=yaml_config)
116+
config = ConfigWrapper(version=version, yaml_config=create_load({
117+
'formats': ['epub']
118+
})()[0])
121119
task = UpdateDocsTask(build_env=build_env, project=project, python_env=python_env,
122120
version=version, search=False, localmedia=False, config=config)
123121
task.build_docs()
@@ -171,8 +169,7 @@ def test_build_pdf_latex_failures(self):
171169

172170
build_env = LocalEnvironment(project=project, version=version, build={})
173171
python_env = Virtualenv(version=version, build_env=build_env)
174-
yaml_config = get_build_config({})
175-
config = ConfigWrapper(version=version, yaml_config=yaml_config)
172+
config = ConfigWrapper(version=version, yaml_config=create_load()()[0])
176173
task = UpdateDocsTask(build_env=build_env, project=project, python_env=python_env,
177174
version=version, search=False, localmedia=False, config=config)
178175

@@ -213,8 +210,7 @@ def test_build_pdf_latex_not_failure(self):
213210

214211
build_env = LocalEnvironment(project=project, version=version, build={})
215212
python_env = Virtualenv(version=version, build_env=build_env)
216-
yaml_config = get_build_config({})
217-
config = ConfigWrapper(version=version, yaml_config=yaml_config)
213+
config = ConfigWrapper(version=version, yaml_config=create_load()()[0])
218214
task = UpdateDocsTask(build_env=build_env, project=project, python_env=python_env,
219215
version=version, search=False, localmedia=False, config=config)
220216

0 commit comments

Comments
 (0)