Repository: pypa/setuptools

Issue:
upload_docs raises AttributeError with Sphinx 1.6
In Sphinx 1.6, `builder_target_dir` was replaced with `builder_target_dirs`, see sphinx-doc/sphinx@2afa0b6627f7e5afb188d5a60c8c4767f6250774 and sphinx-doc/sphinx#3476.

This causes an AttributeError in setuptools:

```pytb
Traceback (most recent call last):
  File "setup.py", line 67, in <module>
    setup(**setup_args)
  File "/usr/lib/python3.5/distutils/core.py", line 148, in setup
    dist.run_commands()
  File "/usr/lib/python3.5/distutils/dist.py", line 955, in run_commands
    self.run_command(cmd)
  File "/usr/lib/python3.5/distutils/dist.py", line 973, in run_command
    cmd_obj.ensure_finalized()
  File "/usr/lib/python3.5/distutils/cmd.py", line 107, in ensure_finalized
    self.finalize_options()
  File "/home/dmitry/.local/lib/python3.5/site-packages/setuptools/command/upload_docs.py", line 65, in finalize_options
    self.target_dir = build_sphinx.builder_target_dir
  File "/usr/lib/python3.5/distutils/cmd.py", line 103, in __getattr__
    raise AttributeError(attr)
AttributeError: builder_target_dir
```
upload_docs raises AttributeError with Sphinx 1.6
In Sphinx 1.6, `builder_target_dir` was replaced with `builder_target_dirs`, see sphinx-doc/sphinx@2afa0b6627f7e5afb188d5a60c8c4767f6250774 and sphinx-doc/sphinx#3476.

This causes an AttributeError in setuptools:

```pytb
Traceback (most recent call last):
  File "setup.py", line 67, in <module>
    setup(**setup_args)
  File "/usr/lib/python3.5/distutils/core.py", line 148, in setup
    dist.run_commands()
  File "/usr/lib/python3.5/distutils/dist.py", line 955, in run_commands
    self.run_command(cmd)
  File "/usr/lib/python3.5/distutils/dist.py", line 973, in run_command
    cmd_obj.ensure_finalized()
  File "/usr/lib/python3.5/distutils/cmd.py", line 107, in ensure_finalized
    self.finalize_options()
  File "/home/dmitry/.local/lib/python3.5/site-packages/setuptools/command/upload_docs.py", line 65, in finalize_options
    self.target_dir = build_sphinx.builder_target_dir
  File "/usr/lib/python3.5/distutils/cmd.py", line 103, in __getattr__
    raise AttributeError(attr)
AttributeError: builder_target_dir
```


Discussion:
I'm guessing that, going forward, we don't want people using `python setup.py upload_docs`, but I'm not sure what the replacement for it is.

I am not really sure how the API changes between `builder_target_dir` and `builder_target_dirs`. At first I thought it was a trivial rename, but I guess now this returns a list instead of a path, so I'm not sure what other effects this has.

One thing that disturbs me is that this does not seem to have broken our test suite, so I think to start we need some tests for this, even if it's just so that we can start safely removing it.
AFAIU pythonhosted.org is read-only now and this command doesn't make sense anymore. So the correct course of action would be removing it from https://github.com/pypa/setuptools/blob/main/setuptools/command/upload_docs.py and any other places.
@webknjaz 

I believe we can keep the `upload_docs` command because v36.1.0 restored support for `upload_docs` for sites other than PyPi. https://github.com/pypa/setuptools/blob/e1ffc2abbae4f2aa78dd09ee9827d754b7702b7b/CHANGES.rst#v3610. "Only warehouse is dropping support, but services like devpi continue to support docs built by setuptools' plugins." 
I'm guessing that, going forward, we don't want people using `python setup.py upload_docs`, but I'm not sure what the replacement for it is.

I am not really sure how the API changes between `builder_target_dir` and `builder_target_dirs`. At first I thought it was a trivial rename, but I guess now this returns a list instead of a path, so I'm not sure what other effects this has.

One thing that disturbs me is that this does not seem to have broken our test suite, so I think to start we need some tests for this, even if it's just so that we can start safely removing it.
AFAIU pythonhosted.org is read-only now and this command doesn't make sense anymore. So the correct course of action would be removing it from https://github.com/pypa/setuptools/blob/main/setuptools/command/upload_docs.py and any other places.
@webknjaz 

I believe we can keep the `upload_docs` command because v36.1.0 restored support for `upload_docs` for sites other than PyPi. https://github.com/pypa/setuptools/blob/e1ffc2abbae4f2aa78dd09ee9827d754b7702b7b/CHANGES.rst#v3610. "Only warehouse is dropping support, but services like devpi continue to support docs built by setuptools' plugins." 

Resolving patch:
diff --git a/setuptools/command/upload_docs.py b/setuptools/command/upload_docs.py
--- a/setuptools/command/upload_docs.py
+++ b/setuptools/command/upload_docs.py
@@ -2,7 +2,7 @@
 """upload_docs
 
 Implements a Distutils 'upload_docs' subcommand (upload documentation to
-PyPI's pythonhosted.org).
+sites other than PyPi such as devpi).
 """
 
 from base64 import standard_b64encode
@@ -31,7 +31,7 @@ class upload_docs(upload):
     # supported by Warehouse (and won't be).
     DEFAULT_REPOSITORY = 'https://pypi.python.org/pypi/'
 
-    description = 'Upload documentation to PyPI'
+    description = 'Upload documentation to sites other than PyPi such as devpi'
 
     user_options = [
         ('repository=', 'r',
@@ -59,7 +59,7 @@ def finalize_options(self):
         if self.upload_dir is None:
             if self.has_sphinx():
                 build_sphinx = self.get_finalized_command('build_sphinx')
-                self.target_dir = build_sphinx.builder_target_dir
+                self.target_dir = dict(build_sphinx.builder_target_dirs)['html']
             else:
                 build = self.get_finalized_command('build')
                 self.target_dir = os.path.join(build.build_base, 'docs')
@@ -67,7 +67,7 @@ def finalize_options(self):
             self.ensure_dirname('upload_dir')
             self.target_dir = self.upload_dir
         if 'pypi.python.org' in self.repository:
-            log.warn("Upload_docs command is deprecated. Use RTD instead.")
+            log.warn("Upload_docs command is deprecated for PyPi. Use RTD instead.")
         self.announce('Using upload directory %s' % self.target_dir)
 
     def create_zipfile(self, filename):


Test patch:
diff --git a/setuptools/tests/requirements.txt b/setuptools/tests/requirements.txt
--- a/setuptools/tests/requirements.txt
+++ b/setuptools/tests/requirements.txt
@@ -11,3 +11,4 @@ paver; python_version>="3.6"
 futures; python_version=="2.7"
 pip>=19.1 # For proper file:// URLs support.
 jaraco.envs
+sphinx
diff --git a/setuptools/tests/test_sphinx_upload_docs.py b/setuptools/tests/test_sphinx_upload_docs.py
new file mode 100644
--- /dev/null
+++ b/setuptools/tests/test_sphinx_upload_docs.py
@@ -0,0 +1,41 @@
+import pytest
+import os
+
+from setuptools.command.upload_docs import upload_docs
+from setuptools.dist import Distribution
+
+
+@pytest.fixture
+def sphinx_doc_sample_project(tmpdir_cwd):
+    # setup.py
+    with open('setup.py', 'wt') as f:
+        f.write('from setuptools import setup; setup()\n')
+
+    os.makedirs('build/docs')
+
+    # A test conf.py for Sphinx
+    with open('build/docs/conf.py', 'w') as f:
+        f.write("project = 'test'")
+
+    # A test index.rst for Sphinx
+    with open('build/docs/index.rst', 'w') as f:
+        f.write(".. toctree::\
+                    :maxdepth: 2\
+                    :caption: Contents:")
+
+
+@pytest.mark.usefixtures('sphinx_doc_sample_project')
+class TestSphinxUploadDocs:
+    def test_sphinx_doc(self):
+        params = dict(
+            name='foo',
+            packages=['test'],
+        )
+        dist = Distribution(params)
+
+        cmd = upload_docs(dist)
+
+        cmd.initialize_options()
+        assert cmd.upload_dir is None
+        assert cmd.has_sphinx() is True
+        cmd.finalize_options()