-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
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
Implement sphinx key from v2 config #4482
Changes from 19 commits
f7dc3bd
09ea0dc
0648f65
e9db42c
7fe49ee
954e277
292f912
19810f4
fb48f7c
1083999
d35c23c
58adc35
819a9fa
e6b9dd5
db40aa7
b4c22dd
6ee2d66
4750efd
aa25732
189726c
dc99eb8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -42,8 +42,12 @@ class BaseSphinx(BaseBuilder): | |
def __init__(self, *args, **kwargs): | ||
super(BaseSphinx, self).__init__(*args, **kwargs) | ||
try: | ||
config_file = self.config.sphinx.configuration | ||
config_dir = os.path.dirname(config_file) if config_file else None | ||
self.old_artifact_path = os.path.join( | ||
self.project.conf_dir(self.version.slug), self.sphinx_build_dir) | ||
config_dir or self.project.conf_dir(self.version.slug), | ||
self.sphinx_build_dir | ||
) | ||
except ProjectConfigurationError: | ||
docs_dir = self.docs_dir() | ||
self.old_artifact_path = os.path.join( | ||
|
@@ -69,7 +73,7 @@ def get_config_params(self): | |
# TODO this should be handled better in the theme | ||
conf_py_path = os.path.join( | ||
os.path.sep, | ||
self.version.get_conf_py_path(), | ||
self.config.sphinx.configuration or self.version.get_conf_py_path(), | ||
'', | ||
) | ||
remote_version = self.version.commit_name | ||
|
@@ -151,14 +155,15 @@ def get_config_params(self): | |
|
||
def append_conf(self, **__): | ||
"""Find or create a ``conf.py`` with a rendered ``doc_builder/conf.py.tmpl`` appended""" | ||
try: | ||
self.version.get_conf_py_path() | ||
except ProjectConfigurationError: | ||
if self.config.sphinx.configuration is None: | ||
master_doc = self.create_index(extension='rst') | ||
self._write_config(master_doc=master_doc) | ||
|
||
try: | ||
outfile_path = self.project.conf_file(self.version.slug) | ||
outfile_path = ( | ||
self.config.sphinx.configuration or | ||
self.project.conf_file(self.version.slug) | ||
) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems here we're implying that if you set a sphinx configuration file, but it doesn't exist, we just create it? I feel like we should error out if you set up a config file and it doesn't actually exist, it will be confusing to the user for the build to pass in this case. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is to keep compatibility with the behavior when the user doesn't set a config file and rtd tries to find one. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That part I understand, but this seemed to be creating a 3rd option, where the config was specified but not found, and therefore the config was generated instead. This might not be an issue, I interpreted |
||
outfile = codecs.open(outfile_path, encoding='utf-8', mode='a') | ||
except (ProjectConfigurationError, IOError): | ||
trace = sys.exc_info()[2] | ||
|
@@ -199,6 +204,8 @@ def build(self): | |
] | ||
if self._force: | ||
build_command.append('-E') | ||
if self.config.sphinx.fail_on_warning: | ||
build_command.append('-W') | ||
build_command.extend([ | ||
'-b', | ||
self.sphinx_builder, | ||
|
@@ -209,9 +216,15 @@ def build(self): | |
'.', | ||
self.sphinx_build_dir, | ||
]) | ||
config_file = ( | ||
self.config.sphinx.configuration or | ||
self.project.conf_file(self.version.slug) | ||
) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here. And I feel like we shouldn't be guessing here. If the config file was specified and it exists, that's when we should set the config file path. Likewise, if we're generating a config, we set a config file path when we find out the user doesn't have a config and didn't specify a config file path. This will cut down on magical decisions that tend to hide errors from us. |
||
cmd_ret = self.run( | ||
*build_command, cwd=project.conf_dir(self.version.slug), | ||
bin_path=self.python_env.venv_bin()) | ||
*build_command, | ||
cwd=os.path.dirname(config_file), | ||
bin_path=self.python_env.venv_bin() | ||
) | ||
return cmd_ret.successful | ||
|
||
|
||
|
@@ -347,7 +360,9 @@ class PdfBuilder(BaseSphinx): | |
|
||
def build(self): | ||
self.clean() | ||
cwd = self.project.conf_dir(self.version.slug) | ||
config_file = self.config.sphinx.configuration | ||
config_dir = os.path.dirname(config_file) if config_file else None | ||
cwd = config_dir or self.project.conf_dir(self.version.slug) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This logic looks duplicated, is there a way to reduce this duplication if so? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I knew you were not going to like it p: haha, so all this is to make sure that we are keeping the previous behavior. If a config file doesn't exist, we create one, and also we try to find one in the whole project. I guess we can do a big refactor to do this step early, before parsing the config file (but not sure if this worth it). Or maybe we can use a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, this could either be a property, or even just simply set the common values on |
||
|
||
# Default to this so we can return it always. | ||
self.run( | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,7 +31,10 @@ def load_yaml_config(version): | |
python_version = 3 if project.python_interpreter == 'python3' else 2 | ||
allow_v2 = project.has_feature(Feature.ALLOW_V2_CONFIG_FILE) | ||
try: | ||
sphinx_configuration = version.get_conf_py_path() | ||
sphinx_configuration = path.join( | ||
version.get_conf_py_path(), | ||
'conf.py' | ||
) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's not clear to me why this change is needed. Shouldn't the full path have already been returned by There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was bitten by the name here too, this only returns the directory name. There is a test unskipped that covers this case. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. huh. is there a reason why this was working then? It seems like it shouldn't have worked at all if it was just the directory. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We didn't have a test, but I added them in https://github.com/rtfd/readthedocs.org/pull/4456/files#diff-5a2cc4147895068b33fe5bf89e4e6e08R589 (skipped) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also currently we are reading this from the database, that's why the builds don't fail |
||
except ProjectConfigurationError: | ||
sphinx_configuration = None | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -232,7 +232,6 @@ def test_requirements_file(self, load_config): | |
self.assertEqual(config.python.requirements, '__init__.py') | ||
|
||
|
||
@pytest.mark.skip | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tests are un-skipped here |
||
@pytest.mark.django_db | ||
@mock.patch('readthedocs.projects.models.Project.checkout_path') | ||
class TestLoadConfigV2(object): | ||
|
@@ -290,12 +289,10 @@ def test_report_using_invalid_version(self, checkout_path, tmpdir): | |
|
||
@pytest.mark.parametrize('config', [{}, {'formats': []}]) | ||
@patch('readthedocs.projects.models.Project.repo_nonblockinglock', new=MagicMock()) | ||
@patch('readthedocs.doc_builder.backends.sphinx.SearchBuilder.build') | ||
@patch('readthedocs.doc_builder.backends.sphinx.HtmlBuilder.build') | ||
@patch('readthedocs.doc_builder.backends.sphinx.HtmlBuilder.append_conf') | ||
def test_build_formats_default_empty( | ||
self, append_conf, html_build, search_build, | ||
checkout_path, config, tmpdir): | ||
self, append_conf, html_build, checkout_path, config, tmpdir): | ||
""" | ||
The default value for formats is [], which means no extra | ||
formats are build. | ||
|
@@ -304,22 +301,26 @@ def test_build_formats_default_empty( | |
self.create_config_file(tmpdir, config) | ||
|
||
update_docs = self.get_update_docs_task() | ||
python_env = Virtualenv( | ||
version=self.version, | ||
build_env=update_docs.build_env, | ||
config=update_docs.config | ||
) | ||
update_docs.python_env = python_env | ||
outcomes = update_docs.build_docs() | ||
|
||
# No extra formats were triggered | ||
assert outcomes['html'] | ||
assert outcomes['search'] | ||
assert not outcomes['localmedia'] | ||
assert not outcomes['pdf'] | ||
assert not outcomes['epub'] | ||
|
||
@patch('readthedocs.projects.models.Project.repo_nonblockinglock', new=MagicMock()) | ||
@patch('readthedocs.projects.tasks.UpdateDocsTaskStep.build_docs_class') | ||
@patch('readthedocs.doc_builder.backends.sphinx.SearchBuilder.build') | ||
@patch('readthedocs.doc_builder.backends.sphinx.HtmlBuilder.build') | ||
@patch('readthedocs.doc_builder.backends.sphinx.HtmlBuilder.append_conf') | ||
def test_build_formats_only_pdf( | ||
self, append_conf, html_build, search_build, build_docs_class, | ||
self, append_conf, html_build, build_docs_class, | ||
checkout_path, tmpdir): | ||
""" | ||
Only the pdf format is build. | ||
|
@@ -328,11 +329,17 @@ def test_build_formats_only_pdf( | |
self.create_config_file(tmpdir, {'formats': ['pdf']}) | ||
|
||
update_docs = self.get_update_docs_task() | ||
python_env = Virtualenv( | ||
version=self.version, | ||
build_env=update_docs.build_env, | ||
config=update_docs.config | ||
) | ||
update_docs.python_env = python_env | ||
|
||
outcomes = update_docs.build_docs() | ||
|
||
# Only pdf extra format was triggered | ||
assert outcomes['html'] | ||
assert outcomes['search'] | ||
build_docs_class.assert_called_with('sphinx_pdf') | ||
assert outcomes['pdf'] | ||
assert not outcomes['localmedia'] | ||
|
@@ -642,6 +649,35 @@ def test_sphinx_configuration_default( | |
append_conf.assert_called_once() | ||
move.assert_called_once() | ||
|
||
@patch('readthedocs.doc_builder.backends.sphinx.BaseSphinx.move') | ||
@patch('readthedocs.doc_builder.backends.sphinx.BaseSphinx.append_conf') | ||
@patch('readthedocs.doc_builder.backends.sphinx.BaseSphinx.run') | ||
def test_sphinx_configuration_default( | ||
self, run, append_conf, move, checkout_path, tmpdir): | ||
"""Should be default to find a conf.py file.""" | ||
checkout_path.return_value = str(tmpdir) | ||
|
||
apply_fs(tmpdir, {'conf.py': ''}) | ||
self.create_config_file(tmpdir, {}) | ||
self.project.conf_py_file = '' | ||
self.project.save() | ||
|
||
update_docs = self.get_update_docs_task() | ||
config = update_docs.config | ||
python_env = Virtualenv( | ||
version=self.version, | ||
build_env=update_docs.build_env, | ||
config=config | ||
) | ||
update_docs.python_env = python_env | ||
|
||
update_docs.build_docs_html() | ||
|
||
args, kwargs = run.call_args | ||
assert kwargs['cwd'] == str(tmpdir) | ||
append_conf.assert_called_once() | ||
move.assert_called_once() | ||
|
||
@patch('readthedocs.doc_builder.backends.sphinx.BaseSphinx.move') | ||
@patch('readthedocs.doc_builder.backends.sphinx.BaseSphinx.append_conf') | ||
@patch('readthedocs.doc_builder.backends.sphinx.BaseSphinx.run') | ||
|
@@ -716,6 +752,7 @@ def test_sphinx_fail_on_warning( | |
append_conf.assert_called_once() | ||
move.assert_called_once() | ||
|
||
@pytest.mark.skip | ||
@patch('readthedocs.doc_builder.backends.mkdocs.BaseMkdocs.move') | ||
@patch('readthedocs.doc_builder.backends.mkdocs.BaseMkdocs.append_conf') | ||
@patch('readthedocs.doc_builder.backends.mkdocs.BaseMkdocs.run') | ||
|
@@ -754,6 +791,7 @@ def test_mkdocs_configuration( | |
append_conf.assert_called_once() | ||
move.assert_called_once() | ||
|
||
@pytest.mark.skip | ||
@patch('readthedocs.doc_builder.backends.mkdocs.BaseMkdocs.move') | ||
@patch('readthedocs.doc_builder.backends.mkdocs.BaseMkdocs.append_conf') | ||
@patch('readthedocs.doc_builder.backends.mkdocs.BaseMkdocs.run') | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I need to keep this one, as here we catch and re-raise the exception to the user