Skip to content

Commit bfa6bdb

Browse files
stsewdagjohnson
authored andcommitted
Implement submodules key from v2 config (#4493)
* Test conf.py default value * Break submodules operation * Use the config object to update submodules * Introduce a dummy object in tests * Linter * Add test for submodules v2 * Split call * Fix env * Fix boolean operator * Set v1 compatible with submodules key * Fix tests * Whitespace * Unskip tests * Fix merge * Test correct defaults * Fix test * Docstrings * One more test
1 parent 5ffb6bc commit bfa6bdb

File tree

9 files changed

+291
-74
lines changed

9 files changed

+291
-74
lines changed

readthedocs/config/config.py

+15-2
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,14 @@ def mkdocs(self):
549549
fail_on_warning=False,
550550
)
551551

552+
@property
553+
def submodules(self):
554+
return Submodules(
555+
include=ALL,
556+
exclude=[],
557+
recursive=True,
558+
)
559+
552560

553561
class BuildConfigV2(BuildConfigBase):
554562

@@ -871,7 +879,8 @@ def validate_submodules(self):
871879
submodules['include'] = include
872880

873881
with self.catch_validation_error('submodules.exclude'):
874-
exclude = raw_submodules.get('exclude', [])
882+
default = [] if submodules['include'] else ALL
883+
exclude = raw_submodules.get('exclude', default)
875884
if exclude != ALL:
876885
exclude = [
877886
validate_string(submodule)
@@ -880,7 +889,11 @@ def validate_submodules(self):
880889
submodules['exclude'] = exclude
881890

882891
with self.catch_validation_error('submodules'):
883-
if submodules['exclude'] and submodules['include']:
892+
is_including = bool(submodules['include'])
893+
is_excluding = (
894+
submodules['exclude'] == ALL or bool(submodules['exclude'])
895+
)
896+
if is_including and is_excluding:
884897
self.error(
885898
'submodules',
886899
'You can not exclude and include submodules '

readthedocs/config/tests/test_config.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -1552,6 +1552,13 @@ def test_validates_different_filetype_sphinx(self):
15521552
assert 'configured as "Sphinx HtmlDir"' in str(excinfo.value)
15531553
assert 'your "sphinx.builder" key does not match' in str(excinfo.value)
15541554

1555+
def test_submodule_defaults(self):
1556+
build = self.get_build_config({})
1557+
build.validate()
1558+
assert build.submodules.include == []
1559+
assert build.submodules.exclude == ALL
1560+
assert build.submodules.recursive is False
1561+
15551562
@pytest.mark.parametrize('value', [[], 'invalid', 0])
15561563
def test_submodules_check_invalid_type(self, value):
15571564
build = self.get_build_config({'submodules': value})
@@ -1682,7 +1689,7 @@ def test_submodules_recursive_explict_default(self):
16821689
})
16831690
build.validate()
16841691
assert build.submodules.include == []
1685-
assert build.submodules.exclude == []
1692+
assert build.submodules.exclude == ALL
16861693
assert build.submodules.recursive is False
16871694

16881695
build = self.get_build_config({

readthedocs/projects/tasks.py

+24-7
Original file line numberDiff line numberDiff line change
@@ -115,13 +115,7 @@ def sync_repo(self):
115115
identifier=self.version.identifier,
116116
),
117117
)
118-
version_repo = self.project.vcs_repo(
119-
self.version.slug,
120-
# When called from ``SyncRepositoryTask.run`` we don't have
121-
# a ``setup_env`` so we use just ``None`` and commands won't
122-
# be recorded
123-
getattr(self, 'setup_env', None),
124-
)
118+
version_repo = self.get_vcs_repo()
125119
version_repo.checkout(self.version.identifier)
126120
finally:
127121
after_vcs.send(sender=self.version)
@@ -432,6 +426,8 @@ def run_setup(self, record=True):
432426
exception=str(e),
433427
))
434428

429+
self.additional_vcs_operations()
430+
435431
if self.setup_env.failure or self.config is None:
436432
self._log('Failing build because of setup failure: %s' % self.setup_env.failure)
437433

@@ -448,6 +444,27 @@ def run_setup(self, record=True):
448444

449445
return True
450446

447+
def additional_vcs_operations(self):
448+
"""
449+
Execution of tasks that involve the project's VCS.
450+
451+
All this tasks have access to the configuration object.
452+
"""
453+
version_repo = self.get_vcs_repo()
454+
if version_repo.supports_submodules:
455+
version_repo.update_submodules(self.config)
456+
457+
def get_vcs_repo(self):
458+
"""Get the VCS object of the current project."""
459+
version_repo = self.project.vcs_repo(
460+
self.version.slug,
461+
# When called from ``SyncRepositoryTask.run`` we don't have
462+
# a ``setup_env`` so we use just ``None`` and commands won't
463+
# be recorded
464+
getattr(self, 'setup_env', None),
465+
)
466+
return version_repo
467+
451468
def run_build(self, docker, record):
452469
"""
453470
Build the docs in an environment.

readthedocs/rtd_tests/tests/test_backend.py

+21-11
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
11
# -*- coding: utf-8 -*-
22

3-
from __future__ import absolute_import, unicode_literals
3+
from __future__ import (
4+
absolute_import, division, print_function, unicode_literals)
5+
46
from os.path import exists
57

8+
import django_dynamic_fixture as fixture
69
import pytest
710
from django.contrib.auth.models import User
8-
import django_dynamic_fixture as fixture
11+
from mock import Mock
912

13+
from readthedocs.config import ALL
1014
from readthedocs.projects.exceptions import RepositoryError
11-
from readthedocs.projects.models import Project, Feature
15+
from readthedocs.projects.models import Feature, Project
1216
from readthedocs.rtd_tests.base import RTDTestCase
13-
14-
from readthedocs.rtd_tests.utils import create_git_tag, make_test_git, make_test_hg
17+
from readthedocs.rtd_tests.utils import (
18+
create_git_tag, make_test_git, make_test_hg)
1519

1620

1721
class TestGitBackend(RTDTestCase):
@@ -28,6 +32,10 @@ def setUp(self):
2832
repo=git_repo
2933
)
3034
self.project.users.add(self.eric)
35+
self.dummy_conf = Mock()
36+
# These are the default values from v1
37+
self.dummy_conf.submodules.include = ALL
38+
self.dummy_conf.submodules.exclude = []
3139

3240
def test_parse_branches(self):
3341
data = """
@@ -77,30 +85,32 @@ def test_check_for_submodules(self):
7785
repo = self.project.vcs_repo()
7886

7987
repo.checkout()
80-
self.assertFalse(repo.are_submodules_available())
88+
self.assertFalse(repo.are_submodules_available(self.dummy_conf))
8189

8290
# The submodule branch contains one submodule
8391
repo.checkout('submodule')
84-
self.assertTrue(repo.are_submodules_available())
92+
self.assertTrue(repo.are_submodules_available(self.dummy_conf))
8593

8694
def test_skip_submodule_checkout(self):
8795
repo = self.project.vcs_repo()
8896
repo.checkout('submodule')
89-
self.assertTrue(repo.are_submodules_available())
97+
self.assertTrue(repo.are_submodules_available(self.dummy_conf))
9098
feature = fixture.get(
9199
Feature,
92100
projects=[self.project],
93101
feature_id=Feature.SKIP_SUBMODULES,
94102
)
95103
self.assertTrue(self.project.has_feature(Feature.SKIP_SUBMODULES))
96-
self.assertFalse(repo.are_submodules_available())
104+
self.assertFalse(repo.are_submodules_available(self.dummy_conf))
97105

98106
def test_check_submodule_urls(self):
99107
repo = self.project.vcs_repo()
100108
repo.checkout('submodule')
101-
self.assertTrue(repo.are_submodules_valid())
109+
valid, _ = repo.validate_submodules(self.dummy_conf)
110+
self.assertTrue(valid)
102111
repo.checkout('relativesubmodule')
103-
self.assertTrue(repo.are_submodules_valid())
112+
valid, _ = repo.validate_submodules(self.dummy_conf)
113+
self.assertTrue(valid)
104114

105115
@pytest.mark.xfail(strict=True, reason="Fixture is not working correctly")
106116
def test_check_invalid_submodule_urls(self):

readthedocs/rtd_tests/tests/test_config_integration.py

+103-1
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,14 @@
1313
from mock import MagicMock, PropertyMock, patch
1414

1515
from readthedocs.builds.models import Version
16-
from readthedocs.config import BuildConfigV1, InvalidConfig, ProjectConfig
16+
from readthedocs.config import ALL, BuildConfigV1, InvalidConfig, ProjectConfig
1717
from readthedocs.config.tests.utils import apply_fs
1818
from readthedocs.doc_builder.config import load_yaml_config
1919
from readthedocs.doc_builder.environments import LocalBuildEnvironment
2020
from readthedocs.doc_builder.python_environments import Conda, Virtualenv
2121
from readthedocs.projects import tasks
2222
from readthedocs.projects.models import Feature, Project
23+
from readthedocs.rtd_tests.utils import create_git_submodule, make_git_repo
2324

2425

2526
def create_load(config=None):
@@ -889,3 +890,104 @@ def test_mkdocs_fail_on_warning(
889890
assert '--strict' in args
890891
append_conf.assert_called_once()
891892
move.assert_called_once()
893+
894+
@pytest.mark.parametrize('value,expected', [(ALL, ['one', 'two', 'three']),
895+
(['one', 'two'], ['one', 'two'])])
896+
@patch('readthedocs.vcs_support.backends.git.Backend.checkout_submodules')
897+
def test_submodules_include(self, checkout_submodules,
898+
checkout_path, tmpdir, value, expected):
899+
checkout_path.return_value = str(tmpdir)
900+
self.create_config_file(
901+
tmpdir,
902+
{
903+
'submodules': {
904+
'include': value,
905+
},
906+
}
907+
)
908+
909+
git_repo = make_git_repo(str(tmpdir))
910+
create_git_submodule(git_repo, 'one')
911+
create_git_submodule(git_repo, 'two')
912+
create_git_submodule(git_repo, 'three')
913+
914+
update_docs = self.get_update_docs_task()
915+
checkout_path.return_value = git_repo
916+
update_docs.additional_vcs_operations()
917+
918+
args, kwargs = checkout_submodules.call_args
919+
assert set(args[0]) == set(expected)
920+
assert update_docs.config.submodules.recursive is False
921+
922+
@patch('readthedocs.vcs_support.backends.git.Backend.checkout_submodules')
923+
def test_submodules_exclude(self, checkout_submodules,
924+
checkout_path, tmpdir):
925+
checkout_path.return_value = str(tmpdir)
926+
self.create_config_file(
927+
tmpdir,
928+
{
929+
'submodules': {
930+
'exclude': ['one'],
931+
'recursive': True,
932+
},
933+
}
934+
)
935+
936+
git_repo = make_git_repo(str(tmpdir))
937+
create_git_submodule(git_repo, 'one')
938+
create_git_submodule(git_repo, 'two')
939+
create_git_submodule(git_repo, 'three')
940+
941+
update_docs = self.get_update_docs_task()
942+
checkout_path.return_value = git_repo
943+
update_docs.additional_vcs_operations()
944+
945+
args, kwargs = checkout_submodules.call_args
946+
assert set(args[0]) == {'two', 'three'}
947+
assert update_docs.config.submodules.recursive is True
948+
949+
@patch('readthedocs.vcs_support.backends.git.Backend.checkout_submodules')
950+
def test_submodules_exclude_all(self, checkout_submodules,
951+
checkout_path, tmpdir):
952+
checkout_path.return_value = str(tmpdir)
953+
self.create_config_file(
954+
tmpdir,
955+
{
956+
'submodules': {
957+
'exclude': ALL,
958+
'recursive': True,
959+
},
960+
}
961+
)
962+
963+
git_repo = make_git_repo(str(tmpdir))
964+
create_git_submodule(git_repo, 'one')
965+
create_git_submodule(git_repo, 'two')
966+
create_git_submodule(git_repo, 'three')
967+
968+
update_docs = self.get_update_docs_task()
969+
checkout_path.return_value = git_repo
970+
update_docs.additional_vcs_operations()
971+
972+
checkout_submodules.assert_not_called()
973+
974+
@patch('readthedocs.vcs_support.backends.git.Backend.checkout_submodules')
975+
def test_submodules_default_exclude_all(self, checkout_submodules,
976+
checkout_path, tmpdir):
977+
978+
checkout_path.return_value = str(tmpdir)
979+
self.create_config_file(
980+
tmpdir,
981+
{}
982+
)
983+
984+
git_repo = make_git_repo(str(tmpdir))
985+
create_git_submodule(git_repo, 'one')
986+
create_git_submodule(git_repo, 'two')
987+
create_git_submodule(git_repo, 'three')
988+
989+
update_docs = self.get_update_docs_task()
990+
checkout_path.return_value = git_repo
991+
update_docs.additional_vcs_operations()
992+
993+
checkout_submodules.assert_not_called()

readthedocs/rtd_tests/tests/test_doc_builder.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def test_create_conf_py(
6363
having a ``TypeError`` because of an encoding problem in Python3)
6464
"""
6565
tmp_dir = tempfile.mkdtemp()
66-
checkout_path.return_value = tmp_dir
66+
checkout_path.return_value = tmp_dir
6767
docs_dir.return_value = tmp_dir
6868
create_index.return_value = 'README.rst'
6969
get_config_params.return_value = {}
@@ -191,7 +191,6 @@ def test_get_theme_name(self, checkout_path):
191191
}
192192
self.assertEqual(builder.get_theme_name(config), 'mydir')
193193

194-
195194
@patch('readthedocs.doc_builder.base.BaseBuilder.run')
196195
@patch('readthedocs.projects.models.Project.checkout_path')
197196
def test_append_conf_create_yaml(self, checkout_path, run):

0 commit comments

Comments
 (0)