diff options
214 files changed, 1638 insertions, 3538 deletions
diff --git a/.appveyor.yml b/.appveyor.yml index a0c948246..9da9bc56d 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -5,8 +5,6 @@ environment: PYTHONWARNINGS: all matrix: - - PYTHON: 27 - TEST_IGNORE: --ignore py35 - PYTHON: 37 - PYTHON: 37-x64 diff --git a/.travis.yml b/.travis.yml index f156d59d7..f14bf6495 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,16 +11,9 @@ env: matrix: include: - - python: 'pypy' - env: TOXENV=pypy - - python: '2.7' + - python: '3.5' env: - TOXENV=du13 - - PYTEST_ADDOPTS="--cov ./ --cov-append --cov-config setup.cfg" - - python: '3.4' - env: TOXENV=py34 - - python: '3.5' - env: TOXENV=py35 - python: '3.6' env: - TOXENV=py36 @@ -38,6 +38,7 @@ Other contributors, listed alphabetically, are: * Zac Hatfield-Dodds -- doctest reporting improvements * Doug Hellmann -- graphviz improvements * Tim Hoffmann -- theme improvements +* Antti Kaihola -- doctest extension (skipif option) * Dave Kuhlman -- original LaTeX writer * Blaise Laflamme -- pyramid theme * Chris Lamb -- reproducibility fixes @@ -1,3 +1,55 @@ +Release 2.0.0 (in development) +============================== + +Dependencies +------------ + +* LaTeX builder now depends on TeX Live 2015 or above + +Incompatible changes +-------------------- + +* Drop python 2.7 and 3.4 support +* Drop docutils 0.11 support +* The default setting for :confval:`master_doc` is changed to ``'index'`` which + has been longly used as default of sphinx-quickstart. +* LaTeX: Move message resources to ``sphinxmessage.sty`` +* LaTeX: Stop using ``\captions<lang>`` macro for some labels + +Deprecated +---------- + +* The ``suffix`` argument of ``env.doc2path()`` is deprecated. +* The string style ``base`` argument of ``env.doc2path()`` is deprecated. +* ``sphinx.ext.doctest.doctest_encode()`` +* ``sphinx.testing.util.remove_unicode_literal()`` +* ``sphinx.util.osutil.walk()`` +* ``translatablestrings`` variable for LaTeX template + +For more details, see `deprecation APIs list +<http://www.sphinx-doc.org/en/master/extdev/index.html#deprecated-apis>`_ + +Features added +-------------- +* #1618: The search results preview of generated HTML documentation is + reader-friendlier: instead of showing the snippets as raw reStructuredText + markup, Sphinx now renders the corresponding HTML. This means the Sphinx + extension `Sphinx: pretty search results`__ is no longer necessary. Note that + changes to the search function of your custom or 3rd-pary HTML template might + overwrite this improvement. + +__ https://github.com/sphinx-contrib/sphinx-pretty-searchresults + +* #4182: autodoc: Support :confval:`suppress_warnings` +* #4018: htmlhelp: Add :confval:`htmlhelp_file_suffix` and + :confval:`htmlhelp_link_suffix` + +Bugs fixed +---------- + +Testing +-------- + Release 1.8.2 (in development) ============================== @@ -2101,7 +2153,7 @@ Incompatible changes parsing is attempted to distinguish valid code. To get the old behavior back, add ``highlight_language = "python"`` to conf.py. * `Locale Date Markup Language - <http://unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns>`_ like + <https://unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns>`_ like ``"MMMM dd, YYYY"`` is default format for `today_fmt` and `html_last_updated_fmt`. However strftime format like ``"%B %d, %Y"`` is also supported for backward compatibility until Sphinx-1.5. Later format will be disabled from Sphinx-1.5. @@ -12,714 +12,385 @@ interesting examples. Documentation using the alabaster theme --------------------------------------- -* `Alabaster`__ -* `Blinker`__ -* `Calibre`__ -* `Click`__ (customized) -* `coala`__ (customized) -* `CodePy`__ -* `Fabric`__ -* `Fityk`__ -* `Flask`__ -* `Flask-OpenID`__ -* `Invoke`__ -* `Jinja`__ -* `Lino`__ (customized) -* `marbl`__ -* `MDAnalysis`__ (customized) -* `MeshPy`__ -* `PyCUDA`__ -* `PyOpenCL`__ -* `PyLangAcq`__ -* `pytest`__ (customized) -* `python-apt`__ -* `PyVisfile`__ -* `Requests`__ -* `searx`__ -* `Spyder`__ (customized) -* `Tablib`__ -* `urllib3`__ (customized) -* `Werkzeug`__ (customized) - -__ https://alabaster.readthedocs.io/ -__ https://pythonhosted.org/blinker/ -__ https://manual.calibre-ebook.com/ -__ http://click.pocoo.org/ -__ https://docs.coala.io/ -__ https://documen.tician.de/codepy/ -__ http://docs.fabfile.org/ -__ http://fityk.nieto.pl/ -__ http://flask.pocoo.org/docs/ -__ https://pythonhosted.org/Flask-OpenID/ -__ http://docs.pyinvoke.org/ -__ http://jinja.pocoo.org/docs/ -__ http://www.lino-framework.org/ -__ https://getmarbl.readthedocs.io/ -__ http://www.mdanalysis.org/docs/ -__ https://documen.tician.de/meshpy/ -__ https://documen.tician.de/pycuda/ -__ https://documen.tician.de/pyopencl/ -__ http://pylangacq.org/ -__ https://docs.pytest.org/ -__ https://apt.alioth.debian.org/python-apt-doc/ -__ https://documen.tician.de/pyvisfile/ -__ http://www.python-requests.org/ -__ https://asciimoo.github.io/searx/ -__ https://docs.spyder-ide.org/ -__ http://docs.python-tablib.org/ -__ https://urllib3.readthedocs.io/ -__ http://werkzeug.pocoo.org/docs/ +* `Alabaster <https://alabaster.readthedocs.io/>`__ +* `Blinker <https://pythonhosted.org/blinker/>`__ +* `Calibre <https://manual.calibre-ebook.com/>`__ +* `Click <http://click.pocoo.org/>`__ (customized) +* `coala <https://docs.coala.io/>`__ (customized) +* `CodePy <https://documen.tician.de/codepy/>`__ +* `Fabric <https://docs.fabfile.org/>`__ +* `Fityk <https://fityk.nieto.pl/>`__ +* `Flask <http://flask.pocoo.org/docs/>`__ +* `Flask-OpenID <https://pythonhosted.org/Flask-OpenID/>`__ +* `Invoke <https://docs.pyinvoke.org/>`__ +* `Jinja <http://jinja.pocoo.org/docs/>`__ +* `Lino <http://www.lino-framework.org/>`__ (customized) +* `marbl <https://getmarbl.readthedocs.io/>`__ +* `MDAnalysis <https://www.mdanalysis.org/docs/>`__ (customized) +* `MeshPy <https://documen.tician.de/meshpy/>`__ +* `PyCUDA <https://documen.tician.de/pycuda/>`__ +* `PyOpenCL <https://documen.tician.de/pyopencl/>`__ +* `PyLangAcq <http://pylangacq.org/>`__ +* `pytest <https://docs.pytest.org/>`__ (customized) +* `python-apt <https://apt.alioth.debian.org/python-apt-doc/>`__ +* `PyVisfile <https://documen.tician.de/pyvisfile/>`__ +* `Requests <http://www.python-requests.org/>`__ +* `searx <https://asciimoo.github.io/searx/>`__ +* `Spyder <https://docs.spyder-ide.org/>`__ (customized) +* `Tablib <http://docs.python-tablib.org/>`__ +* `urllib3 <https://urllib3.readthedocs.io/>`__ (customized) +* `Werkzeug <http://werkzeug.pocoo.org/docs/>`__ (customized) Documentation using the classic theme ------------------------------------- -* `Advanced Generic Widgets`__ (customized) -* `Apache CouchDB`__ (customized) -* `APSW`__ -* `Arb`__ -* `Bazaar`__ (customized) -* `Beautiful Soup`__ -* `Blender`__ -* `Bugzilla`__ -* `Buildbot`__ -* `CMake`__ (customized) -* `Chaco`__ (customized) -* `Cormoran`__ -* `DEAP`__ (customized) -* `Director`__ -* `EZ-Draw`__ (customized) -* `F2py`__ -* `Generic Mapping Tools (GMT)`__ (customized) -* `Genomedata`__ -* `GetFEM++`__ (customized) -* `Glasgow Haskell Compiler`__ (customized) -* `Grok`__ (customized) -* `GROMACS`__ -* `GSL Shell`__ -* `Hands-on Python Tutorial`__ -* `Kaa`__ (customized) -* `Leo`__ -* `LEPL`__ (customized) -* `Mayavi`__ (customized) -* `MediaGoblin`__ (customized) -* `mpmath`__ -* `OpenCV`__ (customized) -* `OpenEXR`__ -* `OpenGDA`__ -* `Peach^3`__ (customized) -* `Plone`__ (customized) -* `PyEMD`__ -* `Pyevolve`__ -* `Pygame`__ (customized) -* `PyMQI`__ -* `PyQt4`__ (customized) -* `PyQt5`__ (customized) -* `Python 2`__ -* `Python 3`__ (customized) -* `Python Packaging Authority`__ (customized) -* `Ring programming language`__ (customized) -* `SageMath`__ (customized) -* `Segway`__ -* `simuPOP`__ (customized) -* `Sprox`__ (customized) -* `SymPy`__ -* `TurboGears`__ (customized) -* `tvtk`__ -* `Varnish`__ (customized, alabaster for index) -* `Waf`__ -* `wxPython Phoenix`__ (customized) -* `z3c`__ -* `zc.async`__ (customized) -* `Zope`__ (customized) - -__ http://xoomer.virgilio.it/infinity77/AGW_Docs/ -__ http://docs.couchdb.org/ -__ https://rogerbinns.github.io/apsw/ -__ http://arblib.org/ -__ http://doc.bazaar.canonical.com/ -__ https://www.crummy.com/software/BeautifulSoup/bs4/doc/ -__ https://docs.blender.org/api/current/ -__ https://bugzilla.readthedocs.io/ -__ https://docs.buildbot.net/latest/ -__ https://cmake.org/documentation/ -__ http://docs.enthought.com/chaco/ -__ http://cormoran.nhopkg.org/docs/ -__ https://deap.readthedocs.io/ -__ https://pythonhosted.org/director/ -__ https://pageperso.lif.univ-mrs.fr/~edouard.thiel/ez-draw/doc/en/html/ez-manual.html -__ http://f2py.sourceforge.net/docs/ -__ http://gmt.soest.hawaii.edu/doc/latest/ -__ https://noble.gs.washington.edu/proj/genomedata/doc/1.3.3/ -__ http://getfem.org/ -__ https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/ -__ http://grok.zope.org/doc/current/ -__ http://manual.gromacs.org/documentation/ -__ http://www.nongnu.org/gsl-shell/ -__ http://anh.cs.luc.edu/python/hands-on/3.1/handsonHtml/ -__ http://api.freevo.org/kaa-base/ -__ http://leoeditor.com/ -__ http://www.acooke.org/lepl/ -__ http://docs.enthought.com/mayavi/mayavi/ -__ https://mediagoblin.readthedocs.io/ -__ http://mpmath.org/doc/current/ -__ http://docs.opencv.org/ -__ http://excamera.com/articles/26/doc/index.html -__ http://www.opengda.org/gdadoc/html/ -__ https://peach3.nl/doc/latest/userdoc/ -__ https://docs.plone.org/ -__ https://pyemd.readthedocs.io/ -__ http://pyevolve.sourceforge.net/ -__ https://www.pygame.org/docs/ -__ https://pythonhosted.org/pymqi/ -__ http://pyqt.sourceforge.net/Docs/PyQt4/ -__ http://pyqt.sourceforge.net/Docs/PyQt5/ -__ https://docs.python.org/2/ -__ https://docs.python.org/3/ -__ https://www.pypa.io/ -__ http://ring-lang.sourceforge.net/doc/ -__ https://doc.sagemath.org/ -__ http://noble.gs.washington.edu/proj/segway/doc/1.1.0/segway.html -__ http://simupop.sourceforge.net/manual_release/build/userGuide.html -__ http://sprox.org/ -__ http://docs.sympy.org/ -__ https://turbogears.readthedocs.io/ -__ http://docs.enthought.com/mayavi/tvtk/ -__ https://www.varnish-cache.org/docs/ -__ https://waf.io/apidocs/ -__ https://wxpython.org/Phoenix/docs/html/main.html -__ http://www.ibiblio.org/paulcarduner/z3ctutorial/ -__ https://pythonhosted.org/zc.async/ -__ https://docs.zope.org/zope2/ +* `Advanced Generic Widgets <http://xoomer.virgilio.it/infinity77/AGW_Docs/>`__ (customized) +* `Apache CouchDB <http://docs.couchdb.org/>`__ (customized) +* `APSW <https://rogerbinns.github.io/apsw/>`__ +* `Arb <http://arblib.org/>`__ +* `Bazaar <http://doc.bazaar.canonical.com/>`__ (customized) +* `Beautiful Soup <https://www.crummy.com/software/BeautifulSoup/bs4/doc/>`__ +* `Blender <https://docs.blender.org/api/current/>`__ +* `Bugzilla <https://bugzilla.readthedocs.io/>`__ +* `Buildbot <https://docs.buildbot.net/latest/>`__ +* `CMake <https://cmake.org/documentation/>`__ (customized) +* `Chaco <https://docs.enthought.com/chaco/>`__ (customized) +* `Cormoran <http://cormoran.nhopkg.org/docs/>`__ +* `DEAP <https://deap.readthedocs.io/>`__ (customized) +* `Director <https://pythonhosted.org/director/>`__ +* `EZ-Draw <https://pageperso.lif.univ-mrs.fr/~edouard.thiel/ez-draw/doc/en/html/ez-manual.html>`__ (customized) +* `F2py <http://f2py.sourceforge.net/docs/>`__ +* `Generic Mapping Tools (GMT) <https://gmt.soest.hawaii.edu/doc/latest/>`__ (customized) +* `Genomedata <https://noble.gs.washington.edu/proj/genomedata/doc/1.3.3/>`__ +* `GetFEM++ <http://getfem.org/>`__ (customized) +* `Glasgow Haskell Compiler <https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/>`__ (customized) +* `Grok <http://grok.zope.org/doc/current/>`__ (customized) +* `GROMACS <http://manual.gromacs.org/documentation/>`__ +* `GSL Shell <https://www.nongnu.org/gsl-shell/>`__ +* `Hands-on Python Tutorial <https://anh.cs.luc.edu/python/hands-on/3.1/handsonHtml/>`__ +* `Kaa <https://api.freevo.org/kaa-base/>`__ (customized) +* `Leo <https://leoeditor.com/>`__ +* `LEPL <http://www.acooke.org/lepl/>`__ (customized) +* `Mayavi <https://docs.enthought.com/mayavi/mayavi/>`__ (customized) +* `MediaGoblin <https://mediagoblin.readthedocs.io/>`__ (customized) +* `mpmath <http://mpmath.org/doc/current/>`__ +* `OpenCV <https://docs.opencv.org/>`__ (customized) +* `OpenEXR <http://excamera.com/articles/26/doc/index.html>`__ +* `OpenGDA <http://www.opengda.org/gdadoc/html/>`__ +* `Peach^3 <https://peach3.nl/doc/latest/userdoc/>`__ (customized) +* `Plone <https://docs.plone.org/>`__ (customized) +* `PyEMD <https://pyemd.readthedocs.io/>`__ +* `Pyevolve <http://pyevolve.sourceforge.net/>`__ +* `Pygame <https://www.pygame.org/docs/>`__ (customized) +* `PyMQI <https://pythonhosted.org/pymqi/>`__ +* `PyQt4 <http://pyqt.sourceforge.net/Docs/PyQt4/>`__ (customized) +* `PyQt5 <http://pyqt.sourceforge.net/Docs/PyQt5/>`__ (customized) +* `Python 2 <https://docs.python.org/2/>`__ +* `Python 3 <https://docs.python.org/3/>`__ (customized) +* `Python Packaging Authority <https://www.pypa.io/>`__ (customized) +* `Ring programming language <http://ring-lang.sourceforge.net/doc/>`__ (customized) +* `SageMath <https://doc.sagemath.org/>`__ (customized) +* `Segway <https://noble.gs.washington.edu/proj/segway/doc/1.1.0/segway.html>`__ +* `simuPOP <http://simupop.sourceforge.net/manual_release/build/userGuide.html>`__ (customized) +* `Sprox <http://sprox.org/>`__ (customized) +* `SymPy <https://docs.sympy.org/>`__ +* `TurboGears <https://turbogears.readthedocs.io/>`__ (customized) +* `tvtk <https://docs.enthought.com/mayavi/tvtk/>`__ +* `Varnish <https://www.varnish-cache.org/docs/>`__ (customized, alabaster for index) +* `Waf <https://waf.io/apidocs/>`__ +* `wxPython Phoenix <https://wxpython.org/Phoenix/docs/html/main.html>`__ (customized +* `z3c <https://www.ibiblio.org/paulcarduner/z3ctutorial/>`__ +* `zc.async <https://pythonhosted.org/zc.async/>`__ (customized) +* `Zope <https://docs.zope.org/zope2/>`__ (customized) Documentation using the sphinxdoc theme --------------------------------------- -* `cartopy`__ -* `Jython`__ -* `Matplotlib`__ -* `MDAnalysis Tutorial`__ -* `NetworkX`__ -* `PyCantonese`__ -* `Pyre`__ -* `pySPACE`__ -* `Pysparse`__ -* `PyTango`__ -* `Python Wild Magic`__ (customized) -* `Reteisi`__ (customized) -* `Sqlkit`__ (customized) -* `Turbulenz`__ - -__ http://scitools.org.uk/cartopy/docs/latest/ -__ http://www.jython.org/docs/ -__ https://matplotlib.org/ -__ http://www.mdanalysis.org/MDAnalysisTutorial/ -__ https://networkx.github.io/ -__ http://pycantonese.org/ -__ http://docs.danse.us/pyre/sphinx/ -__ https://pyspace.github.io/pyspace/ -__ http://pysparse.sourceforge.net/ -__ http://www.esrf.eu/computing/cs/tango/tango_doc/kernel_doc/pytango/latest/ -__ https://vmlaker.github.io/pythonwildmagic/ -__ http://www.reteisi.org/contents.html -__ http://sqlkit.argolinux.org/ -__ http://docs.turbulenz.com/ +* `cartopy <https://scitools.org.uk/cartopy/docs/latest/>`__ +* `Jython <http://www.jython.org/docs/>`__ +* `Matplotlib <https://matplotlib.org/>`__ +* `MDAnalysis Tutorial <https://www.mdanalysis.org/MDAnalysisTutorial/>`__ +* `NetworkX <https://networkx.github.io/>`__ +* `PyCantonese <http://pycantonese.org/>`__ +* `Pyre <http://docs.danse.us/pyre/sphinx/>`__ +* `pySPACE <https://pyspace.github.io/pyspace/>`__ +* `Pysparse <http://pysparse.sourceforge.net/>`__ +* `PyTango <https://www.esrf.eu/computing/cs/tango/tango_doc/kernel_doc/pytango/latest/>`__ +* `Python Wild Magic <https://vmlaker.github.io/pythonwildmagic/>`__ (customized) +* `Reteisi <http://www.reteisi.org/contents.html>`__ (customized) +* `Sqlkit <http://sqlkit.argolinux.org/>`__ (customized) +* `Turbulenz <http://docs.turbulenz.com/>`__ Documentation using the nature theme ------------------------------------ -* `Alembic`__ -* `Cython`__ -* `easybuild`__ -* `jsFiddle`__ -* `libLAS`__ (customized) -* `Lmod`__ -* `MapServer`__ (customized) -* `Pandas`__ -* `pyglet`__ (customized) -* `Setuptools`__ -* `Spring Python`__ -* `StatsModels`__ (customized) -* `Sylli`__ - -__ http://alembic.zzzcomputing.com/ -__ http://docs.cython.org/ -__ https://easybuild.readthedocs.io/ -__ http://doc.jsfiddle.net/ -__ https://www.liblas.org/ -__ https://lmod.readthedocs.io/ -__ http://mapserver.org/ -__ https://pandas.pydata.org/pandas-docs/stable/ -__ https://pyglet.readthedocs.io/ -__ https://setuptools.readthedocs.io/ -__ https://docs.spring.io/spring-python/1.2.x/sphinx/html/ -__ http://www.statsmodels.org/ -__ http://sylli.sourceforge.net/ +* `Alembic <http://alembic.zzzcomputing.com/>`__ +* `Cython <http://docs.cython.org/>`__ +* `easybuild <https://easybuild.readthedocs.io/>`__ +* `jsFiddle <http://doc.jsfiddle.net/>`__ +* `libLAS <https://www.liblas.org/>`__ (customized) +* `Lmod <https://lmod.readthedocs.io/>`__ +* `MapServer <https://mapserver.org/>`__ (customized) +* `Pandas <https://pandas.pydata.org/pandas-docs/stable/>`__ +* `pyglet <https://pyglet.readthedocs.io/>`__ (customized) +* `Setuptools <https://setuptools.readthedocs.io/>`__ +* `Spring Python <https://docs.spring.io/spring-python/1.2.x/sphinx/html/>`__ +* `StatsModels <https://www.statsmodels.org/>`__ (customized) +* `Sylli <http://sylli.sourceforge.net/>`__ Documentation using another builtin theme ----------------------------------------- -* `Breathe`__ (haiku) -* `MPipe`__ (sphinx13) -* `NLTK`__ (agogo) -* `Programmieren mit PyGTK und Glade (German)`__ (agogo, customized) -* `PyPubSub`__ (bizstyle) -* `Pylons`__ (pyramid) -* `Pyramid web framework`__ (pyramid) -* `Sphinx`__ (sphinx13) :-) -* `Valence`__ (haiku, customized) - -__ https://breathe.readthedocs.io/ -__ https://vmlaker.github.io/mpipe/ -__ http://www.nltk.org/ -__ http://www.florian-diesch.de/doc/python-und-glade/online/ -__ https://pypubsub.readthedocs.io/ -__ http://docs.pylonsproject.org/projects/pylons-webframework/ -__ https://docs.pylonsproject.org/projects/pyramid/ -__ http://www.sphinx-doc.org/ -__ http://docs.valence.desire2learn.com/ +* `Breathe <https://breathe.readthedocs.io/>`__ (haiku) +* `MPipe <https://vmlaker.github.io/mpipe/>`__ (sphinx13) +* `NLTK <https://www.nltk.org/>`__ (agogo) +* `Programmieren mit PyGTK und Glade (German) <https://www.florian-diesch.de/doc/python-und-glade/online/>`__ (agogo, customized) +* `PyPubSub <https://pypubsub.readthedocs.io/>`__ (bizstyle) +* `Pylons <https://docs.pylonsproject.org/projects/pylons-webframework/>`__ (pyramid) +* `Pyramid web framework <https://docs.pylonsproject.org/projects/pyramid/>`__ (pyramid) +* `Sphinx <http://www.sphinx-doc.org/>`__ (sphinx13) :-) +* `Valence <https://docs.valence.desire2learn.com/>`__ (haiku, customized) Documentation using sphinx_rtd_theme ------------------------------------ -* `Annotator`__ -* `Ansible`__ (customized) -* `Arcade`__ -* `aria2`__ -* `ASE`__ -* `Autofac`__ -* `BigchainDB`__ -* `Blocks`__ -* `bootstrap-datepicker`__ -* `Certbot`__ -* `Chainer`__ (customized) -* `CherryPy`__ -* `CodeIgniter`__ -* `Conda`__ -* `Corda`__ -* `Dask`__ -* `Databricks`__ (customized) -* `Dataiku DSS`__ -* `edX`__ -* `Electrum`__ -* `Elemental`__ -* `ESWP3`__ -* `Ethereum Homestead`__ -* `Fidimag`__ -* `Flake8`__ -* `GeoNode`__ -* `Godot`__ -* `Graylog`__ -* `GPAW`__ (customized) -* `HDF5 for Python (h5py)`__ -* `Hyperledger Fabric`__ -* `Hyperledger Sawtooth`__ -* `IdentityServer`__ -* `Idris`__ -* `javasphinx`__ -* `Julia`__ -* `Jupyter Notebook`__ -* `Lasagne`__ -* `latexindent.pl`__ -* `Linguistica`__ -* `Linux kernel`__ -* `MathJax`__ -* `MDTraj`__ (customized) -* `MICrobial Community Analysis (micca)`__ -* `MicroPython`__ -* `Minds`__ (customized) -* `Mink`__ -* `Mockery`__ -* `mod_wsgi`__ -* `MoinMoin`__ -* `Mopidy`__ -* `MyHDL`__ -* `Nextflow`__ -* `NICOS`__ (customized) -* `Pelican`__ -* `picamera`__ -* `Pillow`__ -* `pip`__ -* `Paver`__ -* `peewee`__ -* `Phinx`__ -* `phpMyAdmin`__ -* `PROS`__ (customized) -* `Pweave`__ -* `PyPy`__ -* `python-sqlparse`__ -* `PyVISA`__ -* `Read The Docs`__ -* `Free your information from their silos (French)`__ (customized) -* `Releases Sphinx extension`__ -* `Qtile`__ -* `Quex`__ -* `Satchmo`__ -* `Scapy`__ -* `SimPy`__ -* `SlamData`__ -* `Solidity`__ -* `Sonos Controller (SoCo)`__ -* `Sphinx AutoAPI`__ -* `sphinx-argparse`__ -* `Sphinx-Gallery`__ (customized) -* `SpotBugs`__ -* `StarUML`__ -* `Sublime Text Unofficial Documentation`__ -* `SunPy`__ -* `Sylius`__ -* `Tango Controls`__ (customized) -* `Topshelf`__ -* `Theano`__ -* `ThreatConnect`__ -* `Tuleap`__ -* `TYPO3`__ (customized) -* `uWSGI`__ -* `Wagtail`__ -* `Web Application Attack and Audit Framework (w3af)`__ -* `Weblate`__ -* `x265`__ -* `ZeroNet`__ - -__ http://docs.annotatorjs.org/ -__ https://docs.ansible.com/ -__ http://arcade.academy/ -__ https://aria2.github.io/manual/en/html/ -__ https://wiki.fysik.dtu.dk/ase/ -__ http://docs.autofac.org/ -__ https://docs.bigchaindb.com/ -__ https://blocks.readthedocs.io/ -__ https://bootstrap-datepicker.readthedocs.io/ -__ https://letsencrypt.readthedocs.io/ -__ https://docs.chainer.org/ -__ http://docs.cherrypy.org/ -__ https://www.codeigniter.com/user_guide/ -__ https://conda.io/docs/ -__ https://docs.corda.net/ -__ https://dask.pydata.org/ -__ https://docs.databricks.com/ -__ https://doc.dataiku.com/ -__ http://docs.edx.org/ -__ http://docs.electrum.org/ -__ http://libelemental.org/documentation/dev/ -__ https://eswp3.readthedocs.io/ -__ http://www.ethdocs.org/ -__ https://fidimag.readthedocs.io/ -__ http://flake8.pycqa.org/ -__ http://docs.geonode.org/ -__ https://godot.readthedocs.io/ -__ http://docs.graylog.org/ -__ https://wiki.fysik.dtu.dk/gpaw/ -__ http://docs.h5py.org/ -__ https://hyperledger-fabric.readthedocs.io/ -__ https://intelledger.github.io/ -__ http://docs.identityserver.io/ -__ http://docs.idris-lang.org/ -__ https://bronto-javasphinx.readthedocs.io/ -__ https://julia.readthedocs.io/ -__ https://jupyter-notebook.readthedocs.io/ -__ https://lasagne.readthedocs.io/ -__ https://latexindentpl.readthedocs.io/ -__ https://linguistica-uchicago.github.io/lxa5/ -__ https://www.kernel.org/doc/html/latest/index.html -__ https://docs.mathjax.org/ -__ http://mdtraj.org/latest/ -__ http://micca.org/docs/latest/ -__ https://docs.micropython.org/ -__ https://www.minds.org/docs/ -__ http://mink.behat.org/ -__ http://docs.mockery.io/ -__ https://modwsgi.readthedocs.io/ -__ https://moin-20.readthedocs.io/ -__ https://docs.mopidy.com/ -__ http://docs.myhdl.org/ -__ https://www.nextflow.io/docs/latest/index.html -__ https://forge.frm2.tum.de/nicos/doc/nicos-master/ -__ http://docs.getpelican.com/ -__ https://picamera.readthedocs.io/ -__ https://pillow.readthedocs.io/ -__ https://pip.pypa.io/ -__ https://paver.readthedocs.io/ -__ http://docs.peewee-orm.com/ -__ http://docs.phinx.org/ -__ https://docs.phpmyadmin.net/ -__ https://pros.cs.purdue.edu/v5/ -__ http://mpastell.com/pweave/ -__ http://doc.pypy.org/ -__ https://sqlparse.readthedocs.io/ -__ https://pyvisa.readthedocs.io/ -__ https://docs.readthedocs.io/ -__ http://redaction-technique.org/ -__ https://releases.readthedocs.io/ -__ http://docs.qtile.org/ -__ http://quex.sourceforge.net/doc/html/main.html -__ http://docs.satchmoproject.com/ -__ https://scapy.readthedocs.io/ -__ http://simpy.readthedocs.io/ -__ http://docs.slamdata.com/ -__ https://solidity.readthedocs.io/ -__ http://docs.python-soco.com/ -__ https://sphinx-autoapi.readthedocs.io/ -__ https://sphinx-argparse.readthedocs.io/ -__ https://sphinx-gallery.readthedocs.io/ -__ https://spotbugs.readthedocs.io/ -__ http://docs.staruml.io/ -__ http://docs.sublimetext.info/ -__ http://docs.sunpy.org/ -__ http://docs.sylius.org/ -__ https://tango-controls.readthedocs.io/ -__ http://docs.topshelf-project.com/ -__ http://www.deeplearning.net/software/theano/ -__ https://docs.threatconnect.com/ -__ https://tuleap.net/doc/en/ -__ https://docs.typo3.org/ -__ https://uwsgi-docs.readthedocs.io/ -__ http://docs.wagtail.io/ -__ http://docs.w3af.org/ -__ https://docs.weblate.org/ -__ https://x265.readthedocs.io/ -__ https://zeronet.readthedocs.io/ +* `Annotator <http://docs.annotatorjs.org/>`__ +* `Ansible <https://docs.ansible.com/>`__ (customized) +* `Arcade <http://arcade.academy/>`__ +* `aria2 <https://aria2.github.io/manual/en/html/>`__ +* `ASE <https://wiki.fysik.dtu.dk/ase/>`__ +* `Autofac <http://docs.autofac.org/>`__ +* `BigchainDB <https://docs.bigchaindb.com/>`__ +* `Blocks <https://blocks.readthedocs.io/>`__ +* `bootstrap-datepicker <https://bootstrap-datepicker.readthedocs.io/>`__ +* `Certbot <https://letsencrypt.readthedocs.io/>`__ +* `Chainer <https://docs.chainer.org/>`__ (customized) +* `CherryPy <https://docs.cherrypy.org/>`__ +* `CodeIgniter <https://www.codeigniter.com/user_guide/>`__ +* `Conda <https://conda.io/docs/>`__ +* `Corda <https://docs.corda.net/>`__ +* `Dask <https://dask.pydata.org/>`__ +* `Databricks <https://docs.databricks.com/>`__ (customized) +* `Dataiku DSS <https://doc.dataiku.com/>`__ +* `edX <https://docs.edx.org/>`__ +* `Electrum <http://docs.electrum.org/>`__ +* `Elemental <http://libelemental.org/documentation/dev/>`__ +* `ESWP3 <https://eswp3.readthedocs.io/>`__ +* `Ethereum Homestead <http://www.ethdocs.org/>`__ +* `Fidimag <https://fidimag.readthedocs.io/>`__ +* `Flake8 <http://flake8.pycqa.org/>`__ +* `GeoNode <http://docs.geonode.org/>`__ +* `Godot <https://godot.readthedocs.io/>`__ +* `Graylog <http://docs.graylog.org/>`__ +* `GPAW <https://wiki.fysik.dtu.dk/gpaw/>`__ (customized) +* `HDF5 for Python (h5py) <http://docs.h5py.org/>`__ +* `Hyperledger Fabric <https://hyperledger-fabric.readthedocs.io/>`__ +* `Hyperledger Sawtooth <https://intelledger.github.io/>`__ +* `IdentityServer <http://docs.identityserver.io/>`__ +* `Idris <http://docs.idris-lang.org/>`__ +* `javasphinx <https://bronto-javasphinx.readthedocs.io/>`__ +* `Julia <https://julia.readthedocs.io/>`__ +* `Jupyter Notebook <https://jupyter-notebook.readthedocs.io/>`__ +* `Lasagne <https://lasagne.readthedocs.io/>`__ +* `latexindent.pl <https://latexindentpl.readthedocs.io/>`__ +* `Linguistica <https://linguistica-uchicago.github.io/lxa5/>`__ +* `Linux kernel <https://www.kernel.org/doc/html/latest/index.html>`__ +* `MathJax <https://docs.mathjax.org/>`__ +* `MDTraj <http://mdtraj.org/latest/>`__ (customized) +* `MICrobial Community Analysis (micca) <http://micca.org/docs/latest/>`__ +* `MicroPython <https://docs.micropython.org/>`__ +* `Minds <https://www.minds.org/docs/>`__ (customized) +* `Mink <http://mink.behat.org/>`__ +* `Mockery <http://docs.mockery.io/>`__ +* `mod_wsgi <https://modwsgi.readthedocs.io/>`__ +* `MoinMoin <https://moin-20.readthedocs.io/>`__ +* `Mopidy <https://docs.mopidy.com/>`__ +* `MyHDL <http://docs.myhdl.org/>`__ +* `Nextflow <https://www.nextflow.io/docs/latest/index.html>`__ +* `NICOS <https://forge.frm2.tum.de/nicos/doc/nicos-master/>`__ (customized) +* `Pelican <http://docs.getpelican.com/>`__ +* `picamera <https://picamera.readthedocs.io/>`__ +* `Pillow <https://pillow.readthedocs.io/>`__ +* `pip <https://pip.pypa.io/>`__ +* `Paver <https://paver.readthedocs.io/>`__ +* `peewee <http://docs.peewee-orm.com/>`__ +* `Phinx <http://docs.phinx.org/>`__ +* `phpMyAdmin <https://docs.phpmyadmin.net/>`__ +* `PROS <https://pros.cs.purdue.edu/v5/>`__ (customized) +* `Pweave <http://mpastell.com/pweave/>`__ +* `PyPy <http://doc.pypy.org/>`__ +* `python-sqlparse <https://sqlparse.readthedocs.io/>`__ +* `PyVISA <https://pyvisa.readthedocs.io/>`__ +* `Read The Docs <https://docs.readthedocs.io/>`__ +* `Free your information from their silos (French) <http://redaction-technique.org/>`__ (customized) +* `Releases Sphinx extension <https://releases.readthedocs.io/>`__ +* `Qtile <http://docs.qtile.org/>`__ +* `Quex <http://quex.sourceforge.net/doc/html/main.html>`__ +* `Satchmo <http://docs.satchmoproject.com/>`__ +* `Scapy <https://scapy.readthedocs.io/>`__ +* `SimPy <https://simpy.readthedocs.io/>`__ +* `SlamData <https://newdocs.slamdata.com>`__ +* `Solidity <https://solidity.readthedocs.io/>`__ +* `Sonos Controller (SoCo) <http://docs.python-soco.com/>`__ +* `Sphinx AutoAPI <https://sphinx-autoapi.readthedocs.io/>`__ +* `sphinx-argparse <https://sphinx-argparse.readthedocs.io/>`__ +* `Sphinx-Gallery <https://sphinx-gallery.readthedocs.io/>`__ (customized) +* `SpotBugs <https://spotbugs.readthedocs.io/>`__ +* `StarUML <https://docs.staruml.io/>`__ +* `Sublime Text Unofficial Documentation <http://docs.sublimetext.info/>`__ +* `SunPy <https://docs.sunpy.org/>`__ +* `Sylius <http://docs.sylius.org/>`__ +* `Tango Controls <https://tango-controls.readthedocs.io/>`__ (customized) +* `Topshelf <http://docs.topshelf-project.com/>`__ +* `Theano <http://www.deeplearning.net/software/theano/>`__ +* `ThreatConnect <https://docs.threatconnect.com/>`__ +* `Tuleap <https://tuleap.net/doc/en/>`__ +* `TYPO3 <https://docs.typo3.org/>`__ (customized) +* `uWSGI <https://uwsgi-docs.readthedocs.io/>`__ +* `Wagtail <https://docs.wagtail.io/>`__ +* `Web Application Attack and Audit Framework (w3af) <http://docs.w3af.org/>`__ +* `Weblate <https://docs.weblate.org/>`__ +* `x265 <https://x265.readthedocs.io/>`__ +* `ZeroNet <https://zeronet.readthedocs.io/>`__ Documentation using sphinx_bootstrap_theme ------------------------------------------ -* `Bootstrap Theme`__ -* `C/C++ Software Development with Eclipse`__ -* `Dataverse`__ -* `e-cidadania`__ -* `Hangfire`__ -* `Hedge`__ -* `ObsPy`__ -* `Open Dylan`__ -* `Pootle`__ -* `PyUblas`__ -* `seaborn`__ - -__ https://ryan-roemer.github.io/sphinx-bootstrap-theme/ -__ http://eclipsebook.in/ -__ http://guides.dataverse.org/ -__ https://e-cidadania.readthedocs.io/ -__ http://docs.hangfire.io/ -__ https://documen.tician.de/hedge/ -__ https://docs.obspy.org/ -__ https://opendylan.org/documentation/ -__ http://docs.translatehouse.org/projects/pootle/ -__ https://documen.tician.de/pyublas/ -__ https://seaborn.pydata.org/ +* `Bootstrap Theme <https://ryan-roemer.github.io/sphinx-bootstrap-theme/>`__ +* `C/C++ Software Development with Eclipse <https://eclipsebook.in/>`__ +* `Dataverse <http://guides.dataverse.org/>`__ +* `e-cidadania <https://e-cidadania.readthedocs.io/>`__ +* `Hangfire <http://docs.hangfire.io/>`__ +* `Hedge <https://documen.tician.de/hedge/>`__ +* `ObsPy <https://docs.obspy.org/>`__ +* `Open Dylan <https://opendylan.org/documentation/>`__ +* `Pootle <http://docs.translatehouse.org/projects/pootle/>`__ +* `PyUblas <https://documen.tician.de/pyublas/>`__ +* `seaborn <https://seaborn.pydata.org/>`__ Documentation using a custom theme or integrated in a website ------------------------------------------------------------- -* `Apache Cassandra`__ -* `Astropy`__ -* `Bokeh`__ -* `Boto 3`__ -* `CakePHP`__ -* `CasperJS`__ -* `Ceph`__ -* `Chef`__ -* `CKAN`__ -* `Confluent Platform`__ -* `Django`__ -* `Doctrine`__ -* `Enterprise Toolkit for Acrobat products`__ -* `Gameduino`__ -* `gensim`__ -* `GeoServer`__ -* `gevent`__ -* `GHC - Glasgow Haskell Compiler`__ -* `Guzzle`__ -* `H2O.ai`__ -* `Istihza (Turkish Python documentation project)`__ -* `Kombu`__ -* `Lasso`__ -* `Mako`__ -* `MirrorBrain`__ -* `MongoDB`__ -* `Music21`__ -* `MyHDL`__ -* `nose`__ -* `ns-3`__ -* `NumPy`__ -* `ObjectListView`__ -* `OpenERP`__ -* `OpenCV`__ -* `OpenLayers`__ -* `OpenTURNS`__ -* `Open vSwitch`__ -* `PlatformIO`__ -* `PyEphem`__ -* `Pygments`__ -* `Plone User Manual (German)`__ -* `PSI4`__ -* `PyMOTW`__ -* `python-aspectlib`__ (`sphinx_py3doc_enhanced_theme <https://pypi.org/project/sphinx_py3doc_enhanced_theme/>`__) -* `QGIS`__ -* `qooxdoo`__ -* `Roundup`__ -* `SaltStack`__ -* `scikit-learn`__ -* `SciPy`__ -* `Scrapy`__ -* `Seaborn`__ -* `Selenium`__ -* `Self`__ -* `Substance D`__ -* `Sulu`__ -* `SQLAlchemy`__ -* `tinyTiM`__ -* `Twisted`__ -* `Ubuntu Packaging Guide`__ -* `WebFaction`__ -* `WTForms`__ - -__ https://cassandra.apache.org/doc/ -__ http://docs.astropy.org/ -__ https://bokeh.pydata.org/ -__ https://boto3.readthedocs.io/ -__ https://book.cakephp.org/ -__ http://docs.casperjs.org/ -__ http://docs.ceph.com/docs/master/ -__ https://docs.chef.io/ -__ http://docs.ckan.org/ -__ http://docs.confluent.io/ -__ https://docs.djangoproject.com/ -__ http://docs.doctrine-project.org/ -__ https://www.adobe.com/devnet-docs/acrobatetk/ -__ http://excamera.com/sphinx/gameduino/ -__ https://radimrehurek.com/gensim/ -__ http://docs.geoserver.org/ -__ http://www.gevent.org/ -__ http://downloads.haskell.org/~ghc/master/users-guide/ -__ http://docs.guzzlephp.org/en/stable/ -__ http://docs.h2o.ai/ -__ https://belgeler.yazbel.com/python-istihza/ -__ http://docs.kombu.me/ -__ http://lassoguide.com/ -__ http://docs.makotemplates.org/ -__ http://mirrorbrain.org/docs/ -__ https://docs.mongodb.com/ -__ http://web.mit.edu/music21/doc/ -__ http://docs.myhdl.org/en/latest/ -__ https://nose.readthedocs.io/ -__ https://www.nsnam.org/documentation/ -__ https://docs.scipy.org/doc/numpy/reference/ -__ http://objectlistview.sourceforge.net/python/ -__ https://doc.odoo.com/ -__ http://docs.opencv.org/ -__ http://docs.openlayers.org/ -__ http://openturns.github.io/openturns/master/ -__ http://docs.openvswitch.org/ -__ http://docs.platformio.org/ -__ http://rhodesmill.org/pyephem/ -__ http://pygments.org/docs/ -__ https://www.hasecke.com/plone-benutzerhandbuch/4.0/ -__ http://www.psicode.org/psi4manual/master/index.html -__ https://pymotw.com/2/ -__ https://python-aspectlib.readthedocs.io/ -__ https://qgis.org/en/docs/index.html -__ http://www.qooxdoo.org/current/ -__ http://www.roundup-tracker.org/ -__ https://docs.saltstack.com/ -__ http://scikit-learn.org/stable/ -__ https://docs.scipy.org/doc/scipy/refrence/ -__ https://doc.scrapy.org/ -__ https://seaborn.pydata.org/ -__ http://docs.seleniumhq.org/docs/ -__ http://www.selflanguage.org/ -__ https://docs.pylonsproject.org/projects/substanced/ -__ http://docs.sulu.io/ -__ https://docs.sqlalchemy.org/ -__ http://tinytim.sourceforge.net/docs/2.0/ -__ http://twistedmatrix.com/documents/current/ -__ http://packaging.ubuntu.com/html/ -__ https://docs.webfaction.com/ -__ https://wtforms.readthedocs.io/ +* `Apache Cassandra <https://cassandra.apache.org/doc/>`__ +* `Astropy <http://docs.astropy.org/>`__ +* `Bokeh <https://bokeh.pydata.org/>`__ +* `Boto 3 <https://boto3.readthedocs.io/>`__ +* `CakePHP <https://book.cakephp.org/>`__ +* `CasperJS <http://docs.casperjs.org/>`__ +* `Ceph <http://docs.ceph.com/docs/master/>`__ +* `Chef <https://docs.chef.io/>`__ +* `CKAN <https://docs.ckan.org/>`__ +* `Confluent Platform <https://docs.confluent.io/>`__ +* `Django <https://docs.djangoproject.com/>`__ +* `Doctrine <https://www.doctrine-project.org/>`__ +* `Enterprise Toolkit for Acrobat products <https://www.adobe.com/devnet-docs/acrobatetk/>`__ +* `Gameduino <http://excamera.com/sphinx/gameduino/>`__ +* `gensim <https://radimrehurek.com/gensim/>`__ +* `GeoServer <http://docs.geoserver.org/>`__ +* `gevent <http://www.gevent.org/>`__ +* `GHC - Glasgow Haskell Compiler <https://downloads.haskell.org/~ghc/master/users-guide/>`__ +* `Guzzle <http://docs.guzzlephp.org/en/stable/>`__ +* `H2O.ai <http://docs.h2o.ai/>`__ +* `Istihza (Turkish Python documentation project) <https://belgeler.yazbel.com/python-istihza/>`__ +* `Kombu <http://docs.kombu.me/>`__ +* `Lasso <http://lassoguide.com/>`__ +* `Mako <http://docs.makotemplates.org/>`__ +* `MirrorBrain <http://mirrorbrain.org/docs/>`__ +* `MongoDB <https://docs.mongodb.com/>`__ +* `Music21 <https://web.mit.edu/music21/doc/>`__ +* `MyHDL <http://docs.myhdl.org/en/latest/>`__ +* `nose <https://nose.readthedocs.io/>`__ +* `ns-3 <https://www.nsnam.org/documentation/>`__ +* `NumPy <https://docs.scipy.org/doc/numpy/reference/>`__ +* `ObjectListView <http://objectlistview.sourceforge.net/python/>`__ +* `OpenERP <https://doc.odoo.com/>`__ +* `OpenCV <https://docs.opencv.org/>`__ +* `OpenLayers <http://docs.openlayers.org/>`__ +* `OpenTURNS <https://openturns.github.io/openturns/master/>`__ +* `Open vSwitch <http://docs.openvswitch.org/>`__ +* `PlatformIO <https://docs.platformio.org/>`__ +* `PyEphem <http://rhodesmill.org/pyephem/>`__ +* `Pygments <http://pygments.org/docs/>`__ +* `Plone User Manual (German) <https://www.hasecke.com/plone-benutzerhandbuch/4.0/>`__ +* `PSI4 <http://www.psicode.org/psi4manual/master/index.html>`__ +* `PyMOTW <https://pymotw.com/2/>`__ +* `python-aspectlib <https://python-aspectlib.readthedocs.io/>`__ (`sphinx_py3doc_enhanced_theme <https://pypi.org/project/sphinx_py3doc_enhanced_theme/>`__) +* `QGIS <https://qgis.org/en/docs/index.html>`__ +* `qooxdoo <https://www.qooxdoo.org/current/>`__ +* `Roundup <http://www.roundup-tracker.org/>`__ +* `SaltStack <https://docs.saltstack.com/>`__ +* `scikit-learn <http://scikit-learn.org/stable/>`__ +* `SciPy <https://docs.scipy.org/doc/scipy/refrence/>`__ +* `Scrapy <https://doc.scrapy.org/>`__ +* `Seaborn <https://seaborn.pydata.org/>`__ +* `Selenium <https://docs.seleniumhq.org/docs/>`__ +* `Self <http://www.selflanguage.org/>`__ +* `Substance D <https://docs.pylonsproject.org/projects/substanced/>`__ +* `Sulu <http://docs.sulu.io/>`__ +* `SQLAlchemy <https://docs.sqlalchemy.org/>`__ +* `tinyTiM <http://tinytim.sourceforge.net/docs/2.0/>`__ +* `Twisted <https://twistedmatrix.com/documents/current/>`__ +* `Ubuntu Packaging Guide <http://packaging.ubuntu.com/html/>`__ +* `WebFaction <https://docs.webfaction.com/>`__ +* `WTForms <https://wtforms.readthedocs.io/>`__ Homepages and other non-documentation sites ------------------------------------------- -* `Arizona State University PHY494/PHY598/CHM598 Simulation approaches to Bio-and Nanophysics`__ (classic) -* `Benoit Boissinot`__ (classic, customized) -* `Computer Networks, Parallelization, and Simulation Laboratory (CNPSLab)`__ (sphinx_rtd_theme) -* `Deep Learning Tutorials`__ (sphinxdoc) -* `Loyola University Chicago COMP 339-439 Distributed Systems course`__ (sphinx_bootstrap_theme) -* `Pylearn2`__ (sphinxdoc, customized) -* `PyXLL`__ (sphinx_bootstrap_theme, customized) -* `SciPy Cookbook`__ (sphinx_rtd_theme) -* `The Wine Cellar Book`__ (sphinxdoc) -* `Thomas Cokelaer's Python, Sphinx and reStructuredText tutorials`__ (standard) -* `UC Berkeley ME233 Advanced Control Systems II course`__ (sphinxdoc) - -__ https://becksteinlab.physics.asu.edu/pages/courses/2013/SimBioNano/ -__ https://bboissin.appspot.com/ -__ https://lab.miletic.net/ -__ http://www.deeplearning.net/tutorial/ -__ http://books.cs.luc.edu/distributedsystems/ -__ http://www.deeplearning.net/software/pylearn2/ -__ https://www.pyxll.com/ -__ https://scipy-cookbook.readthedocs.io/ -__ https://www.thewinecellarbook.com/doc/en/ -__ http://thomas-cokelaer.info/tutorials/ -__ https://berkeley-me233.github.io/ +* `Arizona State University PHY494/PHY598/CHM598 Simulation approaches to Bio-and Nanophysics <https://becksteinlab.physics.asu.edu/pages/courses/2013/SimBioNano/>`__ (classic) +* `Benoit Boissinot <https://bboissin.appspot.com/>`__ (classic, customized) +* `Computer Networks, Parallelization, and Simulation Laboratory (CNPSLab) <https://lab.miletic.net/>`__ (sphinx_rtd_theme) +* `Deep Learning Tutorials <http://www.deeplearning.net/tutorial/>`__ (sphinxdoc) +* `Loyola University Chicago COMP 339-439 Distributed Systems course <https://books.cs.luc.edu/distributedsystems/>`__ (sphinx_bootstrap_theme) +* `Pylearn2 <http://www.deeplearning.net/software/pylearn2/>`__ (sphinxdoc, customized) +* `PyXLL <https://www.pyxll.com/>`__ (sphinx_bootstrap_theme, customized) +* `SciPy Cookbook <https://scipy-cookbook.readthedocs.io/>`__ (sphinx_rtd_theme) +* `The Wine Cellar Book <https://www.thewinecellarbook.com/doc/en/>`__ (sphinxdoc) +* `Thomas Cokelaer's Python, Sphinx and reStructuredText tutorials <https://thomas-cokelaer.info/tutorials/>`__ (standard) +* `UC Berkeley ME233 Advanced Control Systems II course <https://berkeley-me233.github.io/>`__ (sphinxdoc) Books produced using Sphinx --------------------------- -* `"Die Wahrheit des Sehens. Der DEKALOG von Krzysztof Kieślowski"`__ -* `"Expert Python Programming"`__ -* `"Expert Python Programming" (Japanese translation)`__ -* `"The Hitchhiker's Guide to Python"`__ -* `"LassoGuide"`__ -* `"Learning Sphinx" (in Japanese)`__ -* `"Mercurial: the definitive guide (Second edition)"`__ -* `"Pioneers and Prominent Men of Utah"`__ -* `"Pomodoro Technique Illustrated" (Japanese translation)`__ -* `"Python Professional Programming" (in Japanese)`__ -* `"Redmine Primer 5th Edition (in Japanese)"`__ -* `"The repoze.bfg Web Application Framework"`__ -* `"Simple and Steady Way of Learning for Software Engineering" (in Japanese)`__ -* `"Software-Dokumentation mit Sphinx"`__ -* `"Theoretical Physics Reference"`__ -* `"The Varnish Book"`__ - -__ https://literatur.hasecke.com/post/die-wahrheit-des-sehens-dekalog-kieslowski/ -__ https://www.packtpub.com/application-development/expert-python-programming -__ https://www.amazon.co.jp/dp/4048686291/ -__ http://docs.python-guide.org/en/latest/ -__ http://www.lassosoft.com/Lasso-Documentation -__ https://www.oreilly.co.jp/books/9784873116488/ -__ https://book.mercurial-scm.org/ -__ http://pioneers.rstebbing.com/ -__ https://www.amazon.co.jp/dp/4048689525/ -__ http://www.amazon.co.jp/dp/4798032948/ -__ https://www.shuwasystem.co.jp/products/7980html/4825.html -__ https://www.amazon.com/repoze-bfg-Web-Application-Framework-Version/dp/0615345379 -__ https://www.amazon.co.jp/dp/477414259X/ -__ https://www.amazon.de/dp/1497448689/ -__ http://www.theoretical-physics.net/ -__ https://info.varnish-software.com/the-varnish-book +* `"The Art of Community" (Japanese translation) <https://www.oreilly.co.jp/books/9784873114958/>`__ +* `"Die Wahrheit des Sehens. Der DEKALOG von Krzysztof Kieślowski" <https://literatur.hasecke.com/post/die-wahrheit-des-sehens-dekalog-kieslowski/>`__ +* `"Expert Python Programming" <https://www.packtpub.com/application-development/expert-python-programming>`__ +* `"Expert Python Programming" (Japanese translation) <https://www.amazon.co.jp/dp/4048686291/>`__ +* `"Expert Python Programming 2nd Edition" (Japanese translation) <https://www.amazon.co.jp/dp/4048930613/>`__ +* `"The Hitchhiker's Guide to Python" <https://docs.python-guide.org/>`__ +* `"LassoGuide" <http://www.lassosoft.com/Lasso-Documentation>`__ +* `"Learning Sphinx" (in Japanese) <https://www.oreilly.co.jp/books/9784873116488/>`__ +* `"Learning System Programming with Go (Japanese)" <https://www.lambdanote.com/products/go>`__ +* `"Mercurial: the definitive guide (Second edition)" <https://book.mercurial-scm.org/>`__ +* `"Mithril -- The fastest clientside MVC (Japanese)" <https://www.oreilly.co.jp/books/9784873117447/>`__ +* `"Pioneers and Prominent Men of Utah" <http://pioneers.rstebbing.com/>`__ +* `"Pomodoro Technique Illustrated" (Japanese translation) <https://www.amazon.co.jp/dp/4048689525/>`__ +* `"Python Professional Programming" (in Japanese) <http://www.amazon.co.jp/dp/4798032948/>`__ +* `"Python Professional Programming 2nd Edition" (in Japanese) <https://www.amazon.co.jp/dp/479804315X/>`__ +* `"Python Professional Programming 3rd Edition" (in Japanese) <https://www.amazon.co.jp/dp/4798053821/>`__ +* `"Real World HTTP -- Learning The Internet and Web Technology via its history and code (Japanese)" <https://www.oreilly.co.jp/books/9784873118048/>`__ +* `"Redmine Primer 5th Edition (in Japanese)" <https://www.shuwasystem.co.jp/products/7980html/4825.html>`__ +* `"The repoze.bfg Web Application Framework" <https://www.amazon.com/repoze-bfg-Web-Application-Framework-Version/dp/0615345379>`__ +* `"The Self-Taught Programmer" (Japanese translation) <https://www.amazon.co.jp/dp/4822292274/>`__ +* `"Simple and Steady Way of Learning for Software Engineering" (in Japanese) <https://www.amazon.co.jp/dp/477414259X/>`__ +* `"Software-Dokumentation mit Sphinx" <https://www.amazon.de/dp/1497448689/>`__ +* `"Theoretical Physics Reference" <https://www.theoretical-physics.net/>`__ +* `"The Varnish Book" <https://info.varnish-software.com/the-varnish-book>`__ Theses produced using Sphinx ---------------------------- * `"A Web-Based System for Comparative Analysis of OpenStreetMap Data by the Use - of CouchDB"`__ -* `"Content Conditioning and Distribution for Dynamic Virtual Worlds"`__ -* `"The Sphinx Thesis Resource"`__ - -__ https://www.yumpu.com/et/document/view/11722645/masterthesis-markusmayr-0542042 -__ https://www.cs.princeton.edu/research/techreps/TR-941-12 -__ https://jterrace.github.io/sphinxtr/ + of CouchDB" <https://www.yumpu.com/et/document/view/11722645/masterthesis-markusmayr-0542042>`__ +* `"Content Conditioning and Distribution for Dynamic Virtual Worlds" <https://www.cs.princeton.edu/research/techreps/TR-941-12>`__ +* `"The Sphinx Thesis Resource" <https://jterrace.github.io/sphinxtr/>`__ Projects integrating Sphinx functionality ----------------------------------------- -* `Read the Docs`__, a software-as-a-service documentation hosting platform, uses - Sphinx to automatically build documentation updates that are pushed to GitHub +* `Read the Docs <https://readthedocs.org/>`__, a software-as-a-service documentation hosting platform, uses + Sphinx to automatically build documentation updates that are pushed to GitHub. -* `Spyder`__, the Scientific Python Development Environment, uses Sphinx in its +* `Spyder <https://docs.spyder-ide.org/help.html>`__, the Scientific Python Development Environment, uses Sphinx in its help pane to render rich documentation for functions, classes and methods - automatically or on-demand - - __ https://readthedocs.org/ - __ https://docs.spyder-ide.org/help.html + automatically or on-demand. diff --git a/doc/conf.py b/doc/conf.py index 724d355d0..e06d70150 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -144,3 +144,10 @@ def setup(app): names=['param'], can_collapse=True) app.add_object_type('event', 'event', 'pair: %s; event', parse_event, doc_field_types=[fdesc]) + + # workaround for RTD + from sphinx.util import logging + logger = logging.getLogger(__name__) + app.info = lambda *args, **kwargs: logger.info(*args, **kwargs) + app.warn = lambda *args, **kwargs: logger.warning(*args, **kwargs) + app.debug = lambda *args, **kwargs: logger.debug(*args, **kwargs) diff --git a/doc/develop.rst b/doc/develop.rst index d2a51b8e2..60ccaf79b 100644 --- a/doc/develop.rst +++ b/doc/develop.rst @@ -127,7 +127,7 @@ own extensions. .. _NumPy style: https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt .. _hyphenator: https://github.com/mnater/hyphenator .. _exceltable: https://pythonhosted.org/sphinxcontrib-exceltable/ -.. _YouTube: http://www.youtube.com/ +.. _YouTube: https://www.youtube.com/ .. _ClearQuest: https://www.ibm.com/us-en/marketplace/rational-clearquest .. _Zope interfaces: https://zopeinterface.readthedocs.io/en/latest/README.html .. _slideshare: https://www.slideshare.net/ diff --git a/doc/extdev/appapi.rst b/doc/extdev/appapi.rst index ee612765c..5509d6a91 100644 --- a/doc/extdev/appapi.rst +++ b/doc/extdev/appapi.rst @@ -115,32 +115,6 @@ Emitting events .. automethod:: emit_firstresult(event, \*arguments) -Producing messages / logging ----------------------------- - -The application object also provides support for emitting leveled messages. - -.. note:: - - There is no "error" call: in Sphinx, errors are defined as things that stop - the build; just raise an exception (:exc:`sphinx.errors.SphinxError` or a - custom subclass) to do that. - -.. deprecated:: 1.6 - - Please use :ref:`logging-api` instead. - -.. automethod:: Sphinx.warn - -.. automethod:: Sphinx.info - -.. automethod:: Sphinx.verbose - -.. automethod:: Sphinx.debug - -.. automethod:: Sphinx.debug2 - - Sphinx runtime information -------------------------- diff --git a/doc/extdev/envapi.rst b/doc/extdev/envapi.rst index 442cfde15..818a50f8d 100644 --- a/doc/extdev/envapi.rst +++ b/doc/extdev/envapi.rst @@ -39,10 +39,6 @@ Build environment API **Utility methods** - .. automethod:: warn - - .. automethod:: warn_node - .. automethod:: doc2path .. automethod:: relfn2path diff --git a/doc/extdev/index.rst b/doc/extdev/index.rst index bbfcd4188..b75afc40c 100644 --- a/doc/extdev/index.rst +++ b/doc/extdev/index.rst @@ -122,6 +122,31 @@ The following is a list of deprecated interfaces. - (will be) Removed - Alternatives + * - ``suffix`` argument of ``BuildEnvironment.doc2path()`` + - 2.0 + - 4.0 + - N/A + + * - string style ``base`` argument of ``BuildEnvironment.doc2path()`` + - 2.0 + - 4.0 + - ``os.path.join()`` + + * - ``sphinx.ext.doctest.doctest_encode()`` + - 2.0 + - 4.0 + - N/A + + * - ``sphinx.testing.util.remove_unicode_literal()`` + - 2.0 + - 4.0 + - N/A + + * - ``sphinx.util.osutil.walk()`` + - 2.0 + - 4.0 + - ``os.walk()`` + * - :rst:dir:`highlightlang` - 1.8 - 4.0 diff --git a/doc/faq.rst b/doc/faq.rst index b2d6cc9e6..021143f40 100644 --- a/doc/faq.rst +++ b/doc/faq.rst @@ -205,7 +205,7 @@ The following list gives some hints for the creation of epub files: .. _Epubcheck: https://github.com/IDPF/epubcheck .. _Calibre: https://calibre-ebook.com/ .. _FBreader: https://fbreader.org/ -.. _Bookworm: http://www.oreilly.com/bookworm/index.html +.. _Bookworm: https://www.oreilly.com/bookworm/index.html .. _kindlegen: https://www.amazon.com/gp/feature.html?docId=1000765211 .. _texinfo-faq: diff --git a/doc/intl.rst b/doc/intl.rst index 129665dde..ab062dca1 100644 --- a/doc/intl.rst +++ b/doc/intl.rst @@ -326,4 +326,4 @@ There is `sphinx translation page`_ for Sphinx (master) documentation. .. _`sphinx-intl`: https://pypi.org/project/sphinx-intl/ .. _Transifex: https://www.transifex.com/ .. _`sphinx translation page`: https://www.transifex.com/sphinx-doc/sphinx-doc/ -.. _`Transifex Client documentation`: http://docs.transifex.com/developer/client/ +.. _`Transifex Client documentation`: https://docs.transifex.com/client/introduction/ diff --git a/doc/intro.rst b/doc/intro.rst index d44003b71..622d16b83 100644 --- a/doc/intro.rst +++ b/doc/intro.rst @@ -55,15 +55,13 @@ See the :ref:`pertinent section in the FAQ list <usingwith>`. Prerequisites ------------- -Sphinx needs at least **Python 2.7** or **Python 3.4** to run, as well as the -docutils_ and Jinja2_ libraries. Sphinx should work with docutils version 0.10 -or some (not broken) SVN trunk snapshot. If you like to have source code -highlighting support, you must also install the Pygments_ library. +Sphinx needs at least **Python 3.5** to run, as well as the docutils_ and +Jinja2_ libraries. Sphinx should work with docutils version 0.12 or some (not +broken) SVN trunk snapshot. .. _reStructuredText: http://docutils.sourceforge.net/rst.html .. _docutils: http://docutils.sourceforge.net/ .. _Jinja2: http://jinja.pocoo.org/ -.. _Pygments: http://pygments.org/ Usage diff --git a/doc/usage/builders/index.rst b/doc/usage/builders/index.rst index bb107162c..e7ad13bd1 100644 --- a/doc/usage/builders/index.rst +++ b/doc/usage/builders/index.rst @@ -165,16 +165,17 @@ The builder's "name" must be given to the **-b** command-line option of * ``texlive-fonts-recommended`` * ``texlive-latex-extra`` * ``latexmk`` (for ``make latexpdf`` on GNU/Linux and MacOS X) - * ``latex-xcolor`` (old Ubuntu) * ``texlive-luatex``, ``texlive-xetex`` (see :confval:`latex_engine`) - The testing of Sphinx LaTeX is done on Ubuntu trusty with the above - mentioned packages, which are from a TeXLive 2013 snapshot dated - February 2014. + The testing of Sphinx LaTeX is done on Ubuntu xenial with the above mentioned + packages, which are from a TeXLive 2015 snapshot dated March 2016. .. versionchanged:: 1.6 Formerly, testing had been done on Ubuntu precise (TeXLive 2009). + .. versionchanged:: 2.0 + Formerly, testing had been done on Ubuntu trusty (TeXLive 2013). + .. note:: Since 1.6, ``make latexpdf`` uses ``latexmk`` (not on Windows). This @@ -215,7 +216,7 @@ Note that a direct PDF builder is being provided by `rinohtype`_. The builder's name is ``rinoh``. Refer to the `rinohtype manual`_ for details. .. _rinohtype: https://github.com/brechtm/rinohtype -.. _rinohtype manual: http://www.mos6581.org/rinohtype/quickstart.html#sphinx-builder +.. _rinohtype manual: https://www.mos6581.org/rinohtype/quickstart.html#sphinx-builder .. module:: sphinx.builders.text .. class:: TextBuilder diff --git a/doc/usage/configuration.rst b/doc/usage/configuration.rst index 208cd24a2..e7be5fb44 100644 --- a/doc/usage/configuration.rst +++ b/doc/usage/configuration.rst @@ -149,7 +149,10 @@ General configuration .. confval:: master_doc The document name of the "master" document, that is, the document that - contains the root :rst:dir:`toctree` directive. Default is ``'contents'``. + contains the root :rst:dir:`toctree` directive. Default is ``'index'``. + + .. versionchanged:: 2.0 + The defualt is changed to ``'index'`` from ``'contents'``. .. confval:: exclude_patterns @@ -1108,12 +1111,6 @@ that use Sphinx's HTMLWriter class. If true, the reST sources are included in the HTML build as :file:`_sources/{name}`. The default is ``True``. - .. warning:: - - If this config value is set to ``False``, the JavaScript search function - will only display the titles of matching documents, and no excerpt from - the matching contents. - .. confval:: html_show_sourcelink If true (and :confval:`html_copy_source` is true as well), links to the @@ -1251,7 +1248,7 @@ that use Sphinx's HTMLWriter class. :'sphinx.search.ja.DefaultSplitter': TinySegmenter algorithm. This is default splitter. - :'sphinx.search.ja.MeCabSplitter': + :'sphinx.search.ja.MecabSplitter': MeCab binding. To use this splitter, 'mecab' python binding or dynamic link library ('libmecab.so' for linux, 'libmecab.dll' for windows) is required. @@ -1353,6 +1350,19 @@ Options for HTML help output Output file base name for HTML help builder. Default is ``'pydoc'``. +.. confval:: htmlhelp_file_suffix + + This is the file name suffix for generated HTML help files. The + default is ``".html"``. + + .. versionadded:: 2.0 + +.. confval:: htmlhelp_link_suffix + + Suffix for generated links to HTML files. The default is ``".html"``. + + .. versionadded:: 2.0 + .. _applehelp-options: diff --git a/doc/usage/extensions/autodoc.rst b/doc/usage/extensions/autodoc.rst index d33378435..e604cad28 100644 --- a/doc/usage/extensions/autodoc.rst +++ b/doc/usage/extensions/autodoc.rst @@ -45,6 +45,10 @@ docstrings to correct reStructuredText before :mod:`autodoc` processes them. .. _NumPy: https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt + +Directives +---------- + :mod:`autodoc` provides several directives that are versions of the usual :rst:dir:`py:module`, :rst:dir:`py:class` and so forth. On parsing time, they import the corresponding module and extract the docstring of the given objects, @@ -306,6 +310,9 @@ inserting them into the page source under a suitable :rst:dir:`py:module`, well-behaved decorating functions. +Configuration +------------- + There are also new config values that you can set: .. confval:: autoclass_content @@ -432,6 +439,16 @@ There are also new config values that you can set: .. versionadded:: 1.7 +.. confval:: suppress_warnings + :noindex: + + :mod:`autodoc` supports to suppress warning messages via + :confval:`suppress_warnings`. It allows following warnings types in + addition: + + * autodoc + * autodoc.import_object + Docstring preprocessing ----------------------- diff --git a/doc/usage/extensions/example_google.py b/doc/usage/extensions/example_google.py index 4f6abacdf..07870297e 100644 --- a/doc/usage/extensions/example_google.py +++ b/doc/usage/extensions/example_google.py @@ -178,7 +178,7 @@ class ExampleError(Exception): self.code = code -class ExampleClass(object): +class ExampleClass: """The summary line for a class docstring should fit on one line. If the class has public attributes, they may be documented here diff --git a/doc/usage/extensions/example_numpy.py b/doc/usage/extensions/example_numpy.py index dbee080c3..479aea489 100644 --- a/doc/usage/extensions/example_numpy.py +++ b/doc/usage/extensions/example_numpy.py @@ -223,7 +223,7 @@ class ExampleError(Exception): self.code = code -class ExampleClass(object): +class ExampleClass: """The summary line for a class docstring should fit on one line. If the class has public attributes, they may be documented here diff --git a/doc/usage/extensions/inheritance.rst b/doc/usage/extensions/inheritance.rst index ef78d04fe..c66f4130f 100644 --- a/doc/usage/extensions/inheritance.rst +++ b/doc/usage/extensions/inheritance.rst @@ -54,7 +54,7 @@ It adds this directive: E D F """ - class A(object): + class A: pass class B(A): diff --git a/doc/usage/extensions/math.rst b/doc/usage/extensions/math.rst index 9daa03186..9e62c1425 100644 --- a/doc/usage/extensions/math.rst +++ b/doc/usage/extensions/math.rst @@ -183,7 +183,7 @@ Sphinx. The default is empty (not configured). -.. _Using in-line configuration options: http://docs.mathjax.org/en/latest/configuration.html#using-in-line-configuration-options +.. _Using in-line configuration options: https://docs.mathjax.org/en/latest/configuration.html#using-in-line-configuration-options :mod:`sphinx.ext.jsmath` -- Render math via JavaScript ------------------------------------------------------ diff --git a/doc/usage/extensions/napoleon.rst b/doc/usage/extensions/napoleon.rst index 504ef015a..dc52e6375 100644 --- a/doc/usage/extensions/napoleon.rst +++ b/doc/usage/extensions/napoleon.rst @@ -409,10 +409,10 @@ sure that "sphinx.ext.napoleon" is enabled in `conf.py`:: .. attribute:: attr1 - *int* - Description of `attr1` + :type: int + .. confval:: napoleon_use_param True to use a ``:param:`` role for each function parameter. False to diff --git a/doc/usage/installation.rst b/doc/usage/installation.rst index 5c0d7de75..7efb6735d 100644 --- a/doc/usage/installation.rst +++ b/doc/usage/installation.rst @@ -12,10 +12,9 @@ Installing Sphinx Overview -------- -Sphinx is written in `Python`__ and supports both Python 2.7 and Python 3.3+. -We recommend the latter. +Sphinx is written in `Python`__ and supports Python 3.5+. -__ http://docs.python-guide.org/en/latest/ +__ https://docs.python-guide.org/ Linux @@ -73,7 +72,7 @@ Homebrew For more information, refer to the `package overview`__. -__ http://formulae.brew.sh/formula/sphinx-doc +__ https://formulae.brew.sh/formula/sphinx-doc MacPorts ~~~~~~~~ @@ -121,9 +120,9 @@ Once Python is installed, you can install Sphinx using :command:`pip`. Refer to the :ref:`pip installation instructions <install-pypi>` below for more information. -__ http://docs.python-guide.org/en/latest/ -__ http://docs.python-guide.org/en/latest/starting/install3/win/ -__ http://docs.python-guide.org/en/latest/starting/install/win/ +__ https://docs.python-guide.org/ +__ https://docs.python-guide.org/starting/install3/win/ +__ https://docs.python-guide.org/starting/install/win/ .. _install-pypi: diff --git a/doc/usage/markdown.rst b/doc/usage/markdown.rst index f67b94cbd..ed0cce013 100644 --- a/doc/usage/markdown.rst +++ b/doc/usage/markdown.rst @@ -15,7 +15,7 @@ parsing the `CommonMark`__ Markdown flavor. __ https://daringfireball.net/projects/markdown/ __ https://recommonmark.readthedocs.io/en/latest/index.html __ https://github.com/rtfd/CommonMark-py -__ http://commonmark.org/ +__ https://commonmark.org/ Configuration ------------- @@ -11,15 +11,15 @@ import sphinx with open('README.rst') as f: long_desc = f.read() -if sys.version_info < (2, 7) or (3, 0) <= sys.version_info < (3, 4): - print('ERROR: Sphinx requires at least Python 2.7 or 3.4 to run.') +if sys.version_info < (3, 5): + print('ERROR: Sphinx requires at least Python 3.5 to run.') sys.exit(1) install_requires = [ 'six>=1.5', 'Jinja2>=2.3', 'Pygments>=2.0', - 'docutils>=0.11', + 'docutils>=0.12', 'snowballstemmer>=1.1', 'babel>=1.3,!=2.0', 'alabaster>=0.7,<0.8', @@ -27,7 +27,6 @@ install_requires = [ 'requests>=2.0.0', 'setuptools', 'packaging', - 'sphinxcontrib-websupport', ] extras_require = { @@ -35,9 +34,6 @@ extras_require = { ':sys_platform=="win32"': [ 'colorama>=0.3.5', ], - ':python_version<"3.5"': [ - 'typing' - ], 'websupport': [ 'sqlalchemy>=0.9', 'whoosh>=2.0', @@ -49,11 +45,6 @@ extras_require = { 'html5lib', 'flake8>=3.5.0', 'flake8-import-order', - ], - 'test:python_version<"3"': [ - 'enum34', - ], - 'test:python_version>="3"': [ 'mypy', 'typed_ast', ], @@ -65,7 +56,7 @@ extras_require = { cmdclass = {} -class Tee(object): +class Tee: def __init__(self, stream): self.stream = stream self.buffer = StringIO() @@ -195,12 +186,11 @@ setup( 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3 :: Only', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', 'Framework :: Setuptools Plugin', @@ -235,7 +225,7 @@ setup( 'build_sphinx = sphinx.setup_command:BuildDoc', ], }, - python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", + python_requires=">=3.5", install_requires=install_requires, extras_require=extras_require, cmdclass=cmdclass, diff --git a/sphinx/__init__.py b/sphinx/__init__.py index 9b1405728..cdd3b0418 100644 --- a/sphinx/__init__.py +++ b/sphinx/__init__.py @@ -15,12 +15,10 @@ from __future__ import absolute_import import os -import sys import warnings from os import path from .deprecation import RemovedInNextVersionWarning -from .deprecation import RemovedInSphinx20Warning if False: # For type annotation @@ -37,8 +35,8 @@ if 'PYTHONWARNINGS' not in os.environ: warnings.filterwarnings('ignore', "'U' mode is deprecated", DeprecationWarning, module='docutils.io') -__version__ = '1.8.2+' -__released__ = '1.8.2' # used when Sphinx builds its own docs +__version__ = '2.0.0+' +__released__ = '2.0.0' # used when Sphinx builds its own docs #: Version info for better programmatic use. #: @@ -48,7 +46,7 @@ __released__ = '1.8.2' # used when Sphinx builds its own docs #: #: .. versionadded:: 1.2 #: Before version 1.2, check the string ``sphinx.__version__``. -version_info = (1, 8, 2, 'beta', 0) +version_info = (2, 0, 0, 'beta', 0) package_dir = path.abspath(path.dirname(__file__)) @@ -68,47 +66,3 @@ if __version__.endswith('+'): __display_version__ += '/' + out.decode().strip() except Exception: pass - - -def main(argv=sys.argv): # type: ignore - # type: (List[unicode]) -> int - from .cmd import build - warnings.warn( - '`sphinx.main()` has moved to `sphinx.cmd.build.main()`.', - RemovedInSphinx20Warning, - stacklevel=2, - ) - argv = argv[1:] # skip first argument to adjust arguments (refs: #4615) - return build.main(argv) - - -def build_main(argv=sys.argv): - """Sphinx build "main" command-line entry.""" - from .cmd import build - warnings.warn( - '`sphinx.build_main()` has moved to `sphinx.cmd.build.build_main()`.', - RemovedInSphinx20Warning, - stacklevel=2, - ) - return build.build_main(argv[1:]) # skip first argument to adjust arguments (refs: #4615) - - -def make_main(argv=sys.argv): - """Sphinx build "make mode" entry.""" - from .cmd import build - warnings.warn( - '`sphinx.build_main()` has moved to `sphinx.cmd.build.make_main()`.', - RemovedInSphinx20Warning, - stacklevel=2, - ) - return build.make_main(argv[1:]) # skip first argument to adjust arguments (refs: #4615) - - -if __name__ == '__main__': - from .cmd import build - warnings.warn( - '`sphinx` has moved to `sphinx.build`.', - RemovedInSphinx20Warning, - stacklevel=2, - ) - build.main() diff --git a/sphinx/addnodes.py b/sphinx/addnodes.py index 331fe5225..b69533751 100644 --- a/sphinx/addnodes.py +++ b/sphinx/addnodes.py @@ -20,7 +20,7 @@ if False: from typing import List, Sequence # NOQA -class translatable(object): +class translatable: """Node which supports translation. The translation goes forward with following steps: @@ -53,7 +53,7 @@ class translatable(object): raise NotImplementedError -class not_smartquotable(object): +class not_smartquotable: """A node which does not support smart-quotes.""" support_smartquotes = False diff --git a/sphinx/apidoc.py b/sphinx/apidoc.py deleted file mode 100644 index 95a1d14f7..000000000 --- a/sphinx/apidoc.py +++ /dev/null @@ -1,41 +0,0 @@ -# -*- coding: utf-8 -*- -""" - sphinx.apidoc - ~~~~~~~~~~~~~ - - This file has moved to :py:mod:`sphinx.ext.apidoc`. - - :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. - :license: BSD, see LICENSE for details. -""" - -import sys -import warnings - -from sphinx.deprecation import RemovedInSphinx20Warning -from sphinx.ext.apidoc import main as _main - -if False: - # For type annotation - from typing import List # NOQA - from sphinx.application import Sphinx # NOQA - - -def main(argv=sys.argv): - # type: (List[str]) -> None - warnings.warn( - '`sphinx.apidoc.main()` has moved to `sphinx.ext.apidoc.main()`.', - RemovedInSphinx20Warning, - stacklevel=2, - ) - _main(argv[1:]) # skip first argument to adjust arguments (refs: #4615) - - -# So program can be started with "python -m sphinx.apidoc ..." -if __name__ == "__main__": - warnings.warn( - '`sphinx.apidoc` has moved to `sphinx.ext.apidoc`.', - RemovedInSphinx20Warning, - stacklevel=2, - ) - main() diff --git a/sphinx/application.py b/sphinx/application.py index 64433915a..c595b7719 100644 --- a/sphinx/application.py +++ b/sphinx/application.py @@ -13,6 +13,7 @@ from __future__ import print_function import os +import pickle import sys import warnings from collections import deque @@ -20,8 +21,6 @@ from inspect import isclass from os import path from docutils.parsers.rst import Directive, directives, roles -from six import itervalues -from six.moves import cPickle as pickle from six.moves import cStringIO import sphinx @@ -29,7 +28,7 @@ from sphinx import package_dir, locale from sphinx.config import Config, check_unicode from sphinx.config import CONFIG_FILENAME # NOQA # for compatibility (RemovedInSphinx30) from sphinx.deprecation import ( - RemovedInSphinx20Warning, RemovedInSphinx30Warning, RemovedInSphinx40Warning + RemovedInSphinx30Warning, RemovedInSphinx40Warning ) from sphinx.environment import BuildEnvironment from sphinx.errors import ApplicationError, ConfigError, VersionRequirementError @@ -118,7 +117,7 @@ ENV_PICKLE_FILENAME = 'environment.pickle' logger = logging.getLogger(__name__) -class Sphinx(object): +class Sphinx: """The main application class and extensibility interface. :ivar srcdir: Directory containing source. @@ -250,11 +249,6 @@ class Sphinx(object): self.config.init_values() self.emit('config-inited', self.config) - # check primary_domain if requested - primary_domain = self.config.primary_domain - if primary_domain and not self.registry.has_domain(primary_domain): - logger.warning(__('primary_domain %r not found, ignored.'), primary_domain) - # create the builder self.builder = self.create_builder(buildername) # set up the build environment @@ -369,72 +363,6 @@ class Sphinx(object): self.emit('build-finished', None) self.builder.cleanup() - # ---- logging handling ---------------------------------------------------- - def warn(self, message, location=None, type=None, subtype=None): - # type: (unicode, unicode, unicode, unicode) -> None - """Emit a warning. - - If *location* is given, it should either be a tuple of (*docname*, - *lineno*) or a string describing the location of the warning as well as - possible. - - *type* and *subtype* are used to suppress warnings with - :confval:`suppress_warnings`. - - .. deprecated:: 1.6 - Use :mod:`sphinx.util.logging` instead. - """ - warnings.warn('app.warning() is now deprecated. Use sphinx.util.logging instead.', - RemovedInSphinx20Warning) - logger.warning(message, type=type, subtype=subtype, location=location) - - def info(self, message='', nonl=False): - # type: (unicode, bool) -> None - """Emit an informational message. - - If *nonl* is true, don't emit a newline at the end (which implies that - more info output will follow soon.) - - .. deprecated:: 1.6 - Use :mod:`sphinx.util.logging` instead. - """ - warnings.warn('app.info() is now deprecated. Use sphinx.util.logging instead.', - RemovedInSphinx20Warning) - logger.info(message, nonl=nonl) - - def verbose(self, message, *args, **kwargs): - # type: (unicode, Any, Any) -> None - """Emit a verbose informational message. - - .. deprecated:: 1.6 - Use :mod:`sphinx.util.logging` instead. - """ - warnings.warn('app.verbose() is now deprecated. Use sphinx.util.logging instead.', - RemovedInSphinx20Warning) - logger.verbose(message, *args, **kwargs) - - def debug(self, message, *args, **kwargs): - # type: (unicode, Any, Any) -> None - """Emit a debug-level informational message. - - .. deprecated:: 1.6 - Use :mod:`sphinx.util.logging` instead. - """ - warnings.warn('app.debug() is now deprecated. Use sphinx.util.logging instead.', - RemovedInSphinx20Warning) - logger.debug(message, *args, **kwargs) - - def debug2(self, message, *args, **kwargs): - # type: (unicode, Any, Any) -> None - """Emit a lowlevel debug-level informational message. - - .. deprecated:: 1.6 - Use :mod:`sphinx.util.logging` instead. - """ - warnings.warn('app.debug2() is now deprecated. Use debug() instead.', - RemovedInSphinx20Warning) - logger.debug(message, *args, **kwargs) - # ---- general extensibility interface ------------------------------------- def setup_extension(self, extname): @@ -913,21 +841,6 @@ class Sphinx(object): ref_nodeclass, objname, doc_field_types, override=override) - def add_description_unit(self, directivename, rolename, indextemplate='', - parse_node=None, ref_nodeclass=None, objname='', - doc_field_types=[]): - # type: (unicode, unicode, unicode, Callable, nodes.Node, unicode, List) -> None - """Deprecated alias for :meth:`add_object_type`. - - .. deprecated:: 1.6 - Use :meth:`add_object_type` instead. - """ - warnings.warn('app.add_description_unit() is now deprecated. ' - 'Use app.add_object_type() instead.', - RemovedInSphinx20Warning) - self.add_object_type(directivename, rolename, indextemplate, parse_node, - ref_nodeclass, objname, doc_field_types) - def add_crossref_type(self, directivename, rolename, indextemplate='', ref_nodeclass=None, objname='', override=False): # type: (unicode, unicode, unicode, nodes.Node, unicode, bool) -> None @@ -1282,7 +1195,7 @@ class Sphinx(object): else: raise ValueError('parallel type %s is not supported' % typ) - for ext in itervalues(self.extensions): + for ext in self.extensions.values(): allowed = getattr(ext, attrname, None) if allowed is None: logger.warning(message, ext.name) @@ -1294,7 +1207,7 @@ class Sphinx(object): return True -class TemplateBridge(object): +class TemplateBridge: """ This class defines the interface for a "template bridge", that is, a class that renders templates given a template name and a context. diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py index 9c7537631..077ab6abe 100644 --- a/sphinx/builders/__init__.py +++ b/sphinx/builders/__init__.py @@ -9,14 +9,12 @@ :license: BSD, see LICENSE for details. """ +import pickle import time -import warnings from os import path from docutils import nodes -from six.moves import cPickle as pickle -from sphinx.deprecation import RemovedInSphinx20Warning from sphinx.environment import CONFIG_OK, CONFIG_CHANGED_REASON from sphinx.environment.adapters.asset import ImageAdapter from sphinx.errors import SphinxError @@ -54,7 +52,7 @@ if False: logger = logging.getLogger(__name__) -class Builder(object): +class Builder: """ Builds target formats from the reST sources. """ @@ -97,8 +95,6 @@ class Builder(object): self.app = app # type: Sphinx self.env = None # type: BuildEnvironment - self.warn = app.warn # type: Callable - self.info = app.info # type: Callable self.config = app.config # type: Config self.tags = app.tags # type: Tags self.tags.add(self.format) @@ -138,22 +134,6 @@ class Builder(object): """ return self.app.registry.create_translator(self, *args) - @property - def translator_class(self): - # type: () -> Callable[[Any], nodes.NodeVisitor] - """Return a class of translator. - - .. deprecated:: 1.6 - """ - translator_class = self.app.registry.get_translator_class(self) - if translator_class is None and self.default_translator_class is None: - warnings.warn('builder.translator_class() is now deprecated. ' - 'Please use builder.create_translator() and ' - 'builder.default_translator_class instead.', - RemovedInSphinx20Warning) - return None - return self.create_translator - # helper methods def init(self): # type: () -> None @@ -556,7 +536,7 @@ class Builder(object): doctree.settings.env = None doctree.settings.record_dependencies = None - doctree_filename = self.env.doc2path(docname, self.env.doctreedir, '.doctree') + doctree_filename = path.join(self.doctreedir, docname + '.doctree') ensuredir(path.dirname(doctree_filename)) with open(doctree_filename, 'wb') as f: pickle.dump(doctree, f, pickle.HIGHEST_PROTOCOL) diff --git a/sphinx/builders/applehelp.py b/sphinx/builders/applehelp.py index 79d57210c..b29ce3d95 100644 --- a/sphinx/builders/applehelp.py +++ b/sphinx/builders/applehelp.py @@ -10,7 +10,6 @@ """ from __future__ import print_function -import codecs import pipes import plistlib import shlex @@ -36,13 +35,6 @@ if False: logger = logging.getLogger(__name__) -# Use plistlib.dump in 3.4 and above -try: - write_plist = plistlib.dump # type: ignore -except AttributeError: - write_plist = plistlib.writePlist - - # False access page (used because helpd expects strict XHTML) access_page_template = '''\ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"\ @@ -174,7 +166,7 @@ class AppleHelpBuilder(StandaloneHTMLBuilder): logger.info(bold(__('writing Info.plist... ')), nonl=True) with open(path.join(contents_dir, 'Info.plist'), 'wb') as f: - write_plist(info_plist, f) + plistlib.dump(info_plist, f) # type: ignore logger.info(__('done')) # Copy the icon, if one is supplied @@ -193,7 +185,7 @@ class AppleHelpBuilder(StandaloneHTMLBuilder): # Build the access page logger.info(bold(__('building access page...')), nonl=True) - with codecs.open(path.join(language_dir, '_access.html'), 'w') as f: # type: ignore + with open(path.join(language_dir, '_access.html'), 'w') as f: f.write(access_page_template % { 'toc': htmlescape(toc, quote=True), 'title': htmlescape(self.config.applehelp_title) diff --git a/sphinx/builders/changes.py b/sphinx/builders/changes.py index 3f9bffa0d..888a011da 100644 --- a/sphinx/builders/changes.py +++ b/sphinx/builders/changes.py @@ -9,12 +9,9 @@ :license: BSD, see LICENSE for details. """ -import codecs from os import path from typing import cast -from six import iteritems - from sphinx import package_dir from sphinx.builders import Builder from sphinx.domains.changeset import ChangeSetDomain @@ -109,15 +106,17 @@ class ChangesBuilder(Builder): 'version': version, 'docstitle': self.config.html_title, 'shorttitle': self.config.html_short_title, - 'libchanges': sorted(iteritems(libchanges)), + 'libchanges': sorted(libchanges.items()), 'apichanges': sorted(apichanges), - 'otherchanges': sorted(iteritems(otherchanges)), + 'otherchanges': sorted(otherchanges.items()), 'show_copyright': self.config.html_show_copyright, 'show_sphinx': self.config.html_show_sphinx, } - with codecs.open(path.join(self.outdir, 'index.html'), 'w', 'utf8') as f: # type: ignore # NOQA + with open(path.join(self.outdir, 'index.html'), 'w', # type: ignore + encoding='utf8') as f: f.write(self.templates.render('changes/frameset.html', ctx)) - with codecs.open(path.join(self.outdir, 'changes.html'), 'w', 'utf8') as f: # type: ignore # NOQA + with open(path.join(self.outdir, 'changes.html'), 'w', # type: ignore + encoding='utf8') as f: f.write(self.templates.render('changes/versionchanges.html', ctx)) hltext = ['.. versionadded:: %s' % version, @@ -135,8 +134,8 @@ class ChangesBuilder(Builder): logger.info(bold(__('copying source files...'))) for docname in self.env.all_docs: - with codecs.open(self.env.doc2path(docname), 'r', # type: ignore - self.env.config.source_encoding) as f: + with open(self.env.doc2path(docname), 'r', # type: ignore + encoding=self.env.config.source_encoding) as f: try: lines = f.readlines() except UnicodeDecodeError: @@ -144,7 +143,7 @@ class ChangesBuilder(Builder): continue targetfn = path.join(self.outdir, 'rst', os_path(docname)) + '.html' ensuredir(path.dirname(targetfn)) - with codecs.open(targetfn, 'w', 'utf-8') as f: # type: ignore + with open(targetfn, 'w', encoding='utf-8') as f: # type: ignore text = ''.join(hl(i + 1, line) for (i, line) in enumerate(lines)) ctx = { 'filename': self.env.doc2path(docname, None), @@ -152,7 +151,7 @@ class ChangesBuilder(Builder): } f.write(self.templates.render('changes/rstsource.html', ctx)) themectx = dict(('theme_' + key, val) for (key, val) in - iteritems(self.theme.get_options({}))) + self.theme.get_options({}).items()) copy_asset_file(path.join(package_dir, 'themes', 'default', 'static', 'default.css_t'), self.outdir, context=themectx, renderer=self.templates) copy_asset_file(path.join(package_dir, 'themes', 'basic', 'static', 'basic.css'), diff --git a/sphinx/builders/devhelp.py b/sphinx/builders/devhelp.py index fc2c0b1c9..f81154984 100644 --- a/sphinx/builders/devhelp.py +++ b/sphinx/builders/devhelp.py @@ -15,6 +15,7 @@ from __future__ import absolute_import import gzip import re from os import path +from typing import Any from docutils import nodes @@ -23,6 +24,7 @@ from sphinx.builders.html import StandaloneHTMLBuilder from sphinx.environment.adapters.indexentries import IndexEntries from sphinx.locale import __ from sphinx.util import logging +from sphinx.util.nodes import NodeMatcher from sphinx.util.osutil import make_filename try: @@ -32,7 +34,7 @@ except ImportError: if False: # For type annotation - from typing import Any, Dict, List # NOQA + from typing import Dict, List # NOQA from sphinx.application import Sphinx # NOQA @@ -100,12 +102,8 @@ class DevhelpBuilder(StandaloneHTMLBuilder): parent.attrib['link'] = node['refuri'] parent.attrib['name'] = node.astext() - def istoctree(node): - # type: (nodes.Node) -> bool - return isinstance(node, addnodes.compact_paragraph) and \ - 'toctree' in node - - for node in tocdoc.traverse(istoctree): + matcher = NodeMatcher(addnodes.compact_paragraph, toctree=Any) + for node in tocdoc.traverse(matcher): write_toc(node, chapters) # Index diff --git a/sphinx/builders/gettext.py b/sphinx/builders/gettext.py index 9981012a0..fbec9aae2 100644 --- a/sphinx/builders/gettext.py +++ b/sphinx/builders/gettext.py @@ -18,7 +18,7 @@ from os import path, walk, getenv from time import time from uuid import uuid4 -from six import iteritems, StringIO +from six import StringIO from sphinx.builders import Builder from sphinx.domains.python import pairindextypes @@ -63,7 +63,7 @@ msgstr "" """[1:] -class Catalog(object): +class Catalog: """Catalog of translatable messages.""" def __init__(self): @@ -85,7 +85,7 @@ class Catalog(object): self.metadata[msg].append((origin.source, origin.line, origin.uid)) -class MsgOrigin(object): +class MsgOrigin: """ Origin holder for Catalog message origin. """ @@ -272,7 +272,7 @@ class MessageCatalogBuilder(I18nBuilder): ctime = datetime.fromtimestamp( timestamp, ltz).strftime('%Y-%m-%d %H:%M%z'), ) - for textdomain, catalog in status_iterator(iteritems(self.catalogs), # type: ignore + for textdomain, catalog in status_iterator(self.catalogs.items(), # type: ignore __("writing message catalogs... "), "darkgreen", len(self.catalogs), self.app.verbosity, diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index e8558d054..9689b34ff 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -9,7 +9,7 @@ :license: BSD, see LICENSE for details. """ -import codecs +import pickle import posixpath import re import sys @@ -25,14 +25,13 @@ from docutils.frontend import OptionParser from docutils.io import DocTreeInput, StringOutput from docutils.readers.doctree import Reader as DoctreeReader from docutils.utils import relative_path -from six import iteritems, text_type, string_types -from six.moves import cPickle as pickle +from six import text_type, string_types from sphinx import package_dir, __display_version__ from sphinx.application import ENV_PICKLE_FILENAME from sphinx.builders import Builder from sphinx.config import string_classes -from sphinx.deprecation import RemovedInSphinx20Warning, RemovedInSphinx30Warning +from sphinx.deprecation import RemovedInSphinx30Warning from sphinx.environment.adapters.asset import ImageAdapter from sphinx.environment.adapters.indexentries import IndexEntries from sphinx.environment.adapters.toctree import TocTree @@ -92,53 +91,6 @@ def get_stable_hash(obj): return md5(text_type(obj).encode('utf8')).hexdigest() -class CSSContainer(list): - """The container for stylesheets. - - To support the extensions which access the container directly, this wraps - the entry with Stylesheet class. - """ - def append(self, obj): - # type: (Union[unicode, Stylesheet]) -> None - if isinstance(obj, Stylesheet): - super(CSSContainer, self).append(obj) - else: - super(CSSContainer, self).append(Stylesheet(obj)) - - def insert(self, index, obj): - # type: (int, Union[unicode, Stylesheet]) -> None - warnings.warn('builder.css_files is deprecated. ' - 'Please use app.add_stylesheet() instead.', - RemovedInSphinx20Warning) - if isinstance(obj, Stylesheet): - super(CSSContainer, self).insert(index, obj) - else: - super(CSSContainer, self).insert(index, Stylesheet(obj)) - - def extend(self, other): # type: ignore - # type: (List[Union[unicode, Stylesheet]]) -> None - warnings.warn('builder.css_files is deprecated. ' - 'Please use app.add_stylesheet() instead.', - RemovedInSphinx20Warning) - for item in other: - self.append(item) - - def __iadd__(self, other): # type: ignore - # type: (List[Union[unicode, Stylesheet]]) -> CSSContainer - warnings.warn('builder.css_files is deprecated. ' - 'Please use app.add_stylesheet() instead.', - RemovedInSphinx20Warning) - for item in other: - self.append(item) - return self - - def __add__(self, other): - # type: (List[Union[unicode, Stylesheet]]) -> CSSContainer - ret = CSSContainer(self) - ret += other - return ret - - class Stylesheet(text_type): """A metadata of stylesheet. @@ -216,7 +168,7 @@ class JavaScript(text_type): return self -class BuildInfo(object): +class BuildInfo: """buildinfo file manipulator. HTMLBuilder and its family are storing their own envdata to ``.buildinfo``. @@ -256,10 +208,6 @@ class BuildInfo(object): return (self.config_hash == other.config_hash and self.tags_hash == other.tags_hash) - def __ne__(self, other): # type: ignore - # type: (BuildInfo) -> bool - return not (self == other) # for py27 - def dump(self, f): # type: (IO) -> None f.write('# Sphinx build info version 1\n' @@ -311,7 +259,7 @@ class StandaloneHTMLBuilder(Builder): super(StandaloneHTMLBuilder, self).__init__(app) # CSS files - self.css_files = CSSContainer() # type: List[Dict[unicode, unicode]] + self.css_files = [] # type: List[Dict[unicode, unicode]] # JS files self.script_files = JSContainer() # type: List[JavaScript] @@ -330,20 +278,23 @@ class StandaloneHTMLBuilder(Builder): self.init_highlighter() self.init_css_files() self.init_js_files() - if self.config.html_file_suffix is not None: - self.out_suffix = self.config.html_file_suffix - if self.config.html_link_suffix is not None: - self.link_suffix = self.config.html_link_suffix + html_file_suffix = self.get_builder_config('file_suffix', 'html') + if html_file_suffix is not None: + self.out_suffix = html_file_suffix + + html_link_suffix = self.get_builder_config('link_suffix', 'html') + if html_link_suffix is not None: + self.link_suffix = html_link_suffix else: self.link_suffix = self.out_suffix self.use_index = self.get_builder_config('use_index', 'html') if self.config.html_experimental_html5_writer and not html5_ready: - self.app.warn(('html_experimental_html5_writer is set, but current version ' - 'is old. Docutils\' version should be 0.13 or newer, but %s.') % - docutils.__version__) + logger.warning(__('html_experimental_html5_writer is set, but current version ' + 'is old. Docutils\' version should be 0.13 or newer, but %s.'), + docutils.__version__) def create_build_info(self): # type: () -> BuildInfo @@ -633,7 +584,7 @@ class StandaloneHTMLBuilder(Builder): if self.theme: self.globalcontext.update( ('theme_' + key, val) for (key, val) in - iteritems(self.theme.get_options(self.theme_options))) + self.theme.get_options(self.theme_options).items()) self.globalcontext.update(self.config.html_context) def get_doc_context(self, docname, body, metatags): @@ -1003,9 +954,9 @@ class StandaloneHTMLBuilder(Builder): try: searchindexfn = path.join(self.outdir, self.searchindex_filename) if self.indexer_dumps_unicode: - f = codecs.open(searchindexfn, 'r', encoding='utf-8') # type: ignore + f = open(searchindexfn, 'r', encoding='utf-8') # type: ignore else: - f = open(searchindexfn, 'rb') # type: ignore + f = open(searchindexfn, 'rb') with f: self.indexer.load(f, self.indexer_format) except (IOError, OSError, ValueError): @@ -1067,7 +1018,7 @@ class StandaloneHTMLBuilder(Builder): # user sidebar settings html_sidebars = self.get_builder_config('sidebars', 'html') - for pattern, patsidebars in iteritems(html_sidebars): + for pattern, patsidebars in html_sidebars.items(): if patmatch(pagename, pattern): if matched: if has_wildcard(pattern): @@ -1085,14 +1036,6 @@ class StandaloneHTMLBuilder(Builder): if sidebars is None: # keep defaults pass - elif isinstance(sidebars, string_types): - # 0.x compatible mode: insert custom sidebar before searchbox - customsidebar = sidebars - sidebars = None - warnings.warn('Now html_sidebars only allows list of sidebar ' - 'templates as a value. Support for a string value ' - 'will be removed at Sphinx-2.0.', - RemovedInSphinx20Warning) ctx['sidebars'] = sidebars ctx['customsidebar'] = customsidebar @@ -1162,7 +1105,7 @@ class StandaloneHTMLBuilder(Builder): warnings.warn('The template function warn() was deprecated. ' 'Use warning() instead.', RemovedInSphinx30Warning) - self.warn(*args, **kwargs) + logger.warning(*args, **kwargs) return '' # return empty string ctx['warn'] = warn @@ -1192,7 +1135,8 @@ class StandaloneHTMLBuilder(Builder): # outfilename's path is in general different from self.outdir ensuredir(path.dirname(outfilename)) try: - with codecs.open(outfilename, 'w', ctx['encoding'], 'xmlcharrefreplace') as f: # type: ignore # NOQA + with open(outfilename, 'w', # type: ignore + encoding=ctx['encoding'], errors='xmlcharrefreplace') as f: f.write(output) except (IOError, OSError) as err: logger.warning(__("error writing file %s: %s"), outfilename, err) @@ -1229,9 +1173,9 @@ class StandaloneHTMLBuilder(Builder): # first write to a temporary file, so that if dumping fails, # the existing index won't be overwritten if self.indexer_dumps_unicode: - f = codecs.open(searchindexfn + '.tmp', 'w', encoding='utf-8') # type: ignore + f = open(searchindexfn + '.tmp', 'w', encoding='utf-8') # type: ignore else: - f = open(searchindexfn + '.tmp', 'wb') # type: ignore + f = open(searchindexfn + '.tmp', 'wb') with f: self.indexer.dump(f, self.indexer_format) movefile(searchindexfn + '.tmp', searchindexfn) @@ -1346,8 +1290,8 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder): # There are related codes in inline_all_toctres() and # HTMLTranslter#add_secnumber(). new_secnumbers = {} # type: Dict[unicode, Tuple[int, ...]] - for docname, secnums in iteritems(self.env.toc_secnumbers): - for id, secnum in iteritems(secnums): + for docname, secnums in self.env.toc_secnumbers.items(): + for id, secnum in secnums.items(): alias = "%s/%s" % (docname, id) new_secnumbers[alias] = secnum @@ -1366,11 +1310,11 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder): # HTMLTranslter#add_fignumber(). new_fignumbers = {} # type: Dict[unicode, Dict[unicode, Tuple[int, ...]]] # {u'foo': {'figure': {'id2': (2,), 'id1': (1,)}}, u'bar': {'figure': {'id1': (3,)}}} - for docname, fignumlist in iteritems(self.env.toc_fignumbers): - for figtype, fignums in iteritems(fignumlist): + for docname, fignumlist in self.env.toc_fignumbers.items(): + for figtype, fignums in fignumlist.items(): alias = "%s/%s" % (docname, figtype) new_fignumbers.setdefault(alias, {}) - for id, fignum in iteritems(fignums): + for id, fignum in fignums.items(): new_fignumbers[alias][id] = fignum return {self.config.master_doc: new_fignumbers} @@ -1488,9 +1432,9 @@ class SerializingHTMLBuilder(StandaloneHTMLBuilder): def dump_context(self, context, filename): # type: (Dict, unicode) -> None if self.implementation_dumps_unicode: - f = codecs.open(filename, 'w', encoding='utf-8') # type: ignore + f = open(filename, 'w', encoding='utf-8') # type: ignore else: - f = open(filename, 'wb') # type: ignore + f = open(filename, 'wb') with f: self.implementation.dump(context, f, *self.additional_dump_args) diff --git a/sphinx/builders/htmlhelp.py b/sphinx/builders/htmlhelp.py index 49d48361e..252e5050d 100644 --- a/sphinx/builders/htmlhelp.py +++ b/sphinx/builders/htmlhelp.py @@ -11,7 +11,6 @@ """ from __future__ import print_function -import codecs import os from os import path @@ -19,6 +18,7 @@ from docutils import nodes from sphinx import addnodes from sphinx.builders.html import StandaloneHTMLBuilder +from sphinx.config import string_classes from sphinx.environment.adapters.indexentries import IndexEntries from sphinx.locale import __ from sphinx.util import logging @@ -133,8 +133,9 @@ that the their then there these they this to was will with """.split() -# The following list includes only languages supported by Sphinx. -# See http://msdn.microsoft.com/en-us/library/ms930130.aspx for more. +# The following list includes only languages supported by Sphinx. See +# https://docs.microsoft.com/en-us/previous-versions/windows/embedded/ms930130(v=msdn.10) +# for more. chm_locales = { # lang: LCID, encoding 'ca': (0x403, 'cp1252'), @@ -195,10 +196,10 @@ class HTMLHelpBuilder(StandaloneHTMLBuilder): def init(self): # type: () -> None - StandaloneHTMLBuilder.init(self) - # the output files for HTML help must be .html only + # the output files for HTML help is .html by default self.out_suffix = '.html' self.link_suffix = '.html' + StandaloneHTMLBuilder.init(self) # determine the correct locale setting locale = chm_locales.get(self.config.language) if locale is not None: @@ -207,8 +208,8 @@ class HTMLHelpBuilder(StandaloneHTMLBuilder): def open_file(self, outdir, basename, mode='w'): # type: (unicode, unicode, unicode) -> IO # open a file with the correct encoding for the selected language - return codecs.open(path.join(outdir, basename), mode, # type: ignore - self.encoding, 'xmlcharrefreplace') + return open(path.join(outdir, basename), mode, # type: ignore + encoding=self.encoding, errors='xmlcharrefreplace') def update_page_context(self, pagename, templatename, ctx, event_arg): # type: (unicode, unicode, Dict, unicode) -> None @@ -341,6 +342,8 @@ def setup(app): app.add_builder(HTMLHelpBuilder) app.add_config_value('htmlhelp_basename', lambda self: make_filename(self.project), None) + app.add_config_value('htmlhelp_file_suffix', None, 'html', string_classes) + app.add_config_value('htmlhelp_link_suffix', None, 'html', string_classes) return { 'version': 'builtin', diff --git a/sphinx/builders/latex/__init__.py b/sphinx/builders/latex/__init__.py index bbbcce0dc..c4335885f 100644 --- a/sphinx/builders/latex/__init__.py +++ b/sphinx/builders/latex/__init__.py @@ -34,7 +34,10 @@ from sphinx.util.docutils import SphinxFileOutput, new_document from sphinx.util.fileutil import copy_asset_file from sphinx.util.nodes import inline_all_toctrees from sphinx.util.osutil import SEP, make_filename -from sphinx.writers.latex import DEFAULT_SETTINGS, LaTeXWriter, LaTeXTranslator +from sphinx.util.template import LaTeXRenderer +from sphinx.writers.latex import ( + ADDITIONAL_SETTINGS, DEFAULT_SETTINGS, LaTeXWriter, LaTeXTranslator +) if False: # For type annotation @@ -126,11 +129,14 @@ class LaTeXBuilder(Builder): def init(self): # type: () -> None + self.context = {} # type: Dict[unicode, Any] self.docnames = [] # type: Iterable[unicode] self.document_data = [] # type: List[Tuple[unicode, unicode, unicode, unicode, unicode, bool]] # NOQA self.usepackages = self.app.registry.latex_packages texescape.init() + self.init_context() + def get_outdated_docs(self): # type: () -> Union[unicode, List[unicode]] return 'all documents' # for now @@ -167,6 +173,31 @@ class LaTeXBuilder(Builder): docname = docname[:-5] self.titles.append((docname, entry[2])) + def init_context(self): + # type: () -> None + self.context = DEFAULT_SETTINGS.copy() + + # Add special settings for latex_engine + self.context.update(ADDITIONAL_SETTINGS.get(self.config.latex_engine, {})) + + # for xelatex+French, don't use polyglossia by default + if self.config.latex_engine == 'xelatex': + if self.config.language: + if self.config.language[:2] == 'fr': + self.context['polyglossia'] = '' + self.context['babel'] = r'\usepackage{babel}' + + # Apply user settings to context + self.context.update(self.config.latex_elements) + self.context['release'] = self.config.release + self.context['use_xindy'] = self.config.latex_use_xindy + + # for compatibilities + self.context['indexname'] = _('Index') + if self.config.release: + # Show the release label only if release value exists + self.context['releasename'] = _('Release') + def write_stylesheet(self): # type: () -> None highlighter = highlighting.PygmentsBridge('latex', self.config.pygments_style) @@ -210,6 +241,8 @@ class LaTeXBuilder(Builder): doctree['tocdepth'] = tocdepth self.apply_transforms(doctree) self.post_process_images(doctree) + self.update_doc_context(title, author) + logger.info(__("writing... "), nonl=1) doctree.settings = docsettings doctree.settings.author = author @@ -231,6 +264,11 @@ class LaTeXBuilder(Builder): return contentsname + def update_doc_context(self, title, author): + # type: (unicode, unicode) -> None + self.context['title'] = title + self.context['author'] = author + def assemble_doctree(self, indexfile, toctree_only, appendices): # type: (unicode, bool, List[unicode]) -> nodes.Node from docutils import nodes # NOQA @@ -290,6 +328,7 @@ class LaTeXBuilder(Builder): def finish(self): # type: () -> None self.copy_image_files() + self.write_message_catalog() # copy TeX support files from texinputs # configure usage of xindy (impacts Makefile and latexmkrc) @@ -353,6 +392,11 @@ class LaTeXBuilder(Builder): logger.warning(__('cannot copy image file %r: %s'), path.join(self.srcdir, src), err) + def write_message_catalog(self): + # type: () -> None + filename = path.join(package_dir, 'templates', 'latex', 'sphinxmessages.sty_t') + copy_asset_file(filename, self.outdir, context={}, renderer=LaTeXRenderer()) + def validate_config_values(app, config): # type: (Sphinx, Config) -> None diff --git a/sphinx/builders/latex/transforms.py b/sphinx/builders/latex/transforms.py index 160c8c324..afc580f9d 100644 --- a/sphinx/builders/latex/transforms.py +++ b/sphinx/builders/latex/transforms.py @@ -16,6 +16,7 @@ from sphinx.builders.latex.nodes import ( captioned_literal_block, footnotemark, footnotetext, math_reference, thebibliography ) from sphinx.transforms import SphinxTransform +from sphinx.util.nodes import NodeMatcher if False: # For type annotation @@ -30,7 +31,7 @@ class FootnoteDocnameUpdater(SphinxTransform): TARGET_NODES = (nodes.footnote, nodes.footnote_reference) def apply(self): - for node in self.document.traverse(lambda n: isinstance(n, self.TARGET_NODES)): + for node in self.document.traverse(NodeMatcher(*self.TARGET_NODES)): node['docname'] = self.env.docname @@ -536,14 +537,14 @@ class CitationReferenceTransform(SphinxTransform): if self.app.builder.name != 'latex': return + matcher = NodeMatcher(addnodes.pending_xref, refdomain='std', reftype='citation') citations = self.env.get_domain('std').data['citations'] - for node in self.document.traverse(addnodes.pending_xref): - if node['refdomain'] == 'std' and node['reftype'] == 'citation': - docname, labelid, _ = citations.get(node['reftarget'], ('', '', 0)) - if docname: - citation_ref = nodes.citation_reference('', *node.children, - docname=docname, refname=labelid) - node.replace_self(citation_ref) + for node in self.document.traverse(matcher): + docname, labelid, _ = citations.get(node['reftarget'], ('', '', 0)) + if docname: + citation_ref = nodes.citation_reference('', *node.children, + docname=docname, refname=labelid) + node.replace_self(citation_ref) class MathReferenceTransform(SphinxTransform): @@ -577,10 +578,10 @@ class LiteralBlockTransform(SphinxTransform): if self.app.builder.name != 'latex': return - for node in self.document.traverse(nodes.container): - if node.get('literal_block') is True: - newnode = captioned_literal_block('', *node.children, **node.attributes) - node.replace_self(newnode) + matcher = NodeMatcher(nodes.container, literal_block=True) + for node in self.document.traverse(matcher): + newnode = captioned_literal_block('', *node.children, **node.attributes) + node.replace_self(newnode) class DocumentTargetTransform(SphinxTransform): diff --git a/sphinx/builders/linkcheck.py b/sphinx/builders/linkcheck.py index c3fd1e88a..2189411ee 100644 --- a/sphinx/builders/linkcheck.py +++ b/sphinx/builders/linkcheck.py @@ -9,7 +9,6 @@ :license: BSD, see LICENSE for details. """ -import codecs import re import socket import threading @@ -308,7 +307,8 @@ class CheckExternalLinksBuilder(Builder): def write_entry(self, what, docname, line, uri): # type: (unicode, unicode, int, unicode) -> None - with codecs.open(path.join(self.outdir, 'output.txt'), 'a', 'utf-8') as output: # type: ignore # NOQA + with open(path.join(self.outdir, 'output.txt'), 'a', # type: ignore + encoding='utf-8') as output: output.write("%s:%s: [%s] %s\n" % (self.env.doc2path(docname, None), line, what, uri)) diff --git a/sphinx/builders/qthelp.py b/sphinx/builders/qthelp.py index a90fd1e44..74d0d7c98 100644 --- a/sphinx/builders/qthelp.py +++ b/sphinx/builders/qthelp.py @@ -9,7 +9,6 @@ :license: BSD, see LICENSE for details. """ -import codecs import os import posixpath import re @@ -145,7 +144,8 @@ class QtHelpBuilder(StandaloneHTMLBuilder): nspace = nspace.lower() # write the project file - with codecs.open(path.join(outdir, outname + '.qhp'), 'w', 'utf-8') as f: # type: ignore # NOQA + with open(path.join(outdir, outname + '.qhp'), 'w', # type: ignore + encoding='utf-8') as f: body = render_file('project.qhp', outname=outname, title=self.config.html_title, version=self.config.version, project=self.config.project, namespace=nspace, @@ -159,7 +159,8 @@ class QtHelpBuilder(StandaloneHTMLBuilder): startpage = 'qthelp://' + posixpath.join(nspace, 'doc', 'index.html') logger.info(__('writing collection project file...')) - with codecs.open(path.join(outdir, outname + '.qhcp'), 'w', 'utf-8') as f: # type: ignore # NOQA + with open(path.join(outdir, outname + '.qhcp'), 'w', # type: ignore + encoding='utf-8') as f: body = render_file('project.qhcp', outname=outname, title=self.config.html_short_title, homepage=homepage, startpage=startpage) diff --git a/sphinx/builders/text.py b/sphinx/builders/text.py index 81209d165..a43332971 100644 --- a/sphinx/builders/text.py +++ b/sphinx/builders/text.py @@ -9,7 +9,6 @@ :license: BSD, see LICENSE for details. """ -import codecs from os import path from docutils.io import StringOutput @@ -51,8 +50,7 @@ class TextBuilder(Builder): if docname not in self.env.all_docs: yield docname continue - targetname = self.env.doc2path(docname, self.outdir, - self.out_suffix) + targetname = path.join(self.outdir, docname + self.out_suffix) try: targetmtime = path.getmtime(targetname) except Exception: @@ -82,7 +80,7 @@ class TextBuilder(Builder): outfilename = path.join(self.outdir, os_path(docname) + self.out_suffix) ensuredir(path.dirname(outfilename)) try: - with codecs.open(outfilename, 'w', 'utf-8') as f: # type: ignore + with open(outfilename, 'w', encoding='utf-8') as f: # type: ignore f.write(self.writer.output) except (IOError, OSError) as err: logger.warning(__("error writing file %s: %s"), outfilename, err) diff --git a/sphinx/builders/xml.py b/sphinx/builders/xml.py index 6198532c9..03aefe006 100644 --- a/sphinx/builders/xml.py +++ b/sphinx/builders/xml.py @@ -9,7 +9,6 @@ :license: BSD, see LICENSE for details. """ -import codecs from os import path from docutils import nodes @@ -54,8 +53,7 @@ class XMLBuilder(Builder): if docname not in self.env.all_docs: yield docname continue - targetname = self.env.doc2path(docname, self.outdir, - self.out_suffix) + targetname = path.join(self.outdir, docname + self.out_suffix) try: targetmtime = path.getmtime(targetname) except Exception: @@ -95,7 +93,7 @@ class XMLBuilder(Builder): outfilename = path.join(self.outdir, os_path(docname) + self.out_suffix) ensuredir(path.dirname(outfilename)) try: - with codecs.open(outfilename, 'w', 'utf-8') as f: # type: ignore + with open(outfilename, 'w', encoding='utf-8') as f: # type: ignore f.write(self.writer.output) except (IOError, OSError) as err: logger.warning(__("error writing file %s: %s"), outfilename, err) diff --git a/sphinx/cmd/make_mode.py b/sphinx/cmd/make_mode.py index cf8673623..447b2ed01 100644 --- a/sphinx/cmd/make_mode.py +++ b/sphinx/cmd/make_mode.py @@ -59,7 +59,7 @@ BUILDERS = [ ] -class Make(object): +class Make: def __init__(self, srcdir, builddir, opts): # type: (unicode, unicode, List[unicode]) -> None diff --git a/sphinx/cmd/quickstart.py b/sphinx/cmd/quickstart.py index e7a669557..d919af720 100644 --- a/sphinx/cmd/quickstart.py +++ b/sphinx/cmd/quickstart.py @@ -17,8 +17,8 @@ import os import re import sys import time +import warnings from collections import OrderedDict -from io import open from os import path # try to import readline, unix specific enhancement @@ -34,12 +34,12 @@ except ImportError: USE_LIBEDIT = False from docutils.utils import column_width -from six import PY2, PY3, text_type, binary_type -from six.moves import input +from six import text_type, binary_type from six.moves.urllib.parse import quote as urlquote import sphinx.locale from sphinx import __display_version__, package_dir +from sphinx.deprecation import RemovedInSphinx40Warning from sphinx.locale import __ from sphinx.util import texescape from sphinx.util.console import ( # type: ignore @@ -185,20 +185,6 @@ def do_prompt(text, default=None, validator=nonempty): prompt = PROMPT_PREFIX + '%s [%s]: ' % (text, default) # type: unicode else: prompt = PROMPT_PREFIX + text + ': ' - if PY2: - # for Python 2.x, try to get a Unicode string out of it - if prompt.encode('ascii', 'replace').decode('ascii', 'replace') \ - != prompt: - if TERM_ENCODING: - prompt = prompt.encode(TERM_ENCODING) - else: - print(turquoise(__('* Note: non-ASCII default value provided ' - 'and terminal encoding unknown -- assuming ' - 'UTF-8 or Latin-1.'))) - try: - prompt = prompt.encode('utf-8') - except UnicodeEncodeError: - prompt = prompt.encode('latin1') if USE_LIBEDIT: # Note: libedit has a problem for combination of ``input()`` and escape # sequence (see #5335). To avoid the problem, all prompts are not colored @@ -222,10 +208,9 @@ def do_prompt(text, default=None, validator=nonempty): def convert_python_source(source, rex=re.compile(r"[uU]('.*?')")): # type: (unicode, Pattern) -> unicode # remove Unicode literal prefixes - if PY3: - return rex.sub('\\1', source) - else: - return source + warnings.warn('convert_python_source() is deprecated.', + RemovedInSphinx40Warning) + return rex.sub('\\1', source) class QuickstartRenderer(SphinxRenderer): @@ -399,7 +384,7 @@ def generate(d, overwrite=True, silent=False, templatedir=None): if 'mastertocmaxdepth' not in d: d['mastertocmaxdepth'] = 2 - d['PY3'] = PY3 + d['PY3'] = True d['project_fn'] = make_filename(d['project']) d['project_url'] = urlquote(d['project'].encode('idna')) d['project_manpage'] = d['project_fn'].lower() @@ -445,7 +430,7 @@ def generate(d, overwrite=True, silent=False, templatedir=None): if overwrite or not path.isfile(fpath): if 'quiet' not in d: print(__('Creating file %s.') % fpath) - with open(fpath, 'wt', encoding='utf-8', newline=newline) as f: + with open(fpath, 'wt', encoding='utf-8', newline=newline) as f: # type: ignore f.write(content) else: if 'quiet' not in d: @@ -455,7 +440,7 @@ def generate(d, overwrite=True, silent=False, templatedir=None): if not conf_path or not path.isfile(conf_path): conf_path = os.path.join(package_dir, 'templates', 'quickstart', 'conf.py_t') with open(conf_path) as f: - conf_text = convert_python_source(f.read()) + conf_text = f.read() write_file(path.join(srcdir, 'conf.py'), template.render_string(conf_text, d)) diff --git a/sphinx/config.py b/sphinx/config.py index d4e1ab055..c3a81291f 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -17,9 +17,7 @@ from collections import OrderedDict from os import path, getenv from typing import Any, NamedTuple, Union -from six import ( - PY2, PY3, iteritems, string_types, binary_type, text_type, integer_types, class_types -) +from six import string_types, binary_type, text_type, integer_types from sphinx.deprecation import RemovedInSphinx30Warning from sphinx.errors import ConfigError, ExtensionError @@ -38,15 +36,12 @@ if False: logger = logging.getLogger(__name__) CONFIG_FILENAME = 'conf.py' -UNSERIALIZABLE_TYPES = class_types + (types.ModuleType, types.FunctionType) +UNSERIALIZABLE_TYPES = (type, types.ModuleType, types.FunctionType) copyright_year_re = re.compile(r'^((\d{4}-)?)(\d{4})(?=[ ,])') -if PY3: - unicode = str # special alias for static typing... - ConfigValue = NamedTuple('ConfigValue', [('name', str), ('value', Any), - ('rebuild', Union[bool, unicode])]) + ('rebuild', Union[bool, text_type])]) def is_serializable(obj): @@ -55,7 +50,7 @@ def is_serializable(obj): if isinstance(obj, UNSERIALIZABLE_TYPES): return False elif isinstance(obj, dict): - for key, value in iteritems(obj): + for key, value in obj.items(): if not is_serializable(key) or not is_serializable(value): return False elif isinstance(obj, (list, tuple, set)): @@ -64,7 +59,7 @@ def is_serializable(obj): return True -class ENUM(object): +class ENUM: """represents the config value should be a one of candidates. Example: @@ -83,11 +78,9 @@ class ENUM(object): string_classes = [text_type] # type: List -if PY2: - string_classes.append(binary_type) # => [str, unicode] -class Config(object): +class Config: """Configuration file abstraction. The config object makes the values of all config values available as @@ -119,7 +112,7 @@ class Config(object): locale_dirs = (['locales'], 'env', []), figure_language_filename = (u'{root}.{language}{ext}', 'env', [str]), - master_doc = ('contents', 'env', []), + master_doc = ('index', 'env', []), source_suffix = ({'.rst': 'restructuredtext'}, 'env', Any), source_encoding = ('utf-8-sig', 'env', []), source_parsers = ({}, 'env', []), @@ -261,7 +254,7 @@ class Config(object): def init_values(self): # type: () -> None config = self._raw_config - for valname, value in iteritems(self.overrides): + for valname, value in self.overrides.items(): try: if '.' in valname: realvalname, key = valname.split('.', 1) @@ -310,7 +303,7 @@ class Config(object): def __iter__(self): # type: () -> Generator[ConfigValue, None, None] - for name, value in iteritems(self.values): + for name, value in self.values.items(): yield ConfigValue(name, getattr(self, name), value[1]) # type: ignore def add(self, name, default, rebuild, types): @@ -331,7 +324,7 @@ class Config(object): """Obtains serializable data for pickling.""" # remove potentially pickling-problematic values from config __dict__ = {} - for key, value in iteritems(self.__dict__): + for key, value in self.__dict__.items(): if key.startswith('_') or not is_serializable(value): pass else: @@ -339,7 +332,7 @@ class Config(object): # create a picklable copy of values list __dict__['values'] = {} - for key, value in iteritems(self.values): # type: ignore + for key, value in self.values.items(): # type: ignore real_value = getattr(self, key) if not is_serializable(real_value): # omit unserializable value @@ -367,9 +360,8 @@ def eval_config_file(filename, tags): try: execfile_(filename, namespace) except SyntaxError as err: - msg = __("There is a syntax error in your configuration file: %s") - if PY3: - msg += __("\nDid you change the syntax from 2.x to 3.x?") + msg = __("There is a syntax error in your configuration file: %s\n" + "Did you change the syntax from 2.x to 3.x?") raise ConfigError(msg % err) except SystemExit: msg = __("The configuration file (or one of the modules it imports) " @@ -403,8 +395,8 @@ def convert_source_suffix(app, config): # if dict, convert it to OrderedDict config.source_suffix = OrderedDict(config.source_suffix) # type: ignore else: - logger.warning(__("The config value `source_suffix' expected to " - "a string, list of strings or dictionary. " + logger.warning(__("The config value `source_suffix' expects " + "a string, list of strings, or dictionary. " "But `%r' is given." % source_suffix)) @@ -471,11 +463,18 @@ def check_confval_types(app, config): continue # at least we share a non-trivial base class if annotations: - msg = __("The config value `{name}' has type `{current.__name__}', " - "expected to {permitted}.") + msg = __("The config value `{name}' has type `{current.__name__}'; " + "expected {permitted}.") + wrapped_annotations = ["`{}'".format(c.__name__) for c in annotations] + if len(wrapped_annotations) > 2: + permitted = "{}, or {}".format( + ", ".join(wrapped_annotations[:-1]), + wrapped_annotations[-1]) + else: + permitted = " or ".join(wrapped_annotations) logger.warning(msg.format(name=confval.name, current=type(confval.value), - permitted=str([c.__name__ for c in annotations]))) + permitted=permitted)) else: msg = __("The config value `{name}' has type `{current.__name__}', " "defaults to `{default.__name__}'.") @@ -491,19 +490,28 @@ def check_unicode(config): """ nonascii_re = re.compile(br'[\x80-\xff]') - for name, value in iteritems(config._raw_config): + for name, value in config._raw_config.items(): if isinstance(value, binary_type) and nonascii_re.search(value): logger.warning(__('the config value %r is set to a string with non-ASCII ' 'characters; this can lead to Unicode errors occurring. ' 'Please use Unicode strings, e.g. %r.'), name, u'Content') +def check_primary_domain(app, config): + # type: (Sphinx, Config) -> None + primary_domain = config.primary_domain + if primary_domain and not app.registry.has_domain(primary_domain): + logger.warning(__('primary_domain %r not found, ignored.'), primary_domain) + config.primary_domain = None # type: ignore + + def setup(app): # type: (Sphinx) -> Dict[unicode, Any] app.connect('config-inited', convert_source_suffix) app.connect('config-inited', init_numfig_format) app.connect('config-inited', correct_copyright_year) app.connect('config-inited', check_confval_types) + app.connect('config-inited', check_primary_domain) return { 'version': 'builtin', diff --git a/sphinx/deprecation.py b/sphinx/deprecation.py index f95e8dbff..ea43a71f4 100644 --- a/sphinx/deprecation.py +++ b/sphinx/deprecation.py @@ -17,10 +17,6 @@ if False: from typing import Any, Dict, Type # NOQA -class RemovedInSphinx20Warning(DeprecationWarning): - pass - - class RemovedInSphinx30Warning(PendingDeprecationWarning): pass @@ -29,7 +25,7 @@ class RemovedInSphinx40Warning(PendingDeprecationWarning): pass -RemovedInNextVersionWarning = RemovedInSphinx20Warning +RemovedInNextVersionWarning = RemovedInSphinx30Warning class DeprecatedDict(dict): diff --git a/sphinx/directives/__init__.py b/sphinx/directives/__init__.py index 1177a258e..1898a1c0c 100644 --- a/sphinx/directives/__init__.py +++ b/sphinx/directives/__init__.py @@ -233,7 +233,7 @@ class DefaultDomain(SphinxDirective): domain_name = self.arguments[0].lower() # if domain_name not in env.domains: # # try searching by label - # for domain in itervalues(env.domains): + # for domain in env.domains.values(): # if domain.label.lower() == domain_name: # domain_name = domain.name # break diff --git a/sphinx/directives/code.py b/sphinx/directives/code.py index a98ab5883..e14a451d9 100644 --- a/sphinx/directives/code.py +++ b/sphinx/directives/code.py @@ -7,7 +7,6 @@ :license: BSD, see LICENSE for details. """ -import codecs import sys import warnings from difflib import unified_diff @@ -177,7 +176,7 @@ class CodeBlock(SphinxDirective): return [literal] -class LiteralIncludeReader(object): +class LiteralIncludeReader: INVALID_OPTIONS_PAIR = [ ('lineno-match', 'lineno-start'), ('lineno-match', 'append'), @@ -213,7 +212,8 @@ class LiteralIncludeReader(object): def read_file(self, filename, location=None): # type: (unicode, Any) -> List[unicode] try: - with codecs.open(filename, 'r', self.encoding, errors='strict') as f: # type: ignore # NOQA + with open(filename, 'r', # type: ignore + encoding=self.encoding, errors='strict') as f: text = f.read() # type: unicode if 'tab-width' in self.options: text = text.expandtabs(self.options['tab-width']) diff --git a/sphinx/directives/other.py b/sphinx/directives/other.py index 3776536b8..ab3cfc37c 100644 --- a/sphinx/directives/other.py +++ b/sphinx/directives/other.py @@ -14,7 +14,6 @@ from docutils.parsers.rst import directives from docutils.parsers.rst.directives.admonitions import BaseAdmonition from docutils.parsers.rst.directives.misc import Class from docutils.parsers.rst.directives.misc import Include as BaseInclude -from six.moves import range from sphinx import addnodes from sphinx.domains.changeset import VersionChange # NOQA # for compatibility diff --git a/sphinx/domains/__init__.py b/sphinx/domains/__init__.py index 41db13cb6..4824f4fb0 100644 --- a/sphinx/domains/__init__.py +++ b/sphinx/domains/__init__.py @@ -12,8 +12,6 @@ import copy -from six import iteritems - from sphinx.errors import SphinxError from sphinx.locale import _ @@ -28,7 +26,7 @@ if False: from sphinx.util.typing import RoleFunction # NOQA -class ObjType(object): +class ObjType: """ An ObjType is the description for a type of object that a domain can document. In the object_types attribute of Domain subclasses, object type @@ -55,7 +53,7 @@ class ObjType(object): self.attrs.update(attrs) -class Index(object): +class Index: """ An Index is the description for a domain-specific index. To add an index to a domain, subclass Index, overriding the three name attributes: @@ -113,7 +111,7 @@ class Index(object): raise NotImplementedError -class Domain(object): +class Domain: """ A Domain is meant to be a group of "object" description directives for objects of a similar nature, and corresponding roles to create references to @@ -183,7 +181,7 @@ class Domain(object): self.data = env.domaindata[self.name] if self.data['version'] != self.data_version: raise IOError('data of %r domain out of date' % self.label) - for name, obj in iteritems(self.object_types): + for name, obj in self.object_types.items(): for rolename in obj.roles: self._role2type.setdefault(rolename, []).append(name) self._type2role[name] = obj.roles[0] if obj.roles else '' diff --git a/sphinx/domains/changeset.py b/sphinx/domains/changeset.py index ed878b1d3..669d8c5b7 100644 --- a/sphinx/domains/changeset.py +++ b/sphinx/domains/changeset.py @@ -12,7 +12,6 @@ from typing import NamedTuple from docutils import nodes -from six import iteritems from sphinx import addnodes from sphinx import locale @@ -112,7 +111,7 @@ class ChangeSetDomain(Domain): def clear_doc(self, docname): # type: (unicode) -> None - for version, changes in iteritems(self.data['changes']): + for version, changes in self.data['changes'].items(): for changeset in changes[:]: if changeset.docname == docname: changes.remove(changeset) @@ -120,7 +119,7 @@ class ChangeSetDomain(Domain): def merge_domaindata(self, docnames, otherdata): # type: (List[unicode], Dict) -> None # XXX duplicates? - for version, otherchanges in iteritems(otherdata['changes']): + for version, otherchanges in otherdata['changes'].items(): changes = self.data['changes'].setdefault(version, []) for changeset in otherchanges: if changeset.docname in docnames: diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 9c8b3a3c6..df3dfb578 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -14,7 +14,7 @@ from copy import deepcopy from docutils import nodes, utils from docutils.parsers.rst import directives -from six import iteritems, text_type +from six import text_type from sphinx import addnodes from sphinx.directives import ObjectDescription @@ -72,7 +72,7 @@ logger = logging.getLogger(__name__) Grammar ---------------------------------------------------------------------------- - See http://www.nongnu.org/hcb/ for the grammar, + See https://www.nongnu.org/hcb/ for the grammar, and https://github.com/cplusplus/draft/blob/master/source/grammar.tex, and https://github.com/cplusplus/concepts-ts for the newest grammar. @@ -336,7 +336,7 @@ _fold_operator_re = re.compile(r'''(?x) | != | [<>=/*%+|&^~-]=? ''') -# see http://en.cppreference.com/w/cpp/keyword +# see https://en.cppreference.com/w/cpp/keyword _keywords = [ 'alignas', 'alignof', 'and', 'and_eq', 'asm', 'auto', 'bitand', 'bitor', 'bool', 'break', 'case', 'catch', 'char', 'char16_t', 'char32_t', 'class', @@ -604,17 +604,13 @@ class ASTBase(UnicodeMixin): if type(self) is not type(other): return False try: - for key, value in iteritems(self.__dict__): + for key, value in self.__dict__.items(): if value != getattr(other, key): return False except AttributeError: return False return True - def __ne__(self, other): - # type: (Any) -> bool - return not self.__eq__(other) - __hash__ = None # type: Callable[[], int] def clone(self): @@ -3592,7 +3588,7 @@ class ASTNamespace(ASTBase): self.templatePrefix = templatePrefix -class SymbolLookupResult(object): +class SymbolLookupResult: def __init__(self, symbols, parentSymbol, identOrOp, templateParams, templateArgs): # type: (Iterator[Symbol], Symbol, Union[ASTIdentifier, ASTOperator], Any, ASTTemplateArgs) -> None # NOQA self.symbols = symbols @@ -3602,7 +3598,7 @@ class SymbolLookupResult(object): self.templateArgs = templateArgs -class Symbol(object): +class Symbol: debug_lookup = False def _assert_invariants(self): @@ -4299,9 +4295,9 @@ class Symbol(object): return ''.join(res) -class DefinitionParser(object): +class DefinitionParser: # those without signedness and size modifiers - # see http://en.cppreference.com/w/cpp/language/types + # see https://en.cppreference.com/w/cpp/language/types _simple_fundemental_types = ( 'void', 'bool', 'char', 'wchar_t', 'char16_t', 'char32_t', 'int', 'float', 'double', 'auto' @@ -4584,7 +4580,7 @@ class DefinitionParser(object): return ASTCharLiteral(prefix, data) except UnicodeDecodeError as e: self.fail("Can not handle character literal. Internal error was: %s" % e) - except UnsupportedMultiCharacterCharLiteral as e: + except UnsupportedMultiCharacterCharLiteral: self.fail("Can not handle character literal" " resulting in multiple decoded characters.") @@ -6591,7 +6587,7 @@ class CPPXRefRole(XRefRole): return title, target -class CPPExprRole(object): +class CPPExprRole: def __init__(self, asCode): if asCode: # render the expression as inline code @@ -6603,7 +6599,7 @@ class CPPExprRole(object): self.node_type = nodes.inline def __call__(self, typ, rawtext, text, lineno, inliner, options={}, content=[]): - class Warner(object): + class Warner: def warn(self, msg): inliner.reporter.warning(msg, line=lineno) text = utils.unescape(text).replace('\n', ' ') @@ -6722,7 +6718,7 @@ class CPPDomain(Domain): target, node, contnode, emitWarnings=True): # type: (BuildEnvironment, unicode, Builder, unicode, unicode, nodes.Node, nodes.Node, bool) -> nodes.Node # NOQA - class Warner(object): + class Warner: def warn(self, msg): if emitWarnings: logger.warning(msg, location=node) diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index 427c86aa9..77083cc3c 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -13,7 +13,6 @@ import re from docutils import nodes from docutils.parsers.rst import directives -from six import iteritems from sphinx import addnodes, locale from sphinx.deprecation import DeprecatedDict, RemovedInSphinx30Warning @@ -114,7 +113,7 @@ def _pseudo_parse_arglist(signode, arglist): # This override allows our inline type specifiers to behave like :class: link # when it comes to handling "." and "~" prefixes. -class PyXrefMixin(object): +class PyXrefMixin: def make_xref(self, rolename, # type: unicode domain, # type: unicode @@ -536,7 +535,7 @@ class PyClassmember(PyObject): return '' -class PyDecoratorMixin(object): +class PyDecoratorMixin: """ Mixin for decorator directives. """ @@ -677,7 +676,7 @@ class PythonModuleIndex(Index): ignores = self.domain.env.config['modindex_common_prefix'] # type: ignore ignores = sorted(ignores, key=len, reverse=True) # list of all modules, sorted by module name - modules = sorted(iteritems(self.domain.data['modules']), + modules = sorted(self.domain.data['modules'].items(), key=lambda x: x[0].lower()) # sort out collapsable modules prev_modname = '' @@ -727,7 +726,7 @@ class PythonModuleIndex(Index): collapse = len(modules) - num_toplevels < num_toplevels # sort by first letter - sorted_content = sorted(iteritems(content)) + sorted_content = sorted(content.items()) return sorted_content, collapse @@ -923,9 +922,9 @@ class PythonDomain(Domain): def get_objects(self): # type: () -> Iterator[Tuple[unicode, unicode, unicode, unicode, unicode, int]] - for modname, info in iteritems(self.data['modules']): + for modname, info in self.data['modules'].items(): yield (modname, modname, 'module', info[0], 'module-' + modname, 0) - for refname, (docname, type) in iteritems(self.data['objects']): + for refname, (docname, type) in self.data['objects'].items(): if type != 'module': # modules are already handled yield (refname, refname, type, docname, refname, 1) diff --git a/sphinx/domains/rst.py b/sphinx/domains/rst.py index 1a5c3ba0c..026d2a59d 100644 --- a/sphinx/domains/rst.py +++ b/sphinx/domains/rst.py @@ -11,8 +11,6 @@ import re -from six import iteritems - from sphinx import addnodes from sphinx.directives import ObjectDescription from sphinx.domains import Domain, ObjType @@ -172,7 +170,7 @@ class ReSTDomain(Domain): def get_objects(self): # type: () -> Iterator[Tuple[unicode, unicode, unicode, unicode, unicode, int]] - for (typ, name), docname in iteritems(self.data['objects']): + for (typ, name), docname in self.data['objects'].items(): yield name, name, typ, docname, typ + '-' + name, 1 diff --git a/sphinx/domains/std.py b/sphinx/domains/std.py index 37bac2acc..2bbc92f88 100644 --- a/sphinx/domains/std.py +++ b/sphinx/domains/std.py @@ -17,7 +17,6 @@ from copy import copy from docutils import nodes from docutils.parsers.rst import directives from docutils.statemachine import ViewList -from six import iteritems from sphinx import addnodes from sphinx.deprecation import RemovedInSphinx30Warning @@ -529,7 +528,7 @@ class StandardDomain(Domain): # set up enumerable nodes self.enumerable_nodes = copy(self.enumerable_nodes) # create a copy for this instance - for node, settings in iteritems(env.app.registry.enumerable_nodes): + for node, settings in env.app.registry.enumerable_nodes.items(): self.enumerable_nodes[node] = settings def clear_doc(self, docname): @@ -607,7 +606,7 @@ class StandardDomain(Domain): def note_labels(self, env, docname, document): # type: (BuildEnvironment, unicode, nodes.Node) -> None labels, anonlabels = self.data['labels'], self.data['anonlabels'] - for name, explicit in iteritems(document.nametypes): + for name, explicit in document.nametypes.items(): if not explicit: continue labelid = document.nameids[name] @@ -647,7 +646,7 @@ class StandardDomain(Domain): def check_consistency(self): # type: () -> None - for name, (docname, labelid, lineno) in iteritems(self.data['citations']): + for name, (docname, labelid, lineno) in self.data['citations'].items(): if name not in self.data['citation_refs']: logger.warning(__('Citation [%s] is not referenced.'), name, type='ref', subtype='citation', @@ -887,20 +886,20 @@ class StandardDomain(Domain): # handle the special 'doc' reference here for doc in self.env.all_docs: yield (doc, clean_astext(self.env.titles[doc]), 'doc', doc, '', -1) - for (prog, option), info in iteritems(self.data['progoptions']): + for (prog, option), info in self.data['progoptions'].items(): if prog: fullname = ".".join([prog, option]) yield (fullname, fullname, 'cmdoption', info[0], info[1], 1) else: yield (option, option, 'cmdoption', info[0], info[1], 1) - for (type, name), info in iteritems(self.data['objects']): + for (type, name), info in self.data['objects'].items(): yield (name, name, type, info[0], info[1], self.object_types[type].attrs['searchprio']) - for name, info in iteritems(self.data['labels']): + for name, info in self.data['labels'].items(): yield (name, info[2], 'label', info[0], info[1], -1) # add anonymous-only labels as well non_anon_labels = set(self.data['labels']) - for name, info in iteritems(self.data['anonlabels']): + for name, info in self.data['anonlabels'].items(): if name not in non_anon_labels: yield (name, name, 'label', info[0], info[1], -1) diff --git a/sphinx/environment/__init__.py b/sphinx/environment/__init__.py index 4a79db3db..000c8a417 100644 --- a/sphinx/environment/__init__.py +++ b/sphinx/environment/__init__.py @@ -10,20 +10,16 @@ """ import os -import re +import pickle import sys import warnings from collections import defaultdict from copy import copy +from io import BytesIO from os import path -from docutils.utils import get_source_line -from six import BytesIO, next -from six.moves import cPickle as pickle - from sphinx import addnodes -from sphinx.deprecation import RemovedInSphinx20Warning, RemovedInSphinx30Warning -from sphinx.environment.adapters.indexentries import IndexEntries +from sphinx.deprecation import RemovedInSphinx30Warning, RemovedInSphinx40Warning from sphinx.environment.adapters.toctree import TocTree from sphinx.errors import SphinxError, BuildEnvironmentError, DocumentError, ExtensionError from sphinx.locale import __ @@ -65,9 +61,7 @@ default_settings = { # This is increased every time an environment attribute is added # or changed to properly invalidate pickle files. -# -# NOTE: increase base version by 2 to have distinct numbers for Py2 and 3 -ENV_VERSION = 54 + (sys.version_info[0] - 2) +ENV_VERSION = 56 # config status CONFIG_OK = 1 @@ -94,7 +88,7 @@ class NoUri(Exception): pass -class BuildEnvironment(object): +class BuildEnvironment: """ The environment in which the ReST files are translated. Stores an inventory of cross-file targets and provides doctree @@ -125,9 +119,6 @@ class BuildEnvironment(object): self.settings = default_settings.copy() self.settings['env'] = self - # the function to write warning messages with - self._warnfunc = None # type: Callable - # All "docnames" here are /-separated and relative and exclude # the source suffix. @@ -273,11 +264,6 @@ class BuildEnvironment(object): # Allow to disable by 3rd party extension (workaround) self.settings.setdefault('smart_quotes', True) - def set_warnfunc(self, func): - # type: (Callable) -> None - warnings.warn('env.set_warnfunc() is now deprecated. Use sphinx.util.logging instead.', - RemovedInSphinx20Warning) - def set_versioning_method(self, method, compare): # type: (unicode, bool) -> None """This sets the doctree versioning method for this environment. @@ -297,21 +283,6 @@ class BuildEnvironment(object): self.versioning_condition = condition self.versioning_compare = compare - def warn(self, docname, msg, lineno=None, **kwargs): - # type: (unicode, unicode, int, Any) -> None - """Emit a warning. - - This differs from using ``app.warn()`` in that the warning may not - be emitted instantly, but collected for emitting all warnings after - the update of the environment. - """ - self.app.warn(msg, location=(docname, lineno), **kwargs) # type: ignore - - def warn_node(self, msg, node, **kwargs): - # type: (unicode, nodes.Node, Any) -> None - """Like :meth:`warn`, but with source information taken from *node*.""" - self._warnfunc(msg, '%s:%s' % get_source_line(node), **kwargs) - def clear_doc(self, docname): # type: (unicode) -> None """Remove all traces of a source file in the inventory.""" @@ -367,6 +338,13 @@ class BuildEnvironment(object): If *base* is a path string, return absolute path under that. If *suffix* is not None, add it instead of config.source_suffix. """ + if suffix: + warnings.warn('The suffix argument for doc2path() is deprecated.', + RemovedInSphinx40Warning) + if base not in (True, None): + warnings.warn('The string style base argument for doc2path() is deprecated.', + RemovedInSphinx40Warning) + docname = docname.replace(SEP, path.sep) if suffix is None: # Use first candidate if there is not a file for any suffix @@ -466,8 +444,8 @@ class BuildEnvironment(object): added.add(docname) continue # if the doctree file is not there, rebuild - if not path.isfile(self.doc2path(docname, self.doctreedir, - '.doctree')): + filename = path.join(self.doctreedir, docname + '.doctree') + if not path.isfile(filename): changed.add(docname) continue # check the "reread always" list @@ -565,32 +543,6 @@ class BuildEnvironment(object): """ self.reread_always.add(self.docname) - def note_toctree(self, docname, toctreenode): - # type: (unicode, addnodes.toctree) -> None - """Note a TOC tree directive in a document and gather information about - file relations from it. - """ - warnings.warn('env.note_toctree() is deprecated. ' - 'Use sphinx.environment.adapters.toctree.TocTree instead.', - RemovedInSphinx20Warning) - TocTree(self).note(docname, toctreenode) - - def get_toc_for(self, docname, builder): - # type: (unicode, Builder) -> Dict[unicode, nodes.Node] - """Return a TOC nodetree -- for use on the same page only!""" - warnings.warn('env.get_toc_for() is deprecated. ' - 'Use sphinx.environment.adapters.toctre.TocTree instead.', - RemovedInSphinx20Warning) - return TocTree(self).get_toc_for(docname, builder) - - def get_toctree_for(self, docname, builder, collapse, **kwds): - # type: (unicode, Builder, bool, Any) -> addnodes.toctree - """Return the global TOC nodetree.""" - warnings.warn('env.get_toctree_for() is deprecated. ' - 'Use sphinx.environment.adapters.toctre.TocTree instead.', - RemovedInSphinx20Warning) - return TocTree(self).get_toctree_for(docname, builder, collapse, **kwds) - def get_domain(self, domainname): # type: (unicode) -> Domain """Return the domain instance with the specified name. @@ -607,8 +559,8 @@ class BuildEnvironment(object): def get_doctree(self, docname): # type: (unicode) -> nodes.Node """Read the doctree for a file from the pickle and return it.""" - doctree_filename = self.doc2path(docname, self.doctreedir, '.doctree') - with open(doctree_filename, 'rb') as f: + filename = path.join(self.doctreedir, docname + '.doctree') + with open(filename, 'rb') as f: doctree = pickle.load(f) doctree.settings.env = self doctree.reporter = LoggingReporter(self.doc2path(docname)) @@ -678,16 +630,6 @@ class BuildEnvironment(object): # allow custom references to be resolved self.app.emit('doctree-resolved', doctree, docname) - def create_index(self, builder, group_entries=True, - _fixre=re.compile(r'(.*) ([(][^()]*[)])')): - # type: (Builder, bool, Pattern) -> List[Tuple[unicode, List[Tuple[unicode, List[unicode]]]]] # NOQA - warnings.warn('env.create_index() is deprecated. ' - 'Use sphinx.environment.adapters.indexentreis.IndexEntries instead.', - RemovedInSphinx20Warning) - return IndexEntries(self).create_index(builder, - group_entries=group_entries, - _fixre=_fixre) - def collect_relations(self): # type: () -> Dict[unicode, List[unicode]] traversed = set() @@ -804,7 +746,7 @@ class BuildEnvironment(object): @classmethod def loads(cls, string, app=None): - # type: (unicode, Sphinx) -> BuildEnvironment + # type: (bytes, Sphinx) -> BuildEnvironment warnings.warn('BuildEnvironment.loads() is deprecated. ' 'Please use pickle.loads() instead.', RemovedInSphinx30Warning) diff --git a/sphinx/environment/adapters/asset.py b/sphinx/environment/adapters/asset.py index 91f2cf8eb..fa1141f65 100644 --- a/sphinx/environment/adapters/asset.py +++ b/sphinx/environment/adapters/asset.py @@ -14,7 +14,7 @@ if False: from sphinx.environment import BuildEnvironment # NOQA -class ImageAdapter(object): +class ImageAdapter: def __init__(self, env): # type: (BuildEnvironment) -> None self.env = env diff --git a/sphinx/environment/adapters/indexentries.py b/sphinx/environment/adapters/indexentries.py index 7c31fc3d5..d39fb057d 100644 --- a/sphinx/environment/adapters/indexentries.py +++ b/sphinx/environment/adapters/indexentries.py @@ -13,7 +13,7 @@ import re import unicodedata from itertools import groupby -from six import text_type, iteritems +from six import text_type from sphinx.locale import _, __ from sphinx.util import split_into, logging @@ -27,7 +27,7 @@ if False: logger = logging.getLogger(__name__) -class IndexEntries(object): +class IndexEntries: def __init__(self, env): # type: (BuildEnvironment) -> None self.env = env @@ -60,7 +60,7 @@ class IndexEntries(object): # maintain links in sorted/deterministic order bisect.insort(entry[0], (main, uri)) - for fn, entries in iteritems(self.env.indexentries): + for fn, entries in self.env.indexentries.items(): # new entry types must be listed in directives/other.py! for type, value, tid, main, index_key in entries: try: @@ -146,7 +146,7 @@ class IndexEntries(object): # type: (Tuple[unicode, List]) -> unicode # hack: mutating the subitems dicts to a list in the keyfunc k, v = item - v[1] = sorted((si, se) for (si, (se, void, void)) in iteritems(v[1])) + v[1] = sorted((si, se) for (si, (se, void, void)) in v[1].items()) if v[2] is None: # now calculate the key if k.startswith(u'\N{RIGHT-TO-LEFT MARK}'): diff --git a/sphinx/environment/adapters/toctree.py b/sphinx/environment/adapters/toctree.py index 565396ec4..c309fb27c 100644 --- a/sphinx/environment/adapters/toctree.py +++ b/sphinx/environment/adapters/toctree.py @@ -10,7 +10,6 @@ """ from docutils import nodes -from six import iteritems from sphinx import addnodes from sphinx.locale import __ @@ -27,7 +26,7 @@ if False: logger = logging.getLogger(__name__) -class TocTree(object): +class TocTree: def __init__(self, env): # type: (BuildEnvironment) -> None self.env = env @@ -261,7 +260,7 @@ class TocTree(object): def get_toctree_ancestors(self, docname): # type: (unicode) -> List[unicode] parent = {} - for p, children in iteritems(self.env.toctree_includes): + for p, children in self.env.toctree_includes.items(): for child in children: parent[child] = p ancestors = [] # type: List[unicode] diff --git a/sphinx/environment/collectors/__init__.py b/sphinx/environment/collectors/__init__.py index 9d9f5347c..30e1e9480 100644 --- a/sphinx/environment/collectors/__init__.py +++ b/sphinx/environment/collectors/__init__.py @@ -9,8 +9,6 @@ :license: BSD, see LICENSE for details. """ -from six import itervalues - if False: # For type annotation from typing import Dict, List, Set # NOQA @@ -19,7 +17,7 @@ if False: from sphinx.environment import BuildEnvironment # NOQA -class EnvironmentCollector(object): +class EnvironmentCollector: """An EnvironmentCollector is a specific data collector from each document. It gathers data and stores :py:class:`BuildEnvironment @@ -44,7 +42,7 @@ class EnvironmentCollector(object): def disable(self, app): # type: (Sphinx) -> None assert self.listener_ids is not None - for listener_id in itervalues(self.listener_ids): + for listener_id in self.listener_ids.values(): app.disconnect(listener_id) self.listener_ids = None diff --git a/sphinx/environment/collectors/asset.py b/sphinx/environment/collectors/asset.py index 725431dfa..d41931ed3 100644 --- a/sphinx/environment/collectors/asset.py +++ b/sphinx/environment/collectors/asset.py @@ -15,7 +15,6 @@ from os import path from docutils import nodes from docutils.utils import relative_path -from six import iteritems, itervalues from sphinx import addnodes from sphinx.environment.collectors import EnvironmentCollector @@ -87,7 +86,7 @@ class ImageCollector(EnvironmentCollector): # map image paths to unique image names (so that they can be put # into a single directory) - for imgpath in itervalues(candidates): + for imgpath in candidates.values(): app.env.dependencies[docname].add(imgpath) if not os.access(path.join(app.srcdir, imgpath), os.R_OK): logger.warning(__('image file not readable: %s') % imgpath, @@ -108,7 +107,7 @@ class ImageCollector(EnvironmentCollector): except (OSError, IOError) as err: logger.warning(__('image file %s not readable: %s') % (filename, err), location=node, type='image', subtype='not_readable') - for key, files in iteritems(globbed): + for key, files in globbed.items(): candidates[key] = sorted(files, key=len)[0] # select by similarity diff --git a/sphinx/environment/collectors/dependencies.py b/sphinx/environment/collectors/dependencies.py index de0b7c080..761a51e38 100644 --- a/sphinx/environment/collectors/dependencies.py +++ b/sphinx/environment/collectors/dependencies.py @@ -9,12 +9,13 @@ :license: BSD, see LICENSE for details. """ +import os from os import path from docutils.utils import relative_path from sphinx.environment.collectors import EnvironmentCollector -from sphinx.util.osutil import getcwd, fs_encoding +from sphinx.util.osutil import fs_encoding if False: # For type annotation @@ -40,7 +41,7 @@ class DependenciesCollector(EnvironmentCollector): def process_doc(self, app, doctree): # type: (Sphinx, nodes.Node) -> None """Process docutils-generated dependency info.""" - cwd = getcwd() + cwd = os.getcwd() frompath = path.join(path.normpath(app.srcdir), 'dummy') deps = doctree.settings.record_dependencies if not deps: diff --git a/sphinx/environment/collectors/indexentries.py b/sphinx/environment/collectors/indexentries.py index a9ba897d0..2a5abcc8e 100644 --- a/sphinx/environment/collectors/indexentries.py +++ b/sphinx/environment/collectors/indexentries.py @@ -50,11 +50,7 @@ class IndexEntriesCollector(EnvironmentCollector): node.parent.remove(node) else: for entry in node['entries']: - if len(entry) == 5: - # Since 1.4: new index structure including index_key (5th column) - entries.append(entry) - else: - entries.append(entry + (None,)) + entries.append(entry) def setup(app): diff --git a/sphinx/environment/collectors/toctree.py b/sphinx/environment/collectors/toctree.py index fe67bbcba..462997164 100644 --- a/sphinx/environment/collectors/toctree.py +++ b/sphinx/environment/collectors/toctree.py @@ -10,7 +10,6 @@ """ from docutils import nodes -from six import iteritems from sphinx import addnodes from sphinx.environment.adapters.toctree import TocTree @@ -295,7 +294,7 @@ class TocTreeCollector(EnvironmentCollector): if env.config.numfig: _walk_doc(env.config.master_doc, tuple()) # type: ignore - for docname, fignums in iteritems(env.toc_fignumbers): + for docname, fignums in env.toc_fignumbers.items(): if fignums != old_fignumbers.get(docname): rewrite_needed.append(docname) diff --git a/sphinx/events.py b/sphinx/events.py index fb62d1776..0b76d6c9e 100644 --- a/sphinx/events.py +++ b/sphinx/events.py @@ -14,8 +14,6 @@ from __future__ import print_function from collections import OrderedDict, defaultdict -from six import itervalues - from sphinx.errors import ExtensionError from sphinx.locale import __ @@ -45,7 +43,7 @@ core_events = { } # type: Dict[unicode, unicode] -class EventManager(object): +class EventManager: def __init__(self): # type: () -> None self.events = core_events.copy() @@ -70,13 +68,13 @@ class EventManager(object): def disconnect(self, listener_id): # type: (int) -> None - for event in itervalues(self.listeners): + for event in self.listeners.values(): event.pop(listener_id, None) def emit(self, name, *args): # type: (unicode, Any) -> List results = [] - for callback in itervalues(self.listeners[name]): + for callback in self.listeners[name].values(): results.append(callback(*args)) return results diff --git a/sphinx/ext/apidoc.py b/sphinx/ext/apidoc.py index a154f6449..31cdb5855 100644 --- a/sphinx/ext/apidoc.py +++ b/sphinx/ext/apidoc.py @@ -32,7 +32,7 @@ from sphinx import __display_version__, package_dir from sphinx.cmd.quickstart import EXTENSIONS from sphinx.locale import __ from sphinx.util import rst -from sphinx.util.osutil import FileAvoidWrite, ensuredir, walk +from sphinx.util.osutil import FileAvoidWrite, ensuredir if False: # For type annotation @@ -235,7 +235,7 @@ def recurse_tree(rootpath, excludes, opts): root_package = None toplevels = [] - for root, subs, files in walk(rootpath, followlinks=followlinks): + for root, subs, files in os.walk(rootpath, followlinks=followlinks): # document only Python module files (that aren't excluded) py_files = sorted(f for f in files if path.splitext(f)[1] in PY_SUFFIXES and diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index 6823505ee..8411e1ac4 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -18,14 +18,12 @@ import warnings from typing import Any from docutils.statemachine import ViewList -from six import iteritems, itervalues, text_type, class_types, string_types +from six import text_type, string_types import sphinx -from sphinx.deprecation import RemovedInSphinx20Warning, RemovedInSphinx30Warning -from sphinx.errors import ExtensionError +from sphinx.deprecation import RemovedInSphinx30Warning from sphinx.ext.autodoc.importer import mock, import_object, get_object_members from sphinx.ext.autodoc.importer import _MockImporter # to keep compatibility # NOQA -from sphinx.ext.autodoc.inspector import format_annotation, formatargspec # to keep compatibility # NOQA from sphinx.locale import _, __ from sphinx.pycode import ModuleAnalyzer, PycodeError from sphinx.util import logging @@ -124,58 +122,6 @@ def merge_special_members_option(options): options['members'] = options['special-members'] -class AutodocReporter(object): - """ - A reporter replacement that assigns the correct source name - and line number to a system message, as recorded in a ViewList. - """ - def __init__(self, viewlist, reporter): - # type: (ViewList, Reporter) -> None - warnings.warn('AutodocReporter is now deprecated. ' - 'Use sphinx.util.docutils.switch_source_input() instead.', - RemovedInSphinx20Warning) - self.viewlist = viewlist - self.reporter = reporter - - def __getattr__(self, name): - # type: (unicode) -> Any - return getattr(self.reporter, name) - - def system_message(self, level, message, *children, **kwargs): - # type: (int, unicode, Any, Any) -> nodes.system_message - if 'line' in kwargs and 'source' not in kwargs: - try: - source, line = self.viewlist.items[kwargs['line']] - except IndexError: - pass - else: - kwargs['source'] = source - kwargs['line'] = line - return self.reporter.system_message(level, message, - *children, **kwargs) - - def debug(self, *args, **kwargs): - # type: (Any, Any) -> nodes.system_message - if self.reporter.debug_flag: - return self.system_message(0, *args, **kwargs) - - def info(self, *args, **kwargs): - # type: (Any, Any) -> nodes.system_message - return self.system_message(1, *args, **kwargs) - - def warning(self, *args, **kwargs): - # type: (Any, Any) -> nodes.system_message - return self.system_message(2, *args, **kwargs) - - def error(self, *args, **kwargs): - # type: (Any, Any) -> nodes.system_message - return self.system_message(3, *args, **kwargs) - - def severe(self, *args, **kwargs): - # type: (Any, Any) -> nodes.system_message - return self.system_message(4, *args, **kwargs) - - # Some useful event listener factories for autodoc-process-docstring. def cut_lines(pre, post=0, what=None): @@ -255,7 +201,7 @@ class Options(dict): return None -class Documenter(object): +class Documenter: """ A Documenter knows how to autodocument a single object type. When registered with the AutoDirective, it will be used to document objects @@ -356,7 +302,8 @@ class Documenter(object): explicit_modname, path, base, args, retann = \ py_ext_sig_re.match(self.name).groups() except AttributeError: - logger.warning(__('invalid signature for auto%s (%r)') % (self.objtype, self.name)) + logger.warning(__('invalid signature for auto%s (%r)') % (self.objtype, self.name), + type='autodoc') return False # support explicit module and class name separation via :: @@ -393,7 +340,7 @@ class Documenter(object): self.module, self.parent, self.object_name, self.object = ret return True except ImportError as exc: - logger.warning(exc.args[0]) + logger.warning(exc.args[0], type='autodoc', subtype='import_object') self.env.note_reread() return False @@ -456,7 +403,7 @@ class Documenter(object): args = self.format_args() except Exception as err: logger.warning(__('error while formatting arguments for %s: %s') % - (self.fullname, err)) + (self.fullname, err), type='autodoc') args = None retann = self.retann @@ -578,12 +525,12 @@ class Documenter(object): selected.append((name, members[name].value)) else: logger.warning(__('missing attribute %s in object %s') % - (name, self.fullname)) + (name, self.fullname), type='autodoc') return False, sorted(selected) elif self.options.inherited_members: - return False, sorted((m.name, m.value) for m in itervalues(members)) + return False, sorted((m.name, m.value) for m in members.values()) else: - return False, sorted((m.name, m.value) for m in itervalues(members) + return False, sorted((m.name, m.value) for m in members.values() if m.directly_defined) def filter_members(self, members, want_all): @@ -667,7 +614,7 @@ class Documenter(object): except Exception as exc: logger.warning(__('autodoc: failed to determine %r to be documented.' 'the following exception was raised:\n%s'), - member, exc) + member, exc, type='autodoc') keep = False if keep: @@ -705,7 +652,7 @@ class Documenter(object): # document non-skipped members memberdocumenters = [] # type: List[Tuple[Documenter, bool]] for (mname, member, isattr) in self.filter_members(members, want_all): - classes = [cls for cls in itervalues(self.documenters) + classes = [cls for cls in self.documenters.values() if cls.can_document_member(member, mname, isattr, self)] if not classes: # don't know how to document this member @@ -760,7 +707,7 @@ class Documenter(object): __('don\'t know which module to import for autodocumenting ' '%r (try placing a "module" or "currentmodule" directive ' 'in the document, or giving an explicit module name)') % - self.name) + self.name, type='autodoc') return # now, import the module and get object to document @@ -851,7 +798,8 @@ class ModuleDocumenter(Documenter): def resolve_name(self, modname, parents, path, base): # type: (str, Any, str, Any) -> Tuple[str, List[unicode]] if modname is not None: - logger.warning(__('"::" in automodule name doesn\'t make sense')) + logger.warning(__('"::" in automodule name doesn\'t make sense'), + type='autodoc') return (path or '') + base, [] def parse_name(self): @@ -859,7 +807,8 @@ class ModuleDocumenter(Documenter): ret = Documenter.parse_name(self) if self.args or self.retann: logger.warning(__('signature arguments or return annotation ' - 'given for automodule %s') % self.fullname) + 'given for automodule %s') % self.fullname, + type='autodoc') return ret def add_directive_header(self, sig): @@ -894,7 +843,9 @@ class ModuleDocumenter(Documenter): logger.warning( __('__all__ should be a list of strings, not %r ' '(in module %s) -- ignoring __all__') % - (memberlist, self.fullname)) + (memberlist, self.fullname), + type='autodoc' + ) # fall back to all members return True, safe_getmembers(self.object) else: @@ -907,7 +858,9 @@ class ModuleDocumenter(Documenter): logger.warning( __('missing attribute mentioned in :members: or __all__: ' 'module %s, attribute %s') % - (safe_getattr(self.object, '__name__', '???'), mname)) + (safe_getattr(self.object, '__name__', '???'), mname), + type='autodoc' + ) return False, ret @@ -965,7 +918,7 @@ class ClassLevelDocumenter(Documenter): return modname, parents + [base] -class DocstringSignatureMixin(object): +class DocstringSignatureMixin: """ Mixin for FunctionDocumenter and MethodDocumenter to provide the feature of reading the signature from the docstring. @@ -1108,7 +1061,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type: @classmethod def can_document_member(cls, member, membername, isattr, parent): # type: (Any, unicode, bool, Any) -> bool - return isinstance(member, class_types) + return isinstance(member, type) def import_object(self): # type: () -> Any @@ -1260,8 +1213,7 @@ class ExceptionDocumenter(ClassDocumenter): @classmethod def can_document_member(cls, member, membername, isattr, parent): # type: (Any, unicode, bool, Any) -> bool - return isinstance(member, class_types) and \ - issubclass(member, BaseException) # type: ignore + return isinstance(member, type) and issubclass(member, BaseException) class DataDocumenter(ModuleLevelDocumenter): @@ -1392,7 +1344,7 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter): # exported anywhere by Python return isdatadesc or (not isinstance(parent, ModuleDocumenter) and not inspect.isroutine(member) and - not isinstance(member, class_types)) + not isinstance(member, type)) def document_members(self, all_members=False): # type: (bool) -> None @@ -1475,87 +1427,16 @@ class InstanceAttributeDocumenter(AttributeDocumenter): AttributeDocumenter.add_content(self, more_content, no_docstring=True) -class DeprecatedDict(dict): - def __init__(self, message): - # type: (str) -> None - self.message = message - super(DeprecatedDict, self).__init__() - - def __setitem__(self, key, value): - # type: (unicode, Any) -> None - warnings.warn(self.message, RemovedInSphinx20Warning) - super(DeprecatedDict, self).__setitem__(key, value) - - def setdefault(self, key, default=None): - # type: (unicode, Any) -> None - warnings.warn(self.message, RemovedInSphinx20Warning) - super(DeprecatedDict, self).setdefault(key, default) - - def update(self, other=None): # type: ignore - # type: (Dict) -> None - warnings.warn(self.message, RemovedInSphinx20Warning) - super(DeprecatedDict, self).update(other) - - -class AutodocRegistry(object): - """ - A registry of Documenters and attrgetters. - - Note: When importing an object, all items along the import chain are - accessed using the descendant's *_special_attrgetters*, thus this - dictionary should include all necessary functions for accessing - attributes of the parents. - """ - # a registry of objtype -> documenter class (Deprecated) - _registry = DeprecatedDict( - 'AutoDirective._registry has been deprecated. ' - 'Please use app.add_autodocumenter() instead.' - ) # type: Dict[unicode, Type[Documenter]] - - # a registry of type -> getattr function - _special_attrgetters = DeprecatedDict( - 'AutoDirective._special_attrgetters has been deprecated. ' - 'Please use app.add_autodoc_attrgetter() instead.' - ) # type: Dict[Type, Callable] - - -AutoDirective = AutodocRegistry # for backward compatibility - - -def add_documenter(cls): - # type: (Type[Documenter]) -> None - """Register a new Documenter.""" - warnings.warn('sphinx.ext.autodoc.add_documenter() has been deprecated. ' - 'Please use app.add_autodocumenter() instead.', - RemovedInSphinx20Warning) - - if not issubclass(cls, Documenter): - raise ExtensionError('autodoc documenter %r must be a subclass ' - 'of Documenter' % cls) - # actually, it should be possible to override Documenters - # if cls.objtype in AutoDirective._registry: - # raise ExtensionError('autodoc documenter for %r is already ' - # 'registered' % cls.objtype) - AutoDirective._registry[cls.objtype] = cls - - def get_documenters(app): # type: (Sphinx) -> Dict[unicode, Type[Documenter]] """Returns registered Documenter classes""" - classes = dict(AutoDirective._registry) # registered directly - if app: - classes.update(app.registry.documenters) # registered by API - return classes + return app.registry.documenters def autodoc_attrgetter(app, obj, name, *defargs): # type: (Sphinx, Any, unicode, Any) -> Any """Alternative getattr() for types""" - candidates = dict(AutoDirective._special_attrgetters) - if app: - candidates.update(app.registry.autodoc_attrgettrs) - - for typ, func in iteritems(candidates): + for typ, func in app.registry.autodoc_attrgettrs.items(): if isinstance(obj, typ): return func(obj, name, *defargs) @@ -1582,7 +1463,7 @@ def merge_autodoc_default_flags(app, config): else: logger.warning( __("Ignoring invalid option in autodoc_default_flags: %r"), - option + option, type='autodoc' ) diff --git a/sphinx/ext/autodoc/directive.py b/sphinx/ext/autodoc/directive.py index 3a3434fc8..e831cab64 100644 --- a/sphinx/ext/autodoc/directive.py +++ b/sphinx/ext/autodoc/directive.py @@ -34,7 +34,7 @@ AUTODOC_DEFAULT_OPTIONS = ['members', 'undoc-members', 'inherited-members', 'ignore-module-all', 'exclude-members'] -class DummyOptionSpec(object): +class DummyOptionSpec: """An option_spec allows any options.""" def __getitem__(self, key): @@ -42,7 +42,7 @@ class DummyOptionSpec(object): return lambda x: x -class DocumenterBridge(object): +class DocumenterBridge: """A parameters container for Documenters.""" def __init__(self, env, reporter, options, lineno): diff --git a/sphinx/ext/autodoc/importer.py b/sphinx/ext/autodoc/importer.py index a2280e82b..45bab042b 100644 --- a/sphinx/ext/autodoc/importer.py +++ b/sphinx/ext/autodoc/importer.py @@ -16,8 +16,6 @@ import warnings from collections import namedtuple from types import FunctionType, MethodType, ModuleType -from six import PY2, iteritems - from sphinx.util import logging from sphinx.util.inspect import isenumclass, safe_getattr @@ -28,7 +26,7 @@ if False: logger = logging.getLogger(__name__) -class _MockObject(object): +class _MockObject: """Used by autodoc_mock_imports.""" def __new__(cls, *args, **kwargs): @@ -93,7 +91,7 @@ class _MockModule(ModuleType): return o -class _MockImporter(object): +class _MockImporter: def __init__(self, names): # type: (List[str]) -> None self.names = names @@ -219,8 +217,6 @@ def import_object(modname, objpath, objtype='', attrgetter=safe_getattr, warning else: errmsg += '; the following exception was raised:\n%s' % traceback.format_exc() - if PY2: - errmsg = errmsg.decode('utf-8') # type: ignore logger.debug(errmsg) raise ImportError(errmsg) @@ -234,12 +230,6 @@ def get_object_members(subject, objpath, attrgetter, analyzer=None): # the members directly defined in the class obj_dict = attrgetter(subject, '__dict__', {}) - # Py34 doesn't have enum members in __dict__. - if sys.version_info[:2] == (3, 4) and isenumclass(subject): - obj_dict = dict(obj_dict) - for name, value in subject.__members__.items(): - obj_dict[name] = value - members = {} # type: Dict[str, Attribute] # enum members @@ -249,7 +239,7 @@ def get_object_members(subject, objpath, attrgetter, analyzer=None): members[name] = Attribute(name, True, value) superclass = subject.__mro__[1] - for name, value in iteritems(obj_dict): + for name, value in obj_dict.items(): if name not in superclass.__dict__: members[name] = Attribute(name, True, value) diff --git a/sphinx/ext/autodoc/inspector.py b/sphinx/ext/autodoc/inspector.py deleted file mode 100644 index be42237c6..000000000 --- a/sphinx/ext/autodoc/inspector.py +++ /dev/null @@ -1,187 +0,0 @@ -# -*- coding: utf-8 -*- -""" - sphinx.ext.autodoc.inspector - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Inspect utilities for autodoc - - :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. - :license: BSD, see LICENSE for details. -""" - -import typing -import warnings - -from six import StringIO, string_types - -from sphinx.deprecation import RemovedInSphinx20Warning -from sphinx.util.inspect import object_description - -if False: - # For type annotation - from typing import Any, Callable, Dict, Tuple # NOQA - - -def format_annotation(annotation): - # type: (Any) -> str - """Return formatted representation of a type annotation. - - Show qualified names for types and additional details for types from - the ``typing`` module. - - Displaying complex types from ``typing`` relies on its private API. - """ - warnings.warn('format_annotation() is now deprecated. ' - 'Please use sphinx.util.inspect.Signature instead.', - RemovedInSphinx20Warning) - if isinstance(annotation, typing.TypeVar): # type: ignore - return annotation.__name__ - if annotation == Ellipsis: - return '...' - if not isinstance(annotation, type): - return repr(annotation) - - qualified_name = (annotation.__module__ + '.' + annotation.__qualname__ # type: ignore - if annotation else repr(annotation)) - - if annotation.__module__ == 'builtins': - return annotation.__qualname__ # type: ignore - else: - if hasattr(typing, 'GenericMeta') and \ - isinstance(annotation, typing.GenericMeta): - # In Python 3.5.2+, all arguments are stored in __args__, - # whereas __parameters__ only contains generic parameters. - # - # Prior to Python 3.5.2, __args__ is not available, and all - # arguments are in __parameters__. - params = None - if hasattr(annotation, '__args__'): - if annotation.__args__ is None or len(annotation.__args__) <= 2: # type: ignore # NOQA - params = annotation.__args__ # type: ignore - else: # typing.Callable - args = ', '.join(format_annotation(a) for a in annotation.__args__[:-1]) # type: ignore # NOQA - result = format_annotation(annotation.__args__[-1]) # type: ignore - return '%s[[%s], %s]' % (qualified_name, args, result) - elif hasattr(annotation, '__parameters__'): - params = annotation.__parameters__ # type: ignore - if params is not None: - param_str = ', '.join(format_annotation(p) for p in params) - return '%s[%s]' % (qualified_name, param_str) - elif (hasattr(typing, 'UnionMeta') and - isinstance(annotation, typing.UnionMeta) and # type: ignore - hasattr(annotation, '__union_params__')): - params = annotation.__union_params__ - if params is not None: - param_str = ', '.join(format_annotation(p) for p in params) - return '%s[%s]' % (qualified_name, param_str) - elif (hasattr(typing, 'CallableMeta') and - isinstance(annotation, typing.CallableMeta) and # type: ignore - getattr(annotation, '__args__', None) is not None and - hasattr(annotation, '__result__')): - # Skipped in the case of plain typing.Callable - args = annotation.__args__ - if args is None: - return qualified_name - elif args is Ellipsis: - args_str = '...' - else: - formatted_args = (format_annotation(a) for a in args) - args_str = '[%s]' % ', '.join(formatted_args) - return '%s[%s, %s]' % (qualified_name, - args_str, - format_annotation(annotation.__result__)) - elif (hasattr(typing, 'TupleMeta') and - isinstance(annotation, typing.TupleMeta) and # type: ignore - hasattr(annotation, '__tuple_params__') and - hasattr(annotation, '__tuple_use_ellipsis__')): - params = annotation.__tuple_params__ - if params is not None: - param_strings = [format_annotation(p) for p in params] - if annotation.__tuple_use_ellipsis__: - param_strings.append('...') - return '%s[%s]' % (qualified_name, - ', '.join(param_strings)) - return qualified_name - - -def formatargspec(function, args, varargs=None, varkw=None, defaults=None, - kwonlyargs=(), kwonlydefaults={}, annotations={}): - # type: (Callable, Tuple[str, ...], str, str, Any, Tuple, Dict, Dict[str, Any]) -> str - """Return a string representation of an ``inspect.FullArgSpec`` tuple. - - An enhanced version of ``inspect.formatargspec()`` that handles typing - annotations better. - """ - warnings.warn('formatargspec() is now deprecated. ' - 'Please use sphinx.util.inspect.Signature instead.', - RemovedInSphinx20Warning) - - def format_arg_with_annotation(name): - # type: (str) -> str - if name in annotations: - return '%s: %s' % (name, format_annotation(get_annotation(name))) - return name - - def get_annotation(name): - # type: (str) -> str - value = annotations[name] - if isinstance(value, string_types): - return introspected_hints.get(name, value) - else: - return value - - try: - introspected_hints = (typing.get_type_hints(function) # type: ignore - if typing and hasattr(function, '__code__') else {}) - except Exception: - introspected_hints = {} - - fd = StringIO() - fd.write('(') - - formatted = [] - defaults_start = len(args) - len(defaults) if defaults else len(args) - - for i, arg in enumerate(args): - arg_fd = StringIO() - if isinstance(arg, list): - # support tupled arguments list (only for py2): def foo((x, y)) - arg_fd.write('(') - arg_fd.write(format_arg_with_annotation(arg[0])) - for param in arg[1:]: - arg_fd.write(', ') - arg_fd.write(format_arg_with_annotation(param)) - arg_fd.write(')') - else: - arg_fd.write(format_arg_with_annotation(arg)) - if defaults and i >= defaults_start: - arg_fd.write(' = ' if arg in annotations else '=') - arg_fd.write(object_description(defaults[i - defaults_start])) # type: ignore - formatted.append(arg_fd.getvalue()) - - if varargs: - formatted.append('*' + format_arg_with_annotation(varargs)) - - if kwonlyargs: - if not varargs: - formatted.append('*') - - for kwarg in kwonlyargs: - arg_fd = StringIO() - arg_fd.write(format_arg_with_annotation(kwarg)) - if kwonlydefaults and kwarg in kwonlydefaults: - arg_fd.write(' = ' if kwarg in annotations else '=') - arg_fd.write(object_description(kwonlydefaults[kwarg])) # type: ignore - formatted.append(arg_fd.getvalue()) - - if varkw: - formatted.append('**' + format_arg_with_annotation(varkw)) - - fd.write(', '.join(formatted)) - fd.write(')') - - if 'return' in annotations: - fd.write(' -> ') - fd.write(format_annotation(get_annotation('return'))) - - return fd.getvalue() diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index c450733ab..10adfc9ce 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -58,7 +58,6 @@ import os import posixpath import re import sys -import warnings from types import ModuleType from docutils import nodes @@ -70,7 +69,6 @@ from six import text_type import sphinx from sphinx import addnodes -from sphinx.deprecation import RemovedInSphinx20Warning from sphinx.environment.adapters.toctree import TocTree from sphinx.ext.autodoc import get_documenters from sphinx.ext.autodoc.directive import DocumenterBridge, Options @@ -176,8 +174,8 @@ class FakeDirective(DocumenterBridge): super(FakeDirective, self).__init__({}, None, Options(), 0) # type: ignore -def get_documenter(*args): - # type: (Any) -> Type[Documenter] +def get_documenter(app, obj, parent): + # type: (Sphinx, Any, Any) -> Type[Documenter] """Get an autodoc.Documenter class suitable for documenting the given object. @@ -186,16 +184,6 @@ def get_documenter(*args): belongs to. """ from sphinx.ext.autodoc import DataDocumenter, ModuleDocumenter - if len(args) == 3: - # new style arguments: (app, obj, parent) - app, obj, parent = args - else: - # old style arguments: (obj, parent) - app = _app - obj, parent = args - warnings.warn('the interface of get_documenter() has been changed. ' - 'Please give application object as first argument.', - RemovedInSphinx20Warning) if inspect.ismodule(obj): # ModuleDocumenter.can_document_member always returns False diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py index cb27a664d..20a06aeb4 100644 --- a/sphinx/ext/autosummary/generate.py +++ b/sphinx/ext/autosummary/generate.py @@ -20,7 +20,6 @@ from __future__ import print_function import argparse -import codecs import locale import os import pydoc @@ -51,7 +50,7 @@ if False: from sphinx.ext.autodoc import Documenter # NOQA -class DummyApplication(object): +class DummyApplication: """Dummy Application class for sphinx-autogen command.""" def __init__(self): @@ -249,8 +248,8 @@ def find_autosummary_in_files(filenames): """ documented = [] # type: List[Tuple[unicode, unicode, unicode]] for filename in filenames: - with codecs.open(filename, 'r', encoding='utf-8', # type: ignore - errors='ignore') as f: + with open(filename, 'r', encoding='utf-8', # type: ignore + errors='ignore') as f: lines = f.read().splitlines() documented.extend(find_autosummary_in_lines(lines, filename=filename)) return documented diff --git a/sphinx/ext/coverage.py b/sphinx/ext/coverage.py index d6d865665..76b721adf 100644 --- a/sphinx/ext/coverage.py +++ b/sphinx/ext/coverage.py @@ -12,12 +12,10 @@ import glob import inspect +import pickle import re from os import path -from six import iteritems -from six.moves import cPickle as pickle - import sphinx from sphinx.builders import Builder from sphinx.locale import __ @@ -73,7 +71,7 @@ class CoverageBuilder(Builder): logger.warning(__('invalid regex %r in coverage_c_regexes'), exp) self.c_ignorexps = {} # type: Dict[unicode, List[Pattern]] - for (name, exps) in iteritems(self.config.coverage_ignore_c_items): + for (name, exps) in self.config.coverage_ignore_c_items.items(): self.c_ignorexps[name] = compile_regex_list('coverage_ignore_c_items', exps) self.mod_ignorexps = compile_regex_list('coverage_ignore_modules', @@ -127,7 +125,7 @@ class CoverageBuilder(Builder): write_header(op, 'Undocumented C API elements', '=') op.write('\n') - for filename, undoc in iteritems(self.c_undoc): + for filename, undoc in self.c_undoc.items(): write_header(op, filename) for typ, name in sorted(undoc): op.write(' * %-50s [%9s]\n' % (name, typ)) @@ -247,7 +245,7 @@ class CoverageBuilder(Builder): if undoc['classes']: op.write('Classes:\n') for name, methods in sorted( - iteritems(undoc['classes'])): + undoc['classes'].items()): if not methods: op.write(' * %s\n' % name) else: diff --git a/sphinx/ext/doctest.py b/sphinx/ext/doctest.py index c35b3d284..b2244d032 100644 --- a/sphinx/ext/doctest.py +++ b/sphinx/ext/doctest.py @@ -11,27 +11,28 @@ """ from __future__ import absolute_import -import codecs import doctest import re import sys import time +import warnings from os import path from docutils import nodes from docutils.parsers.rst import directives from packaging.specifiers import SpecifierSet, InvalidSpecifier from packaging.version import Version -from six import itervalues, StringIO, binary_type, text_type, PY2 +from six import StringIO, binary_type import sphinx from sphinx.builders import Builder +from sphinx.deprecation import RemovedInSphinx40Warning from sphinx.locale import __ from sphinx.util import force_decode, logging from sphinx.util.console import bold # type: ignore from sphinx.util.docutils import SphinxDirective from sphinx.util.nodes import set_source_info -from sphinx.util.osutil import fs_encoding, relpath +from sphinx.util.osutil import relpath if False: # For type annotation @@ -43,18 +44,12 @@ logger = logging.getLogger(__name__) blankline_re = re.compile(r'^\s*<BLANKLINE>', re.MULTILINE) doctestopt_re = re.compile(r'#\s*doctest:.+$', re.MULTILINE) -if PY2: - def doctest_encode(text, encoding): - # type: (str, unicode) -> unicode - if isinstance(text, text_type): - text = text.encode(encoding) - if text.startswith(codecs.BOM_UTF8): - text = text[len(codecs.BOM_UTF8):] - return text -else: - def doctest_encode(text, encoding): - # type: (unicode, unicode) -> unicode - return text + +def doctest_encode(text, encoding): + # type: (unicode, unicode) -> unicode + warnings.warn('doctest_encode() is deprecated.', + RemovedInSphinx40Warning) + return text def is_allowed_version(spec, version): @@ -203,7 +198,7 @@ parser = doctest.DocTestParser() # helper classes -class TestGroup(object): +class TestGroup: def __init__(self, name): # type: (unicode) -> None self.name = name @@ -236,7 +231,7 @@ class TestGroup(object): self.name, self.setup, self.cleanup, self.tests) -class TestCode(object): +class TestCode: def __init__(self, code, type, filename, lineno, options=None): # type: (unicode, unicode, Optional[str], int, Optional[Dict]) -> None self.code = code @@ -318,8 +313,8 @@ class DocTestBuilder(Builder): date = time.strftime('%Y-%m-%d %H:%M:%S') self.outfile = None # type: IO - self.outfile = codecs.open(path.join(self.outdir, 'output.txt'), # type: ignore - 'w', encoding='utf-8') + self.outfile = open(path.join(self.outdir, 'output.txt'), # type: ignore + 'w', encoding='utf-8') self.outfile.write(('Results of doctest builder run on %s\n' '==================================%s\n') % (date, '=' * len(date))) @@ -382,7 +377,7 @@ Doctest summary self.test_doc(docname, doctree) def get_filename_for_node(self, node, docname): - # type: (nodes.Node, unicode) -> str + # type: (nodes.Node, unicode) -> unicode """Try to get the file which actually contains the doctest, not the filename of the document it's included in.""" try: @@ -390,8 +385,6 @@ Doctest summary .rsplit(':docstring of ', maxsplit=1)[0] except Exception: filename = self.env.doc2path(docname, base=None) - if PY2: - return filename.encode(fs_encoding) return filename @staticmethod @@ -444,7 +437,7 @@ Doctest summary logger.warning(__('no code/output in %s block at %s:%s'), node.get('testnodetype', 'doctest'), filename, line_number) - code = TestCode(source, type=node.get('testnodetype', 'doctest'), + code = TestCode(source, type=node.get('testnodetype', 'doctest'), # type: ignore filename=filename, lineno=line_number, options=node.get('options')) node_groups = node.get('groups', ['default']) @@ -456,24 +449,24 @@ Doctest summary groups[groupname] = TestGroup(groupname) groups[groupname].add_code(code) for code in add_to_all_groups: - for group in itervalues(groups): + for group in groups.values(): group.add_code(code) if self.config.doctest_global_setup: code = TestCode(self.config.doctest_global_setup, 'testsetup', filename=None, lineno=0) - for group in itervalues(groups): + for group in groups.values(): group.add_code(code, prepend=True) if self.config.doctest_global_cleanup: code = TestCode(self.config.doctest_global_cleanup, 'testcleanup', filename=None, lineno=0) - for group in itervalues(groups): + for group in groups.values(): group.add_code(code) if not groups: return self._out('\nDocument: %s\n----------%s\n' % (docname, '-' * len(docname))) - for group in itervalues(groups): + for group in groups.values(): self.test_group(group) # Separately count results from setup code res_f, res_t = self.setup_runner.summarize(self._out, verbose=False) @@ -501,9 +494,9 @@ Doctest summary # type: (Any, List[TestCode], Any) -> bool examples = [] for testcode in testcodes: - examples.append(doctest.Example( # type: ignore - doctest_encode(testcode.code, self.env.config.source_encoding), '', # type: ignore # NOQA - lineno=testcode.lineno)) + example = doctest.Example(testcode.code, '', # type: ignore + lineno=testcode.lineno) + examples.append(example) if not examples: return True # simulate a doctest with the code @@ -528,9 +521,8 @@ Doctest summary if len(code) == 1: # ordinary doctests (code/output interleaved) try: - test = parser.get_doctest( # type: ignore - doctest_encode(code[0].code, self.env.config.source_encoding), {}, # type: ignore # NOQA - group.name, code[0].filename, code[0].lineno) + test = parser.get_doctest(code[0].code, {}, group.name, # type: ignore + code[0].filename, code[0].lineno) except Exception: logger.warning(__('ignoring invalid doctest code: %r'), code[0].code, location=(code[0].filename, code[0].lineno)) @@ -555,11 +547,10 @@ Doctest summary exc_msg = m.group('msg') else: exc_msg = None - example = doctest.Example( # type: ignore - doctest_encode(code[0].code, self.env.config.source_encoding), output, # type: ignore # NOQA - exc_msg=exc_msg, - lineno=code[0].lineno, - options=options) + example = doctest.Example(code[0].code, output, # type: ignore + exc_msg=exc_msg, + lineno=code[0].lineno, + options=options) test = doctest.DocTest([example], {}, group.name, # type: ignore code[0].filename, code[0].lineno, None) self.type = 'exec' # multiple statements again diff --git a/sphinx/ext/extlinks.py b/sphinx/ext/extlinks.py index 29bfe928f..106748dbb 100644 --- a/sphinx/ext/extlinks.py +++ b/sphinx/ext/extlinks.py @@ -25,7 +25,6 @@ """ from docutils import nodes, utils -from six import iteritems import sphinx from sphinx.util.nodes import split_explicit_title @@ -64,7 +63,7 @@ def make_link_role(base_url, prefix): def setup_link_roles(app): # type: (Sphinx) -> None - for name, (base_url, prefix) in iteritems(app.config.extlinks): + for name, (base_url, prefix) in app.config.extlinks.items(): app.add_role(name, make_link_role(base_url, prefix)) diff --git a/sphinx/ext/graphviz.py b/sphinx/ext/graphviz.py index 5ed39cfcf..57d63f120 100644 --- a/sphinx/ext/graphviz.py +++ b/sphinx/ext/graphviz.py @@ -10,7 +10,6 @@ :license: BSD, see LICENSE for details. """ -import codecs import posixpath import re from hashlib import sha1 @@ -44,7 +43,7 @@ class GraphvizError(SphinxError): category = 'Graphviz error' -class ClickableMapDefinition(object): +class ClickableMapDefinition: """A manipulator for clickable map file of graphviz.""" maptag_re = re.compile('<map id="(.*?)"') href_re = re.compile('href=".*?"') @@ -142,7 +141,7 @@ class Graphviz(SphinxDirective): rel_filename, filename = self.env.relfn2path(argument) self.env.note_dependency(rel_filename) try: - with codecs.open(filename, 'r', 'utf-8') as fp: # type: ignore + with open(filename, 'r', encoding='utf-8') as fp: # type: ignore dotcode = fp.read() except (IOError, OSError): return [document.reporter.warning( @@ -309,7 +308,7 @@ def render_dot_html(self, node, code, options, prefix='graphviz', self.body.append('<p class="warning">%s</p>' % alt) self.body.append('</object></div>\n') else: - with codecs.open(outfn + '.map', 'r', encoding='utf-8') as mapfile: # type: ignore + with open(outfn + '.map', 'r', encoding='utf-8') as mapfile: # type: ignore imgmap = ClickableMapDefinition(outfn + '.map', mapfile.read(), dot=code) if imgmap.clickable: # has a map diff --git a/sphinx/ext/imgmath.py b/sphinx/ext/imgmath.py index 18f04a095..cbd38b84c 100644 --- a/sphinx/ext/imgmath.py +++ b/sphinx/ext/imgmath.py @@ -9,7 +9,6 @@ :license: BSD, see LICENSE for details. """ -import codecs import posixpath import re import shutil @@ -123,7 +122,7 @@ def compile_math(latex, builder): """Compile LaTeX macros for math to DVI.""" tempdir = ensure_tempdir(builder) filename = path.join(tempdir, 'math.tex') - with codecs.open(filename, 'w', 'utf-8') as f: # type: ignore + with open(filename, 'w', encoding='utf-8') as f: # type: ignore f.write(latex) # build latex command; old versions of latex don't have the diff --git a/sphinx/ext/inheritance_diagram.py b/sphinx/ext/inheritance_diagram.py index 750fcd4f7..3815f6ba1 100644 --- a/sphinx/ext/inheritance_diagram.py +++ b/sphinx/ext/inheritance_diagram.py @@ -36,6 +36,7 @@ r""" :license: BSD, see LICENSE for details. """ +import builtins import inspect import re import sys @@ -44,7 +45,6 @@ from hashlib import md5 from docutils import nodes from docutils.parsers.rst import directives from six import text_type -from six.moves import builtins import sphinx from sphinx.ext.graphviz import render_dot_html, render_dot_latex, \ @@ -129,7 +129,7 @@ class InheritanceException(Exception): pass -class InheritanceGraph(object): +class InheritanceGraph: """ Given a list of classes, determines the set of classes that they inherit from all the way to the root "object", and then is able to generate a diff --git a/sphinx/ext/intersphinx.py b/sphinx/ext/intersphinx.py index e3f8dc275..482ed1957 100644 --- a/sphinx/ext/intersphinx.py +++ b/sphinx/ext/intersphinx.py @@ -30,17 +30,15 @@ import functools import posixpath import sys import time -import warnings from os import path from docutils import nodes from docutils.utils import relative_path -from six import PY3, iteritems, string_types +from six import string_types, text_type from six.moves.urllib.parse import urlsplit, urlunsplit import sphinx from sphinx.builders.html import INVENTORY_FILENAME -from sphinx.deprecation import RemovedInSphinx20Warning from sphinx.locale import _, __ from sphinx.util import requests, logging from sphinx.util.inventory import InventoryFile @@ -52,15 +50,12 @@ if False: from sphinx.config import Config # NOQA from sphinx.environment import BuildEnvironment # NOQA - if PY3: - unicode = str - - Inventory = Dict[unicode, Dict[unicode, Tuple[unicode, unicode, unicode, unicode]]] + Inventory = Dict[text_type, Dict[text_type, Tuple[text_type, text_type, text_type, text_type]]] # NOQA logger = logging.getLogger(__name__) -class InventoryAdapter(object): +class InventoryAdapter: """Inventory adapter for environment""" def __init__(self, env): @@ -214,7 +209,7 @@ def load_mappings(app): cache_time = now - app.config.intersphinx_cache_limit * 86400 inventories = InventoryAdapter(app.builder.env) update = False - for key, value in iteritems(app.config.intersphinx_mapping): + for key, value in app.config.intersphinx_mapping.items(): name = None # type: unicode uri = None # type: unicode inv = None # type: Union[unicode, Tuple[unicode, ...]] @@ -286,7 +281,7 @@ def load_mappings(app): for name, _x, invdata in named_vals + unnamed_vals: if name: inventories.named_inventory[name] = invdata - for type, objects in iteritems(invdata): + for type, objects in invdata.items(): inventories.main_inventory.setdefault(type, {}).update(objects) @@ -380,15 +375,6 @@ def setup(app): } -def debug(argv): - # type: (List[unicode]) -> None - """Debug functionality to print out an inventory""" - warnings.warn('sphinx.ext.intersphinx.debug() is deprecated. ' - 'Please use inspect_main() instead', - RemovedInSphinx20Warning) - inspect_main(argv[1:]) - - def inspect_main(argv): # type: (List[unicode]) -> None """Debug functionality to print out an inventory""" @@ -398,11 +384,11 @@ def inspect_main(argv): file=sys.stderr) sys.exit(1) - class MockConfig(object): + class MockConfig: intersphinx_timeout = None # type: int tls_verify = False - class MockApp(object): + class MockApp: srcdir = '' config = MockConfig() diff --git a/sphinx/ext/napoleon/__init__.py b/sphinx/ext/napoleon/__init__.py index b968f5948..00b78e859 100644 --- a/sphinx/ext/napoleon/__init__.py +++ b/sphinx/ext/napoleon/__init__.py @@ -9,11 +9,7 @@ :license: BSD, see LICENSE for details. """ -import sys - -from six import PY2, iteritems - -import sphinx +from sphinx import __display_version__ as __version__ from sphinx.application import Sphinx from sphinx.ext.napoleon.docstring import GoogleDocstring, NumpyDocstring @@ -22,7 +18,7 @@ if False: from typing import Any, Dict, List # NOQA -class Config(object): +class Config: """Sphinx napoleon extension settings in `conf.py`. Listed below are all the settings used by napoleon and their default @@ -176,10 +172,10 @@ class Config(object): .. attribute:: attr1 - *int* - Description of `attr1` + :type: int + napoleon_use_param : :obj:`bool` (Defaults to True) True to use a ``:param:`` role for each function parameter. False to use a single ``:parameters:`` role for all the parameters. @@ -274,9 +270,9 @@ class Config(object): def __init__(self, **settings): # type: (Any) -> None - for name, (default, rebuild) in iteritems(self._config_values): + for name, (default, rebuild) in self._config_values.items(): setattr(self, name, default) - for name, value in iteritems(settings): + for name, value in settings.items(): setattr(self, name, value) @@ -304,7 +300,8 @@ def setup(app): """ if not isinstance(app, Sphinx): - return # probably called by tests + # probably called by tests + return {'version': __version__, 'parallel_read_safe': True} _patch_python_domain() @@ -312,9 +309,9 @@ def setup(app): app.connect('autodoc-process-docstring', _process_docstring) app.connect('autodoc-skip-member', _skip_member) - for name, (default, rebuild) in iteritems(Config._config_values): + for name, (default, rebuild) in Config._config_values.items(): app.add_config_value(name, default, rebuild) - return {'version': sphinx.__display_version__, 'parallel_read_safe': True} + return {'version': __version__, 'parallel_read_safe': True} def _patch_python_domain(): @@ -435,34 +432,26 @@ def _skip_member(app, what, name, obj, skip, options): if name != '__weakref__' and has_doc and is_member: cls_is_owner = False if what == 'class' or what == 'exception': - if PY2: - cls = getattr(obj, 'im_class', getattr(obj, '__objclass__', - None)) - cls_is_owner = (cls and hasattr(cls, name) and - name in cls.__dict__) - elif sys.version_info >= (3, 3): - qualname = getattr(obj, '__qualname__', '') - cls_path, _, _ = qualname.rpartition('.') - if cls_path: - try: - if '.' in cls_path: - import importlib - import functools - - mod = importlib.import_module(obj.__module__) - mod_path = cls_path.split('.') - cls = functools.reduce(getattr, mod_path, mod) - else: - cls = obj.__globals__[cls_path] - except Exception: - cls_is_owner = False + qualname = getattr(obj, '__qualname__', '') + cls_path, _, _ = qualname.rpartition('.') + if cls_path: + try: + if '.' in cls_path: + import importlib + import functools + + mod = importlib.import_module(obj.__module__) + mod_path = cls_path.split('.') + cls = functools.reduce(getattr, mod_path, mod) else: - cls_is_owner = (cls and hasattr(cls, name) and - name in cls.__dict__) - else: + cls = obj.__globals__[cls_path] + except Exception: cls_is_owner = False + else: + cls_is_owner = (cls and hasattr(cls, name) and # type: ignore + name in cls.__dict__) else: - cls_is_owner = True + cls_is_owner = False if what == 'module' or cls_is_owner: is_init = (name == '__init__') diff --git a/sphinx/ext/napoleon/docstring.py b/sphinx/ext/napoleon/docstring.py index 2e5380472..f8a1327aa 100644 --- a/sphinx/ext/napoleon/docstring.py +++ b/sphinx/ext/napoleon/docstring.py @@ -11,13 +11,12 @@ :license: BSD, see LICENSE for details. """ -import collections import inspect import re +from collections.abc import Callable from functools import partial from six import string_types, u -from six.moves import range from sphinx.ext.napoleon.iterators import modify_iter from sphinx.locale import _ @@ -25,7 +24,7 @@ from sphinx.util.pycompat import UnicodeMixin if False: # For type annotation - from typing import Any, Callable, Dict, List, Tuple, Union # NOQA + from typing import Any, Dict, List, Tuple, Type, Union # NOQA from sphinx.application import Sphinx # NOQA from sphinx.config import Config as SphinxConfig # NOQA @@ -105,6 +104,10 @@ class GoogleDocstring(UnicodeMixin): <BLANKLINE> """ + + _name_rgx = re.compile(r"^\s*(:(?P<role>\w+):`(?P<name>[a-zA-Z0-9_.-]+)`|" + r" (?P<name2>[a-zA-Z0-9_.-]+))\s*", re.X) + def __init__(self, docstring, config=None, app=None, what='', name='', obj=None, options=None): # type: (Union[unicode, List[unicode]], SphinxConfig, Sphinx, unicode, unicode, Any, Any) -> None # NOQA @@ -120,7 +123,7 @@ class GoogleDocstring(UnicodeMixin): what = 'class' elif inspect.ismodule(obj): what = 'module' - elif isinstance(obj, collections.Callable): # type: ignore + elif isinstance(obj, Callable): what = 'function' else: what = 'object' @@ -264,8 +267,9 @@ class GoogleDocstring(UnicodeMixin): # type: () -> Tuple[unicode, List[unicode]] line = next(self._line_iter) _type, colon, _desc = self._partition_field_on_colon(line) - if not colon: + if not colon or not _desc: _type, _desc = _desc, _type + _desc += colon _descs = [_desc] + self._dedent(self._consume_to_end()) _descs = self.__class__(_descs, self._config).lines() return _type, _descs @@ -604,6 +608,7 @@ class GoogleDocstring(UnicodeMixin): lines = [] for _name, _type, _desc in self._consume_fields(): if self._config.napoleon_use_ivar: + _name = self._qualify_name(_name, self._obj) field = ':ivar %s: ' % _name # type: unicode lines.extend(self._format_block(field, _desc)) if _type: @@ -697,39 +702,16 @@ class GoogleDocstring(UnicodeMixin): def _parse_raises_section(self, section): # type: (unicode) -> List[unicode] fields = self._consume_fields(parse_type=False, prefer_type=True) - field_type = ':raises:' - padding = ' ' * len(field_type) - multi = len(fields) > 1 lines = [] # type: List[unicode] for _name, _type, _desc in fields: + m = self._name_rgx.match(_type).groupdict() # type: ignore + if m['role']: + _type = m['name'] + _type = ' ' + _type if _type else '' _desc = self._strip_empty(_desc) - has_desc = any(_desc) - separator = has_desc and ' -- ' or '' - if _type: - has_refs = '`' in _type or ':' in _type - has_space = any(c in ' \t\n\v\f ' for c in _type) - - if not has_refs and not has_space: - _type = ':exc:`%s`%s' % (_type, separator) - elif has_desc and has_space: - _type = '*%s*%s' % (_type, separator) - else: - _type = '%s%s' % (_type, separator) - - if has_desc: - field = [_type + _desc[0]] + _desc[1:] - else: - field = [_type] - else: - field = _desc - if multi: - if lines: - lines.extend(self._format_block(padding + ' * ', field)) - else: - lines.extend(self._format_block(field_type + ' * ', field)) - else: - lines.extend(self._format_block(field_type + ' ', field)) - if lines and lines[-1]: + _descs = ' ' + '\n '.join(_desc) if any(_desc) else '' + lines.append(':raises%s:%s' % (_type, _descs)) + if lines: lines.append('') return lines @@ -803,6 +785,18 @@ class GoogleDocstring(UnicodeMixin): colon, "".join(after_colon).strip()) + def _qualify_name(self, attr_name, klass): + # type: (unicode, Type) -> unicode + if klass and '.' not in attr_name: + if attr_name.startswith('~'): + attr_name = attr_name[1:] + try: + q = klass.__qualname__ + except AttributeError: + q = klass.__name__ + return '~%s.%s' % (q, attr_name) + return attr_name + def _strip_empty(self, lines): # type: (List[unicode]) -> List[unicode] if lines: @@ -976,9 +970,6 @@ class NumpyDocstring(GoogleDocstring): return True return False - _name_rgx = re.compile(r"^\s*(:(?P<role>\w+):`(?P<name>[a-zA-Z0-9_.-]+)`|" - r" (?P<name2>[a-zA-Z0-9_.-]+))\s*", re.X) - def _parse_see_also_section(self, section): # type: (unicode) -> List[unicode] lines = self._consume_to_next_section() diff --git a/sphinx/ext/napoleon/iterators.py b/sphinx/ext/napoleon/iterators.py index 8926c865c..0ad8038f3 100644 --- a/sphinx/ext/napoleon/iterators.py +++ b/sphinx/ext/napoleon/iterators.py @@ -18,7 +18,7 @@ if False: from typing import Any, Iterable # NOQA -class peek_iter(object): +class peek_iter: """An iterator object that supports peeking ahead. Parameters diff --git a/sphinx/ext/viewcode.py b/sphinx/ext/viewcode.py index ca944a636..38c34733b 100644 --- a/sphinx/ext/viewcode.py +++ b/sphinx/ext/viewcode.py @@ -13,7 +13,7 @@ import traceback import warnings from docutils import nodes -from six import iteritems, text_type +from six import text_type import sphinx from sphinx import addnodes @@ -163,7 +163,7 @@ def collect_pages(app): # len(env._viewcode_modules), nonl=1) for modname, entry in status_iterator( - sorted(iteritems(env._viewcode_modules)), # type: ignore + sorted(env._viewcode_modules.items()), # type: ignore 'highlighting module code... ', "blue", len(env._viewcode_modules), # type: ignore app.verbosity, lambda x: x[0]): @@ -188,7 +188,7 @@ def collect_pages(app): # the collected tags (HACK: this only works if the tag boundaries are # properly nested!) maxindex = len(lines) - 1 - for name, docname in iteritems(used): + for name, docname in used.items(): type, start, end = tags[name] backlink = urito(pagename, docname) + '#' + refname + '.' + name lines[start] = ( diff --git a/sphinx/extension.py b/sphinx/extension.py index 732ea327c..4282d5f07 100644 --- a/sphinx/extension.py +++ b/sphinx/extension.py @@ -9,8 +9,6 @@ :license: BSD, see LICENSE for details. """ -from six import iteritems - from sphinx.errors import VersionRequirementError from sphinx.locale import __ from sphinx.util import logging @@ -24,7 +22,7 @@ if False: logger = logging.getLogger(__name__) -class Extension(object): +class Extension: def __init__(self, name, module, **kwargs): # type: (unicode, Any, Any) -> None self.name = name @@ -49,7 +47,7 @@ def verify_needs_extensions(app, config): if config.needs_extensions is None: return - for extname, reqversion in iteritems(config.needs_extensions): + for extname, reqversion in config.needs_extensions.items(): extension = app.extensions.get(extname) if extension is None: logger.warning(__('The %s extension is required by needs_extensions settings, ' diff --git a/sphinx/highlighting.py b/sphinx/highlighting.py index 21e3420ba..e61a891a0 100644 --- a/sphinx/highlighting.py +++ b/sphinx/highlighting.py @@ -62,7 +62,7 @@ _LATEX_ADD_STYLES = r''' ''' -class PygmentsBridge(object): +class PygmentsBridge: # Set these attributes if you want to have different Pygments formatters # than the default ones. html_formatter = HtmlFormatter diff --git a/sphinx/io.py b/sphinx/io.py index ad76c2afb..f18a1e95f 100644 --- a/sphinx/io.py +++ b/sphinx/io.py @@ -18,7 +18,7 @@ from docutils.parsers.rst import Parser as RSTParser from docutils.readers import standalone from docutils.statemachine import StringList, string2lines from docutils.writers import UnfilteredWriter -from six import text_type, iteritems +from six import text_type from typing import Any, Union # NOQA from sphinx.deprecation import RemovedInSphinx30Warning @@ -282,7 +282,7 @@ class FiletypeNotFoundError(Exception): def get_filetype(source_suffix, filename): # type: (Dict[unicode, unicode], unicode) -> unicode - for suffix, filetype in iteritems(source_suffix): + for suffix, filetype in source_suffix.items(): if filename.endswith(suffix): # If default filetype (None), considered as restructuredtext. return filetype or 'restructuredtext' diff --git a/sphinx/jinja2glue.py b/sphinx/jinja2glue.py index f19454c27..c3ed86e09 100644 --- a/sphinx/jinja2glue.py +++ b/sphinx/jinja2glue.py @@ -98,7 +98,7 @@ def accesskey(context, key): return '' -class idgen(object): +class idgen: def __init__(self): # type: () -> None self.id = 0 diff --git a/sphinx/locale/__init__.py b/sphinx/locale/__init__.py index 7f69d1dca..a09447cb1 100644 --- a/sphinx/locale/__init__.py +++ b/sphinx/locale/__init__.py @@ -25,7 +25,7 @@ if False: from typing import Any, Callable, Dict, Iterator, List, Tuple # NOQA -class _TranslationProxy(UserString, object): +class _TranslationProxy(UserString): """ Class for proxy strings from gettext translations. This is a helper for the lazy_* functions from this module. @@ -36,7 +36,6 @@ class _TranslationProxy(UserString, object): This inherits from UserString because some docutils versions use UserString for their Text nodes, which then checks its argument for being either a basestring or UserString, otherwise calls str() -- not unicode() -- on it. - This also inherits from object to make the __new__ method work. """ __slots__ = ('_func', '_args') @@ -65,7 +64,7 @@ class _TranslationProxy(UserString, object): # for the encoding result def encode(self, encoding=None, errors=None): # type: ignore - # type: (unicode, unicode) -> str + # type: (unicode, unicode) -> bytes if encoding: if errors: return self.data.encode(encoding, errors) @@ -139,10 +138,6 @@ class _TranslationProxy(UserString, object): # type: (Any) -> bool return self.data == other - def __ne__(self, other): - # type: (Any) -> bool - return self.data != other - def __gt__(self, other): # type: (unicode) -> bool return self.data > other diff --git a/sphinx/parsers.py b/sphinx/parsers.py index 5f15c4103..9b1fef702 100644 --- a/sphinx/parsers.py +++ b/sphinx/parsers.py @@ -55,8 +55,6 @@ class Parser(docutils.parsers.Parser): self.app = app self.config = app.config self.env = app.env - self.warn = app.warn - self.info = app.info class RSTParser(docutils.parsers.rst.Parser): diff --git a/sphinx/pycode/__init__.py b/sphinx/pycode/__init__.py index 8561169b5..3bd49a305 100644 --- a/sphinx/pycode/__init__.py +++ b/sphinx/pycode/__init__.py @@ -11,9 +11,10 @@ from __future__ import print_function import re +from io import BytesIO from zipfile import ZipFile -from six import iteritems, BytesIO, StringIO +from six import StringIO from sphinx.errors import PycodeError from sphinx.pycode.parser import Parser @@ -24,7 +25,7 @@ if False: from typing import Any, Dict, IO, List, Tuple # NOQA -class ModuleAnalyzer(object): +class ModuleAnalyzer: # cache for analyzer objects -- caches both by module and file name cache = {} # type: Dict[Tuple[unicode, unicode], Any] @@ -111,7 +112,7 @@ class ModuleAnalyzer(object): parser.parse() self.attr_docs = {} - for (scope, comment) in iteritems(parser.comments): + for (scope, comment) in parser.comments.items(): if comment: self.attr_docs[scope] = comment.splitlines() + [''] else: diff --git a/sphinx/pycode/parser.py b/sphinx/pycode/parser.py index 9d464a253..e1aa1f504 100644 --- a/sphinx/pycode/parser.py +++ b/sphinx/pycode/parser.py @@ -17,7 +17,7 @@ import tokenize from token import NAME, NEWLINE, INDENT, DEDENT, NUMBER, OP, STRING from tokenize import COMMENT, NL -from six import PY2, text_type +from six import text_type if False: # For type annotation @@ -59,10 +59,7 @@ def get_lvar_names(node, self=None): # => TypeError """ if self: - if PY2: - self_id = self.id # type: ignore - else: - self_id = self.arg + self_id = self.arg # type: ignore node_name = node.__class__.__name__ if node_name in ('Index', 'Num', 'Slice', 'Str', 'Subscript'): @@ -107,7 +104,7 @@ def dedent_docstring(s): return docstring.lstrip("\r\n").rstrip("\r\n") -class Token(object): +class Token: """Better token wrapper for tokenize module.""" def __init__(self, kind, value, start, end, source): @@ -131,10 +128,6 @@ class Token(object): else: raise ValueError('Unknown value: %r' % other) - def __ne__(self, other): - # type: (Any) -> bool - return not (self == other) - def match(self, *conditions): # type: (Any) -> bool return any(self == candidate for candidate in conditions) @@ -145,7 +138,7 @@ class Token(object): self.value.strip()) -class TokenProcessor(object): +class TokenProcessor: def __init__(self, buffers): # type: (List[unicode]) -> None lines = iter(buffers) @@ -464,7 +457,7 @@ class DefinitionFinder(TokenProcessor): self.context.pop() -class Parser(object): +class Parser: """Python source code parser to pick up variable comments. This is a better wrapper for ``VariableCommentPicker``. diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py deleted file mode 100644 index 8cad0640b..000000000 --- a/sphinx/quickstart.py +++ /dev/null @@ -1,41 +0,0 @@ -# -*- coding: utf-8 -*- -""" - sphinx.quickstart - ~~~~~~~~~~~~~~~~~ - - This file has moved to :py:mod:`sphinx.cmd.quickstart`. - - :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. - :license: BSD, see LICENSE for details. -""" - -import warnings - -from sphinx.cmd.quickstart import main as _main -from sphinx.deprecation import RemovedInSphinx20Warning - -if False: - # For type annotation - from typing import Any # NOQA - - -def main(*args, **kwargs): - # type: (Any, Any) -> None - warnings.warn( - '`sphinx.quickstart.main()` has moved to `sphinx.cmd.quickstart.' - 'main()`.', - RemovedInSphinx20Warning, - stacklevel=2, - ) - args = args[1:] # skip first argument to adjust arguments (refs: #4615) - _main(*args, **kwargs) - - -# So program can be started with "python -m sphinx.quickstart ..." -if __name__ == "__main__": - warnings.warn( - '`sphinx.quickstart` has moved to `sphinx.cmd.quickstart`.', - RemovedInSphinx20Warning, - stacklevel=2, - ) - main() diff --git a/sphinx/registry.py b/sphinx/registry.py index fc9dce316..763a3ce54 100644 --- a/sphinx/registry.py +++ b/sphinx/registry.py @@ -17,7 +17,6 @@ from types import MethodType from docutils.parsers.rst import Directive from pkg_resources import iter_entry_points -from six import iteritems, itervalues from sphinx.deprecation import RemovedInSphinx30Warning from sphinx.domains import ObjType @@ -54,7 +53,7 @@ EXTENSION_BLACKLIST = { } # type: Dict[unicode, unicode] -class SphinxComponentRegistry(object): +class SphinxComponentRegistry: def __init__(self): # type: () -> None #: special attrgetter for autodoc; class object -> attrgetter @@ -170,14 +169,14 @@ class SphinxComponentRegistry(object): def create_domains(self, env): # type: (BuildEnvironment) -> Iterator[Domain] - for DomainClass in itervalues(self.domains): + for DomainClass in self.domains.values(): domain = DomainClass(env) # transplant components added by extensions domain.directives.update(self.domain_directives.get(domain.name, {})) domain.roles.update(self.domain_roles.get(domain.name, {})) domain.indices.extend(self.domain_indices.get(domain.name, [])) - for name, objtype in iteritems(self.domain_object_types.get(domain.name, {})): + for name, objtype in self.domain_object_types.get(domain.name, {}).items(): domain.add_object_type(name, objtype) yield domain @@ -365,7 +364,7 @@ class SphinxComponentRegistry(object): def add_translation_handlers(self, node, **kwargs): # type: (nodes.Node, Any) -> None logger.debug('[app] adding translation_handlers: %r, %r', node, kwargs) - for builder_name, handlers in iteritems(kwargs): + for builder_name, handlers in kwargs.items(): translation_handlers = self.translation_handlers.setdefault(builder_name, {}) try: visit, depart = handlers # unpack once for assertion @@ -391,7 +390,7 @@ class SphinxComponentRegistry(object): # retry with builder.format handlers = self.translation_handlers.get(builder.format, {}) - for name, (visit, depart) in iteritems(handlers): + for name, (visit, depart) in handlers.items(): setattr(translator, 'visit_' + name, MethodType(visit, translator)) if depart: setattr(translator, 'depart_' + name, MethodType(depart, translator)) @@ -512,7 +511,7 @@ class SphinxComponentRegistry(object): def merge_source_suffix(app, config): # type: (Sphinx, Config) -> None """Merge source_suffix which specified by user and added by extensions.""" - for suffix, filetype in iteritems(app.registry.source_suffix): + for suffix, filetype in app.registry.source_suffix.items(): if suffix not in app.config.source_suffix: app.config.source_suffix[suffix] = filetype elif app.config.source_suffix[suffix] is None: diff --git a/sphinx/roles.py b/sphinx/roles.py index b2a540122..fb01a876b 100644 --- a/sphinx/roles.py +++ b/sphinx/roles.py @@ -12,7 +12,6 @@ import re from docutils import nodes, utils -from six import iteritems from sphinx import addnodes from sphinx.errors import SphinxError @@ -45,7 +44,7 @@ generic_docroles = { # -- generic cross-reference role ---------------------------------------------- -class XRefRole(object): +class XRefRole: """ A generic cross-referencing role. To create a callable that can be used as a role function, create an instance of this class. @@ -403,12 +402,12 @@ def setup(app): # type: (Sphinx) -> Dict[unicode, Any] from docutils.parsers.rst import roles - for rolename, nodeclass in iteritems(generic_docroles): + for rolename, nodeclass in generic_docroles.items(): generic = roles.GenericRole(rolename, nodeclass) role = roles.CustomRole(rolename, generic, {'classes': [rolename]}) roles.register_local_role(rolename, role) - for rolename, func in iteritems(specific_docroles): + for rolename, func in specific_docroles.items(): roles.register_local_role(rolename, func) return { diff --git a/sphinx/search/__init__.py b/sphinx/search/__init__.py index 3da6c4ba5..4ae96fb57 100644 --- a/sphinx/search/__init__.py +++ b/sphinx/search/__init__.py @@ -8,11 +8,11 @@ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. """ +import pickle import re from os import path -from six import iteritems, itervalues, text_type, string_types -from six.moves import cPickle as pickle +from six import text_type, string_types from docutils.nodes import raw, comment, title, Text, NodeVisitor, SkipNode @@ -28,7 +28,7 @@ if False: from sphinx.environment import BuildEnvironment # NOQA -class SearchLanguage(object): +class SearchLanguage: """ This class is the base class for search natural language preprocessors. If you want to add support for a new language, you should override the methods @@ -155,7 +155,7 @@ languages = { } # type: Dict[unicode, Any] -class _JavaScriptIndex(object): +class _JavaScriptIndex: """ The search index as javascript file that calls a function on the documentation search object to register the index. @@ -236,7 +236,7 @@ class WordCollector(NodeVisitor): self.found_words.extend(keywords) -class IndexBuilder(object): +class IndexBuilder: """ Helper class that creates a searchindex based on the doctrees passed to the `feed` method. @@ -305,7 +305,7 @@ class IndexBuilder(object): def load_terms(mapping): # type: (Dict[unicode, Any]) -> Dict[unicode, Set[unicode]] rv = {} - for k, v in iteritems(mapping): + for k, v in mapping.items(): if isinstance(v, int): rv[k] = set([index2fn[v]]) else: @@ -328,7 +328,7 @@ class IndexBuilder(object): rv = {} # type: Dict[unicode, Dict[unicode, Tuple[int, int, int, unicode]]] otypes = self._objtypes onames = self._objnames - for domainname, domain in sorted(iteritems(self.env.domains)): + for domainname, domain in sorted(self.env.domains.items()): for fullname, dispname, type, docname, anchor, prio in \ sorted(domain.get_objects()): if docname not in fn2index: @@ -364,7 +364,7 @@ class IndexBuilder(object): # type: (Dict) -> Tuple[Dict[unicode, List[unicode]], Dict[unicode, List[unicode]]] rvs = {}, {} # type: Tuple[Dict[unicode, List[unicode]], Dict[unicode, List[unicode]]] for rv, mapping in zip(rvs, (self._mapping, self._title_mapping)): - for k, v in iteritems(mapping): + for k, v in mapping.items(): if len(v) == 1: fn, = v if fn in fn2index: @@ -383,7 +383,7 @@ class IndexBuilder(object): objects = self.get_objects(fn2index) # populates _objtypes objtypes = dict((v, k[0] + ':' + k[1]) - for (k, v) in iteritems(self._objtypes)) + for (k, v) in self._objtypes.items()) objnames = self._objnames return dict(docnames=docnames, filenames=filenames, titles=titles, terms=terms, objects=objects, objtypes=objtypes, objnames=objnames, @@ -404,9 +404,9 @@ class IndexBuilder(object): new_filenames[docname] = self._filenames[docname] self._titles = new_titles self._filenames = new_filenames - for wordnames in itervalues(self._mapping): + for wordnames in self._mapping.values(): wordnames.intersection_update(docnames) - for wordnames in itervalues(self._title_mapping): + for wordnames in self._title_mapping.values(): wordnames.intersection_update(docnames) def feed(self, docname, filename, title, doctree): diff --git a/sphinx/search/ja.py b/sphinx/search/ja.py index a48a0df69..f28013b0b 100644 --- a/sphinx/search/ja.py +++ b/sphinx/search/ja.py @@ -22,8 +22,6 @@ import re import sys import warnings -from six import iteritems, PY3 - try: import MeCab native_module = True @@ -46,7 +44,7 @@ if False: from typing import Any, Dict, List # NOQA -class BaseSplitter(object): +class BaseSplitter: def __init__(self, options): # type: (Dict) -> None @@ -77,16 +75,12 @@ class MecabSplitter(BaseSplitter): def split(self, input): # type: (unicode) -> List[unicode] - input2 = input if PY3 else input.encode(self.dict_encode) if native_module: - result = self.native.parse(input2) + result = self.native.parse(input) else: result = self.ctypes_libmecab.mecab_sparse_tostr( self.ctypes_mecab, input.encode(self.dict_encode)) - if PY3: - return result.split(' ') - else: - return result.decode(self.dict_encode).split(' ') + return result.split(' ') def init_native(self, options): # type: (Dict) -> None @@ -162,14 +156,14 @@ class JanomeSplitter(BaseSplitter): class DefaultSplitter(BaseSplitter): - patterns_ = dict([(re.compile(pattern), value) for pattern, value in iteritems({ + patterns_ = dict([(re.compile(pattern), value) for pattern, value in { u'[一二三四五六七八九十百千万億兆]': u'M', u'[一-龠々〆ヵヶ]': u'H', u'[ぁ-ん]': u'I', u'[ァ-ヴーア-ン゙ー]': u'K', u'[a-zA-Za-zA-Z]': u'A', u'[0-90-9]': u'N', - })]) + }.items()]) BIAS__ = -332 BC1__ = {u'HH': 6, u'II': 2461, u'KH': 406, u'OH': -1378} BC2__ = {u'AA': -3267, u'AI': 2744, u'AN': -878, u'HH': -4070, u'HM': -1711, @@ -434,7 +428,7 @@ class DefaultSplitter(BaseSplitter): # ctype_ def ctype_(self, char): # type: (unicode) -> unicode - for pattern, value in iteritems(self.patterns_): + for pattern, value in self.patterns_.items(): if pattern.match(char): return value return u'O' diff --git a/sphinx/templates/latex/latex.tex_t b/sphinx/templates/latex/latex.tex_t index 633525551..2c49c3179 100644 --- a/sphinx/templates/latex/latex.tex_t +++ b/sphinx/templates/latex/latex.tex_t @@ -36,7 +36,7 @@ <%= hyperref %> <%= contentsname %> <%= numfig_format %> -<%= translatablestrings %> +\input{sphinxmessages.sty} <%= pageautorefname %> <%= tocdepth %> <%= secnumdepth %> @@ -47,7 +47,11 @@ \release{<%= release %>} \author{<%= author %>} \newcommand{\sphinxlogo}{<%= logo %>} -\renewcommand{\releasename}{<%= releasename %>} +<%- if releasename or release %> +\renewcommand{\releasename}{<%= releasename or _('Release') | e %>} +<%- else %> +\renewcommand{\releasename}{} +<%- endif %> <%= makeindex %> \begin{document} <%= shorthandoff %> @@ -59,6 +63,6 @@ <%= body %> <%= atendofbody %> <%= indices %> -\renewcommand{\indexname}{<%= indexname %>} +\renewcommand{\indexname}{<%= _('Index') | e %>} <%= printindex %> \end{document} diff --git a/sphinx/templates/latex/sphinxmessages.sty_t b/sphinx/templates/latex/sphinxmessages.sty_t new file mode 100644 index 000000000..2413a1725 --- /dev/null +++ b/sphinx/templates/latex/sphinxmessages.sty_t @@ -0,0 +1,10 @@ +% +% sphinxmessages.sty +% +% message resources for Sphinx +% +\renewcommand{\literalblockcontinuedname}{<%= _('continued from previous page') | e %>} +\renewcommand{\literalblockcontinuesname}{<%= _('continues on next page') | e %>} +\renewcommand{\sphinxnonalphabeticalgroupname}{<%= _('Non-alphabetical') | e %>} +\renewcommand{\sphinxsymbolsname}{<%= _('Symbols') | e %>} +\renewcommand{\sphinxnumbersname}{<%= _('Numbers') | e %>} diff --git a/sphinx/templates/quickstart/conf.py_t b/sphinx/templates/quickstart/conf.py_t index 6128c0235..a29a6eb71 100644 --- a/sphinx/templates/quickstart/conf.py_t +++ b/sphinx/templates/quickstart/conf.py_t @@ -62,9 +62,11 @@ templates_path = ['{{ dot }}templates'] # source_suffix = ['.rst', '.md'] source_suffix = '{{ suffix }}' +{% if master_doc != 'index' -%} # The master toctree document. master_doc = '{{ master_str }}' +{% endif -%} # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # diff --git a/sphinx/testing/fixtures.py b/sphinx/testing/fixtures.py index fcf1028fd..4ea634b10 100644 --- a/sphinx/testing/fixtures.py +++ b/sphinx/testing/fixtures.py @@ -173,7 +173,7 @@ def make_app(test_params, monkeypatch): app_.cleanup() -class SharedResult(object): +class SharedResult: cache = {} # type: Dict[str, Dict[str, str]] def store(self, key, app_): diff --git a/sphinx/testing/path.py b/sphinx/testing/path.py index 1c9781dea..78c3cb7bc 100644 --- a/sphinx/testing/path.py +++ b/sphinx/testing/path.py @@ -9,9 +9,8 @@ import os import shutil import sys -from io import open -from six import PY2, text_type +from six import text_type if False: # For type annotation @@ -25,13 +24,6 @@ class path(text_type): """ Represents a path which behaves like a string. """ - if PY2: - def __new__(cls, s, encoding=FILESYSTEMENCODING, errors='strict'): - # type: (unicode, unicode, unicode) -> path - if isinstance(s, str): - s = s.decode(encoding, errors) - return text_type.__new__(cls, s) # type: ignore - return text_type.__new__(cls, s) # type: ignore @property def parent(self): @@ -161,7 +153,7 @@ class path(text_type): """ if isinstance(text, bytes): text = text.decode(encoding) - with open(self, 'w', encoding=encoding, **kwargs) as f: + with open(self, 'w', encoding=encoding, **kwargs) as f: # type: ignore f.write(text) def text(self, encoding='utf-8', **kwargs): @@ -169,8 +161,7 @@ class path(text_type): """ Returns the text in the file. """ - mode = 'rU' if PY2 else 'r' - with open(self, mode=mode, encoding=encoding, **kwargs) as f: + with open(self, mode='r', encoding=encoding, **kwargs) as f: # type: ignore return f.read() def bytes(self): diff --git a/sphinx/testing/util.py b/sphinx/testing/util.py index 4c18d1c0c..cd2073aaa 100644 --- a/sphinx/testing/util.py +++ b/sphinx/testing/util.py @@ -20,7 +20,7 @@ from six import string_types from sphinx import application, locale from sphinx.builders.latex import LaTeXBuilder -from sphinx.ext.autodoc import AutoDirective +from sphinx.deprecation import RemovedInSphinx40Warning from sphinx.pycode import ModuleAnalyzer from sphinx.testing.path import path from sphinx.util.osutil import relpath @@ -93,7 +93,7 @@ def etree_parse(path): return ElementTree.parse(path) # type: ignore -class Struct(object): +class Struct: def __init__(self, **kwds): # type: (Any) -> None self.__dict__.update(kwds) @@ -146,7 +146,6 @@ class SphinxTestApp(application.Sphinx): def cleanup(self, doctrees=False): # type: (bool) -> None - AutoDirective._registry.clear() ModuleAnalyzer.cache.clear() LaTeXBuilder.usepackages = [] locale.translators.clear() @@ -165,7 +164,7 @@ class SphinxTestApp(application.Sphinx): return '<%s buildername=%r>' % (self.__class__.__name__, self.builder.name) -class SphinxTestAppWrapperForSkipBuilding(object): +class SphinxTestAppWrapperForSkipBuilding: """ This class is a wrapper for SphinxTestApp to speed up the test by skipping `app.build` process if it is already built and there is even one output @@ -193,6 +192,8 @@ _unicode_literals_re = re.compile(r'u(".*?")|u(\'.*?\')') def remove_unicode_literals(s): # type: (unicode) -> unicode + warnings.warn('remove_unicode_literals() is deprecated.', + RemovedInSphinx40Warning) return _unicode_literals_re.sub(lambda x: x.group(1) or x.group(2), s) diff --git a/sphinx/themes/agogo/layout.html b/sphinx/themes/agogo/layout.html index f2c880537..f50a2d78f 100644 --- a/sphinx/themes/agogo/layout.html +++ b/sphinx/themes/agogo/layout.html @@ -52,8 +52,6 @@ <form class="search" action="{{ pathto('search') }}" method="get"> <input type="text" name="q" /> <input type="submit" value="{{ _('Go') }}" /> - <input type="hidden" name="check_keywords" value="yes" /> - <input type="hidden" name="area" value="default" /> </form> </div> {%- endblock %} diff --git a/sphinx/themes/basic/opensearch.xml b/sphinx/themes/basic/opensearch.xml index 03875be49..a750b3041 100644 --- a/sphinx/themes/basic/opensearch.xml +++ b/sphinx/themes/basic/opensearch.xml @@ -4,7 +4,7 @@ <Description>{% trans docstitle=docstitle|e %}Search {{ docstitle }}{% endtrans %}</Description> <InputEncoding>utf-8</InputEncoding> <Url type="text/html" method="get" - template="{{ use_opensearch }}/{{ pathto('search') }}?q={searchTerms}&check_keywords=yes&area=default"/> + template="{{ use_opensearch }}/{{ pathto('search') }}?q={searchTerms}"/> <LongName>{{ docstitle|e }}</LongName> {% block extra %} {# Put e.g. an <Image> element here. #} {% endblock %} </OpenSearchDescription> diff --git a/sphinx/themes/basic/searchbox.html b/sphinx/themes/basic/searchbox.html index 506877410..6feaa93e0 100644 --- a/sphinx/themes/basic/searchbox.html +++ b/sphinx/themes/basic/searchbox.html @@ -14,8 +14,6 @@ <form class="search" action="{{ pathto('search') }}" method="get"> <input type="text" name="q" /> <input type="submit" value="{{ _('Go') }}" /> - <input type="hidden" name="check_keywords" value="yes" /> - <input type="hidden" name="area" value="default" /> </form> </div> </div> diff --git a/sphinx/themes/basic/static/searchtools.js b/sphinx/themes/basic/static/searchtools.js index 7473859b2..f5183ba7d 100644 --- a/sphinx/themes/basic/static/searchtools.js +++ b/sphinx/themes/basic/static/searchtools.js @@ -56,6 +56,14 @@ var Search = { _queued_query : null, _pulse_status : -1, + htmlToText : function(htmlString) { + var htmlElement = document.createElement('span'); + htmlElement.innerHTML = htmlString; + $(htmlElement).find('.headerlink').remove(); + docContent = $(htmlElement).find('[role=main]')[0]; + return docContent.textContent || docContent.innerText; + }, + init : function() { var params = $.getQueryParameters(); if (params.q) { @@ -260,11 +268,7 @@ var Search = { displayNextItem(); }); } else if (DOCUMENTATION_OPTIONS.HAS_SOURCE) { - var suffix = DOCUMENTATION_OPTIONS.SOURCELINK_SUFFIX; - if (suffix === undefined) { - suffix = '.txt'; - } - $.ajax({url: DOCUMENTATION_OPTIONS.URL_ROOT + '_sources/' + item[5] + (item[5].slice(-suffix.length) === suffix ? '' : suffix), + $.ajax({url: DOCUMENTATION_OPTIONS.URL_ROOT + item[0] + DOCUMENTATION_OPTIONS.FILE_SUFFIX, dataType: "text", complete: function(jqxhr, textstatus) { var data = jqxhr.responseText; @@ -457,7 +461,8 @@ var Search = { * words. the first one is used to find the occurrence, the * latter for highlighting it. */ - makeSearchSummary : function(text, keywords, hlwords) { + makeSearchSummary : function(htmlText, keywords, hlwords) { + var text = Search.htmlToText(htmlText); var textLower = text.toLowerCase(); var start = 0; $.each(keywords, function() { diff --git a/sphinx/theming.py b/sphinx/theming.py index 944c446c3..6687dd27b 100644 --- a/sphinx/theming.py +++ b/sphinx/theming.py @@ -9,19 +9,16 @@ :license: BSD, see LICENSE for details. """ +import configparser import os import shutil import tempfile -import warnings from os import path from zipfile import ZipFile import pkg_resources -from six import string_types, iteritems -from six.moves import configparser from sphinx import package_dir -from sphinx.deprecation import RemovedInSphinx20Warning from sphinx.errors import ThemeError from sphinx.locale import __ from sphinx.util import logging @@ -53,7 +50,7 @@ def extract_zip(filename, targetdir): fp.write(archive.read(name)) -class Theme(object): +class Theme: """A Theme is a set of HTML templates and configurations. This class supports both theme directory and theme archive (zipped theme).""" @@ -75,7 +72,7 @@ class Theme(object): extract_zip(theme_path, self.themedir) self.config = configparser.RawConfigParser() - self.config.read(path.join(self.themedir, THEMECONF)) # type: ignore + self.config.read(path.join(self.themedir, THEMECONF)) try: inherit = self.config.get('theme', 'inherit') @@ -107,7 +104,7 @@ class Theme(object): base theme chain. """ try: - return self.config.get(section, name) # type: ignore + return self.config.get(section, name) except (configparser.NoOptionError, configparser.NoSectionError): if self.base: return self.base.get_config(section, name, default) @@ -131,7 +128,7 @@ class Theme(object): except configparser.NoSectionError: pass - for option, value in iteritems(overrides): + for option, value in overrides.items(): if option not in options: logger.warning(__('unsupported theme option %r given') % option) else: @@ -161,7 +158,7 @@ def is_archived_theme(filename): return False -class HTMLThemeFactory(object): +class HTMLThemeFactory: """A factory class for HTML Themes.""" def __init__(self, app): @@ -176,7 +173,7 @@ class HTMLThemeFactory(object): # type: () -> None """Load built-in themes.""" themes = self.find_themes(path.join(package_dir, 'themes')) - for name, theme in iteritems(themes): + for name, theme in themes.items(): self.themes[name] = theme def load_additional_themes(self, theme_paths): @@ -185,7 +182,7 @@ class HTMLThemeFactory(object): for theme_path in theme_paths: abs_theme_path = path.abspath(path.join(self.app.confdir, theme_path)) themes = self.find_themes(abs_theme_path) - for name, theme in iteritems(themes): + for name, theme in themes.items(): self.themes[name] = theme def load_extra_theme(self, name): @@ -229,25 +226,6 @@ class HTMLThemeFactory(object): except StopIteration: pass - # look up for old styled entry_points - for entry_point in pkg_resources.iter_entry_points('sphinx_themes'): - target = entry_point.load() - if callable(target): - themedir = target() - if not isinstance(themedir, string_types): - logger.warning(__('Theme extension %r does not respond correctly.') % - entry_point.module_name) - else: - themedir = target - - themes = self.find_themes(themedir) - for entry, theme in iteritems(themes): - if name == entry: - warnings.warn('``sphinx_themes`` entry point is now deprecated. ' - 'Please use ``sphinx.html_themes`` instead.', - RemovedInSphinx20Warning) - self.themes[name] = theme - def find_themes(self, theme_path): # type: (unicode) -> Dict[unicode, unicode] """Search themes from specified directory.""" diff --git a/sphinx/transforms/i18n.py b/sphinx/transforms/i18n.py index f49e27df3..727a12be6 100644 --- a/sphinx/transforms/i18n.py +++ b/sphinx/transforms/i18n.py @@ -10,6 +10,7 @@ """ from os import path +from typing import Any from docutils import nodes from docutils.io import StringInput @@ -22,14 +23,14 @@ from sphinx.transforms import SphinxTransform from sphinx.util import split_index_msg, logging from sphinx.util.i18n import find_catalog from sphinx.util.nodes import ( - LITERAL_TYPE_NODES, IMAGE_TYPE_NODES, + LITERAL_TYPE_NODES, IMAGE_TYPE_NODES, NodeMatcher, extract_messages, is_pending_meta, traverse_translatable_index, ) from sphinx.util.pycompat import indent if False: # For type annotation - from typing import Any, Dict, List, Tuple # NOQA + from typing import Dict, List, Tuple # NOQA from sphinx.application import Sphinx # NOQA from sphinx.config import Config # NOQA @@ -183,11 +184,8 @@ class Locale(SphinxTransform): self.document.note_implicit_target(section_node) # replace target's refname to new target name - def is_named_target(node): - # type: (nodes.Node) -> bool - return isinstance(node, nodes.target) and \ - node.get('refname') == old_name - for old_target in self.document.traverse(is_named_target): + matcher = NodeMatcher(nodes.target, refname=old_name) + for old_target in self.document.traverse(matcher): old_target['refname'] = new_name processed = True @@ -276,16 +274,14 @@ class Locale(SphinxTransform): continue # skip # auto-numbered foot note reference should use original 'ids'. - def is_autofootnote_ref(node): - # type: (nodes.Node) -> bool - return isinstance(node, nodes.footnote_reference) and node.get('auto') - def list_replace_or_append(lst, old, new): # type: (List, Any, Any) -> None if old in lst: lst[lst.index(old)] = new else: lst.append(new) + + is_autofootnote_ref = NodeMatcher(nodes.footnote_reference, auto=Any) old_foot_refs = node.traverse(is_autofootnote_ref) new_foot_refs = patch.traverse(is_autofootnote_ref) if len(old_foot_refs) != len(new_foot_refs): @@ -328,10 +324,7 @@ class Locale(SphinxTransform): # * reference target ".. _Python: ..." is not translatable. # * use translated refname for section refname. # * inline reference "`Python <...>`_" has no 'refname'. - def is_refnamed_ref(node): - # type: (nodes.Node) -> bool - return isinstance(node, nodes.reference) and \ - 'refname' in node + is_refnamed_ref = NodeMatcher(nodes.reference, refname=Any) old_refs = node.traverse(is_refnamed_ref) new_refs = patch.traverse(is_refnamed_ref) if len(old_refs) != len(new_refs): @@ -358,10 +351,7 @@ class Locale(SphinxTransform): self.document.note_refname(new) # refnamed footnote should use original 'ids'. - def is_refnamed_footnote_ref(node): - # type: (nodes.Node) -> bool - return isinstance(node, nodes.footnote_reference) and \ - 'refname' in node + is_refnamed_footnote_ref = NodeMatcher(nodes.footnote_reference, refname=Any) old_foot_refs = node.traverse(is_refnamed_footnote_ref) new_foot_refs = patch.traverse(is_refnamed_footnote_ref) refname_ids_map = {} @@ -380,10 +370,7 @@ class Locale(SphinxTransform): new["ids"] = refname_ids_map[refname] # citation should use original 'ids'. - def is_citation_ref(node): - # type: (nodes.Node) -> bool - return isinstance(node, nodes.citation_reference) and \ - 'refname' in node + is_citation_ref = NodeMatcher(nodes.citation_reference, refname=Any) old_cite_refs = node.traverse(is_citation_ref) new_cite_refs = patch.traverse(is_citation_ref) refname_ids_map = {} @@ -474,10 +461,7 @@ class Locale(SphinxTransform): node['entries'] = new_entries # remove translated attribute that is used for avoiding double translation. - def has_translatable(node): - # type: (nodes.Node) -> bool - return isinstance(node, nodes.Element) and 'translated' in node - for node in self.document.traverse(has_translatable): + for node in self.document.traverse(NodeMatcher(translated=Any)): node.delattr('translated') @@ -492,7 +476,8 @@ class RemoveTranslatableInline(SphinxTransform): from sphinx.builders.gettext import MessageCatalogBuilder if isinstance(self.app.builder, MessageCatalogBuilder): return - for inline in self.document.traverse(nodes.inline): - if 'translatable' in inline: - inline.parent.remove(inline) - inline.parent += inline.children + + matcher = NodeMatcher(nodes.inline, translatable=Any) + for inline in self.document.traverse(matcher): + inline.parent.remove(inline) + inline.parent += inline.children diff --git a/sphinx/transforms/post_transforms/__init__.py b/sphinx/transforms/post_transforms/__init__.py index 6e53fec1d..a5cb897d9 100644 --- a/sphinx/transforms/post_transforms/__init__.py +++ b/sphinx/transforms/post_transforms/__init__.py @@ -9,13 +9,9 @@ :license: BSD, see LICENSE for details. """ -import warnings - from docutils import nodes -from docutils.utils import get_source_line from sphinx import addnodes -from sphinx.deprecation import RemovedInSphinx20Warning from sphinx.environment import NoUri from sphinx.locale import __ from sphinx.transforms import SphinxTransform @@ -32,35 +28,6 @@ if False: logger = logging.getLogger(__name__) -class DocReferenceMigrator(SphinxTransform): - """Migrate :doc: reference to std domain.""" - - default_priority = 5 # before ReferencesResolver - - def apply(self): - # type: () -> None - for node in self.document.traverse(addnodes.pending_xref): - if node.get('reftype') == 'doc' and node.get('refdomain') is None: - source, line = get_source_line(node) - if source and line: - location = "%s:%s" % (source, line) - elif source: - location = "%s:" % source - elif line: - location = "<unknown>:%s" % line - else: - location = None - - message = ('Invalid pendig_xref node detected. ' - ':doc: reference should have refdomain=std attribute.') - if location: - warnings.warn("%s: %s" % (location, message), - RemovedInSphinx20Warning) - else: - warnings.warn(message, RemovedInSphinx20Warning) - node['refdomain'] = 'std' - - class ReferencesResolver(SphinxTransform): """ Resolves cross-references on doctrees. @@ -191,7 +158,6 @@ class OnlyNodeTransform(SphinxTransform): def setup(app): # type: (Sphinx) -> Dict[unicode, Any] - app.add_post_transform(DocReferenceMigrator) app.add_post_transform(ReferencesResolver) app.add_post_transform(OnlyNodeTransform) diff --git a/sphinx/transforms/references.py b/sphinx/transforms/references.py index 40efbf615..bb7c51da7 100644 --- a/sphinx/transforms/references.py +++ b/sphinx/transforms/references.py @@ -11,7 +11,6 @@ from docutils import nodes from docutils.transforms.references import Substitutions -from six import itervalues from sphinx.transforms import SphinxTransform @@ -34,5 +33,5 @@ class SphinxDomains(SphinxTransform): def apply(self): # type: () -> None - for domain in itervalues(self.env.domains): + for domain in self.env.domains.values(): domain.process_doc(self.env, self.env.docname, self.document) diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py index a9d112a9f..8bb18debd 100644 --- a/sphinx/util/__init__.py +++ b/sphinx/util/__init__.py @@ -27,8 +27,7 @@ from os import path from time import mktime, strptime from docutils.utils import relative_path -from six import text_type, binary_type, itervalues -from six.moves import range +from six import text_type, binary_type from six.moves.urllib.parse import urlsplit, urlunsplit, quote_plus, parse_qsl, urlencode from sphinx.deprecation import RemovedInSphinx30Warning @@ -88,7 +87,7 @@ def get_matching_files(dirname, exclude_matchers=()): dirname = path.normpath(path.abspath(dirname)) dirlen = len(dirname) + 1 # exclude final os.path.sep - for root, dirs, files in walk(dirname, followlinks=True): + for root, dirs, files in os.walk(dirname, followlinks=True): relativeroot = root[dirlen:] qdirs = enumerate(path_stabilize(path.join(relativeroot, dn)) @@ -267,7 +266,7 @@ def save_traceback(app): jinja2.__version__, # type: ignore last_msgs)).encode('utf-8')) if app is not None: - for ext in itervalues(app.extensions): + for ext in app.extensions.values(): modfile = getattr(ext.module, '__file__', 'unknown') if isinstance(modfile, bytes): modfile = modfile.decode(fs_encoding, 'replace') @@ -404,7 +403,7 @@ def detect_encoding(readline): # Low-level utility functions and classes. -class Tee(object): +class Tee: """ File-like object writing to two streams. """ @@ -536,7 +535,7 @@ def format_exception_cut_frames(x=1): return ''.join(res) -class PeekableIterator(object): +class PeekableIterator: """ An iterator which wraps any iterable and makes it possible to peek to see what's the next item. @@ -677,8 +676,7 @@ def xmlname_checker(): [u'\u2C00', u'\u2FEF'], [u'\u3001', u'\uD7FF'], [u'\uF900', u'\uFDCF'], [u'\uFDF0', u'\uFFFD']] - if sys.version_info.major == 3: - name_start_chars.append([u'\U00010000', u'\U000EFFFF']) + name_start_chars.append([u'\U00010000', u'\U000EFFFF']) name_chars = [ u"\\-", u"\\.", [u'0', u'9'], u'\u00B7', [u'\u0300', u'\u036F'], diff --git a/sphinx/util/build_phase.py b/sphinx/util/build_phase.py index e5a53551c..48e75675d 100644 --- a/sphinx/util/build_phase.py +++ b/sphinx/util/build_phase.py @@ -9,10 +9,7 @@ :license: BSD, see LICENSE for details. """ -try: - from enum import IntEnum -except ImportError: # py27 - IntEnum = object # type: ignore +from enum import IntEnum class BuildPhase(IntEnum): diff --git a/sphinx/util/compat.py b/sphinx/util/compat.py index 43ced1f5e..1f3b0f445 100644 --- a/sphinx/util/compat.py +++ b/sphinx/util/compat.py @@ -14,9 +14,12 @@ from __future__ import absolute_import import sys import warnings -from six import string_types, iteritems +from docutils.utils import get_source_line +from six import string_types -from sphinx.deprecation import RemovedInSphinx30Warning +from sphinx import addnodes +from sphinx.deprecation import RemovedInSphinx30Warning, RemovedInSphinx40Warning +from sphinx.transforms import SphinxTransform from sphinx.util import import_object if False: @@ -32,7 +35,7 @@ def deprecate_source_parsers(app, config): warnings.warn('The config variable "source_parsers" is deprecated. ' 'Please use app.add_source_parser() API instead.', RemovedInSphinx30Warning) - for suffix, parser in iteritems(config.source_parsers): + for suffix, parser in config.source_parsers.items(): if isinstance(parser, string_types): parser = import_object(parser, 'source parser') # type: ignore app.add_source_parser(suffix, parser) @@ -52,8 +55,23 @@ def register_application_for_autosummary(app): autosummary._app = app +class IndexEntriesMigrator(SphinxTransform): + """Migrating indexentries from old style (4columns) to new style (5columns).""" + default_priority = 700 + + def apply(self): + for node in self.document.traverse(addnodes.index): + for entries in node['entries']: + if len(entries) == 4: + source, line = get_source_line(node) + warnings.warn('An old styled index node found: %r at (%s:%s)' % + (node, source, line), RemovedInSphinx40Warning) + entries.extend([None]) + + def setup(app): # type: (Sphinx) -> Dict[unicode, Any] + app.add_transform(IndexEntriesMigrator) app.connect('config-inited', deprecate_source_parsers) app.connect('builder-inited', register_application_for_autosummary) diff --git a/sphinx/util/docfields.py b/sphinx/util/docfields.py index 94968e148..d0899130e 100644 --- a/sphinx/util/docfields.py +++ b/sphinx/util/docfields.py @@ -36,7 +36,7 @@ def _is_single_paragraph(node): return False -class Field(object): +class Field: """A doc field that is never grouped. It can have an argument or not, the argument can be linked using a specified *rolename*. Field should be used for doc fields that usually don't occur more than once. @@ -235,7 +235,7 @@ class TypedField(GroupedField): return nodes.field('', fieldname, fieldbody) -class DocFieldTransformer(object): +class DocFieldTransformer: """ Transforms field lists in "doc field" syntax into better-looking equivalents, using the field type definitions given on a domain. diff --git a/sphinx/util/docutils.py b/sphinx/util/docutils.py index 874eb6baf..9ce4087ac 100644 --- a/sphinx/util/docutils.py +++ b/sphinx/util/docutils.py @@ -10,7 +10,6 @@ """ from __future__ import absolute_import -import codecs import os import re import types @@ -148,7 +147,7 @@ class ElementLookupError(Exception): pass -class sphinx_domains(object): +class sphinx_domains: """Monkey-patch directive and role dispatch, so that domain-specific markup takes precedence. """ @@ -223,7 +222,7 @@ class sphinx_domains(object): return self.role_func(name, lang_module, lineno, reporter) -class WarningStream(object): +class WarningStream: def write(self, text): # type: (unicode) -> None matched = report_re.search(text) @@ -314,7 +313,7 @@ class SphinxFileOutput(FileOutput): # type: (unicode) -> unicode if (self.destination_path and self.autoclose and 'b' not in self.mode and self.overwrite_if_changed and os.path.exists(self.destination_path)): - with codecs.open(self.destination_path, encoding=self.encoding) as f: + with open(self.destination_path, encoding=self.encoding) as f: # type: ignore # skip writing: content not changed if f.read() == data: return data diff --git a/sphinx/util/fileutil.py b/sphinx/util/fileutil.py index fcbc8abe6..7dc376807 100644 --- a/sphinx/util/fileutil.py +++ b/sphinx/util/fileutil.py @@ -10,13 +10,12 @@ """ from __future__ import absolute_import -import codecs import os import posixpath from docutils.utils import relative_path -from sphinx.util.osutil import copyfile, ensuredir, walk +from sphinx.util.osutil import copyfile, ensuredir if False: # For type annotation @@ -44,15 +43,15 @@ def copy_asset_file(source, destination, context=None, renderer=None): # Use source filename if destination points a directory destination = os.path.join(destination, os.path.basename(source)) - if source.lower().endswith('_t') and context: + if source.lower().endswith('_t') and context is not None: if renderer is None: from sphinx.util.template import SphinxRenderer renderer = SphinxRenderer() - with codecs.open(source, 'r', encoding='utf-8') as fsrc: # type: ignore + with open(source, 'r', encoding='utf-8') as fsrc: # type: ignore if destination.lower().endswith('_t'): destination = destination[:-2] - with codecs.open(destination, 'w', encoding='utf-8') as fdst: # type: ignore + with open(destination, 'w', encoding='utf-8') as fdst: # type: ignore fdst.write(renderer.render_string(fsrc.read(), context)) else: copyfile(source, destination) @@ -83,7 +82,7 @@ def copy_asset(source, destination, excluded=lambda path: False, context=None, r copy_asset_file(source, destination, context, renderer) return - for root, dirs, files in walk(source, followlinks=True): + for root, dirs, files in os.walk(source, followlinks=True): reldir = relative_path(source, root) for dir in dirs[:]: if excluded(posixpath.join(reldir, dir)): diff --git a/sphinx/util/i18n.py b/sphinx/util/i18n.py index edaebebda..4e25115a7 100644 --- a/sphinx/util/i18n.py +++ b/sphinx/util/i18n.py @@ -9,7 +9,6 @@ :license: BSD, see LICENSE for details. """ import gettext -import io import os import re import warnings @@ -26,7 +25,7 @@ from sphinx.errors import SphinxError from sphinx.locale import __ from sphinx.util import logging from sphinx.util.matching import Matcher -from sphinx.util.osutil import SEP, relpath, walk +from sphinx.util.osutil import SEP, relpath logger = logging.getLogger(__name__) @@ -69,14 +68,14 @@ class CatalogInfo(LocaleFileInfoBase): def write_mo(self, locale): # type: (unicode) -> None - with io.open(self.po_path, 'rt', encoding=self.charset) as file_po: + with open(self.po_path, 'rt', encoding=self.charset) as file_po: # type: ignore try: po = read_po(file_po, locale) except Exception as exc: logger.warning(__('reading error: %s, %s'), self.po_path, exc) return - with io.open(self.mo_path, 'wb') as file_mo: + with open(self.mo_path, 'wb') as file_mo: try: write_mo(file_mo, po) except Exception as exc: @@ -140,7 +139,7 @@ def find_catalog_source_files(locale_dirs, locale, domains=None, gettext_compact if not path.exists(base_dir): continue # locale path is not found - for dirpath, dirnames, filenames in walk(base_dir, followlinks=True): + for dirpath, dirnames, filenames in os.walk(base_dir, followlinks=True): filenames = [f for f in filenames if f.endswith('.po')] for filename in filenames: if excluded(path.join(relpath(dirpath, base_dir), filename)): diff --git a/sphinx/util/images.py b/sphinx/util/images.py index d5e0db2d7..85fbf17f1 100644 --- a/sphinx/util/images.py +++ b/sphinx/util/images.py @@ -14,11 +14,12 @@ import base64 import imghdr import warnings from collections import OrderedDict +from io import BytesIO from os import path from typing import NamedTuple import imagesize -from six import PY3, BytesIO, iteritems +from six import text_type from sphinx.deprecation import RemovedInSphinx30Warning @@ -34,9 +35,6 @@ if False: # For type annotation from typing import Dict, IO, List, Tuple # NOQA -if PY3: - unicode = str # special alias for static typing... - mime_suffixes = OrderedDict([ ('.gif', 'image/gif'), ('.jpg', 'image/jpeg'), @@ -46,8 +44,8 @@ mime_suffixes = OrderedDict([ ('.svgz', 'image/svg+xml'), ]) # type: Dict[unicode, unicode] -DataURI = NamedTuple('DataURI', [('mimetype', unicode), - ('charset', unicode), +DataURI = NamedTuple('DataURI', [('mimetype', text_type), + ('charset', text_type), ('data', bytes)]) @@ -81,7 +79,7 @@ def guess_mimetype_for_stream(stream, default=None): def guess_mimetype(filename='', content=None, default=None): - # type: (unicode, unicode, unicode) -> unicode + # type: (unicode, bytes, unicode) -> unicode _, ext = path.splitext(filename.lower()) if ext in mime_suffixes: return mime_suffixes[ext] @@ -98,7 +96,7 @@ def guess_mimetype(filename='', content=None, default=None): def get_image_extension(mimetype): # type: (unicode) -> unicode - for ext, _mimetype in iteritems(mime_suffixes): + for ext, _mimetype in mime_suffixes.items(): if mimetype == _mimetype: return ext @@ -128,7 +126,7 @@ def parse_data_uri(uri): def test_svg(h, f): - # type: (unicode, IO) -> unicode + # type: (bytes, IO) -> unicode """An additional imghdr library helper; test the header is SVG's or not.""" try: if '<svg' in h.decode('utf-8').lower(): diff --git a/sphinx/util/inspect.py b/sphinx/util/inspect.py index 6fdbcc123..541d5f5c5 100644 --- a/sphinx/util/inspect.py +++ b/sphinx/util/inspect.py @@ -10,15 +10,15 @@ """ from __future__ import absolute_import +import builtins +import enum import inspect import re import sys import typing -from collections import OrderedDict from functools import partial -from six import PY2, PY3, StringIO, binary_type, string_types, itervalues -from six.moves import builtins +from six import StringIO, binary_type, string_types from sphinx.util import force_decode from sphinx.util import logging @@ -33,112 +33,73 @@ logger = logging.getLogger(__name__) memory_address_re = re.compile(r' at 0x[0-9a-f]{8,16}(?=>)', re.IGNORECASE) -if PY3: - # Copied from the definition of inspect.getfullargspec from Python master, - # and modified to remove the use of special flags that break decorated - # callables and bound methods in the name of backwards compatibility. Used - # under the terms of PSF license v2, which requires the above statement - # and the following: - # - # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, - # 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 Python Software - # Foundation; All Rights Reserved - def getargspec(func): - """Like inspect.getfullargspec but supports bound methods, and wrapped - methods.""" - # On 3.5+, signature(int) or similar raises ValueError. On 3.4, it - # succeeds with a bogus signature. We want a TypeError uniformly, to - # match historical behavior. - if (isinstance(func, type) and - is_builtin_class_method(func, "__new__") and - is_builtin_class_method(func, "__init__")): - raise TypeError( - "can't compute signature for built-in type {}".format(func)) - - sig = inspect.signature(func) - - args = [] - varargs = None - varkw = None - kwonlyargs = [] - defaults = () - annotations = {} - defaults = () - kwdefaults = {} - - if sig.return_annotation is not sig.empty: - annotations['return'] = sig.return_annotation - - for param in sig.parameters.values(): - kind = param.kind - name = param.name - - if kind is inspect.Parameter.POSITIONAL_ONLY: - args.append(name) - elif kind is inspect.Parameter.POSITIONAL_OR_KEYWORD: - args.append(name) - if param.default is not param.empty: - defaults += (param.default,) - elif kind is inspect.Parameter.VAR_POSITIONAL: - varargs = name - elif kind is inspect.Parameter.KEYWORD_ONLY: - kwonlyargs.append(name) - if param.default is not param.empty: - kwdefaults[name] = param.default - elif kind is inspect.Parameter.VAR_KEYWORD: - varkw = name - - if param.annotation is not param.empty: - annotations[name] = param.annotation - - if not kwdefaults: - # compatibility with 'func.__kwdefaults__' - kwdefaults = None - - if not defaults: - # compatibility with 'func.__defaults__' - defaults = None - - return inspect.FullArgSpec(args, varargs, varkw, defaults, - kwonlyargs, kwdefaults, annotations) - -else: # 2.7 - def getargspec(func): - # type: (Any) -> Any - """Like inspect.getargspec but supports functools.partial as well.""" - if inspect.ismethod(func): - func = func.__func__ - parts = 0, () # type: Tuple[int, Tuple[unicode, ...]] - if type(func) is partial: - keywords = func.keywords - if keywords is None: - keywords = {} - parts = len(func.args), keywords.keys() - func = func.func - if not inspect.isfunction(func): - raise TypeError('%r is not a Python function' % func) - args, varargs, varkw = inspect.getargs(func.__code__) - func_defaults = func.__defaults__ - if func_defaults is None: - func_defaults = [] - else: - func_defaults = list(func_defaults) - if parts[0]: - args = args[parts[0]:] - if parts[1]: - for arg in parts[1]: - i = args.index(arg) - len(args) # type: ignore - del args[i] - try: - del func_defaults[i] - except IndexError: - pass - return inspect.ArgSpec(args, varargs, varkw, func_defaults) # type: ignore - -try: - import enum -except ImportError: - enum = None +# Copied from the definition of inspect.getfullargspec from Python master, +# and modified to remove the use of special flags that break decorated +# callables and bound methods in the name of backwards compatibility. Used +# under the terms of PSF license v2, which requires the above statement +# and the following: +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, +# 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 Python Software +# Foundation; All Rights Reserved +def getargspec(func): + """Like inspect.getfullargspec but supports bound methods, and wrapped + methods.""" + # On 3.5+, signature(int) or similar raises ValueError. On 3.4, it + # succeeds with a bogus signature. We want a TypeError uniformly, to + # match historical behavior. + if (isinstance(func, type) and + is_builtin_class_method(func, "__new__") and + is_builtin_class_method(func, "__init__")): + raise TypeError( + "can't compute signature for built-in type {}".format(func)) + + sig = inspect.signature(func) # type: ignore + + args = [] + varargs = None + varkw = None + kwonlyargs = [] + defaults = () + annotations = {} + defaults = () + kwdefaults = {} + + if sig.return_annotation is not sig.empty: + annotations['return'] = sig.return_annotation + + for param in sig.parameters.values(): + kind = param.kind + name = param.name + + if kind is inspect.Parameter.POSITIONAL_ONLY: # type: ignore + args.append(name) + elif kind is inspect.Parameter.POSITIONAL_OR_KEYWORD: # type: ignore + args.append(name) + if param.default is not param.empty: + defaults += (param.default,) # type: ignore + elif kind is inspect.Parameter.VAR_POSITIONAL: # type: ignore + varargs = name + elif kind is inspect.Parameter.KEYWORD_ONLY: # type: ignore + kwonlyargs.append(name) + if param.default is not param.empty: + kwdefaults[name] = param.default + elif kind is inspect.Parameter.VAR_KEYWORD: # type: ignore + varkw = name + + if param.annotation is not param.empty: + annotations[name] = param.annotation + + if not kwdefaults: + # compatibility with 'func.__kwdefaults__' + kwdefaults = None + + if not defaults: + # compatibility with 'func.__defaults__' + defaults = None + + return inspect.FullArgSpec(args, varargs, varkw, defaults, # type: ignore + kwonlyargs, kwdefaults, annotations) def isenumclass(x): @@ -275,9 +236,15 @@ def object_description(object): except TypeError: pass # Cannot sort set values, fall back to generic repr else: - template = "{%s}" if PY3 else "set([%s])" - return template % ", ".join(object_description(x) - for x in sorted_values) + return "{%s}" % ", ".join(object_description(x) for x in sorted_values) + if isinstance(object, frozenset): + try: + sorted_values = sorted(object) + except TypeError: + pass # Cannot sort frozenset values, fall back to generic repr + else: + return "frozenset({%s})" % ", ".join(object_description(x) + for x in sorted_values) try: s = repr(object) except Exception: @@ -308,7 +275,7 @@ def is_builtin_class_method(obj, attr_name): return getattr(builtins, safe_getattr(cls, '__name__', '')) is cls # type: ignore -class Parameter(object): +class Parameter: """Fake parameter class for python2.""" POSITIONAL_ONLY = 0 POSITIONAL_OR_KEYWORD = 1 @@ -325,7 +292,7 @@ class Parameter(object): self.annotation = self.empty -class Signature(object): +class Signature: """The Signature object represents the call signature of a callable object and its return annotation. """ @@ -342,20 +309,17 @@ class Signature(object): self.has_retval = has_retval self.partialmethod_with_noargs = False - if PY3: - try: - self.signature = inspect.signature(subject) - except IndexError: - # Until python 3.6.4, cpython has been crashed on inspection for - # partialmethods not having any arguments. - # https://bugs.python.org/issue33009 - if hasattr(subject, '_partialmethod'): - self.signature = None - self.partialmethod_with_noargs = True - else: - raise - else: - self.argspec = getargspec(subject) + try: + self.signature = inspect.signature(subject) # type: ignore + except IndexError: + # Until python 3.6.4, cpython has been crashed on inspection for + # partialmethods not having any arguments. + # https://bugs.python.org/issue33009 + if hasattr(subject, '_partialmethod'): + self.signature = None + self.partialmethod_with_noargs = True + else: + raise try: if ispartial(subject): @@ -375,53 +339,32 @@ class Signature(object): if bound_method: # client gives a hint that the subject is a bound method - if PY3 and inspect.ismethod(subject): + if inspect.ismethod(subject): # inspect.signature already considers the subject is bound method. # So it is not need to skip first argument. self.skip_first_argument = False else: self.skip_first_argument = True else: - if PY3: - # inspect.signature recognizes type of method properly without any hints - self.skip_first_argument = False - else: - # check the subject is bound method or not - self.skip_first_argument = inspect.ismethod(subject) and subject.__self__ # type: ignore # NOQA + # inspect.signature recognizes type of method properly without any hints + self.skip_first_argument = False @property def parameters(self): # type: () -> Dict - if PY3: - if self.partialmethod_with_noargs: - return {} - else: - return self.signature.parameters + if self.partialmethod_with_noargs: + return {} else: - params = OrderedDict() # type: Dict - positionals = len(self.argspec.args) - len(self.argspec.defaults) - for i, arg in enumerate(self.argspec.args): - if i < positionals: - params[arg] = Parameter(arg) - else: - default = self.argspec.defaults[i - positionals] - params[arg] = Parameter(arg, default=default) - if self.argspec.varargs: - params[self.argspec.varargs] = Parameter(self.argspec.varargs, - Parameter.VAR_POSITIONAL) - if self.argspec.keywords: - params[self.argspec.keywords] = Parameter(self.argspec.keywords, - Parameter.VAR_KEYWORD) - return params + return self.signature.parameters @property def return_annotation(self): # type: () -> Any - if PY3 and self.signature: + if self.signature: if self.has_retval: return self.signature.return_annotation else: - return inspect.Parameter.empty + return inspect.Parameter.empty # type: ignore else: return None @@ -429,7 +372,7 @@ class Signature(object): # type: () -> unicode args = [] last_kind = None - for i, param in enumerate(itervalues(self.parameters)): + for i, param in enumerate(self.parameters.values()): # skip first argument if subject is bound method if self.skip_first_argument and i == 0: continue @@ -472,7 +415,7 @@ class Signature(object): args.append(arg.getvalue()) last_kind = param.kind - if PY2 or self.return_annotation is inspect.Parameter.empty: + if self.return_annotation is inspect.Parameter.empty: # type: ignore return '(%s)' % ', '.join(args) else: if 'return' in self.annotations: @@ -643,108 +586,6 @@ class Signature(object): return qualname -if sys.version_info >= (3, 5): - _getdoc = inspect.getdoc -else: - # code copied from the inspect.py module of the standard library - # of Python 3.5 - - def _findclass(func): - # type: (Any) -> Any - cls = sys.modules.get(func.__module__) - if cls is None: - return None - if hasattr(func, 'im_class'): - cls = func.im_class - else: - for name in func.__qualname__.split('.')[:-1]: - cls = getattr(cls, name) - if not inspect.isclass(cls): - return None - return cls - - def _finddoc(obj): - # type: (Any) -> unicode - if inspect.isclass(obj): - for base in obj.__mro__: - if base is not object: - try: - doc = base.__doc__ - except AttributeError: - continue - if doc is not None: - return doc - return None - - if inspect.ismethod(obj) and getattr(obj, '__self__', None): - name = obj.__func__.__name__ - self = obj.__self__ - if (inspect.isclass(self) and - getattr(getattr(self, name, None), '__func__') - is obj.__func__): - # classmethod - cls = self - else: - cls = self.__class__ - elif inspect.isfunction(obj) or inspect.ismethod(obj): - name = obj.__name__ - cls = _findclass(obj) - if cls is None or getattr(cls, name) != obj: - return None - elif inspect.isbuiltin(obj): - name = obj.__name__ - self = obj.__self__ - if (inspect.isclass(self) and - self.__qualname__ + '.' + name == obj.__qualname__): - # classmethod - cls = self - else: - cls = self.__class__ - # Should be tested before isdatadescriptor(). - elif isinstance(obj, property): - func = obj.fget - name = func.__name__ - cls = _findclass(func) - if cls is None or getattr(cls, name) is not obj: - return None - elif inspect.ismethoddescriptor(obj) or inspect.isdatadescriptor(obj): - name = obj.__name__ - cls = obj.__objclass__ - if getattr(cls, name) is not obj: - return None - else: - return None - - for base in cls.__mro__: - try: - doc = getattr(base, name).__doc__ - except AttributeError: - continue - if doc is not None: - return doc - return None - - def _getdoc(object): - # type: (Any) -> unicode - """Get the documentation string for an object. - - All tabs are expanded to spaces. To clean up docstrings that are - indented to line up with blocks of code, any whitespace than can be - uniformly removed from the second line onwards is removed.""" - try: - doc = object.__doc__ - except AttributeError: - return None - if doc is None: - try: - doc = _finddoc(object) - except (AttributeError, TypeError): - return None - if not isinstance(doc, str): - return None - return inspect.cleandoc(doc) - - def getdoc(obj, attrgetter=safe_getattr, allow_inherited=False): # type: (Any, Callable, bool) -> unicode """Get the docstring for the object. @@ -758,6 +599,6 @@ def getdoc(obj, attrgetter=safe_getattr, allow_inherited=False): if ispartial(obj) and doc == obj.__class__.__doc__: return getdoc(obj.func) elif doc is None and allow_inherited: - doc = _getdoc(obj) + doc = inspect.getdoc(obj) return doc diff --git a/sphinx/util/inventory.py b/sphinx/util/inventory.py index ed4e55bc2..58ad8de33 100644 --- a/sphinx/util/inventory.py +++ b/sphinx/util/inventory.py @@ -12,7 +12,7 @@ import os import re import zlib -from six import PY3 +from six import text_type from sphinx.util import logging @@ -22,17 +22,14 @@ if False: from sphinx.builders import Builder # NOQA from sphinx.environment import BuildEnvironment # NOQA - if PY3: - unicode = str - - Inventory = Dict[unicode, Dict[unicode, Tuple[unicode, unicode, unicode, unicode]]] + Inventory = Dict[text_type, Dict[text_type, Tuple[text_type, text_type, text_type, text_type]]] # NOQA BUFSIZE = 16 * 1024 logger = logging.getLogger(__name__) -class InventoryFileReader(object): +class InventoryFileReader: """A file reader for inventory file. This reader supports mixture of texts and compressed texts. @@ -94,7 +91,7 @@ class InventoryFileReader(object): pos = buf.find(b'\n') -class InventoryFile(object): +class InventoryFile: @classmethod def load(cls, stream, uri, joinfunc): # type: (IO, unicode, Callable) -> Inventory diff --git a/sphinx/util/jsdump.py b/sphinx/util/jsdump.py index 6776691cf..17865f956 100644 --- a/sphinx/util/jsdump.py +++ b/sphinx/util/jsdump.py @@ -12,7 +12,7 @@ import re -from six import iteritems, integer_types, string_types +from six import integer_types, string_types from sphinx.util.pycompat import u @@ -102,7 +102,7 @@ def dumps(obj, key=False): return '{%s}' % ','.join(sorted('%s:%s' % ( dumps(key, True), dumps(value) - ) for key, value in iteritems(obj))) + ) for key, value in obj.items())) elif isinstance(obj, set): return '[%s]' % ','.join(sorted(dumps(x) for x in obj)) elif isinstance(obj, (tuple, list)): diff --git a/sphinx/util/logging.py b/sphinx/util/logging.py index 09cef4b48..a5bd7cd05 100644 --- a/sphinx/util/logging.py +++ b/sphinx/util/logging.py @@ -17,7 +17,6 @@ from contextlib import contextmanager from docutils import nodes from docutils.utils import get_source_line -from six import PY2, StringIO from sphinx.errors import SphinxWarning from sphinx.util.console import colorize @@ -162,28 +161,7 @@ class WarningStreamHandler(logging.StreamHandler): pass -class NewLineStreamHandlerPY2(logging.StreamHandler): - """StreamHandler which switches line terminator by record.nonl flag.""" - - def emit(self, record): - # type: (logging.LogRecord) -> None - try: - self.acquire() - stream = self.stream - if getattr(record, 'nonl', False): - # remove return code forcely when nonl=True - self.stream = StringIO() - super(NewLineStreamHandlerPY2, self).emit(record) - stream.write(self.stream.getvalue()[:-1]) - stream.flush() - else: - super(NewLineStreamHandlerPY2, self).emit(record) - finally: - self.stream = stream - self.release() - - -class NewLineStreamHandlerPY3(logging.StreamHandler): +class NewLineStreamHandler(logging.StreamHandler): """StreamHandler which switches line terminator by record.nonl flag.""" def emit(self, record): @@ -193,18 +171,12 @@ class NewLineStreamHandlerPY3(logging.StreamHandler): if getattr(record, 'nonl', False): # skip appending terminator when nonl=True self.terminator = '' - super(NewLineStreamHandlerPY3, self).emit(record) + super(NewLineStreamHandler, self).emit(record) finally: self.terminator = '\n' self.release() -if PY2: - NewLineStreamHandler = NewLineStreamHandlerPY2 -else: - NewLineStreamHandler = NewLineStreamHandlerPY3 - - class MemoryHandler(logging.handlers.BufferingHandler): """Handler buffering all logs.""" @@ -315,7 +287,7 @@ def skip_warningiserror(skip=True): handler.removeFilter(disabler) -class LogCollector(object): +class LogCollector: def __init__(self): # type: () -> None self.logs = [] # type: List[logging.LogRecord] @@ -496,7 +468,7 @@ class ColorizeFormatter(logging.Formatter): return message -class SafeEncodingWriter(object): +class SafeEncodingWriter: """Stream writer which ignores UnicodeEncodeError silently""" def __init__(self, stream): # type: (IO) -> None @@ -518,7 +490,7 @@ class SafeEncodingWriter(object): self.stream.flush() -class LastMessagesWriter(object): +class LastMessagesWriter: """Stream writer which memories last 10 messages to save trackback""" def __init__(self, app, stream): # type: (Sphinx, IO) -> None diff --git a/sphinx/util/matching.py b/sphinx/util/matching.py index bddf84f5c..43e68949a 100644 --- a/sphinx/util/matching.py +++ b/sphinx/util/matching.py @@ -68,7 +68,7 @@ def compile_matchers(patterns): return [re.compile(_translate_pattern(pat)).match for pat in patterns] -class Matcher(object): +class Matcher: """A pattern matcher for Multiple shell-style glob patterns. Note: this modifies the patterns to work with copy_asset(). diff --git a/sphinx/util/nodes.py b/sphinx/util/nodes.py index 03e06c416..ff73819ee 100644 --- a/sphinx/util/nodes.py +++ b/sphinx/util/nodes.py @@ -11,6 +11,7 @@ from __future__ import absolute_import import re +from typing import Any from docutils import nodes from six import text_type @@ -33,6 +34,57 @@ explicit_title_re = re.compile(r'^(.+?)\s*(?<!\x00)<(.*?)>$', re.DOTALL) caption_ref_re = explicit_title_re # b/w compat alias +class NodeMatcher: + """A helper class for Node.traverse(). + + It checks that given node is an instance of specified node-classes and it has + specified node-attributes. + + For example, following example searches ``reference`` node having ``refdomain`` + and ``reftype`` attributes:: + + matcher = NodeMatcher(nodes.reference, refdomain='std', reftype='citation') + doctree.traverse(matcher) + # => [<reference ...>, <reference ...>, ...] + + A special value ``typing.Any`` matches any kind of node-attributes. For example, + following example searches ``reference`` node having ``refdomain`` attributes:: + + from typing import Any + matcher = NodeMatcher(nodes.reference, refdomain=Any) + doctree.traverse(matcher) + # => [<reference ...>, <reference ...>, ...] + """ + + def __init__(self, *classes, **attrs): + # type: (nodes.Node, Any) -> None + self.classes = classes + self.attrs = attrs + + def match(self, node): + # type: (nodes.Node) -> bool + try: + if self.classes and not isinstance(node, self.classes): + return False + + for key, value in self.attrs.items(): + if key not in node: + return False + elif value is Any: + continue + elif node.get(key) != value: + return False + else: + return True + except Exception: + # for non-Element nodes + return False + + def __call__(self, node): + # type: (nodes.Node) -> bool + return self.match(node) + + def get_full_module_name(node): # type: (nodes.Node) -> str """ @@ -241,11 +293,7 @@ def traverse_parent(node, cls=None): def traverse_translatable_index(doctree): # type: (nodes.Node) -> Iterable[Tuple[nodes.Node, List[unicode]]] """Traverse translatable index node from a document tree.""" - def is_block_index(node): - # type: (nodes.Node) -> bool - return isinstance(node, addnodes.index) and \ - node.get('inline') is False - for node in doctree.traverse(is_block_index): + for node in doctree.traverse(NodeMatcher(addnodes.index, inline=False)): if 'raw_entries' in node: entries = node['raw_entries'] else: diff --git a/sphinx/util/osutil.py b/sphinx/util/osutil.py index 4c75009ee..11b082e7d 100644 --- a/sphinx/util/osutil.py +++ b/sphinx/util/osutil.py @@ -13,7 +13,6 @@ from __future__ import print_function import contextlib import errno import filecmp -import locale import os import re import shutil @@ -23,9 +22,9 @@ import warnings from io import BytesIO, StringIO from os import path -from six import PY2, PY3, text_type +from six import text_type -from sphinx.deprecation import RemovedInSphinx30Warning +from sphinx.deprecation import RemovedInSphinx30Warning, RemovedInSphinx40Warning if False: # For type annotation @@ -37,9 +36,6 @@ ENOENT = getattr(errno, 'ENOENT', 0) EPIPE = getattr(errno, 'EPIPE', 0) EINVAL = getattr(errno, 'EINVAL', 0) -if PY3: - unicode = str # special alias for static typing... - # SEP separates path elements in the canonical file names # # Define SEP as a manifest constant, not so much because we expect it to change @@ -94,39 +90,12 @@ def ensuredir(path): raise -# This function is same as os.walk of Python2.7 except a customization -# that check UnicodeError. -# The customization obstacle to replace the function with the os.walk. def walk(top, topdown=True, followlinks=False): # type: (unicode, bool, bool) -> Iterator[Tuple[unicode, List[unicode], List[unicode]]] - """Backport of os.walk from 2.6, where the *followlinks* argument was - added. - """ - names = os.listdir(top) - - dirs, nondirs = [], [] - for name in names: - try: - fullpath = path.join(top, name) - except UnicodeError: - print('%s:: ERROR: non-ASCII filename not supported on this ' - 'filesystem encoding %r, skipped.' % (name, fs_encoding), - file=sys.stderr) - continue - if path.isdir(fullpath): - dirs.append(name) - else: - nondirs.append(name) - - if topdown: - yield top, dirs, nondirs - for name in dirs: - fullpath = path.join(top, name) - if followlinks or not path.islink(fullpath): - for x in walk(fullpath, topdown, followlinks): - yield x - if not topdown: - yield top, dirs, nondirs + warnings.warn('sphinx.util.osutil.walk() is deprecated for removal. ' + 'Please use os.walk() instead.', + RemovedInSphinx40Warning) + return os.walk(top, topdown=topdown, followlinks=followlinks) def mtimes_of_files(dirnames, suffix): @@ -195,19 +164,13 @@ def ustrftime(format, *args): if source_date_epoch is not None: time_struct = time.gmtime(float(source_date_epoch)) args = [time_struct] # type: ignore - if PY2: - # if a locale is set, the time strings are encoded in the encoding - # given by LC_TIME; if that is available, use it - enc = locale.getlocale(locale.LC_TIME)[1] or 'utf-8' - return time.strftime(text_type(format).encode(enc), *args).decode(enc) - else: # Py3 - # On Windows, time.strftime() and Unicode characters will raise UnicodeEncodeError. - # https://bugs.python.org/issue8304 - try: - return time.strftime(format, *args) - except UnicodeEncodeError: - r = time.strftime(format.encode('unicode-escape').decode(), *args) - return r.encode().decode('unicode-escape') + # On Windows, time.strftime() and Unicode characters will raise UnicodeEncodeError. + # https://bugs.python.org/issue8304 + try: + return time.strftime(format, *args) # type: ignore + except UnicodeEncodeError: + r = time.strftime(format.encode('unicode-escape').decode(), *args) # type: ignore + return r.encode().decode('unicode-escape') def relpath(path, start=os.curdir): @@ -243,15 +206,16 @@ def abspath(pathdir): def getcwd(): # type: () -> unicode - if hasattr(os, 'getcwdu'): - return os.getcwdu() + warnings.warn('sphinx.util.osutil.getcwd() is deprecated. ' + 'Please use os.getcwd() instead.', + RemovedInSphinx40Warning) return os.getcwd() @contextlib.contextmanager def cd(target_dir): # type: (unicode) -> Iterator[None] - cwd = getcwd() + cwd = os.getcwd() try: os.chdir(target_dir) yield @@ -259,7 +223,7 @@ def cd(target_dir): os.chdir(cwd) -class FileAvoidWrite(object): +class FileAvoidWrite: """File-like object that buffers output and only writes if content changed. Use this class like when writing to a file to avoid touching the original diff --git a/sphinx/util/parallel.py b/sphinx/util/parallel.py index 066e3c93a..8b3edd314 100644 --- a/sphinx/util/parallel.py +++ b/sphinx/util/parallel.py @@ -15,8 +15,6 @@ import time import traceback from math import sqrt -from six import iteritems - try: import multiprocessing except ImportError: @@ -36,7 +34,7 @@ logger = logging.getLogger(__name__) parallel_available = multiprocessing and (os.name == 'posix') -class SerialTasks(object): +class SerialTasks: """Has the same interface as ParallelTasks, but executes tasks directly.""" def __init__(self, nproc=1): @@ -57,7 +55,7 @@ class SerialTasks(object): pass -class ParallelTasks(object): +class ParallelTasks: """Executes *nproc* tasks in parallel after forking.""" def __init__(self, nproc): @@ -115,7 +113,7 @@ class ParallelTasks(object): def _join_one(self): # type: () -> None - for tid, pipe in iteritems(self._precvs): + for tid, pipe in self._precvs.items(): if pipe.poll(): exc, logs, result = pipe.recv() if exc: diff --git a/sphinx/util/pycompat.py b/sphinx/util/pycompat.py index 8bcf7e4f8..a33a832c8 100644 --- a/sphinx/util/pycompat.py +++ b/sphinx/util/pycompat.py @@ -9,10 +9,12 @@ :license: BSD, see LICENSE for details. """ -import codecs import sys +from html import escape as htmlescape # NOQA +from io import TextIOWrapper # NOQA +from textwrap import indent # type: ignore # NOQA -from six import PY3, text_type, exec_ +from six import text_type, exec_ if False: # For type annotation @@ -25,114 +27,51 @@ NoneType = type(None) # Python 2/3 compatibility # prefix for Unicode strings -if PY3: - u = '' -else: - u = 'u' - - -# TextIOWrapper -if PY3: - from io import TextIOWrapper -else: - def TextIOWrapper(stream, encoding): - # type: (file, str) -> Any - return codecs.lookup(encoding or 'ascii')[2](stream) +u = '' # sys_encoding: some kind of default system encoding; should be used with # a lenient error handler -if PY3: - sys_encoding = sys.getdefaultencoding() -else: - sys_encoding = __import__('locale').getpreferredencoding() +sys_encoding = sys.getdefaultencoding() # terminal_safe(): safely encode a string for printing to the terminal -if PY3: - def terminal_safe(s): - # type: (unicode) -> unicode - return s.encode('ascii', 'backslashreplace').decode('ascii') -else: - def terminal_safe(s): - # type: (unicode) -> unicode - return s.encode('ascii', 'backslashreplace') +def terminal_safe(s): + # type: (unicode) -> unicode + return s.encode('ascii', 'backslashreplace').decode('ascii') # convert_with_2to3(): -if PY3: - # support for running 2to3 over config files - def convert_with_2to3(filepath): - # type: (unicode) -> unicode - from lib2to3.refactor import RefactoringTool, get_fixers_from_package - from lib2to3.pgen2.parse import ParseError - fixers = get_fixers_from_package('lib2to3.fixes') - refactoring_tool = RefactoringTool(fixers) - source = refactoring_tool._read_python_source(filepath)[0] - try: - tree = refactoring_tool.refactor_string(source, 'conf.py') - except ParseError as err: - # do not propagate lib2to3 exceptions - lineno, offset = err.context[1] - # try to match ParseError details with SyntaxError details - raise SyntaxError(err.msg, (filepath, lineno, offset, err.value)) - return text_type(tree) -else: - # no need to refactor on 2.x versions - convert_with_2to3 = None - - -# htmlescape() -if PY3: - from html import escape as htmlescape -else: - from cgi import escape as htmlescape # NOQA - - -# UnicodeMixin -if PY3: - class UnicodeMixin(object): - """Mixin class to handle defining the proper __str__/__unicode__ - methods in Python 2 or 3.""" - - def __str__(self): - return self.__unicode__() -else: - class UnicodeMixin(object): - """Mixin class to handle defining the proper __str__/__unicode__ - methods in Python 2 or 3.""" - - def __str__(self): - # type: () -> str - return self.__unicode__().encode('utf8') # type: ignore - - -# indent() -if PY3: - from textwrap import indent -else: - # backport from python3 - def indent(text, prefix, predicate=None): - # type: (unicode, unicode, Callable) -> unicode - if predicate is None: - def predicate(line): - # type: (unicode) -> unicode - return line.strip() - - def prefixed_lines(): - # type: () -> Generator - for line in text.splitlines(True): - yield (prefix + line if predicate(line) else line) - return ''.join(prefixed_lines()) +# support for running 2to3 over config files +def convert_with_2to3(filepath): + # type: (unicode) -> unicode + from lib2to3.refactor import RefactoringTool, get_fixers_from_package + from lib2to3.pgen2.parse import ParseError + fixers = get_fixers_from_package('lib2to3.fixes') + refactoring_tool = RefactoringTool(fixers) + source = refactoring_tool._read_python_source(filepath)[0] + try: + tree = refactoring_tool.refactor_string(source, 'conf.py') + except ParseError as err: + # do not propagate lib2to3 exceptions + lineno, offset = err.context[1] + # try to match ParseError details with SyntaxError details + raise SyntaxError(err.msg, (filepath, lineno, offset, err.value)) + return text_type(tree) + + +class UnicodeMixin: + """Mixin class to handle defining the proper __str__/__unicode__ + methods in Python 2 or 3.""" + + def __str__(self): + return self.__unicode__() def execfile_(filepath, _globals, open=open): # type: (unicode, Any, Callable) -> None from sphinx.util.osutil import fs_encoding - # get config source -- 'b' is a no-op under 2.x, while 'U' is - # ignored under 3.x (but 3.x compile() accepts \r\n newlines) - mode = 'rb' if PY3 else 'rbU' - with open(filepath, mode) as f: + with open(filepath, 'rb') as f: source = f.read() # compile to a code object, handle syntax errors diff --git a/sphinx/util/stemmer/__init__.py b/sphinx/util/stemmer/__init__.py index a10da7370..43aef5032 100644 --- a/sphinx/util/stemmer/__init__.py +++ b/sphinx/util/stemmer/__init__.py @@ -18,7 +18,7 @@ except ImportError: PYSTEMMER = False -class BaseStemmer(object): +class BaseStemmer: def stem(self, word): # type: (unicode) -> unicode raise NotImplementedError() diff --git a/sphinx/util/stemmer/porter.py b/sphinx/util/stemmer/porter.py index beb860c9e..4f2f1a089 100644 --- a/sphinx/util/stemmer/porter.py +++ b/sphinx/util/stemmer/porter.py @@ -14,7 +14,7 @@ only differing from it at the points maked --DEPARTURE-- below. - See also http://www.tartarus.org/~martin/PorterStemmer + See also https://tartarus.org/martin/PorterStemmer/ The algorithm as described in the paper could be exactly replicated by adjusting the points of DEPARTURE, but this is barely necessary, @@ -29,7 +29,7 @@ """ -class PorterStemmer(object): +class PorterStemmer: def __init__(self): # type: () -> None diff --git a/sphinx/util/tags.py b/sphinx/util/tags.py index 43a351f65..4d0dc7f9a 100644 --- a/sphinx/util/tags.py +++ b/sphinx/util/tags.py @@ -46,7 +46,7 @@ class BooleanParser(Parser): return node -class Tags(object): +class Tags: def __init__(self, tags=None): # type: (List[unicode]) -> None self.tags = dict.fromkeys(tags or [], True) diff --git a/sphinx/util/template.py b/sphinx/util/template.py index 5a415d329..01c772069 100644 --- a/sphinx/util/template.py +++ b/sphinx/util/template.py @@ -16,6 +16,7 @@ from jinja2.sandbox import SandboxedEnvironment from sphinx import package_dir from sphinx.jinja2glue import SphinxFileSystemLoader from sphinx.locale import get_translator +from sphinx.util import texescape if False: # For type annotation @@ -23,7 +24,7 @@ if False: from jinja2.loaders import BaseLoader # NOQA -class BaseRenderer(object): +class BaseRenderer: def __init__(self, loader=None): # type: (BaseLoader) -> None self.env = SandboxedEnvironment(loader=loader, extensions=['jinja2.ext.i18n']) @@ -72,6 +73,10 @@ class LaTeXRenderer(SphinxRenderer): template_path = os.path.join(package_dir, 'templates', 'latex') super(LaTeXRenderer, self).__init__(template_path) + # use texescape as escape filter + self.env.filters['e'] = texescape.escape + self.env.filters['escape'] = texescape.escape + # use JSP/eRuby like tagging instead because curly bracket; the default # tagging of jinja2 is not good for LaTeX sources. self.env.variable_start_string = '<%=' diff --git a/sphinx/util/texescape.py b/sphinx/util/texescape.py index 8d37e0f60..0f1783def 100644 --- a/sphinx/util/texescape.py +++ b/sphinx/util/texescape.py @@ -11,6 +11,10 @@ from __future__ import unicode_literals +if False: + # For type annotation + from typing import Dict # NOQA + tex_replacements = [ # map TeX special chars ('$', r'\$'), @@ -117,11 +121,17 @@ tex_replacements = [ ('Ω', r'\(\Omega\)'), ] -tex_escape_map = {} +tex_escape_map = {} # type: Dict[int, unicode] tex_replace_map = {} tex_hl_escape_map_new = {} +def escape(s): + # type: (unicode) -> unicode + """Escape text for LaTeX output.""" + return s.translate(tex_escape_map) + + def init(): # type: () -> None for a, b in tex_replacements: diff --git a/sphinx/util/typing.py b/sphinx/util/typing.py index a26dac473..d31fa7c68 100644 --- a/sphinx/util/typing.py +++ b/sphinx/util/typing.py @@ -13,15 +13,12 @@ from typing import Callable, Dict, List, Tuple from docutils import nodes from docutils.parsers.rst.states import Inliner -from six import PY3 +from six import text_type -if PY3: - unicode = str - # common role functions -RoleFunction = Callable[[unicode, unicode, unicode, int, Inliner, Dict, List[unicode]], +RoleFunction = Callable[[text_type, text_type, text_type, int, Inliner, Dict, List[text_type]], Tuple[List[nodes.Node], List[nodes.Node]]] # title getter functions for enumerable nodes (see sphinx.domains.std) -TitleGetter = Callable[[nodes.Node], unicode] +TitleGetter = Callable[[nodes.Node], text_type] diff --git a/sphinx/versioning.py b/sphinx/versioning.py index 58b648069..e65c9fd8f 100644 --- a/sphinx/versioning.py +++ b/sphinx/versioning.py @@ -9,13 +9,13 @@ :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. """ +import pickle import warnings from itertools import product from operator import itemgetter +from os import path from uuid import uuid4 -from six import iteritems -from six.moves import cPickle as pickle from six.moves import range, zip_longest from sphinx.deprecation import RemovedInSphinx30Warning @@ -102,7 +102,7 @@ def merge_doctrees(old, new, condition): # choose the old node with the best ratio for each new node and set the uid # as long as the ratio is under a certain value, in which case we consider # them not changed but different - ratios = sorted(iteritems(ratios), key=itemgetter(1)) # type: ignore + ratios = sorted(ratios.items(), key=itemgetter(1)) # type: ignore for (old_node, new_node), ratio in ratios: if new_node in seen: continue @@ -169,7 +169,7 @@ class UIDTransform(SphinxTransform): if env.versioning_compare: # get old doctree try: - filename = env.doc2path(env.docname, env.doctreedir, '.doctree') + filename = path.join(env.doctreedir, env.docname + '.doctree') with open(filename, 'rb') as f: old_doctree = pickle.load(f) except EnvironmentError: diff --git a/sphinx/websupport/__init__.py b/sphinx/websupport/__init__.py deleted file mode 100644 index 51d906fa6..000000000 --- a/sphinx/websupport/__init__.py +++ /dev/null @@ -1,28 +0,0 @@ -# -*- coding: utf-8 -*- -""" - sphinx.websupport - ~~~~~~~~~~~~~~~~~ - - Base Module for web support functions. - - :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. - :license: BSD, see LICENSE for details. -""" - -import warnings - -from sphinx.deprecation import RemovedInSphinx20Warning - -try: - from sphinxcontrib.websupport import WebSupport # NOQA - from sphinxcontrib.websupport import errors # NOQA - from sphinxcontrib.websupport.search import BaseSearch, SEARCH_ADAPTERS # NOQA - from sphinxcontrib.websupport.storage import StorageBackend # NOQA - - warnings.warn('sphinx.websupport module is now provided as sphinxcontrib-websupport. ' - 'sphinx.websupport will be removed at Sphinx-2.0. ' - 'Please use the package instead.', - RemovedInSphinx20Warning) -except ImportError: - warnings.warn('Since Sphinx-1.6, sphinx.websupport module is now separated to ' - 'sphinxcontrib-websupport package. Please add it into your dependency list.') diff --git a/sphinx/websupport/errors.py b/sphinx/websupport/errors.py deleted file mode 100644 index 7456659ec..000000000 --- a/sphinx/websupport/errors.py +++ /dev/null @@ -1,12 +0,0 @@ -# -*- coding: utf-8 -*- -""" - sphinx.websupport.errors - ~~~~~~~~~~~~~~~~~~~~~~~~ - - Contains Error classes for the web support package. - - :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. - :license: BSD, see LICENSE for details. -""" - -from sphinxcontrib.websupport.errors import * # NOQA diff --git a/sphinx/websupport/search/__init__.py b/sphinx/websupport/search/__init__.py deleted file mode 100644 index e1e871ba0..000000000 --- a/sphinx/websupport/search/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -# -*- coding: utf-8 -*- -""" - sphinx.websupport.search - ~~~~~~~~~~~~~~~~~~~~~~~~ - - Server side search support for the web support package. - - :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. - :license: BSD, see LICENSE for details. -""" - -from sphinxcontrib.websupport.search import BaseSearch, SEARCH_ADAPTERS # NOQA diff --git a/sphinx/websupport/search/nullsearch.py b/sphinx/websupport/search/nullsearch.py deleted file mode 100644 index 422b398c9..000000000 --- a/sphinx/websupport/search/nullsearch.py +++ /dev/null @@ -1,12 +0,0 @@ -# -*- coding: utf-8 -*- -""" - sphinx.websupport.search.nullsearch - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - The default search adapter, does nothing. - - :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. - :license: BSD, see LICENSE for details. -""" - -from sphinxcontrib.websupport.search.nullsearch import NullSearch # NOQA diff --git a/sphinx/websupport/search/whooshsearch.py b/sphinx/websupport/search/whooshsearch.py deleted file mode 100644 index 94cce8ed7..000000000 --- a/sphinx/websupport/search/whooshsearch.py +++ /dev/null @@ -1,12 +0,0 @@ -# -*- coding: utf-8 -*- -""" - sphinx.websupport.search.whooshsearch - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Whoosh search adapter. - - :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. - :license: BSD, see LICENSE for details. -""" - -from sphinxcontrib.websupport.search.whooshsearch import WhooshSearch # NOQA diff --git a/sphinx/websupport/search/xapiansearch.py b/sphinx/websupport/search/xapiansearch.py deleted file mode 100644 index 4df4769e2..000000000 --- a/sphinx/websupport/search/xapiansearch.py +++ /dev/null @@ -1,12 +0,0 @@ -# -*- coding: utf-8 -*- -""" - sphinx.websupport.search.xapiansearch - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Xapian search adapter. - - :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. - :license: BSD, see LICENSE for details. -""" - -from sphinxcontrib.websupport.search.xapiansearch import XapianSearch # NOQA diff --git a/sphinx/websupport/storage/__init__.py b/sphinx/websupport/storage/__init__.py deleted file mode 100644 index 727e86da4..000000000 --- a/sphinx/websupport/storage/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -# -*- coding: utf-8 -*- -""" - sphinx.websupport.storage - ~~~~~~~~~~~~~~~~~~~~~~~~~ - - Storage for the websupport package. - - :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. - :license: BSD, see LICENSE for details. -""" - -from sphinxcontrib.websupport.storage import StorageBackend # NOQA diff --git a/sphinx/websupport/storage/differ.py b/sphinx/websupport/storage/differ.py deleted file mode 100644 index 1358d8645..000000000 --- a/sphinx/websupport/storage/differ.py +++ /dev/null @@ -1,12 +0,0 @@ -# -*- coding: utf-8 -*- -""" - sphinx.websupport.storage.differ - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - A differ for creating an HTML representations of proposal diffs - - :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. - :license: BSD, see LICENSE for details. -""" - -from sphinxcontrib.websupport.storage.differ import CombinedHtmlDiff # NOQA diff --git a/sphinx/websupport/storage/sqlalchemy_db.py b/sphinx/websupport/storage/sqlalchemy_db.py deleted file mode 100644 index e1c86dd9d..000000000 --- a/sphinx/websupport/storage/sqlalchemy_db.py +++ /dev/null @@ -1,13 +0,0 @@ -# -*- coding: utf-8 -*- -""" - sphinx.websupport.storage.sqlalchemy_db - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - SQLAlchemy table and mapper definitions used by the - :class:`sphinx.websupport.storage.sqlalchemystorage.SQLAlchemyStorage`. - - :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. - :license: BSD, see LICENSE for details. -""" - -from sphinxcontrib.websupport.storage.sqlalchemy_db import Node, Comment, CommentVote # NOQA diff --git a/sphinx/websupport/storage/sqlalchemystorage.py b/sphinx/websupport/storage/sqlalchemystorage.py deleted file mode 100644 index b018ea0a3..000000000 --- a/sphinx/websupport/storage/sqlalchemystorage.py +++ /dev/null @@ -1,12 +0,0 @@ -# -*- coding: utf-8 -*- -""" - sphinx.websupport.storage.sqlalchemystorage - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - An SQLAlchemy storage backend. - - :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. - :license: BSD, see LICENSE for details. -""" - -from sphinxcontrib.websupport.storage.sqlalchemystorage import SQLAlchemyStorage # NOQA diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 4ef8ddbca..f9af2a1f8 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -20,7 +20,7 @@ from os import path from docutils import nodes, writers from docutils.writers.latex2e import Babel -from six import itervalues, text_type +from six import text_type from sphinx import addnodes from sphinx import highlighting @@ -115,7 +115,6 @@ DEFAULT_SETTINGS = { 'tocdepth': '', 'secnumdepth': '', 'pageautorefname': '', - 'translatablestrings': '', } # type: Dict[unicode, unicode] ADDITIONAL_SETTINGS = { @@ -257,7 +256,7 @@ class ExtBabel(Babel): return None -class Table(object): +class Table: """A table data""" def __init__(self, node): @@ -378,7 +377,7 @@ class Table(object): return None -class TableCell(object): +class TableCell: """A cell data of tables.""" def __init__(self, table, row, col): @@ -472,33 +471,10 @@ class LaTeXTranslator(nodes.NodeVisitor): self.first_param = 0 # sort out some elements - self.elements = DEFAULT_SETTINGS.copy() - self.elements.update(ADDITIONAL_SETTINGS.get(builder.config.latex_engine, {})) - # for xelatex+French, don't use polyglossia - if self.elements['latex_engine'] == 'xelatex': - if builder.config.language: - if builder.config.language[:2] == 'fr': - self.elements.update({ - 'polyglossia': '', - 'babel': '\\usepackage{babel}', - }) - # allow the user to override them all - self.elements.update(builder.config.latex_elements) + self.elements = builder.context.copy() # but some have other interface in config file - self.elements.update({ - 'wrapperclass': self.format_docclass(document.settings.docclass), - # if empty, the title is set to the first section title - 'title': document.settings.title, # treat as a raw LaTeX code - 'release': self.encode(builder.config.release), - 'author': document.settings.author, # treat as a raw LaTeX code - 'indexname': _('Index'), - 'use_xindy': builder.config.latex_use_xindy, - }) - if not self.elements['releasename'] and self.elements['release']: - self.elements.update({ - 'releasename': _('Release'), - }) + self.elements['wrapperclass'] = self.format_docclass(document.settings.docclass) # we assume LaTeX class provides \chapter command except in case # of non-Japanese 'howto' case @@ -651,23 +627,6 @@ class LaTeXTranslator(nodes.NodeVisitor): if self.elements['extraclassoptions']: self.elements['classoptions'] += ',' + \ self.elements['extraclassoptions'] - self.elements['translatablestrings'] = ( - self.babel_renewcommand( - '\\literalblockcontinuedname', self.encode(_('continued from previous page')) - ) + - self.babel_renewcommand( - '\\literalblockcontinuesname', self.encode(_('continues on next page')) - ) + - self.babel_renewcommand( - '\\sphinxnonalphabeticalgroupname', self.encode(_('Non-alphabetical')) - ) + - self.babel_renewcommand( - '\\sphinxsymbolsname', self.encode(_('Symbols')) - ) + - self.babel_renewcommand( - '\\sphinxnumbersname', self.encode(_('Numbers')) - ) - ) self.elements['pageautorefname'] = \ self.babel_defmacro('\\pageautorefname', self.encode(_('page'))) self.elements['numfig_format'] = self.generate_numfig_format(builder) @@ -849,7 +808,7 @@ class LaTeXTranslator(nodes.NodeVisitor): # latex_domain_indices can be False/True or a list of index names indices_config = self.builder.config.latex_domain_indices if indices_config: - for domain in itervalues(self.builder.env.domains): + for domain in self.builder.env.domains.values(): for indexcls in domain.indices: indexname = '%s-%s' % (domain.name, indexcls.name) if isinstance(indices_config, list): diff --git a/sphinx/writers/manpage.py b/sphinx/writers/manpage.py index 45a800533..471e58813 100644 --- a/sphinx/writers/manpage.py +++ b/sphinx/writers/manpage.py @@ -11,16 +11,15 @@ from docutils import nodes from docutils.writers.manpage import ( - MACRO_DEF, Writer, Translator as BaseTranslator ) -import sphinx.util.docutils from sphinx import addnodes from sphinx.locale import admonitionlabels, _ from sphinx.util import logging from sphinx.util.i18n import format_date +from sphinx.util.nodes import NodeMatcher if False: # For type annotation @@ -46,7 +45,7 @@ class ManualPageWriter(Writer): self.output = visitor.astext() -class NestedInlineTransform(object): +class NestedInlineTransform: """ Flatten nested inline nodes: @@ -63,16 +62,13 @@ class NestedInlineTransform(object): def apply(self): # type: () -> None - def is_inline(node): - # type: (nodes.Node) -> bool - return isinstance(node, (nodes.literal, nodes.emphasis, nodes.strong)) - - for node in self.document.traverse(is_inline): - if any(is_inline(subnode) for subnode in node): + matcher = NodeMatcher(nodes.literal, nodes.emphasis, nodes.strong) + for node in self.document.traverse(matcher): + if any(matcher(subnode) for subnode in node): pos = node.parent.index(node) for subnode in reversed(node[1:]): node.remove(subnode) - if is_inline(subnode): + if matcher(subnode): node.parent.insert(pos + 1, subnode) else: newnode = node.__class__('', subnode, **node.attributes) @@ -113,10 +109,6 @@ class ManualPageTranslator(BaseTranslator): self._docinfo['version'] = builder.config.version self._docinfo['manual_group'] = builder.config.project - # In docutils < 0.11 self.append_header() was never called - if sphinx.util.docutils.__version_info__ < (0, 11): - self.body.append(MACRO_DEF) - # Overwrite admonition label translations with our own for label, translation in admonitionlabels.items(): self.language.labels[label] = self.deunicode(translation) diff --git a/sphinx/writers/texinfo.py b/sphinx/writers/texinfo.py index 701880a61..91e534278 100644 --- a/sphinx/writers/texinfo.py +++ b/sphinx/writers/texinfo.py @@ -14,8 +14,6 @@ import textwrap from os import path from docutils import nodes, writers -from six import itervalues -from six.moves import range from sphinx import addnodes, __display_version__ from sphinx.errors import ExtensionError @@ -498,7 +496,7 @@ class TexinfoTranslator(nodes.NodeVisitor): indices_config = self.builder.config.texinfo_domain_indices if indices_config: - for domain in itervalues(self.builder.env.domains): + for domain in self.builder.env.domains.values(): for indexcls in domain.indices: indexname = '%s-%s' % (domain.name, indexcls.name) if isinstance(indices_config, list): diff --git a/tests/conftest.py b/tests/conftest.py index 9f46b1868..65142d84a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -9,7 +9,6 @@ import os import shutil -import sys import docutils import pytest @@ -22,13 +21,6 @@ pytest_plugins = 'sphinx.testing.fixtures' # Exclude 'roots' dirs for pytest test collector collect_ignore = ['roots'] -# Disable Python version-specific -if sys.version_info < (3,): - collect_ignore += ['py3'] - -if sys.version_info < (3, 5): - collect_ignore += ['py35'] - @pytest.fixture(scope='session') def rootdir(): diff --git a/tests/py3/test_util_inspect_py3.py b/tests/py3/test_util_inspect_py3.py deleted file mode 100644 index 6d02025f9..000000000 --- a/tests/py3/test_util_inspect_py3.py +++ /dev/null @@ -1,26 +0,0 @@ -# -*- coding: utf-8 -*- -""" - py3/test_util_inspect - ~~~~~~~~~~~~~~~~~~~~~ - - Tests util.inspect functions. - - :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. - :license: BSD, see LICENSE for details. -""" - -from sphinx.util import inspect - - -def test_Signature_keyword_only_arguments(): - def func1(arg1, arg2, *, arg3=None, arg4=None): - pass - - def func2(*, arg3, arg4): - pass - - sig = inspect.Signature(func1).format_args() - assert sig == '(arg1, arg2, *, arg3=None, arg4=None)' - - sig = inspect.Signature(func2).format_args() - assert sig == '(*, arg3, arg4)' diff --git a/tests/py35/test_autodoc_py35.py b/tests/py35/test_autodoc_py35.py deleted file mode 100644 index 046fb93b4..000000000 --- a/tests/py35/test_autodoc_py35.py +++ /dev/null @@ -1,348 +0,0 @@ -# -*- coding: utf-8 -*- -""" - test_autodoc - ~~~~~~~~~~~~ - - Test the autodoc extension. This tests mainly the Documenters; the auto - directives are tested in a test source file translated by test_build. - - :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. - :license: BSD, see LICENSE for details. -""" - -# "raises" imported for usage by autodoc -import sys - -import pytest -import six -from docutils.statemachine import ViewList -from six import StringIO - -from sphinx.ext.autodoc import add_documenter, FunctionDocumenter, ALL, Options # NOQA -from sphinx.testing.util import SphinxTestApp, Struct -from sphinx.util import logging - -app = None - - -@pytest.fixture(scope='module', autouse=True) -def setup_module(rootdir, sphinx_test_tempdir): - global app - srcdir = sphinx_test_tempdir / 'autodoc-root' - if not srcdir.exists(): - (rootdir / 'test-root').copytree(srcdir) - app = SphinxTestApp(srcdir=srcdir) - app.builder.env.app = app - app.builder.env.temp_data['docname'] = 'dummy' - app.connect('autodoc-process-docstring', process_docstring) - app.connect('autodoc-process-signature', process_signature) - app.connect('autodoc-skip-member', skip_member) - yield - app.cleanup() - - -directive = options = None - - -@pytest.fixture -def setup_test(): - global options, directive - global processed_docstrings, processed_signatures - - options = Options( - inherited_members = False, - undoc_members = False, - private_members = False, - special_members = False, - imported_members = False, - show_inheritance = False, - noindex = False, - annotation = None, - synopsis = '', - platform = '', - deprecated = False, - members = [], - member_order = 'alphabetic', - exclude_members = set(), - ) - - directive = Struct( - env = app.builder.env, - genopt = options, - result = ViewList(), - filename_set = set(), - ) - - processed_docstrings = [] - processed_signatures = [] - - -processed_docstrings = [] -processed_signatures = [] - - -def process_docstring(app, what, name, obj, options, lines): - processed_docstrings.append((what, name)) - if name == 'bar': - lines.extend(['42', '']) - - -def process_signature(app, what, name, obj, options, args, retann): - processed_signatures.append((what, name)) - if name == 'bar': - return '42', None - - -def skip_member(app, what, name, obj, skip, options): - if name in ('__special1__', '__special2__'): - return skip - if name.startswith('_'): - return True - if name == 'skipmeth': - return True - - -@pytest.mark.usefixtures('setup_test') -def test_generate(): - logging.setup(app, app._status, app._warning) - - def assert_warns(warn_str, objtype, name, **kw): - inst = app.registry.documenters[objtype](directive, name) - inst.generate(**kw) - assert len(directive.result) == 0, directive.result - assert warn_str in app._warning.getvalue() - app._warning.truncate(0) - app._warning.seek(0) - - def assert_works(objtype, name, **kw): - inst = app.registry.documenters[objtype](directive, name) - inst.generate(**kw) - assert directive.result - # print '\n'.join(directive.result) - assert app._warning.getvalue() == '' - del directive.result[:] - - def assert_processes(items, objtype, name, **kw): - del processed_docstrings[:] - del processed_signatures[:] - assert_works(objtype, name, **kw) - assert set(processed_docstrings) | set(processed_signatures) == set(items) - - def assert_result_contains(item, objtype, name, **kw): - inst = app.registry.documenters[objtype](directive, name) - inst.generate(**kw) - # print '\n'.join(directive.result) - assert app._warning.getvalue() == '' - assert item in directive.result - del directive.result[:] - - def assert_order(items, objtype, name, member_order, **kw): - inst = app.registry.documenters[objtype](directive, name) - inst.options.member_order = member_order - inst.generate(**kw) - assert app._warning.getvalue() == '' - items = list(reversed(items)) - lineiter = iter(directive.result) - # for line in directive.result: - # if line.strip(): - # print repr(line) - while items: - item = items.pop() - for line in lineiter: - if line == item: - break - else: # ran out of items! - assert False, ('item %r not found in result or not in the ' - ' correct order' % item) - del directive.result[:] - - options.members = [] - - # no module found? - assert_warns("import for autodocumenting 'foobar'", - 'function', 'foobar', more_content=None) - # importing - assert_warns("failed to import module 'test_foobar'", - 'module', 'test_foobar', more_content=None) - # attributes missing - assert_warns("failed to import function 'foobar' from module 'util'", - 'function', 'util.foobar', more_content=None) - # method missing - assert_warns("failed to import method 'Class.foobar' from module 'test_autodoc_py35';", - 'method', 'test_autodoc_py35.Class.foobar', more_content=None) - - # test auto and given content mixing - directive.env.ref_context['py:module'] = 'test_autodoc_py35' - assert_result_contains(' Function.', 'method', 'Class.meth') - add_content = ViewList() - add_content.append('Content.', '', 0) - assert_result_contains(' Function.', 'method', - 'Class.meth', more_content=add_content) - assert_result_contains(' Content.', 'method', - 'Class.meth', more_content=add_content) - - # test check_module - inst = FunctionDocumenter(directive, 'add_documenter') - inst.generate(check_module=True) - assert len(directive.result) == 0 - - # assert that exceptions can be documented - assert_works('exception', 'test_autodoc_py35.CustomEx', all_members=True) - assert_works('exception', 'test_autodoc_py35.CustomEx') - - # test diverse inclusion settings for members - should = [('class', 'test_autodoc_py35.Class')] - assert_processes(should, 'class', 'Class') - should.extend([('method', 'test_autodoc_py35.Class.meth')]) - options.members = ['meth'] - options.exclude_members = set(['excludemeth']) - assert_processes(should, 'class', 'Class') - should.extend([('attribute', 'test_autodoc_py35.Class.prop'), - ('attribute', 'test_autodoc_py35.Class.descr'), - ('attribute', 'test_autodoc_py35.Class.attr'), - ('attribute', 'test_autodoc_py35.Class.docattr'), - ('attribute', 'test_autodoc_py35.Class.udocattr'), - ('attribute', 'test_autodoc_py35.Class.mdocattr'), - ('attribute', 'test_autodoc_py35.Class.inst_attr_comment'), - ('attribute', 'test_autodoc_py35.Class.inst_attr_inline'), - ('attribute', 'test_autodoc_py35.Class.inst_attr_string'), - ('method', 'test_autodoc_py35.Class.moore'), - ]) - if six.PY3 and sys.version_info[:2] >= (3, 5): - should.extend([ - ('method', 'test_autodoc_py35.Class.do_coroutine'), - ]) - options.members = ALL - assert_processes(should, 'class', 'Class') - options.undoc_members = True - should.extend((('attribute', 'test_autodoc_py35.Class.skipattr'), - ('method', 'test_autodoc_py35.Class.undocmeth'), - ('method', 'test_autodoc_py35.Class.roger'))) - assert_processes(should, 'class', 'Class') - options.inherited_members = True - should.append(('method', 'test_autodoc_py35.Class.inheritedmeth')) - assert_processes(should, 'class', 'Class') - - # test special members - options.special_members = ['__special1__'] - should.append(('method', 'test_autodoc_py35.Class.__special1__')) - assert_processes(should, 'class', 'Class') - options.special_members = ALL - should.append(('method', 'test_autodoc_py35.Class.__special2__')) - assert_processes(should, 'class', 'Class') - options.special_members = False - - -# --- generate fodder ------------ -__all__ = ['Class'] - -#: documentation for the integer -integer = 1 - - -class CustomEx(Exception): - """My custom exception.""" - - def f(self): - """Exception method.""" - - -class CustomDataDescriptor(object): - """Descriptor class docstring.""" - - def __init__(self, doc): - self.__doc__ = doc - - def __get__(self, obj, type=None): - if obj is None: - return self - return 42 - - def meth(self): - """Function.""" - return "The Answer" - - -def _funky_classmethod(name, b, c, d, docstring=None): - """Generates a classmethod for a class from a template by filling out - some arguments.""" - def template(cls, a, b, c, d=4, e=5, f=6): - return a, b, c, d, e, f - from functools import partial - function = partial(template, b=b, c=c, d=d) - function.__name__ = name - function.__doc__ = docstring - return classmethod(function) - - -class Base(object): - def inheritedmeth(self): - """Inherited function.""" - - -if six.PY3 and sys.version_info[:2] >= (3, 5): - async def _other_coro_func(): - return "run" - - -class Class(Base): - """Class to document.""" - - descr = CustomDataDescriptor("Descriptor instance docstring.") - - def meth(self): - """Function.""" - - def undocmeth(self): - pass - - def skipmeth(self): - """Method that should be skipped.""" - - def excludemeth(self): - """Method that should be excluded.""" - - # should not be documented - skipattr = 'foo' - - #: should be documented -- süß - attr = 'bar' - - @property - def prop(self): - """Property.""" - - docattr = 'baz' - """should likewise be documented -- süß""" - - udocattr = 'quux' - u"""should be documented as well - süß""" - - # initialized to any class imported from another module - mdocattr = StringIO() - """should be documented as well - süß""" - - roger = _funky_classmethod("roger", 2, 3, 4) - - moore = _funky_classmethod("moore", 9, 8, 7, - docstring="moore(a, e, f) -> happiness") - - def __init__(self, arg): - self.inst_attr_inline = None #: an inline documented instance attr - #: a documented instance attribute - self.inst_attr_comment = None - self.inst_attr_string = None - """a documented instance attribute""" - - def __special1__(self): - """documented special method""" - - def __special2__(self): - # undocumented special method - pass - - if six.PY3 and sys.version_info[:2] >= (3, 5): - - async def do_coroutine(self): - """A documented coroutine function""" - attr_coro_result = await _other_coro_func() # NOQA diff --git a/tests/roots/test-api-set-translator/conf.py b/tests/roots/test-api-set-translator/conf.py index c1ad24e56..9a7312d65 100644 --- a/tests/roots/test-api-set-translator/conf.py +++ b/tests/roots/test-api-set-translator/conf.py @@ -11,7 +11,6 @@ from sphinx.writers.latex import LaTeXTranslator from sphinx.writers.manpage import ManualPageTranslator from sphinx.writers.texinfo import TexinfoTranslator from sphinx.writers.text import TextTranslator -from sphinx.writers.websupport import WebSupportTranslator project = 'test' @@ -54,10 +53,6 @@ class ConfTextTranslator(TextTranslator): pass -class ConfWebSupportTranslator(WebSupportTranslator): - pass - - class ConfXMLTranslator(XMLTranslator): pass @@ -76,6 +71,5 @@ def setup(app): app.set_translator('man', ConfManualPageTranslator) app.set_translator('texinfo', ConfTexinfoTranslator) app.set_translator('text', ConfTextTranslator) - app.set_translator('websupport', ConfWebSupportTranslator) app.set_translator('xml', ConfXMLTranslator) app.set_translator('pseudoxml', ConfPseudoXMLTranslator) diff --git a/tests/roots/test-autosummary/contents.rst b/tests/roots/test-autosummary/index.rst index 5ddc4bd40..5ddc4bd40 100644 --- a/tests/roots/test-autosummary/contents.rst +++ b/tests/roots/test-autosummary/index.rst diff --git a/tests/roots/test-build-text/conf.py b/tests/roots/test-build-text/conf.py index 23d0ae840..fd9eefbf6 100644 --- a/tests/roots/test-build-text/conf.py +++ b/tests/roots/test-build-text/conf.py @@ -1,3 +1,2 @@ -master_doc = 'contents' source_suffix = '.txt' exclude_patterns = ['_build'] diff --git a/tests/roots/test-build-text/contents.txt b/tests/roots/test-build-text/index.txt index ca9f8dc6c..ca9f8dc6c 100644 --- a/tests/roots/test-build-text/contents.txt +++ b/tests/roots/test-build-text/index.txt diff --git a/tests/roots/test-circular/contents.rst b/tests/roots/test-circular/index.rst index 294e674dd..294e674dd 100644 --- a/tests/roots/test-circular/contents.rst +++ b/tests/roots/test-circular/index.rst diff --git a/tests/roots/test-circular/sub.rst b/tests/roots/test-circular/sub.rst index 070c39743..cebfd6587 100644 --- a/tests/roots/test-circular/sub.rst +++ b/tests/roots/test-circular/sub.rst @@ -1,3 +1,3 @@ .. toctree:: - contents + index diff --git a/tests/roots/test-correct-year/contents.rst b/tests/roots/test-correct-year/index.rst index 938dfd503..938dfd503 100644 --- a/tests/roots/test-correct-year/contents.rst +++ b/tests/roots/test-correct-year/index.rst diff --git a/tests/roots/test-directive-only/contents.rst b/tests/roots/test-directive-only/index.rst index 80ec00313..80ec00313 100644 --- a/tests/roots/test-directive-only/contents.rst +++ b/tests/roots/test-directive-only/index.rst diff --git a/tests/roots/test-docutilsconf/contents.txt b/tests/roots/test-docutilsconf/index.txt index b20204e61..b20204e61 100644 --- a/tests/roots/test-docutilsconf/contents.txt +++ b/tests/roots/test-docutilsconf/index.txt diff --git a/tests/roots/test-ext-autodoc/contents.rst b/tests/roots/test-ext-autodoc/index.rst index ce4302204..ce4302204 100644 --- a/tests/roots/test-ext-autodoc/contents.rst +++ b/tests/roots/test-ext-autodoc/index.rst diff --git a/tests/roots/test-ext-autodoc/target/__init__.py b/tests/roots/test-ext-autodoc/target/__init__.py index 9bb50bca9..c97269d35 100644 --- a/tests/roots/test-ext-autodoc/target/__init__.py +++ b/tests/roots/test-ext-autodoc/target/__init__.py @@ -4,7 +4,7 @@ import enum from six import StringIO, add_metaclass -from sphinx.ext.autodoc import add_documenter # NOQA +from sphinx.util import save_traceback # NOQA __all__ = ['Class'] diff --git a/tests/roots/test-ext-autodoc/target/coroutine.py b/tests/roots/test-ext-autodoc/target/coroutine.py new file mode 100644 index 000000000..b3223a820 --- /dev/null +++ b/tests/roots/test-ext-autodoc/target/coroutine.py @@ -0,0 +1,8 @@ +class AsyncClass: + async def do_coroutine(self): + """A documented coroutine function""" + attr_coro_result = await _other_coro_func() # NOQA + + +async def _other_coro_func(): + return "run" diff --git a/tests/roots/test-ext-autosummary/contents.rst b/tests/roots/test-ext-autosummary/index.rst index fc84927bb..fc84927bb 100644 --- a/tests/roots/test-ext-autosummary/contents.rst +++ b/tests/roots/test-ext-autosummary/index.rst diff --git a/tests/roots/test-gettext-template/contents.rst b/tests/roots/test-gettext-template/index.rst index e69de29bb..e69de29bb 100644 --- a/tests/roots/test-gettext-template/contents.rst +++ b/tests/roots/test-gettext-template/index.rst diff --git a/tests/roots/test-inheritance/contents.rst b/tests/roots/test-inheritance/index.rst index db4fbacb8..db4fbacb8 100644 --- a/tests/roots/test-inheritance/contents.rst +++ b/tests/roots/test-inheritance/index.rst diff --git a/tests/roots/test-intl/_templates/index.html b/tests/roots/test-intl/_templates/contents.html index d730545d1..d730545d1 100644 --- a/tests/roots/test-intl/_templates/index.html +++ b/tests/roots/test-intl/_templates/contents.html diff --git a/tests/roots/test-intl/conf.py b/tests/roots/test-intl/conf.py index aafd9ba79..0306ff38e 100644 --- a/tests/roots/test-intl/conf.py +++ b/tests/roots/test-intl/conf.py @@ -4,7 +4,7 @@ project = 'Sphinx intl <Tests>' source_suffix = '.txt' keep_warnings = True templates_path = ['_templates'] -html_additional_pages = {'index': 'index.html'} +html_additional_pages = {'contents': 'contents.html'} release = version = '2013.120' gettext_additional_targets = ['index'] exclude_patterns = ['_build'] diff --git a/tests/roots/test-intl/contents.po b/tests/roots/test-intl/index.po index 76ef049f0..76ef049f0 100644 --- a/tests/roots/test-intl/contents.po +++ b/tests/roots/test-intl/index.po diff --git a/tests/roots/test-intl/contents.txt b/tests/roots/test-intl/index.txt index b818e99c7..cd63b5ec3 100644 --- a/tests/roots/test-intl/contents.txt +++ b/tests/roots/test-intl/index.txt @@ -10,7 +10,7 @@ CONTENTS :numbered: :caption: Table of Contents - subdir/contents + subdir/index bom warnings footnote diff --git a/tests/roots/test-intl/role_xref.po b/tests/roots/test-intl/role_xref.po index 5b6d114c0..81ee22c6e 100644 --- a/tests/roots/test-intl/role_xref.po +++ b/tests/roots/test-intl/role_xref.po @@ -19,8 +19,8 @@ msgstr "" msgid "i18n role xref" msgstr "I18N ROCK'N ROLE XREF" -msgid "link to :term:`Some term`, :ref:`i18n-role-xref`, :doc:`contents`." -msgstr "LINK TO :ref:`i18n-role-xref`, :doc:`contents`, :term:`SOME NEW TERM`." +msgid "link to :term:`Some term`, :ref:`i18n-role-xref`, :doc:`index`." +msgstr "LINK TO :ref:`i18n-role-xref`, :doc:`index`, :term:`SOME NEW TERM`." msgid "same type links" msgstr "SAME TYPE LINKS" @@ -31,8 +31,8 @@ msgstr "LINK TO :term:`SOME OTHER NEW TERM` AND :term:`SOME NEW TERM`." msgid "link to :ref:`i18n-role-xref` and :ref:`same-type-links`." msgstr "LINK TO :ref:`same-type-links` AND :ref:`i18n-role-xref`." -msgid "link to :doc:`contents` and :doc:`glossary_terms`." -msgstr "LINK TO :doc:`glossary_terms` AND :doc:`contents`." +msgid "link to :doc:`index` and :doc:`glossary_terms`." +msgstr "LINK TO :doc:`glossary_terms` AND :doc:`index`." msgid "link to :option:`-m` and :option:`--module`." msgstr "LINK TO :option:`--module` AND :option:`-m`." diff --git a/tests/roots/test-intl/role_xref.txt b/tests/roots/test-intl/role_xref.txt index b3d42d127..875af4667 100644 --- a/tests/roots/test-intl/role_xref.txt +++ b/tests/roots/test-intl/role_xref.txt @@ -5,7 +5,7 @@ i18n role xref ============== -link to :term:`Some term`, :ref:`i18n-role-xref`, :doc:`contents`. +link to :term:`Some term`, :ref:`i18n-role-xref`, :doc:`index`. .. _same-type-links: @@ -16,7 +16,7 @@ link to :term:`Some term` and :term:`Some other term`. link to :ref:`i18n-role-xref` and :ref:`same-type-links`. -link to :doc:`contents` and :doc:`glossary_terms`. +link to :doc:`index` and :doc:`glossary_terms`. link to :option:`-m` and :option:`--module`. diff --git a/tests/roots/test-intl/subdir/contents.txt b/tests/roots/test-intl/subdir/index.txt index 7578ce387..7578ce387 100644 --- a/tests/roots/test-intl/subdir/contents.txt +++ b/tests/roots/test-intl/subdir/index.txt diff --git a/tests/roots/test-numbered-circular/contents.rst b/tests/roots/test-numbered-circular/index.rst index c3129cd48..c3129cd48 100644 --- a/tests/roots/test-numbered-circular/contents.rst +++ b/tests/roots/test-numbered-circular/index.rst diff --git a/tests/roots/test-numbered-circular/sub.rst b/tests/roots/test-numbered-circular/sub.rst index 070c39743..cebfd6587 100644 --- a/tests/roots/test-numbered-circular/sub.rst +++ b/tests/roots/test-numbered-circular/sub.rst @@ -1,3 +1,3 @@ .. toctree:: - contents + index diff --git a/tests/roots/test-root/autodoc_target.py b/tests/roots/test-root/autodoc_target.py index 62ca9f691..4f14afc03 100644 --- a/tests/roots/test-root/autodoc_target.py +++ b/tests/roots/test-root/autodoc_target.py @@ -4,8 +4,6 @@ import enum from six import StringIO, add_metaclass -from sphinx.ext.autodoc import add_documenter # NOQA - __all__ = ['Class'] diff --git a/tests/roots/test-root/conf.py b/tests/roots/test-root/conf.py index d5029a776..42b8295a2 100644 --- a/tests/roots/test-root/conf.py +++ b/tests/roots/test-root/conf.py @@ -18,7 +18,6 @@ jsmath_path = 'dummy.js' templates_path = ['_templates'] -master_doc = 'contents' source_suffix = ['.txt', '.add', '.foo'] project = 'Sphinx <Tests>' @@ -37,8 +36,7 @@ rst_epilog = '.. |subst| replace:: global substitution' html_sidebars = {'**': ['localtoc.html', 'relations.html', 'sourcelink.html', 'customsb.html', 'searchbox.html'], - 'contents': ['contentssb.html', 'localtoc.html', - 'globaltoc.html']} + 'index': ['contentssb.html', 'localtoc.html', 'globaltoc.html']} html_style = 'default.css' html_last_updated_fmt = '%b %d, %Y' html_context = {'hckey': 'hcval', 'hckey_co': 'wrong_hcval_co'} @@ -49,19 +47,19 @@ applehelp_bundle_id = 'org.sphinx-doc.Sphinx.help' applehelp_disable_external_tools = True latex_documents = [ - ('contents', 'SphinxTests.tex', 'Sphinx Tests Documentation', + ('index', 'SphinxTests.tex', 'Sphinx Tests Documentation', 'Georg Brandl \\and someone else', 'manual'), ] latex_additional_files = ['svgimg.svg'] texinfo_documents = [ - ('contents', 'SphinxTests', 'Sphinx Tests', + ('index', 'SphinxTests', 'Sphinx Tests', 'Georg Brandl \\and someone else', 'Sphinx Testing', 'Miscellaneous'), ] man_pages = [ - ('contents', 'SphinxTests', 'Sphinx Tests Documentation', + ('index', 'SphinxTests', 'Sphinx Tests Documentation', 'Georg Brandl and someone else', 1), ] diff --git a/tests/roots/test-root/contents.txt b/tests/roots/test-root/index.txt index d5ff24115..d5ff24115 100644 --- a/tests/roots/test-root/contents.txt +++ b/tests/roots/test-root/index.txt diff --git a/tests/roots/test-setup/doc/contents.txt b/tests/roots/test-setup/doc/index.txt index 56960f53e..56960f53e 100644 --- a/tests/roots/test-setup/doc/contents.txt +++ b/tests/roots/test-setup/doc/index.txt diff --git a/tests/roots/test-templating/contents.txt b/tests/roots/test-templating/index.txt index 04a40e21c..04a40e21c 100644 --- a/tests/roots/test-templating/contents.txt +++ b/tests/roots/test-templating/index.txt diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py index 18ef3b584..f469a6be3 100644 --- a/tests/test_autodoc.py +++ b/tests/test_autodoc.py @@ -17,10 +17,9 @@ from warnings import catch_warnings import pytest from docutils.statemachine import ViewList -from six import PY3 from sphinx.ext.autodoc import ( - AutoDirective, ModuleLevelDocumenter, cut_lines, between, ALL, + ModuleLevelDocumenter, cut_lines, between, ALL, merge_autodoc_default_flags, Options ) from sphinx.ext.autodoc.directive import DocumenterBridge, process_documenter_options @@ -30,11 +29,6 @@ from sphinx.util.docutils import LoggingReporter app = None -if PY3: - ROGER_METHOD = ' .. py:classmethod:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)' -else: - ROGER_METHOD = ' .. py:classmethod:: Class.roger(a, e=5, f=6)' - IS_PYPY = platform.python_implementation() == 'PyPy' @@ -112,7 +106,7 @@ def setup_test(): yield - AutoDirective._special_attrgetters.clear() + app.registry.autodoc_attrgettrs.clear() processed_docstrings = [] @@ -216,7 +210,7 @@ def test_format_signature(): class D: pass - class E(object): + class E: pass # no signature for classes without __init__ for C in (D, E): @@ -226,7 +220,7 @@ def test_format_signature(): def __init__(self, a, b=None): pass - class G(F, object): + class G(F): pass for C in (F, G): assert formatsig('class', 'C', C, None, None) == '(a, b=None)' @@ -243,7 +237,7 @@ def test_format_signature(): some docstring for __init__. ''' - class G2(F2, object): + class G2(F2): pass assert formatsig('class', 'F2', F2, None, None) == \ @@ -399,7 +393,7 @@ def test_get_doc(): assert getdocl('class', E) == ['Class docstring', '', 'Init docstring'] # class does not have __init__ method - class F(object): + class F: """Class docstring""" # docstring in the __init__ method of base class will be discard @@ -413,7 +407,7 @@ def test_get_doc(): assert getdocl('class', F) == ['Class docstring'] # class has __init__ method with no docstring - class G(object): + class G: """Class docstring""" def __init__(self): pass @@ -566,7 +560,7 @@ def test_attrgetter_using(): getattr_spy.append((obj, name)) return None return getattr(obj, name, *defargs) - AutoDirective._special_attrgetters[type] = special_getattr + app.add_autodoc_attrgetter(type, special_getattr) del getattr_spy[:] inst = app.registry.documenters[objtype](directive, name) @@ -722,7 +716,7 @@ def test_autodoc_undoc_members(app): ' .. py:method:: Class.meth()', ' .. py:classmethod:: Class.moore(a, e, f) -> happiness', ' .. py:attribute:: Class.prop', - ROGER_METHOD, + ' .. py:classmethod:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)', ' .. py:attribute:: Class.skipattr', ' .. py:method:: Class.skipmeth()', ' .. py:attribute:: Class.udocattr', @@ -752,7 +746,7 @@ def test_autodoc_imported_members(app): "imported-members": None, "ignore-module-all": None} actual = do_autodoc(app, 'module', 'target', options) - assert '.. py:function:: add_documenter(cls)' in actual + assert '.. py:function:: save_traceback(app)' in actual @pytest.mark.sphinx('html', testroot='ext-autodoc') @@ -802,7 +796,7 @@ def test_autodoc_special_members(app): ' .. py:method:: Class.meth()', ' .. py:classmethod:: Class.moore(a, e, f) -> happiness', ' .. py:attribute:: Class.prop', - ROGER_METHOD, + ' .. py:classmethod:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)', ' .. py:attribute:: Class.skipattr', ' .. py:method:: Class.skipmeth()', ' .. py:attribute:: Class.udocattr', @@ -875,11 +869,6 @@ def test_autodoc_subclass_of_builtin_class(app): @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autodoc_inner_class(app): - if PY3: - builtins = ' alias of :class:`builtins.dict`' - else: - builtins = ' alias of :class:`__builtin__.dict`' - options = {"members": None} actual = do_autodoc(app, 'class', 'target.Outer', options) assert list(actual) == [ @@ -905,7 +894,7 @@ def test_autodoc_inner_class(app): ' .. py:attribute:: Outer.factory', ' :module: target', ' ', - builtins + ' alias of :class:`builtins.dict`' ] actual = do_autodoc(app, 'class', 'target.Outer.Inner', options) @@ -974,7 +963,7 @@ def test_autodoc_member_order(app): ' .. py:attribute:: Class.docattr', ' .. py:attribute:: Class.udocattr', ' .. py:attribute:: Class.mdocattr', - ROGER_METHOD, + ' .. py:classmethod:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)', ' .. py:classmethod:: Class.moore(a, e, f) -> happiness', ' .. py:attribute:: Class.inst_attr_inline', ' .. py:attribute:: Class.inst_attr_comment', @@ -993,7 +982,7 @@ def test_autodoc_member_order(app): ' .. py:method:: Class.excludemeth()', ' .. py:method:: Class.meth()', ' .. py:classmethod:: Class.moore(a, e, f) -> happiness', - ROGER_METHOD, + ' .. py:classmethod:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)', ' .. py:method:: Class.skipmeth()', ' .. py:method:: Class.undocmeth()', ' .. py:attribute:: Class._private_inst_attr', @@ -1028,7 +1017,7 @@ def test_autodoc_member_order(app): ' .. py:method:: Class.meth()', ' .. py:classmethod:: Class.moore(a, e, f) -> happiness', ' .. py:attribute:: Class.prop', - ROGER_METHOD, + ' .. py:classmethod:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)', ' .. py:attribute:: Class.skipattr', ' .. py:method:: Class.skipmeth()', ' .. py:attribute:: Class.udocattr', @@ -1426,8 +1415,24 @@ def test_partialfunction(): assert call_autodoc('module', 'target.partialfunction') == expected -@pytest.mark.skipif(sys.version_info < (3, 4), - reason='functools.partialmethod is available on py34 or above') +@pytest.mark.usefixtures('setup_test') +def test_coroutine(): + options = {"members": None} + actual = do_autodoc(app, 'class', 'target.coroutine.AsyncClass', options) + assert list(actual) == [ + '', + '.. py:class:: AsyncClass', + ' :module: target.coroutine', + '', + ' ', + ' .. py:method:: AsyncClass.do_coroutine()', + ' :module: target.coroutine', + ' ', + ' A documented coroutine function', + ' ' + ] + + @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_partialmethod(app): expected = [ diff --git a/tests/test_build.py b/tests/test_build.py index 47d76b2a2..3c181dda2 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -33,11 +33,6 @@ def nonascii_srcdir(request, rootdir, sphinx_test_tempdir): # If supported, build in a non-ASCII source dir test_name = u'\u65e5\u672c\u8a9e' basedir = sphinx_test_tempdir / request.node.originalname - # Windows with versions prior to 3.2 (I think) doesn't support unicode on system path - # so we force a non-unicode path in that case - if (sys.platform == "win32" and - not (sys.version_info.major >= 3 and sys.version_info.minor >= 2)): - return basedir / 'all' try: srcdir = basedir / test_name if not srcdir.exists(): @@ -51,7 +46,7 @@ def nonascii_srcdir(request, rootdir, sphinx_test_tempdir): ======================= """)) - master_doc = srcdir / 'contents.txt' + master_doc = srcdir / 'index.txt' master_doc.write_text(master_doc.text() + dedent(u""" .. toctree:: @@ -93,10 +88,10 @@ def test_circular_toctree(app, status, warning): warnings = warning.getvalue() assert ( 'circular toctree references detected, ignoring: ' - 'sub <- contents <- sub') in warnings + 'sub <- index <- sub') in warnings assert ( 'circular toctree references detected, ignoring: ' - 'contents <- sub <- contents') in warnings + 'index <- sub <- index') in warnings @pytest.mark.sphinx(buildername='text', testroot='numbered-circular') @@ -105,10 +100,10 @@ def test_numbered_circular_toctree(app, status, warning): warnings = warning.getvalue() assert ( 'circular toctree references detected, ignoring: ' - 'sub <- contents <- sub') in warnings + 'sub <- index <- sub') in warnings assert ( 'circular toctree references detected, ignoring: ' - 'contents <- sub <- contents') in warnings + 'index <- sub <- index') in warnings @pytest.mark.sphinx(buildername='dummy', testroot='images') diff --git a/tests/test_build_applehelp.py b/tests/test_build_applehelp.py index ed0022ce1..9c5d46941 100644 --- a/tests/test_build_applehelp.py +++ b/tests/test_build_applehelp.py @@ -17,12 +17,6 @@ import pytest from sphinx.testing.path import path -# Use plistlib.load in 3.4 and above -try: - read_plist = plistlib.load -except AttributeError: - read_plist = plistlib.readPlist - def check_structure(outdir): contentsdir = outdir / 'Contents' @@ -30,7 +24,7 @@ def check_structure(outdir): assert (contentsdir / 'Info.plist').isfile() with open(contentsdir / 'Info.plist', 'rb') as f: - plist = read_plist(f) + plist = plistlib.load(f) assert plist assert len(plist) assert plist.get('CFBundleIdentifier', None) == 'org.sphinx-doc.Sphinx.help' diff --git a/tests/test_build_epub.py b/tests/test_build_epub.py index f9872f28c..0d45cd289 100644 --- a/tests/test_build_epub.py +++ b/tests/test_build_epub.py @@ -28,7 +28,7 @@ def runnable(command): return p.returncode == 0 -class EPUBElementTree(object): +class EPUBElementTree: """Test helper for content.opf and toc.ncx""" namespaces = { 'idpf': 'http://www.idpf.org/2007/opf', diff --git a/tests/test_build_html.py b/tests/test_build_html.py index aae53615b..8a908b8e3 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -16,10 +16,9 @@ from itertools import cycle, chain import pytest from html5lib import getTreeBuilder, HTMLParser -from six import PY3 from sphinx.errors import ConfigError -from sphinx.testing.util import remove_unicode_literals, strip_escseq +from sphinx.testing.util import strip_escseq from sphinx.util.inventory import InventoryFile @@ -30,10 +29,10 @@ ENV_WARNINGS = """\ %(root)s/autodoc_fodder.py:docstring of autodoc_fodder.MarkupError:\\d+: \ WARNING: Explicit markup ends without a blank line; unexpected unindent. %(root)s/index.rst:\\d+: WARNING: Encoding 'utf-8-sig' used for reading included \ -file u'%(root)s/wrongenc.inc' seems to be wrong, try giving an :encoding: option +file '%(root)s/wrongenc.inc' seems to be wrong, try giving an :encoding: option %(root)s/index.rst:\\d+: WARNING: image file not readable: foo.png %(root)s/index.rst:\\d+: WARNING: download file not readable: %(root)s/nonexisting.png -%(root)s/index.rst:\\d+: WARNING: invalid single index entry u'' +%(root)s/index.rst:\\d+: WARNING: invalid single index entry '' %(root)s/undecodable.rst:\\d+: WARNING: undecodable source characters, replacing \ with "\\?": b?'here: >>>(\\\\|/)xbb<<<((\\\\|/)r)?' """ @@ -45,10 +44,6 @@ HTML_WARNINGS = ENV_WARNINGS + """\ %(root)s/index.rst:\\d+: WARNING: Could not lex literal_block as "c". Highlighting skipped. """ -if PY3: - ENV_WARNINGS = remove_unicode_literals(ENV_WARNINGS) - HTML_WARNINGS = remove_unicode_literals(HTML_WARNINGS) - etree_cache = {} @@ -248,7 +243,7 @@ def test_html_warnings(app, warning): # footnote reference (".//a[@class='footnote-reference']", r'\[1\]'), # created by reference lookup - (".//a[@href='contents.html#ref1']", ''), + (".//a[@href='index.html#ref1']", ''), # ``seealso`` directive (".//div/p[@class='first admonition-title']", 'See also'), # a ``hlist`` directive @@ -347,7 +342,7 @@ def test_html_warnings(app, warning): (".//a[@class='reference internal'][@href='#cmdoption-git-commit-p']/code/span", '-p'), ], - 'contents.html': [ + 'index.html': [ (".//meta[@name='hc'][@content='hcval']", ''), (".//meta[@name='hc_co'][@content='hcval_co']", ''), (".//td[@class='label']", r'\[Ref1\]'), diff --git a/tests/test_build_html5.py b/tests/test_build_html5.py index e4c51eaea..fc2c7e8eb 100644 --- a/tests/test_build_html5.py +++ b/tests/test_build_html5.py @@ -157,7 +157,7 @@ def cached_etree_parse(): # footnote reference (".//a[@class='footnote-reference brackets']", r'1'), # created by reference lookup - (".//a[@href='contents.html#ref1']", ''), + (".//a[@href='index.html#ref1']", ''), # ``seealso`` directive (".//div/p[@class='admonition-title']", 'See also'), # a ``hlist`` directive @@ -249,7 +249,7 @@ def cached_etree_parse(): (".//a[@class='reference internal'][@href='#cmdoption-git-commit-p']/code/span", '-p'), ], - 'contents.html': [ + 'index.html': [ (".//meta[@name='hc'][@content='hcval']", ''), (".//meta[@name='hc_co'][@content='hcval_co']", ''), (".//dt[@class='label']/span[@class='brackets']", r'Ref1'), diff --git a/tests/test_build_htmlhelp.py b/tests/test_build_htmlhelp.py new file mode 100644 index 000000000..6eb7e3689 --- /dev/null +++ b/tests/test_build_htmlhelp.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +""" + test_build_htmlhelp + ~~~~~~~~~~~~~~~~~~~ + + Test the HTML Help builder and check output against XPath. + + :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import pytest + + +@pytest.mark.sphinx('htmlhelp', testroot='basic') +def test_default_htmlhelp_file_suffix(app, warning): + assert app.builder.out_suffix == '.html' + + +@pytest.mark.sphinx('htmlhelp', testroot='basic', + confoverrides={'htmlhelp_file_suffix': '.htm'}) +def test_htmlhelp_file_suffix(app, warning): + assert app.builder.out_suffix == '.htm' diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py index b2ae24c52..5da8a90ee 100644 --- a/tests/test_build_latex.py +++ b/tests/test_build_latex.py @@ -17,11 +17,10 @@ from shutil import copyfile from subprocess import Popen, PIPE import pytest -from six import PY3 from test_build_html import ENV_WARNINGS from sphinx.errors import SphinxError -from sphinx.testing.util import remove_unicode_literals, strip_escseq +from sphinx.testing.util import strip_escseq from sphinx.util import docutils from sphinx.util.osutil import cd, ensuredir from sphinx.writers.latex import LaTeXTranslator @@ -40,9 +39,6 @@ LATEX_WARNINGS = ENV_WARNINGS + """\ %(root)s/index.rst:\\d+: WARNING: Could not lex literal_block as "c". Highlighting skipped. """ -if PY3: - LATEX_WARNINGS = remove_unicode_literals(LATEX_WARNINGS) - # only run latex if all needed packages are there def kpsetest(*filenames): @@ -112,6 +108,7 @@ def skip_if_stylefiles_notfound(testfunc): def test_build_latex_doc(app, status, warning, engine, docclass): app.config.latex_engine = engine app.config.latex_documents[0] = app.config.latex_documents[0][:4] + (docclass,) + app.builder.init_context() LaTeXTranslator.ignore_missing_images = True app.builder.build_all() diff --git a/tests/test_build_texinfo.py b/tests/test_build_texinfo.py index b1fd8c2a9..a1af11df7 100644 --- a/tests/test_build_texinfo.py +++ b/tests/test_build_texinfo.py @@ -15,10 +15,9 @@ import re from subprocess import Popen, PIPE import pytest -from six import PY3 from test_build_html import ENV_WARNINGS -from sphinx.testing.util import remove_unicode_literals, strip_escseq +from sphinx.testing.util import strip_escseq from sphinx.writers.texinfo import TexinfoTranslator @@ -30,9 +29,6 @@ TEXINFO_WARNINGS = ENV_WARNINGS + """\ \\['application/pdf', 'image/svg\\+xml'\\] \\(svgimg.\\*\\) """ -if PY3: - TEXINFO_WARNINGS = remove_unicode_literals(TEXINFO_WARNINGS) - @pytest.mark.sphinx('texinfo', testroot='warnings', freshenv=True) def test_texinfo_warnings(app, status, warning): diff --git a/tests/test_build_text.py b/tests/test_build_text.py index f89187c85..b9e0e61a1 100644 --- a/tests/test_build_text.py +++ b/tests/test_build_text.py @@ -116,8 +116,8 @@ def test_list_items_in_admonition(app, status, warning): @with_text_app() def test_secnums(app, status, warning): app.builder.build_all() - contents = (app.outdir / 'contents.txt').text(encoding='utf8') - lines = contents.splitlines() + index = (app.outdir / 'index.txt').text(encoding='utf8') + lines = index.splitlines() assert lines[0] == "* 1. Section A" assert lines[1] == "" assert lines[2] == "* 2. Section B" @@ -142,8 +142,8 @@ def test_secnums(app, status, warning): app.config.text_secnumber_suffix = " " app.builder.build_all() - contents = (app.outdir / 'contents.txt').text(encoding='utf8') - lines = contents.splitlines() + index = (app.outdir / 'index.txt').text(encoding='utf8') + lines = index.splitlines() assert lines[0] == "* 1 Section A" assert lines[1] == "" assert lines[2] == "* 2 Section B" @@ -168,8 +168,8 @@ def test_secnums(app, status, warning): app.config.text_add_secnumbers = False app.builder.build_all() - contents = (app.outdir / 'contents.txt').text(encoding='utf8') - lines = contents.splitlines() + index = (app.outdir / 'index.txt').text(encoding='utf8') + lines = index.splitlines() assert lines[0] == "* Section A" assert lines[1] == "" assert lines[2] == "* Section B" diff --git a/tests/test_builder.py b/tests/test_builder.py index d58091e8d..dbc9d3dae 100644 --- a/tests/test_builder.py +++ b/tests/test_builder.py @@ -22,7 +22,7 @@ def test_incremental_reading(app): # before second reading, add, modify and remove source files (app.srcdir / 'new.txt').write_text('New file\n========\n') - app.env.all_docs['contents'] = 0 # mark as modified + app.env.all_docs['index'] = 0 # mark as modified (app.srcdir / 'autodoc.txt').unlink() # second reading @@ -31,7 +31,7 @@ def test_incremental_reading(app): # "includes" and "images" are in there because they contain references # to nonexisting downloadable or image files, which are given another # chance to exist - assert set(updated) == set(['contents', 'new', 'includes', 'images']) + assert set(updated) == set(['index', 'new', 'includes', 'images']) assert 'autodoc' not in app.env.all_docs assert 'autodoc' not in app.env.found_docs diff --git a/tests/test_config.py b/tests/test_config.py index 5dd05550c..f8df52863 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -11,7 +11,6 @@ """ import mock import pytest -from six import PY3 import sphinx from sphinx.config import Config, ENUM, string_classes, check_confval_types @@ -78,7 +77,7 @@ def test_extension_values(): config = Config() # check standard settings - assert config.master_doc == 'contents' + assert config.master_doc == 'index' # can't override it by add_config_value() with pytest.raises(ExtensionError) as excinfo: @@ -136,19 +135,6 @@ def test_errors_warnings(logger, tempdir): assert cfg.project == u'Jägermeister' assert logger.called is False - # test the warning for bytestrings with non-ascii content - # bytestrings with non-ascii content are a syntax error in python3 so we - # skip the test there - if PY3: - return - (tempdir / 'conf.py').write_text( - u'# -*- coding: latin-1\nproject = "fooä"\n', encoding='latin-1') - cfg = Config.read(tempdir, {}, None) - - assert logger.warning.called is False - cfg.check_unicode() - assert logger.warning.called is True - def test_errors_if_setup_is_not_callable(tempdir, make_app): # test the error to call setup() in the config file @@ -218,7 +204,7 @@ def test_builtin_conf(app, status, warning): # example classes for type checking -class A(object): +class A: pass @@ -242,7 +228,7 @@ TYPECHECK_WARNINGS = [ ('value8', B(), None, C(), False), # sibling type ('value9', None, None, 'foo', False), # no default or no annotations ('value10', None, None, 123, False), # no default or no annotations - ('value11', None, [str], u'bar', False if PY3 else True), # str vs unicode + ('value11', None, [str], u'bar', False), # str vs unicode ('value12', 'string', None, u'bar', False), # str vs unicode ('value13', None, string_classes, 'bar', False), # string_classes ('value14', None, string_classes, u'bar', False), # string_classes @@ -261,6 +247,27 @@ def test_check_types(logger, name, default, annotation, actual, warned): assert logger.warning.called == warned +TYPECHECK_WARNING_MESSAGES = [ + ('value1', 'string', [str], ['foo', 'bar'], + "The config value `value1' has type `list'; expected `str'."), + ('value1', 'string', [str, int], ['foo', 'bar'], + "The config value `value1' has type `list'; expected `str' or `int'."), + ('value1', 'string', [str, int, tuple], ['foo', 'bar'], + "The config value `value1' has type `list'; expected `str', `int', or `tuple'."), +] + + +@mock.patch("sphinx.config.logger") +@pytest.mark.parametrize("name,default,annotation,actual,message", TYPECHECK_WARNING_MESSAGES) +def test_conf_warning_message(logger, name, default, annotation, actual, message): + config = Config({name: actual}) + config.add(name, default, False, annotation or ()) + config.init_values() + check_confval_types(None, config) + logger.warning.assert_called() + assert logger.warning.call_args[0][0] == message + + @mock.patch("sphinx.config.logger") def test_check_enum(logger): config = Config() diff --git a/tests/test_correct_year.py b/tests/test_correct_year.py index e7501bb6a..8995cef5b 100644 --- a/tests/test_correct_year.py +++ b/tests/test_correct_year.py @@ -33,5 +33,5 @@ def expect_date(request, monkeypatch): @pytest.mark.sphinx('html', testroot='correct-year') def test_correct_year(expect_date, app): app.build() - content = (app.outdir / 'contents.html').text() + content = (app.outdir / 'index.html').text() assert expect_date in content diff --git a/tests/test_docutilsconf.py b/tests/test_docutilsconf.py index 989edc6a8..65e59340f 100644 --- a/tests/test_docutilsconf.py +++ b/tests/test_docutilsconf.py @@ -10,7 +10,6 @@ """ import re -import sys import pytest @@ -27,7 +26,7 @@ def test_html_with_default_docutilsconf(app, status, warning): with patch_docutils(app.confdir): app.builder.build(['contents']) - result = (app.outdir / 'contents.html').text(encoding='utf-8') + result = (app.outdir / 'index.html').text(encoding='utf-8') assert regex_count(r'<th class="field-name">', result) == 1 assert regex_count(r'<th class="field-name" colspan="2">', result) == 1 @@ -45,7 +44,7 @@ def test_html_with_docutilsconf(app, status, warning): with patch_docutils(app.confdir): app.builder.build(['contents']) - result = (app.outdir / 'contents.html').text(encoding='utf-8') + result = (app.outdir / 'index.html').text(encoding='utf-8') assert regex_count(r'<th class="field-name">', result) == 0 assert regex_count(r'<th class="field-name" colspan="2">', result) == 2 @@ -82,9 +81,6 @@ def test_texinfo(app, status, warning): @pytest.mark.sphinx('html', testroot='docutilsconf', docutilsconf='[general]\nsource_link=true\n') -@pytest.mark.skip(sys.platform == "win32" and - not (sys.version_info.major >= 3 and sys.version_info.minor >= 2), - reason="Python < 3.2 on Win32 doesn't handle non-ASCII paths right") def test_docutils_source_link_with_nonascii_file(app, status, warning): srcdir = path(app.srcdir) mb_name = u'\u65e5\u672c\u8a9e' diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py index b8856824b..0d47f69b2 100644 --- a/tests/test_domain_cpp.py +++ b/tests/test_domain_cpp.py @@ -22,7 +22,7 @@ from sphinx.domains.cpp import Symbol, _max_id, _id_prefix def parse(name, string): - class Config(object): + class Config: cpp_id_attributes = ["id_attr"] cpp_paren_attributes = ["paren_attr"] parser = DefinitionParser(string, None, Config()) @@ -86,7 +86,7 @@ def check(name, input, idDict, output=None): def test_fundamental_types(): - # see http://en.cppreference.com/w/cpp/language/types + # see https://en.cppreference.com/w/cpp/language/types for t, id_v2 in cppDomain._id_fundamental_v2.items(): def makeIdV1(): if t == 'decltype(auto)': @@ -804,7 +804,7 @@ not found in `{test}` assert result, expect return set(result.group('classes').split()) - class RoleClasses(object): + class RoleClasses: """Collect the classes from the layout that was generated for a given role.""" def __init__(self, role, root, contents): diff --git a/tests/test_ext_autodoc.py b/tests/test_ext_autodoc.py index 44470771d..50716f8e6 100644 --- a/tests/test_ext_autodoc.py +++ b/tests/test_ext_autodoc.py @@ -20,7 +20,7 @@ from sphinx import addnodes def test_autodoc(app, status, warning): app.builder.build_all() - content = pickle.loads((app.doctreedir / 'contents.doctree').bytes()) + content = pickle.loads((app.doctreedir / 'index.doctree').bytes()) assert isinstance(content[3], addnodes.desc) assert content[3][0].astext() == 'autodoc_dummy_module.test' assert content[3][1].astext() == 'Dummy function using dummy.*' diff --git a/tests/test_ext_autosummary.py b/tests/test_ext_autosummary.py index 9b98a59d1..e75fb71fd 100644 --- a/tests/test_ext_autosummary.py +++ b/tests/test_ext_autosummary.py @@ -10,7 +10,7 @@ """ import pytest -from six import iteritems, StringIO +from six import StringIO from sphinx.ext.autosummary import mangle_signature, import_by_name, extract_summary from sphinx.testing.util import etree_parse @@ -140,7 +140,7 @@ def test_get_items_summary(make_app, app_params): 'C.prop_attr2': 'This is a attribute docstring', 'C.C2': 'This is a nested inner class docstring', } - for key, expected in iteritems(expected_values): + for key, expected in expected_values.items(): assert autosummary_items[key][2] == expected, 'Summary for %s was %r -'\ ' expected %r' % (key, autosummary_items[key], expected) diff --git a/tests/test_ext_doctest.py b/tests/test_ext_doctest.py index f7b6ca5f8..13e0066f6 100644 --- a/tests/test_ext_doctest.py +++ b/tests/test_ext_doctest.py @@ -14,7 +14,6 @@ from collections import Counter import pytest from packaging.specifiers import InvalidSpecifier from packaging.version import InvalidVersion -from six import PY2 from sphinx.ext.doctest import is_allowed_version @@ -112,9 +111,6 @@ def record(directive, part, should_skip): return 'Recorded {} {} {}'.format(directive, part, should_skip) -@pytest.mark.xfail( - PY2, reason='node.source points to document instead of filename', -) @pytest.mark.sphinx('doctest', testroot='ext-doctest-with-autodoc') def test_reporting_with_autodoc(app, status, warning, capfd): # Patch builder to get a copy of the output diff --git a/tests/test_ext_napoleon.py b/tests/test_ext_napoleon.py index 31130ad54..fd7eb0b9f 100644 --- a/tests/test_ext_napoleon.py +++ b/tests/test_ext_napoleon.py @@ -37,7 +37,7 @@ def __special_undoc__(): pass -class SampleClass(object): +class SampleClass: def _private_doc(self): """SampleClass._private_doc.DOCSTRING""" pass diff --git a/tests/test_ext_napoleon_docstring.py b/tests/test_ext_napoleon_docstring.py index a4d127d0d..2bc7f626c 100644 --- a/tests/test_ext_napoleon_docstring.py +++ b/tests/test_ext_napoleon_docstring.py @@ -77,6 +77,34 @@ Sample namedtuple subclass self.assertEqual(expected, actual) +class InlineAttributeTest(BaseDocstringTest): + + def test_class_data_member(self): + config = Config() + docstring = """data member description: + +- a: b +""" + actual = str(GoogleDocstring(docstring, config=config, app=None, + what='attribute', name='some_data', obj=0)) + expected = """data member description: + +- a: b""" + + self.assertEqual(expected, actual) + + def test_class_data_member_inline(self): + config = Config() + docstring = """b: data member description with :ref:`reference`""" + actual = str(GoogleDocstring(docstring, config=config, app=None, + what='attribute', name='some_data', obj=0)) + expected = """data member description with :ref:`reference` + +:type: b""" + + self.assertEqual(expected, actual) + + class GoogleDocstringTest(BaseDocstringTest): docstrings = [( """Single line summary""", @@ -452,8 +480,8 @@ Raises: """, """ Example Function -:raises: * :exc:`RuntimeError` -- A setting wasn't specified, or was invalid. - * :exc:`ValueError` -- Something something value error. +:raises RuntimeError: A setting wasn't specified, or was invalid. +:raises ValueError: Something something value error. """), ################################ (""" @@ -465,7 +493,7 @@ Raises: """, """ Example Function -:raises: :exc:`InvalidDimensionsError` +:raises InvalidDimensionsError: """), ################################ (""" @@ -477,7 +505,7 @@ Raises: """, """ Example Function -:raises: Invalid Dimensions Error +:raises Invalid Dimensions Error: """), ################################ (""" @@ -489,7 +517,7 @@ Raises: """, """ Example Function -:raises: *Invalid Dimensions Error* -- With description +:raises Invalid Dimensions Error: With description """), ################################ (""" @@ -501,7 +529,7 @@ Raises: """, """ Example Function -:raises: :exc:`InvalidDimensionsError` -- If the dimensions couldn't be parsed. +:raises InvalidDimensionsError: If the dimensions couldn't be parsed. """), ################################ (""" @@ -513,7 +541,7 @@ Raises: """, """ Example Function -:raises: *Invalid Dimensions Error* -- If the dimensions couldn't be parsed. +:raises Invalid Dimensions Error: If the dimensions couldn't be parsed. """), ################################ (""" @@ -525,7 +553,7 @@ Raises: """, """ Example Function -:raises: If the dimensions couldn't be parsed. +:raises If the dimensions couldn't be parsed.: """), ################################ (""" @@ -537,7 +565,7 @@ Raises: """, """ Example Function -:raises: :class:`exc.InvalidDimensionsError` +:raises exc.InvalidDimensionsError: """), ################################ (""" @@ -549,8 +577,7 @@ Raises: """, """ Example Function -:raises: :class:`exc.InvalidDimensionsError` -- If the dimensions couldn't """ - """be parsed. +:raises exc.InvalidDimensionsError: If the dimensions couldn't be parsed. """), ################################ (""" @@ -563,9 +590,8 @@ Raises: """, """ Example Function -:raises: :class:`exc.InvalidDimensionsError` -- If the dimensions couldn't """ - """be parsed, - then a :class:`exc.InvalidDimensionsError` will be raised. +:raises exc.InvalidDimensionsError: If the dimensions couldn't be parsed, + then a :class:`exc.InvalidDimensionsError` will be raised. """), ################################ (""" @@ -578,9 +604,8 @@ Raises: """, """ Example Function -:raises: * :class:`exc.InvalidDimensionsError` -- If the dimensions """ - """couldn't be parsed. - * :class:`exc.InvalidArgumentsError` -- If the arguments are invalid. +:raises exc.InvalidDimensionsError: If the dimensions couldn't be parsed. +:raises exc.InvalidArgumentsError: If the arguments are invalid. """), ################################ (""" @@ -593,8 +618,8 @@ Raises: """, """ Example Function -:raises: * :class:`exc.InvalidDimensionsError` - * :class:`exc.InvalidArgumentsError` +:raises exc.InvalidDimensionsError: +:raises exc.InvalidArgumentsError: """)] for docstring, expected in docstrings: actual = str(GoogleDocstring(docstring)) @@ -1346,8 +1371,8 @@ Raises """, """ Example Function -:raises: * :exc:`RuntimeError` -- A setting wasn't specified, or was invalid. - * :exc:`ValueError` -- Something something value error. +:raises RuntimeError: A setting wasn't specified, or was invalid. +:raises ValueError: Something something value error. """), ################################ (""" @@ -1360,7 +1385,7 @@ InvalidDimensionsError """, """ Example Function -:raises: :exc:`InvalidDimensionsError` +:raises InvalidDimensionsError: """), ################################ (""" @@ -1373,7 +1398,7 @@ Invalid Dimensions Error """, """ Example Function -:raises: Invalid Dimensions Error +:raises Invalid Dimensions Error: """), ################################ (""" @@ -1387,7 +1412,7 @@ Invalid Dimensions Error """, """ Example Function -:raises: *Invalid Dimensions Error* -- With description +:raises Invalid Dimensions Error: With description """), ################################ (""" @@ -1401,7 +1426,7 @@ InvalidDimensionsError """, """ Example Function -:raises: :exc:`InvalidDimensionsError` -- If the dimensions couldn't be parsed. +:raises InvalidDimensionsError: If the dimensions couldn't be parsed. """), ################################ (""" @@ -1415,7 +1440,7 @@ Invalid Dimensions Error """, """ Example Function -:raises: *Invalid Dimensions Error* -- If the dimensions couldn't be parsed. +:raises Invalid Dimensions Error: If the dimensions couldn't be parsed. """), ################################ (""" @@ -1428,7 +1453,7 @@ If the dimensions couldn't be parsed. """, """ Example Function -:raises: If the dimensions couldn't be parsed. +:raises If the dimensions couldn't be parsed.: """), ################################ (""" @@ -1441,7 +1466,7 @@ Raises """, """ Example Function -:raises: :class:`exc.InvalidDimensionsError` +:raises exc.InvalidDimensionsError: """), ################################ (""" @@ -1455,8 +1480,7 @@ Raises """, """ Example Function -:raises: :class:`exc.InvalidDimensionsError` -- If the dimensions couldn't """ - """be parsed. +:raises exc.InvalidDimensionsError: If the dimensions couldn't be parsed. """), ################################ (""" @@ -1471,9 +1495,8 @@ Raises """, """ Example Function -:raises: :class:`exc.InvalidDimensionsError` -- If the dimensions couldn't """ - """be parsed, - then a :class:`exc.InvalidDimensionsError` will be raised. +:raises exc.InvalidDimensionsError: If the dimensions couldn't be parsed, + then a :class:`exc.InvalidDimensionsError` will be raised. """), ################################ (""" @@ -1489,10 +1512,8 @@ Raises """, """ Example Function -:raises: * :class:`exc.InvalidDimensionsError` -- If the dimensions """ - """couldn't be parsed. - * :class:`exc.InvalidArgumentsError` -- If the arguments """ - """are invalid. +:raises exc.InvalidDimensionsError: If the dimensions couldn't be parsed. +:raises exc.InvalidArgumentsError: If the arguments are invalid. """), ################################ (""" @@ -1506,8 +1527,8 @@ Raises """, """ Example Function -:raises: * :class:`exc.InvalidDimensionsError` - * :class:`exc.InvalidArgumentsError` +:raises exc.InvalidDimensionsError: +:raises exc.InvalidArgumentsError: """)] for docstring, expected in docstrings: config = Config() diff --git a/tests/test_intl.py b/tests/test_intl.py index c8f90e41c..8a83b711a 100644 --- a/tests/test_intl.py +++ b/tests/test_intl.py @@ -18,7 +18,6 @@ import re import pytest from babel.messages import pofile, mofile from docutils import nodes -from six import string_types from sphinx.testing.util import ( path, etree_parse, strip_escseq, @@ -78,20 +77,7 @@ def _info(app): def elem_gettexts(elem): - def itertext(self): - # this function copied from Python-2.7 'ElementTree.itertext'. - # for compatibility to Python-2.6 - tag = self.tag - if not isinstance(tag, string_types) and tag is not None: - return - if self.text: - yield self.text - for e in self: - for s in itertext(e): - yield s - if e.tail: - yield e.tail - return [_f for _f in [s.strip() for s in itertext(elem)] if _f] + return [_f for _f in [s.strip() for s in elem.itertext()] if _f] def elem_getref(elem): @@ -120,7 +106,7 @@ def assert_count(expected_expr, result, count): @pytest.mark.test_params(shared_result='test_intl_basic') def test_text_toctree(app): app.build() - result = (app.outdir / 'contents.txt').text(encoding='utf-8') + result = (app.outdir / 'index.txt').text(encoding='utf-8') assert_startswith(result, u"CONTENTS\n********\n\nTABLE OF CONTENTS\n") @@ -169,7 +155,7 @@ def test_text_title_underline(app): def test_text_subdirs(app): app.build() # --- check translation in subdirs - result = (app.outdir / 'subdir' / 'contents.txt').text(encoding='utf-8') + result = (app.outdir / 'subdir' / 'index.txt').text(encoding='utf-8') assert_startswith(result, u"1. subdir contents\n******************\n") @@ -462,8 +448,8 @@ def test_text_admonitions(app): def test_gettext_toctree(app): app.build() # --- toctree - expect = read_po(app.srcdir / 'contents.po') - actual = read_po(app.outdir / 'contents.pot') + expect = read_po(app.srcdir / 'index.po') + actual = read_po(app.outdir / 'index.pot') for expect_msg in [m for m in expect if m.id]: assert expect_msg.id in [m.id for m in actual if m.id] @@ -629,7 +615,7 @@ def test_gettext_dont_rebuild_mo(make_app, app_params, build_mo): def test_html_meta(app): app.build() # --- test for meta - result = (app.outdir / 'contents.html').text(encoding='utf-8') + result = (app.outdir / 'index.html').text(encoding='utf-8') expected_expr = '<meta content="TESTDATA FOR I18N" name="description" />' assert expected_expr in result expected_expr = '<meta content="I18N, SPHINX, MARKUP" name="keywords" />' @@ -758,7 +744,7 @@ def test_html_docfields(app): def test_html_template(app): app.build() # --- gettext template - result = (app.outdir / 'index.html').text(encoding='utf-8') + result = (app.outdir / 'contents.html').text(encoding='utf-8') assert "WELCOME" in result assert "SPHINX 2013.120" in result @@ -941,7 +927,7 @@ def test_xml_role_xref(app): para1, ['LINK TO', "I18N ROCK'N ROLE XREF", ',', 'CONTENTS', ',', 'SOME NEW TERM', '.'], - ['i18n-role-xref', 'contents', + ['i18n-role-xref', 'index', 'glossary_terms#term-some-term']) para2 = sec2.findall('paragraph') @@ -958,7 +944,7 @@ def test_xml_role_xref(app): assert_elem( para2[2], ['LINK TO', 'I18N WITH GLOSSARY TERMS', 'AND', 'CONTENTS', '.'], - ['glossary_terms', 'contents']) + ['glossary_terms', 'index']) assert_elem( para2[3], ['LINK TO', '--module', 'AND', '-m', '.'], diff --git a/tests/test_markup.py b/tests/test_markup.py index 802202639..0f401306b 100644 --- a/tests/test_markup.py +++ b/tests/test_markup.py @@ -18,6 +18,7 @@ from docutils.parsers.rst import Parser as RstParser from docutils.transforms.universal import SmartQuotes from sphinx import addnodes +from sphinx.builders.latex import LaTeXBuilder from sphinx.testing.util import assert_node from sphinx.util import texescape from sphinx.util.docutils import sphinx_domains @@ -87,6 +88,9 @@ def verify_re_html(app, parse): def verify_re_latex(app, parse): def verify(rst, latex_expected): document = parse(rst) + app.builder = LaTeXBuilder(app) + app.builder.set_environment(app.env) + app.builder.init_context() latex_translator = ForgivingLaTeXTranslator(document, app.builder) latex_translator.first_document = -1 # don't write \begin{document} document.walkabout(latex_translator) diff --git a/tests/test_pycode.py b/tests/test_pycode.py index 2eab456bc..f35a57c59 100644 --- a/tests/test_pycode.py +++ b/tests/test_pycode.py @@ -12,8 +12,6 @@ import os import sys -from six import PY2 - import sphinx from sphinx.pycode import ModuleAnalyzer @@ -24,20 +22,14 @@ def test_ModuleAnalyzer_for_string(): analyzer = ModuleAnalyzer.for_string('print("Hello world")', 'module_name') assert analyzer.modname == 'module_name' assert analyzer.srcname == '<string>' - if PY2: - assert analyzer.encoding == 'ascii' - else: - assert analyzer.encoding is None + assert analyzer.encoding is None def test_ModuleAnalyzer_for_file(): analyzer = ModuleAnalyzer.for_string(SPHINX_MODULE_PATH, 'sphinx') assert analyzer.modname == 'sphinx' assert analyzer.srcname == '<string>' - if PY2: - assert analyzer.encoding == 'ascii' - else: - assert analyzer.encoding is None + assert analyzer.encoding is None def test_ModuleAnalyzer_for_module(): diff --git a/tests/test_pycode_parser.py b/tests/test_pycode_parser.py index 0875329a4..4fbfefcf2 100644 --- a/tests/test_pycode_parser.py +++ b/tests/test_pycode_parser.py @@ -12,7 +12,6 @@ import sys import pytest -from six import PY2 from sphinx.pycode.parser import Parser @@ -135,7 +134,6 @@ def test_complex_assignment(): assert parser.definitions == {} -@pytest.mark.skipif(PY2, reason='tests for py3 syntax') def test_complex_assignment_py3(): source = ('a, *b, c = (1, 2, 3, 4) #: unpack assignment\n' 'd, *self.attr = (5, 6, 7) #: unpack assignment2\n' diff --git a/tests/test_quickstart.py b/tests/test_quickstart.py index 64c38457d..3140f641e 100644 --- a/tests/test_quickstart.py +++ b/tests/test_quickstart.py @@ -13,8 +13,7 @@ import sys import time import pytest -from six import PY2, text_type, StringIO -from six.moves import input +from six import text_type, StringIO from sphinx import application from sphinx.cmd import quickstart as qs @@ -37,12 +36,7 @@ def mock_input(answers, needanswer=False): raise AssertionError('answer for %r missing and no default ' 'present' % prompt) called.add(prompt) - if PY2: - prompt = str(prompt) # Python2.x raw_input emulation - # `raw_input` encode `prompt` by default encoding to print. - else: - prompt = text_type(prompt) # Python3.x input emulation - # `input` decode prompt by default encoding before print. + prompt = text_type(prompt) for question in answers: if prompt.startswith(qs.PROMPT_PREFIX + question): return answers[question] diff --git a/tests/test_search.py b/tests/test_search.py index 886151831..7b5f73445 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -9,12 +9,12 @@ :license: BSD, see LICENSE for details. """ +from io import BytesIO from collections import namedtuple import pytest from docutils import frontend, utils from docutils.parsers import rst -from six import BytesIO from sphinx.search import IndexBuilder from sphinx.util import jsdump @@ -22,7 +22,7 @@ from sphinx.util import jsdump DummyEnvironment = namedtuple('DummyEnvironment', ['version', 'domains']) -class DummyDomain(object): +class DummyDomain: def __init__(self, data): self.data = data self.object_types = {} diff --git a/tests/test_setup_command.py b/tests/test_setup_command.py index cd1f89c0c..51cfca205 100644 --- a/tests/test_setup_command.py +++ b/tests/test_setup_command.py @@ -94,7 +94,7 @@ def nonascii_srcdir(request, setup_command): ========================== """)) - master_doc = srcdir / 'contents.txt' + master_doc = srcdir / 'index.txt' master_doc.write_bytes((master_doc.text() + dedent(""" .. toctree:: diff --git a/tests/test_templating.py b/tests/test_templating.py index 8eed1fdf8..3ae560c9e 100644 --- a/tests/test_templating.py +++ b/tests/test_templating.py @@ -21,7 +21,7 @@ def test_layout_overloading(make_app, app_params): setup_documenters(app) app.builder.build_update() - result = (app.outdir / 'contents.html').text(encoding='utf-8') + result = (app.outdir / 'index.html').text(encoding='utf-8') assert '<!-- layout overloading -->' in result diff --git a/tests/test_util_images.py b/tests/test_util_images.py index 624690831..5a98fa003 100644 --- a/tests/test_util_images.py +++ b/tests/test_util_images.py @@ -19,7 +19,7 @@ from sphinx.util.images import ( GIF_FILENAME = 'img.gif' PNG_FILENAME = 'img.png' PDF_FILENAME = 'img.pdf' -TXT_FILENAME = 'contents.txt' +TXT_FILENAME = 'index.txt' @pytest.fixture(scope='module') diff --git a/tests/test_util_inspect.py b/tests/test_util_inspect.py index 99033fb6e..bbaf4e97b 100644 --- a/tests/test_util_inspect.py +++ b/tests/test_util_inspect.py @@ -13,7 +13,6 @@ import sys from textwrap import dedent import pytest -from six import PY3 from sphinx.util import inspect @@ -25,15 +24,11 @@ def test_getargspec(): spec = inspect.getargspec(func) assert spec.args == ['a', 'b', 'c', 'd'] assert spec.varargs == 'e' - if PY3: - assert spec.varkw == 'f' - assert spec.defaults == (1, 2) - assert spec.kwonlyargs == [] - assert spec.kwonlydefaults is None - assert spec.annotations == {} - else: - assert spec.keywords == 'f' - assert spec.defaults == [1, 2] + assert spec.varkw == 'f' + assert spec.defaults == (1, 2) + assert spec.kwonlyargs == [] + assert spec.kwonlydefaults is None + assert spec.annotations == {} def test_getargspec_partial(): @@ -42,19 +37,13 @@ def test_getargspec_partial(): partial = functools.partial(func1, 10, c=11) spec = inspect.getargspec(partial) - if PY3: - assert spec.args == ['b'] - assert spec.varargs is None - assert spec.varkw == 'f' - assert spec.defaults is None - assert spec.kwonlyargs == ['c', 'd'] - assert spec.kwonlydefaults == {'c': 11, 'd': 2} - assert spec.annotations == {} - else: - assert spec.args == ['b', 'd'] - assert spec.varargs == 'e' - assert spec.keywords == 'f' - assert spec.defaults == [2] + assert spec.args == ['b'] + assert spec.varargs is None + assert spec.varkw == 'f' + assert spec.defaults is None + assert spec.kwonlyargs == ['c', 'd'] + assert spec.kwonlydefaults == {'c': 11, 'd': 2} + assert spec.annotations == {} def test_getargspec_partial2(): @@ -62,19 +51,8 @@ def test_getargspec_partial2(): pass p = functools.partial(fun, 10, c=11) - if PY3: - # Python 3's partial is rather cleverer than Python 2's, and we - # have to jump through some hoops to define an equivalent function - # in a way that won't confuse Python 2's parser: - ns = {} - exec(dedent(""" - def f_expected(b, *, c=11, d=2): - pass - """), ns) - f_expected = ns["f_expected"] - else: - def f_expected(b, d=2): - pass + def f_expected(b, *, c=11, d=2): + pass expected = inspect.getargspec(f_expected) assert expected == inspect.getargspec(p) @@ -105,13 +83,8 @@ def test_getargspec_bound_methods(): pass assert expected_unbound == inspect.getargspec(Foo.method) - if PY3 and sys.version_info >= (3, 4, 4): - # On py2, the inspect functions don't properly handle bound - # methods (they include a spurious 'self' argument) - assert expected_bound == inspect.getargspec(bound_method) - # On py2, the inspect functions can't properly handle wrapped - # functions (no __wrapped__ support) - assert expected_bound == inspect.getargspec(wrapped_bound_method) + assert expected_bound == inspect.getargspec(bound_method) + assert expected_bound == inspect.getargspec(wrapped_bound_method) def test_Signature(): @@ -143,10 +116,7 @@ def test_Signature_partial(): p = functools.partial(fun, 10, c=11) sig = inspect.Signature(p).format_args() - if sys.version_info < (3,): - assert sig == '(b, d=2)' - else: - assert sig == '(b, *, c=11, d=2)' + assert sig == '(b, *, c=11, d=2)' def test_Signature_methods(): @@ -193,20 +163,13 @@ def test_Signature_methods(): # wrapped bound method sig = inspect.Signature(wrapped_bound_method).format_args() - if sys.version_info < (3,): - assert sig == '(*args, **kwargs)' - elif sys.version_info < (3, 4, 4): - assert sig == '(self, arg1, **kwargs)' - else: - assert sig == '(arg1, **kwargs)' + assert sig == '(arg1, **kwargs)' -@pytest.mark.skipif(sys.version_info < (3, 4), - reason='functools.partialmethod is available on py34 or above') def test_Signature_partialmethod(): from functools import partialmethod - class Foo(object): + class Foo: def meth1(self, arg1, arg2, arg3=None, arg4=None): pass @@ -228,11 +191,9 @@ def test_Signature_partialmethod(): assert sig == '()' -@pytest.mark.skipif(sys.version_info < (3, 4), - reason='type annotation test is available on py34 or above') def test_Signature_annotations(): - from typing_test_data import ( - f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, Node) + from typing_test_data import (f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, + f11, f12, f13, f14, f15, f16, Node) # Class annotations sig = inspect.Signature(f0).format_args() @@ -297,6 +258,14 @@ def test_Signature_annotations(): sig = inspect.Signature(f14).format_args() assert sig == '() -> Any' + # keyword only arguments (1) + sig = inspect.Signature(f15).format_args() + assert sig == '(arg1, arg2, *, arg3=None, arg4=None)' + + # keyword only arguments (2) + sig = inspect.Signature(f16).format_args() + assert sig == '(*, arg3, arg4)' + # type hints by string sig = inspect.Signature(Node.children).format_args() if (3, 5, 0) <= sys.version_info < (3, 5, 3): @@ -309,7 +278,7 @@ def test_Signature_annotations(): def test_safe_getattr_with_default(): - class Foo(object): + class Foo: def __getattr__(self, item): raise Exception @@ -321,7 +290,7 @@ def test_safe_getattr_with_default(): def test_safe_getattr_with_exception(): - class Foo(object): + class Foo: def __getattr__(self, item): raise Exception @@ -336,7 +305,7 @@ def test_safe_getattr_with_exception(): def test_safe_getattr_with_property_exception(): - class Foo(object): + class Foo: @property def bar(self): raise Exception @@ -352,7 +321,7 @@ def test_safe_getattr_with_property_exception(): def test_safe_getattr_with___dict___override(): - class Foo(object): + class Foo: @property def __dict__(self): raise Exception @@ -376,23 +345,29 @@ def test_dictionary_sorting(): def test_set_sorting(): set_ = set("gfedcba") description = inspect.object_description(set_) - if PY3: - assert description == "{'a', 'b', 'c', 'd', 'e', 'f', 'g'}" - else: - assert description == "set(['a', 'b', 'c', 'd', 'e', 'f', 'g'])" + assert description == "{'a', 'b', 'c', 'd', 'e', 'f', 'g'}" def test_set_sorting_fallback(): set_ = set((None, 1)) description = inspect.object_description(set_) - if PY3: - assert description in ("{1, None}", "{None, 1}") - else: - assert description in ("set([1, None])", "set([None, 1])") + assert description in ("{1, None}", "{None, 1}") + + +def test_frozenset_sorting(): + frozenset_ = frozenset("gfedcba") + description = inspect.object_description(frozenset_) + assert description == "frozenset({'a', 'b', 'c', 'd', 'e', 'f', 'g'})" + + +def test_frozenset_sorting_fallback(): + frozenset_ = frozenset((None, 1)) + description = inspect.object_description(frozenset_) + assert description in ("frozenset({1, None})", "frozenset({None, 1})") def test_dict_customtype(): - class CustomType(object): + class CustomType: def __init__(self, value): self._value = value @@ -419,10 +394,5 @@ def test_isstaticmethod(): assert inspect.isstaticmethod(Foo.method1, Foo, 'method1') is True assert inspect.isstaticmethod(Foo.method2, Foo, 'method2') is False - - if sys.version_info < (3, 0): - assert inspect.isstaticmethod(Bar.method1, Bar, 'method1') is False - assert inspect.isstaticmethod(Bar.method2, Bar, 'method2') is False - else: - assert inspect.isstaticmethod(Bar.method1, Bar, 'method1') is True - assert inspect.isstaticmethod(Bar.method2, Bar, 'method2') is False + assert inspect.isstaticmethod(Bar.method1, Bar, 'method1') is True + assert inspect.isstaticmethod(Bar.method2, Bar, 'method2') is False diff --git a/tests/test_util_inventory.py b/tests/test_util_inventory.py index af3a819cd..08a45d247 100644 --- a/tests/test_util_inventory.py +++ b/tests/test_util_inventory.py @@ -11,8 +11,7 @@ import posixpath import zlib - -from six import BytesIO +from io import BytesIO from sphinx.ext.intersphinx import InventoryFile diff --git a/tests/test_util_nodes.py b/tests/test_util_nodes.py index d20b4b892..2fab10c1c 100644 --- a/tests/test_util_nodes.py +++ b/tests/test_util_nodes.py @@ -9,6 +9,7 @@ :license: BSD, see LICENSE for details. """ from textwrap import dedent +from typing import Any import pytest from docutils import frontend @@ -17,7 +18,7 @@ from docutils.parsers import rst from docutils.utils import new_document from sphinx.transforms import ApplySourceWorkaround -from sphinx.util.nodes import extract_messages, clean_astext +from sphinx.util.nodes import NodeMatcher, extract_messages, clean_astext def _transform(doctree): @@ -50,6 +51,38 @@ def assert_node_count(messages, node_type, expect_count): % (node_type, node_list, count, expect_count)) +def test_NodeMatcher(): + doctree = nodes.document(None, None) + doctree += nodes.paragraph('', 'Hello') + doctree += nodes.paragraph('', 'Sphinx', block=1) + doctree += nodes.paragraph('', 'World', block=2) + doctree += nodes.literal_block('', 'blah blah blah', block=3) + + # search by node class + matcher = NodeMatcher(nodes.paragraph) + assert len(doctree.traverse(matcher)) == 3 + + # search by multiple node classes + matcher = NodeMatcher(nodes.paragraph, nodes.literal_block) + assert len(doctree.traverse(matcher)) == 4 + + # search by node attribute + matcher = NodeMatcher(block=1) + assert len(doctree.traverse(matcher)) == 1 + + # search by node attribute (Any) + matcher = NodeMatcher(block=Any) + assert len(doctree.traverse(matcher)) == 3 + + # search by both class and attribute + matcher = NodeMatcher(nodes.paragraph, block=Any) + assert len(doctree.traverse(matcher)) == 2 + + # mismatched + matcher = NodeMatcher(nodes.title) + assert len(doctree.traverse(matcher)) == 0 + + @pytest.mark.parametrize( 'rst,node_cls,count', [ diff --git a/tests/test_websupport.py b/tests/test_websupport.py deleted file mode 100644 index bf12cbade..000000000 --- a/tests/test_websupport.py +++ /dev/null @@ -1,36 +0,0 @@ -# -*- coding: utf-8 -*- -""" - test_websupport - ~~~~~~~~~~~~~~~ - - Test the Web Support Package - - :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. - :license: BSD, see LICENSE for details. -""" - -import pytest - -from sphinx.websupport import WebSupport -try: - sqlalchemy_missing = False - import sqlalchemy # NOQA -except ImportError: - sqlalchemy_missing = True - - -@pytest.mark.skipif(sqlalchemy_missing, reason='needs sqlalchemy') -def test_build(request, rootdir, sphinx_test_tempdir): - settings = { - 'srcdir': rootdir / 'test-basic', - # to use same directory for 'builddir' in each 'support' fixture, using - # 'sphinx_test_tempdir' (static) value instead of 'tempdir' fixture value. - # each test expect result of db value at previous test case. - 'builddir': sphinx_test_tempdir / 'websupport' - } - marker = request.node.get_marker('support') - if marker: - settings.update(marker.kwargs) - - support = WebSupport(**settings) - support.build() diff --git a/tests/typing_test_data.py b/tests/typing_test_data.py index 4dc2d06f5..183d075b6 100644 --- a/tests/typing_test_data.py +++ b/tests/typing_test_data.py @@ -76,6 +76,13 @@ def f14() -> Any: pass +def f15(arg1, arg2, *, arg3=None, arg4=None): + pass + +def f16(*, arg3, arg4): + pass + + class Node: def __init__(self, parent: Optional['Node']) -> None: pass @@ -1,14 +1,14 @@ [tox] minversion = 2.0 -envlist = docs,flake8,mypy,coverage,py{27,34,35,36,37,38,py},du{11,12,13,14} +envlist = docs,flake8,mypy,coverage,py{35,36,37,38},du{12,13,14} [testenv] usedevelop = True passenv = https_proxy http_proxy no_proxy PERL PERL5LIB PYTEST_ADDOPTS EPUBCHECK_PATH description = - py{27,34,35,36,37,38,py}: Run unit tests against {envname}. - du{11,12,13,14}: Run unit tests with the given version of docutils. + py{35,36,37,38}: Run unit tests against {envname}. + du{12,13,14}: Run unit tests with the given version of docutils. # TODO(stephenfin) Replace this with the 'extras' config option when tox 2.4 is # widely available, likely some time after the Ubuntu 18.04 release @@ -16,7 +16,6 @@ description = # https://tox.readthedocs.io/en/latest/config.html#confval-extras=MULTI-LINE-LIST deps = .[test,websupport] - du11: docutils==0.11 du12: docutils==0.12 du13: docutils==0.13.1 du14: docutils==0.14 @@ -66,5 +65,7 @@ commands= basepython = python3 description = Build documentation. +deps = + sphinxcontrib-websupport commands = python setup.py build_sphinx {posargs} diff --git a/utils/bump_version.py b/utils/bump_version.py index 444eefe07..55da12551 100755 --- a/utils/bump_version.py +++ b/utils/bump_version.py @@ -84,7 +84,7 @@ def processing(message): print('done') -class Changes(object): +class Changes: def __init__(self, path): self.path = path self.fetch_version() |