diff --git a/.gitignore b/.gitignore
index f8aad6d40627bc73e2ea8c0f77cc6b61bf4fa219..371fd93295f99ac01448553f2ff5a52472e44b14 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,6 +33,7 @@
 /dbus_python.egg-info/
 /depcomp
 /dist/
+/doc/_build/
 /install-sh
 /libtool
 /ltmain.sh
diff --git a/Makefile.am b/Makefile.am
index d0ed8224aad30998a327fdc5c8e1a063574d72a7..ae5f9de5fd319a0e5bad94241fcd0ac0d5c83dd0 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -416,36 +416,56 @@ _maintainer-update-htmldocs:
 	@echo "*** --enable-html-docs"
 endif
 
-if ENABLE_API_DOCS
-all: api/index.html
-
-clean-local:
-	rm -rf api
-
-APIDOC_PYTHONPATH = $(abs_top_srcdir):$(abs_top_builddir)/.libs
+sphinx_sources = \
+	doc/API_CHANGES.txt \
+	doc/conf.py \
+	doc/dbus.bus.rst \
+	doc/dbus.connection.rst \
+	doc/dbus.decorators.rst \
+	doc/dbus.exceptions.rst \
+	doc/dbus.gi_service.rst \
+	doc/dbus.glib.rst \
+	doc/dbus.gobject_service.rst \
+	doc/dbus.lowlevel.rst \
+	doc/dbus.mainloop.rst \
+	doc/dbus.proxies.rst \
+	doc/dbus.rst \
+	doc/dbus.server.rst \
+	doc/dbus.service.rst \
+	doc/dbus.types.rst \
+	doc/HACKING.txt \
+	doc/index.rst \
+	doc/news.rst \
+	doc/PY3PORT.txt \
+	doc/tutorial.txt \
+	NEWS \
+	README \
+	$(NULL)
+EXTRA_DIST += $(sphinx_sources)
 
-api api/index.html: $(nobase_python_PYTHON) \
-		    _dbus_bindings.la \
-		    _dbus_glib_bindings.la
-	rm -rf api
-	mkdir api
+if ENABLE_API_DOCS
+all: doc/_build/index.html
+
+doc/_build/index.html: $(nobase_python_PYTHON) \
+			_dbus_bindings.la \
+			_dbus_glib_bindings.la \
+			$(sphinx_sources) \
+			$(NULL)
+	rm -rf doc/_build
+	mkdir doc/_build
 	cd $(abs_top_srcdir) && \
-	PYTHONPATH=$(APIDOC_PYTHONPATH) DBUS_PYTHON_NO_DEPRECATED=1 \
-	$(PYTHON) -Wignore::DeprecationWarning \
-	$(EPYDOC) -o $(abs_top_builddir)/api --html \
-		--docformat restructuredtext -v \
-		`find dbus -name '*.py' \
-			| sed -e 's#/__init__\.py##g' \
-				-e 's/\.py\>//g' -e 's#/#.#'g` \
-		|| { rm -rf api; exit 1; }
-
-_maintainer-update-apidocs: api
-	rsync -rtvzPp --chmod=Dg+s,ug+rwX,o=rX api/ \
-	dbus.freedesktop.org:/srv/dbus.freedesktop.org/www/doc/dbus-python/api/
+	abs_top_srcdir='$(abs_top_srcdir)' \
+	abs_top_builddir='$(abs_top_builddir)' \
+	DBUS_PYTHON_NO_DEPRECATED=1 \
+	$(PYTHON) -m sphinx -b html $(abs_top_srcdir)/doc doc/_build
+
+_maintainer-update-apidocs: doc/_build/index.html
+	rsync -rtvzPp --chmod=Dg+s,ug+rwX,o=rX doc/_build/ \
+	dbus.freedesktop.org:/srv/dbus.freedesktop.org/www/doc/dbus-python/
 else
 _maintainer-update-apidocs:
-	@echo "*** Not updating the API docs on the website - install epydoc 3"
-	@echo "*** alpha (or newer) and configure with --enable-api-docs"
+	@echo "*** Not updating the API docs on the website - install sphinx"
+	@echo "*** and configure with --enable-api-docs"
 endif
 
 check_c_sources = \
diff --git a/README b/README
index 7c1c49d1c78dab6b4ac9f66f505ffcb9d2eda35a..f46dbada44fec938f8cb6aa8ccc9bccd1e3bf518 100644
--- a/README
+++ b/README
@@ -4,35 +4,12 @@ dbus-python_: Python bindings for D-Bus
 
 .. _dbus-python: http://www.freedesktop.org/wiki/Software/DBusBindings#python
 
-Usage
-=====
+dbus-python is the original Python binding for ``dbus``, the reference
+implementation of the D-Bus protocol.
 
 Online documentation can be found at
 <http://dbus.freedesktop.org/doc/dbus-python/>.
 
-There is an incomplete tutorial in `doc/tutorial.txt`__
-(if you compile dbus-python with `docutils`_' ``rst2html`` tool installed or
-you're reading this document on the D-Bus website, there's also an
-`HTML version`__).
-
-__ doc/tutorial.txt
-__ doc/tutorial.html
-
-`API documentation`_ is generated if you compile dbus-python with `epydoc`_
-version 3 and `docutils`_ installed. An `online copy of the API documentation`_
-is also available.
-
-.. _API documentation: api/index.html
-.. _epydoc: http://epydoc.sourceforge.net/
-.. _docutils: http://docutils.sourceforge.net/
-.. _online copy of the API documentation:
-   http://dbus.freedesktop.org/doc/dbus-python/api/
-
-To develop on dbus-python, see `doc/HACKING.txt`__ or the `HTML version`__.
-
-__ doc/HACKING.txt
-__ doc/HACKING.html
-
 Problems and alternatives
 =========================
 
diff --git a/configure.ac b/configure.ac
index 6376d159365d656e06d321044430218fea8bd2ae..0912a478d16a2cc53474db7e86ee8876434f4a73 100644
--- a/configure.ac
+++ b/configure.ac
@@ -99,59 +99,22 @@ dnl Building documentation
 
 AX_GENERATE_CHANGELOG
 
-AC_MSG_CHECKING([whether you want to build HTML docs])
-AC_ARG_ENABLE(html-docs,
-AC_HELP_STRING([--enable-html-docs], [Enable HTML documentation building (requires docutils, default: auto-detect)]), enable_html_docs=$enableval, enable_html_docs="if possible")
-AC_MSG_RESULT([$enable_html_docs])
-
-AC_MSG_CHECKING([whether you want to build API docs])
-AC_ARG_ENABLE(api-docs,
-AC_HELP_STRING([--enable-api-docs], [Enable API documentation building (requires epydoc 3 and docutils)]), enable_api_docs=$enableval, enable_api_docs="if possible")
-AC_MSG_RESULT([$enable_api_docs])
-
-
-AS_IF([test "$enable_api_docs" != no || test "$enable_html_docs" != no],
+AC_ARG_ENABLE([documentation],
+  [AC_HELP_STRING([--enable-documentation],
+    [Enable documentation building (requires sphinx and sphinx_rtd_theme)])],
+  [:],
+  [enable_documentation=no])
+AX_PYTHON_MODULE([sphinx])
+AS_IF([test "x$HAVE_PYMOD_SPHINX" = xno],
   [
-  AX_PYTHON_MODULE([docutils])
-  AS_IF([test "$HAVE_PYMOD_DOCUTILS" = no],
-    [
-    AS_IF([test "$enable_api_docs" = "if possible"], [enable_api_docs=no])
-    AS_IF([test "$enable_html_docs" = "if possible"], [enable_html_docs=no])
-    AS_IF([test "$enable_api_docs" != no || test "$enable_html_docs" != no],
-      [AC_MSG_ERROR([cannot compile HTML documentation or API documentation without python-docutils installed])])
-    ])
+  AS_IF([test "$enable_documentation" != no],
+    [AC_MSG_ERROR([cannot build documentation without sphinx Python module])])
   ])
-
-AS_IF([test "${enable_api_docs}" != no],
+AX_PYTHON_MODULE([sphinx_rtd_theme])
+AS_IF([test "x$HAVE_PYMOD_SPHINX_RTD_THEME" = xno],
   [
-  AC_PATH_PROG([EPYDOC], [epydoc])
-  AX_PYTHON_MODULE([epydoc])
-  AC_MSG_CHECKING([epydoc 3])
-  AS_IF([test -n "$EPYDOC" && test "$HAVE_PYMOD_EPYDOC" = yes],
-    [
-    EPYDOC_VERSION=`$EPYDOC --version`
-    AS_CASE(["$EPYDOC_VERSION"],
-      [*ersion?3*],
-        [
-        AC_MSG_RESULT([yes, $EPYDOC_VERSION])
-        ],
-      [*],
-        [
-        AC_MSG_RESULT([no, $EPYDOC_VERSION])
-        EPYDOC=
-        ])
-    ],
-    [
-    EPYDOC=
-    ])
-  AS_IF([test -z "$EPYDOC"],
-    [
-    AS_CASE(["$enable_api_docs"],
-      [if*possible],
-        [enable_api_docs=no],
-      [*],
-        [AC_MSG_ERROR([cannot compile API documentation without epydoc 3.0beta1 or newer installed])])
-    ])
+  AS_IF([test "$enable_documentation" != no],
+    [AC_MSG_ERROR([cannot build documentation without sphinx_rtd_theme Python module])])
   ])
 
 AC_ARG_VAR([DBUS_RUN_SESSION],
diff --git a/dbus/__init__.py b/dbus/__init__.py
index e96bd998c911b5a0442195860ac76a44fafcd505..a2aeac9400d22e22dd66fa7a1d349935fe50d8d7 100644
--- a/dbus/__init__.py
+++ b/dbus/__init__.py
@@ -1,12 +1,6 @@
 """\
 Implements the public API for a D-Bus client. See the dbus.service module
 to export objects or claim well-known names.
-
-..
-  for epydoc's benefit
-
-:NewField SupportedUsage: Supported usage
-:NewField Constructor: Constructor
 """
 
 # Copyright (C) 2003, 2004, 2005, 2006 Red Hat Inc. <http://www.redhat.com/>
diff --git a/dbus/gi_service.py b/dbus/gi_service.py
index c182d0aea75debf96f435d18310bb30b73cc648d..e0917710fa22ea0f6773800813b05daf720f8f33 100644
--- a/dbus/gi_service.py
+++ b/dbus/gi_service.py
@@ -74,9 +74,7 @@ def ExportedGObject__init__(self, conn=None, object_path=None, **kwargs):
                                  bus_name=bus_name)
 
 ExportedGObject__doc__ = '''
-A GObject which is exported on the D-Bus.
-
-:undocumented: __gtype__
+A GObject which is exported on D-Bus.
 '''
 
 ExportedGObject = ExportedGObjectType(
diff --git a/dbus/gobject_service.py b/dbus/gobject_service.py
index 3d096c4726ff44ca55ee647ba3a81269577faa30..ef16009bf92fd85512f1aed391054acd33709d6a 100644
--- a/dbus/gobject_service.py
+++ b/dbus/gobject_service.py
@@ -55,8 +55,6 @@ class ExportedGObject(gobject.GObject, dbus.service.Object):
     the naive approach using simple multiple inheritance won't work. This
     class has `ExportedGObjectType` as its metaclass, which is sufficient
     to make it work correctly.
-
-    :undocumented: __gtype__
     """
     __metaclass__ = ExportedGObjectType
 
diff --git a/dbus/service.py b/dbus/service.py
index 295322955ada4e52d3f636c6c8444fd8ef704461..6093ac5fe3cc0f9d0b7ce5f049be3186e06d22ae 100644
--- a/dbus/service.py
+++ b/dbus/service.py
@@ -75,11 +75,11 @@ class BusName(object):
     If a well-known name is requested multiple times, multiple references
     to the same BusName object will be returned.
 
-    Caveats
-    -------
-    - Assumes that named services are only ever requested using this class -
-      if you request names from the bus directly, confusion may occur.
-    - Does not handle queueing.
+    :Caveats:
+
+        - Assumes that named services are only ever requested using this class -
+          if you request names from the bus directly, confusion may occur.
+        - Does not handle queueing.
     """
     def __new__(cls, name, bus=None, allow_replacement=False , replace_existing=False, do_not_queue=False):
         """Constructor, which may either return an existing cached object
diff --git a/dbus_bindings/bytes.c b/dbus_bindings/bytes.c
index b0ff8179b5f427f5a4822f1bbb7a5c28133ccf32..af8188acd71981c540463b93112ff7cefb18dead 100644
--- a/dbus_bindings/bytes.c
+++ b/dbus_bindings/bytes.c
@@ -49,10 +49,10 @@ PyDoc_STRVAR(Byte_tp_doc,
 "\n"
 "   dbus.Byte(integer or str of length 1[, variant_level])\n"
 "\n"
-"``variant_level`` must be non-negative; the default is 0.\n"
+":py:attr:`variant_level` must be non-negative; the default is 0.\n"
+"\n"
+".. py:attribute:: variant_level\n"
 "\n"
-":IVariables:\n"
-"  `variant_level` : int\n"
 "    Indicates how many nested Variant containers this object\n"
 "    is contained in: if a message's wire format has a variant containing a\n"
 "    variant containing a byte, this is represented in Python by a\n"
diff --git a/dbus_bindings/containers.c b/dbus_bindings/containers.c
index e364f988f24e3d5aa34347c2a1aecd4acb8cf38b..165f6fc4f67e7e4680a27f5031b5ff7313381142 100644
--- a/dbus_bindings/containers.c
+++ b/dbus_bindings/containers.c
@@ -44,7 +44,7 @@ PyDoc_STRVAR(Array_tp_doc,
 "\n"
 "    dbus.Array([iterable][, signature][, variant_level])\n"
 "\n"
-"``variant_level`` must be non-negative; the default is 0.\n"
+":py:attr:`variant_level` must be non-negative; the default is 0.\n"
 "\n"
 "``signature`` is the D-Bus signature string for a single element of the\n"
 "array, or None. If not None it must represent a single complete type, the\n"
@@ -53,13 +53,6 @@ PyDoc_STRVAR(Array_tp_doc,
 "\n"
 "If None (the default), when the Array is sent over\n"
 "D-Bus, the item signature will be guessed from the first element.\n"
-"\n"
-":IVariables:\n"
-"  `variant_level` : int\n"
-"    Indicates how many nested Variant containers this object\n"
-"    is contained in: if a message's wire format has a variant containing a\n"
-"    variant containing an array, this is represented in Python by an\n"
-"    Array with variant_level==2.\n"
 );
 
 static struct PyMemberDef Array_tp_members[] = {
@@ -68,8 +61,11 @@ static struct PyMemberDef Array_tp_members[] = {
      "instance)"},
     {"variant_level", T_LONG, offsetof(DBusPyArray, variant_level),
      READONLY,
-     "The number of nested variants wrapping the real data. "
-     "0 if not in a variant."},
+    "Indicates how many nested Variant containers this object\n"
+    "is contained in: if a message's wire format has a variant containing a\n"
+    "variant containing an array, this is represented in Python by an\n"
+    "Array with variant_level==2.\n"
+    },
     {NULL},
 };
 
@@ -277,7 +273,7 @@ PyDoc_STRVAR(Dict_tp_doc,
 "\n"
 "    Dictionary(mapping_or_iterable=(), signature=None, variant_level=0)\n"
 "\n"
-"``variant_level`` must be non-negative; the default is 0.\n"
+":py:attr:`variant_level` must be non-negative; the default is 0.\n"
 "\n"
 "``signature`` is either a string or None. If a string, it must consist\n"
 "of exactly two complete type signatures, representing the 'key' type\n"
@@ -288,13 +284,6 @@ PyDoc_STRVAR(Dict_tp_doc,
 "If it is None (the default), when the Dictionary is sent over\n"
 "D-Bus, the key and value signatures will be guessed from an arbitrary\n"
 "element of the Dictionary.\n"
-"\n"
-":IVariables:\n"
-"  `variant_level` : int\n"
-"    Indicates how many nested Variant containers this object\n"
-"    is contained in: if a message's wire format has a variant containing a\n"
-"    variant containing an array of DICT_ENTRY, this is represented in\n"
-"    Python by a Dictionary with variant_level==2.\n"
 );
 
 static struct PyMemberDef Dict_tp_members[] = {
@@ -303,8 +292,11 @@ static struct PyMemberDef Dict_tp_members[] = {
      "that of each value in this Dictionary, as a Signature instance."},
     {"variant_level", T_LONG, offsetof(DBusPyDict, variant_level),
      READONLY,
-     "The number of nested variants wrapping the real data. "
-     "0 if not in a variant."},
+    "Indicates how many nested Variant containers this object\n"
+    "is contained in: if a message's wire format has a variant containing a\n"
+    "variant containing a dictionary, this is represented in Python by a\n"
+    "Dictionary with variant_level==2.\n"
+    },
     {NULL},
 };
 
@@ -540,10 +532,10 @@ PyDoc_STRVAR(Struct_tp_doc,
 "If the signature is None (default) it will be guessed\n"
 "from the types of the items during construction.\n"
 "\n"
-"``variant_level`` must be non-negative; the default is 0.\n"
+":py:attr:`variant_level` must be non-negative; the default is 0.\n"
+"\n"
+".. py:attribute:: variant_level\n"
 "\n"
-":IVariables:\n"
-"  `variant_level` : int\n"
 "    Indicates how many nested Variant containers this object\n"
 "    is contained in: if a message's wire format has a variant containing a\n"
 "    variant containing a struct, this is represented in Python by a\n"
diff --git a/dbus_bindings/int.c b/dbus_bindings/int.c
index 66ad9e0b4ae22f6d50ae6b30d3477f959d00b911..871a06567bb7091fb42465067da95de2d6f39fdf 100644
--- a/dbus_bindings/int.c
+++ b/dbus_bindings/int.c
@@ -47,10 +47,10 @@ PyDoc_STRVAR(Boolean_tp_doc,
 "\n"
 "``value`` is converted to 0 or 1 as if by ``int(bool(value))``.\n"
 "\n"
-"``variant_level`` must be non-negative; the default is 0.\n"
+":py:attr:`variant_level` must be non-negative; the default is 0.\n"
+"\n"
+".. py:attribute:: variant_level\n"
 "\n"
-":IVariables:\n"
-"  `variant_level` : int\n"
 "    Indicates how many nested Variant containers this object\n"
 "    is contained in: if a message's wire format has a variant containing a\n"
 "    variant containing a boolean, this is represented in Python by a\n"
@@ -160,8 +160,8 @@ PyDoc_STRVAR(Int16_tp_doc,
 "\n"
 "    variant_level must be non-negative; the default is 0.\n"
 "\n"
-":IVariables:\n"
-"  `variant_level` : int\n"
+".. py:attribute:: variant_level\n"
+"\n"
 "    Indicates how many nested Variant containers this object\n"
 "    is contained in: if a message's wire format has a variant containing a\n"
 "    variant containing an int16, this is represented in Python by an\n"
@@ -248,10 +248,10 @@ PyDoc_STRVAR(UInt16_tp_doc,
 "``value`` must be within the allowed range, or `OverflowError` will be\n"
 "raised.\n"
 "\n"
-"``variant_level`` must be non-negative; the default is 0.\n"
+":py:attr:`variant_level` must be non-negative; the default is 0.\n"
+"\n"
+".. py:attribute:: variant_level\n"
 "\n"
-":IVariables:\n"
-"  `variant_level` : int\n"
 "    Indicates how many nested Variant containers this object\n"
 "    is contained in: if a message's wire format has a variant containing a\n"
 "    variant containing a uint16, this is represented in Python by a\n"
@@ -340,10 +340,10 @@ PyDoc_STRVAR(Int32_tp_doc,
 "``value`` must be within the allowed range, or `OverflowError` will be\n"
 "raised.\n"
 "\n"
-"``variant_level`` must be non-negative; the default is 0.\n"
+":py:attr:`variant_level` must be non-negative; the default is 0.\n"
+"\n"
+".. py:attribute:: variant_level\n"
 "\n"
-":IVariables:\n"
-"  `variant_level` : int\n"
 "    Indicates how many nested Variant containers this object\n"
 "    is contained in: if a message's wire format has a variant containing a\n"
 "    variant containing an int32, this is represented in Python by an\n"
@@ -433,10 +433,10 @@ PyDoc_STRVAR(UInt32_tp_doc,
 "``value`` must be within the allowed range, or `OverflowError` will be\n"
 "raised.\n"
 "\n"
-"``variant_level`` must be non-negative; the default is 0.\n"
+":py:attr:`variant_level` must be non-negative; the default is 0.\n"
+"\n"
+".. py:attribute:: variant_level\n"
 "\n"
-":IVariables:\n"
-"  `variant_level` : int\n"
 "    Indicates how many nested Variant containers this object\n"
 "    is contained in: if a message's wire format has a variant containing a\n"
 "    variant containing a uint32, this is represented in Python by a\n"
@@ -537,10 +537,10 @@ PyDoc_STRVAR(Int64_tp_doc,
 "``value`` must be within the allowed range, or `OverflowError` will be\n"
 "raised.\n"
 "\n"
-"``variant_level`` must be non-negative; the default is 0.\n"
+":py:attr:`variant_level` must be non-negative; the default is 0.\n"
+"\n"
+".. py:attribute:: variant_level\n"
 "\n"
-":IVariables:\n"
-"  `variant_level` : int\n"
 "    Indicates how many nested Variant containers this object\n"
 "    is contained in: if a message's wire format has a variant containing a\n"
 "    variant containing an int64, this is represented in Python by an\n"
@@ -644,10 +644,10 @@ PyDoc_STRVAR(UInt64_tp_doc,
 "``value`` must be within the allowed range, or `OverflowError` will be\n"
 "raised.\n"
 "\n"
-"``variant_level`` must be non-negative; the default is 0.\n"
+":py:attr:`variant_level` must be non-negative; the default is 0.\n"
+"\n"
+".. py:attribute:: variant_level\n"
 "\n"
-":IVariables:\n"
-"  `variant_level` : int\n"
 "    Indicates how many nested Variant containers this object\n"
 "    is contained in: if a message's wire format has a variant containing a\n"
 "    variant containing a uint64, this is represented in Python by a\n"
diff --git a/dbus_bindings/message-append.c b/dbus_bindings/message-append.c
index d20b49394987c49434f462efbdecea2f23dfcd2c..0480ceb6f0184f215183dba1fba788f2ae2e63c6 100644
--- a/dbus_bindings/message-append.c
+++ b/dbus_bindings/message-append.c
@@ -70,7 +70,8 @@ get_variant_level(PyObject *obj)
 }
 
 char dbus_py_Message_append__doc__[] = (
-"set_args(*args[, **kwargs])\n\n"
+"message.append(*args, **kwargs)\n"
+"\n"
 "Set the message's arguments from the positional parameter, according to\n"
 "the signature given by the ``signature`` keyword parameter.\n"
 "\n"
diff --git a/dbus_bindings/signature.c b/dbus_bindings/signature.c
index e3a555cbb05052c43d1701937af08847ef6fceec..574968b7ee9648af51e2da0b9ca98a00ca573a1a 100644
--- a/dbus_bindings/signature.c
+++ b/dbus_bindings/signature.c
@@ -42,10 +42,10 @@ PyDoc_STRVAR(Signature_tp_doc,
 "``value`` must be a valid D-Bus signature (zero or more single complete\n"
 "types).\n"
 "\n"
-"``variant_level`` must be non-negative; the default is 0.\n"
+":py:attr:`variant_level` must be non-negative; the default is 0.\n"
+"\n"
+".. py:attribute:: variant_level\n"
 "\n"
-":IVariables:\n"
-"  `variant_level` : int\n"
 "    Indicates how many nested Variant containers this object\n"
 "    is contained in: if a message's wire format has a variant containing a\n"
 "    variant containing a signature, this is represented in Python by a\n"
diff --git a/dbus_bindings/string.c b/dbus_bindings/string.c
index 8363f98c926acec58d6509f01397344d3d4d49a8..32c1e3199b7574a2ff832365295c181a30623bda 100644
--- a/dbus_bindings/string.c
+++ b/dbus_bindings/string.c
@@ -129,19 +129,16 @@ PyTypeObject DBusPyUTF8String_Type = {
 /* Object path ====================================================== */
 
 PyDoc_STRVAR(ObjectPath_tp_doc,
-"A D-Bus object path, such as '/com/example/MyApp/Documents/abc'.\n"
+"dbus.ObjectPath(path: str[, variant_level: int=0])\n"
+"A D-Bus object path, such as ``/com/example/MyApp/Documents/abc``.\n"
 "\n"
-"ObjectPath is a subtype of str, and object-paths behave like strings.\n"
-"\n"
-"Constructor::\n"
-"\n"
-"    dbus.ObjectPath(path: str, variant_level: int) -> ObjectPath\n"
+"ObjectPath is a subtype of :py:class:`str`, and object-paths behave like strings.\n"
 "\n"
 "path must be an ASCII string following the syntax of object paths.\n"
 "variant_level must be non-negative; the default is 0.\n"
 "\n"
-":IVariables:\n"
-"  `variant_level` : int\n"
+".. py:attribute:: variant_level\n"
+"\n"
 "    Indicates how many nested Variant containers this object\n"
 "    is contained in: if a message's wire format has a variant containing a\n"
 "    variant containing an object path, this is represented in Python by an\n"
@@ -223,20 +220,16 @@ PyDoc_STRVAR(String_tp_doc,
 "    String(value: str or unicode[, variant_level: int]) -> String\n"
 "\n"
 "variant_level must be non-negative; the default is 0.\n"
-"\n"
-":IVariables:\n"
-"  `variant_level` : int\n"
-"    Indicates how many nested Variant containers this object\n"
-"    is contained in: if a message's wire format has a variant containing a\n"
-"    variant containing a string, this is represented in Python by a\n"
-"    String or UTF8String with variant_level==2.\n"
 );
 
 static PyMemberDef String_tp_members[] = {
     {"variant_level", T_LONG, offsetof(DBusPyString, variant_level),
      READONLY,
-     "The number of nested variants wrapping the real data. "
-     "0 if not in a variant"},
+    "Indicates how many nested Variant containers this object\n"
+    "is contained in: if a message's wire format has a variant containing a\n"
+    "variant containing an array, this is represented in Python by a\n"
+    "String or UTF8String with variant_level==2.\n"
+    },
     {NULL},
 };
 
diff --git a/dbus_bindings/unixfd.c b/dbus_bindings/unixfd.c
index 648dd2c0958c0d808eb729d8c862998d24efecae..62a5acb5b22ede7fe6204775bec010237745f1c9 100644
--- a/dbus_bindings/unixfd.c
+++ b/dbus_bindings/unixfd.c
@@ -42,10 +42,10 @@ PyDoc_STRVAR(UnixFd_tp_doc,
 "UnixFd keeps a dup() (duplicate) of the supplied file descriptor. The\n"
 "caller remains responsible for closing the original fd.\n"
 "\n"
-"``variant_level`` must be non-negative; the default is 0.\n"
+":py:attr:`variant_level` must be non-negative; the default is 0.\n"
+"\n"
+".. py:attribute:: variant_level\n"
 "\n"
-":IVariables:\n"
-"  `variant_level` : int\n"
 "    Indicates how many nested Variant containers this object\n"
 "    is contained in: if a message's wire format has a variant containing a\n"
 "    variant containing an Unix Fd, this is represented in Python by an\n"
diff --git a/doc/HACKING.txt b/doc/HACKING.txt
index c01919891b1a70b1d9e7fd9e51befcb7855acb61..b53655c11e2ecb9d4732ce380bc9d62cb966773c 100644
--- a/doc/HACKING.txt
+++ b/doc/HACKING.txt
@@ -1,6 +1,6 @@
-=========================================
-D-Bus Python bindings - notes for hackers
-=========================================
+===============
+Developer notes
+===============
 
 :Author: Simon McVittie, `Collabora`_
 :Date: 2007-01-24
diff --git a/doc/PY3PORT.txt b/doc/PY3PORT.txt
index a028153343406b9fe2d5260f41ed372af2b617a4..e159849a52cf043b175c211fd250c623aff72c4e 100644
--- a/doc/PY3PORT.txt
+++ b/doc/PY3PORT.txt
@@ -1,6 +1,6 @@
-===============================
-Porting python-dbus to Python 3
-===============================
+===================
+Porting to Python 3
+===================
 
 This is an experimental port to Python 3.x where x >= 2.  There are lots of
 great sources for porting C extensions to Python 3, including:
diff --git a/doc/_static/.gitignore b/doc/_static/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/doc/conf.py b/doc/conf.py
new file mode 100644
index 0000000000000000000000000000000000000000..f094a2603d1bf3e91c82c2ed7ad1489bb3e606e9
--- /dev/null
+++ b/doc/conf.py
@@ -0,0 +1,165 @@
+# -*- coding: utf-8 -*-
+#
+# Configuration file for the Sphinx documentation builder.
+#
+# This file does only contain a selection of the most common options. For a
+# full list see the documentation:
+# http://www.sphinx-doc.org/en/stable/config
+
+# -- Path setup --------------------------------------------------------------
+
+import os
+import sys
+
+sys.path.insert(0,
+    os.path.join(
+        os.environ.get('abs_top_builddir', os.path.abspath('..')),
+        '.libs',
+    ),
+)
+sys.path.insert(0, os.environ.get('abs_top_srcdir', os.path.abspath('..')))
+
+import _dbus_bindings
+
+# -- Project information -----------------------------------------------------
+
+project = u'dbus-python'
+copyright = u'2003-2018, D-Bus contributors'
+author = u'D-Bus contributors'
+
+# The short X.Y version
+version = _dbus_bindings.__version__
+# The full version, including alpha/beta/rc tags
+release = version
+
+
+# -- General configuration ---------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#
+# needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [
+    'sphinx.ext.autodoc',
+    'sphinx.ext.coverage',
+]
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix(es) of source filenames.
+# You can specify multiple suffix as a list of string:
+#
+# source_suffix = ['.rst', '.md']
+source_suffix = ['.rst', '.txt']
+
+# The master toctree document.
+master_doc = 'index'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = None
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This pattern also affects html_static_path and html_extra_path .
+exclude_patterns = [u'_build', 'Thumbs.db', '.DS_Store']
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+
+# -- Options for HTML output -------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  See the documentation for
+# a list of builtin themes.
+#
+html_theme = 'sphinx_rtd_theme'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further.  For a list of options available for each theme, see the
+# documentation.
+#
+# html_theme_options = {}
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# Custom sidebar templates, must be a dictionary that maps document names
+# to template names.
+#
+# The default sidebars (for documents that don't match any pattern) are
+# defined by theme itself.  Builtin themes are using these templates by
+# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
+# 'searchbox.html']``.
+#
+# html_sidebars = {}
+
+
+# -- Options for HTMLHelp output ---------------------------------------------
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'dbus-python'
+
+
+# -- Options for LaTeX output ------------------------------------------------
+
+latex_elements = {
+    # The paper size ('letterpaper' or 'a4paper').
+    #
+    # 'papersize': 'letterpaper',
+
+    # The font size ('10pt', '11pt' or '12pt').
+    #
+    # 'pointsize': '10pt',
+
+    # Additional stuff for the LaTeX preamble.
+    #
+    # 'preamble': '',
+
+    # Latex figure (float) alignment
+    #
+    # 'figure_align': 'htbp',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+#  author, documentclass [howto, manual, or own class]).
+latex_documents = [
+    (master_doc, 'dbus-python.tex', u'dbus-python Documentation',
+     u'D-Bus contributors', 'manual'),
+]
+
+
+# -- Options for manual page output ------------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+    (master_doc, 'dbus-python', u'dbus-python Documentation',
+     [author], 3)
+]
+
+
+# -- Options for Texinfo output ----------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+#  dir menu entry, description, category)
+texinfo_documents = [
+    (master_doc, 'dbus-python', u'dbus-python Documentation',
+     author, 'dbus-python',
+     'Python bindings for the reference implementation of D-Bus.',
+     'Miscellaneous'),
+]
+
+
+# -- Extension configuration -------------------------------------------------
diff --git a/doc/dbus.bus.rst b/doc/dbus.bus.rst
new file mode 100644
index 0000000000000000000000000000000000000000..1f78f3e4de5a3a0a86dad8fb6825ccb9df58b329
--- /dev/null
+++ b/doc/dbus.bus.rst
@@ -0,0 +1,7 @@
+dbus.bus module
+===============
+
+.. automodule:: dbus.bus
+    :members:
+    :undoc-members:
+    :show-inheritance:
diff --git a/doc/dbus.connection.rst b/doc/dbus.connection.rst
new file mode 100644
index 0000000000000000000000000000000000000000..7b78212b999d5c5f0c48dad86cceb1f20b13ce62
--- /dev/null
+++ b/doc/dbus.connection.rst
@@ -0,0 +1,7 @@
+dbus.connection module
+======================
+
+.. automodule:: dbus.connection
+    :members:
+    :undoc-members:
+    :show-inheritance:
diff --git a/doc/dbus.decorators.rst b/doc/dbus.decorators.rst
new file mode 100644
index 0000000000000000000000000000000000000000..3f6625cf24f65eec640af3a0414011bab7d0da69
--- /dev/null
+++ b/doc/dbus.decorators.rst
@@ -0,0 +1,7 @@
+dbus.decorators module
+----------------------
+
+.. automodule:: dbus.decorators
+    :members:
+    :undoc-members:
+    :show-inheritance:
diff --git a/doc/dbus.exceptions.rst b/doc/dbus.exceptions.rst
new file mode 100644
index 0000000000000000000000000000000000000000..fea743cc818642cb7406561452230e5a23667332
--- /dev/null
+++ b/doc/dbus.exceptions.rst
@@ -0,0 +1,7 @@
+dbus.exceptions module
+----------------------
+
+.. automodule:: dbus.exceptions
+    :members:
+    :undoc-members:
+    :show-inheritance:
diff --git a/doc/dbus.gi_service.rst b/doc/dbus.gi_service.rst
new file mode 100644
index 0000000000000000000000000000000000000000..945d5a0773d1735708bf1613bbaeb709c9bb3693
--- /dev/null
+++ b/doc/dbus.gi_service.rst
@@ -0,0 +1,7 @@
+dbus.gi\_service module
+-----------------------
+
+.. automodule:: dbus.gi_service
+    :members:
+    :undoc-members:
+    :show-inheritance:
diff --git a/doc/dbus.glib.rst b/doc/dbus.glib.rst
new file mode 100644
index 0000000000000000000000000000000000000000..20958be8ff794a3b0836c0d6cce97e5cc323a96c
--- /dev/null
+++ b/doc/dbus.glib.rst
@@ -0,0 +1,7 @@
+dbus.glib module
+----------------
+
+.. automodule:: dbus.glib
+    :members:
+    :undoc-members:
+    :show-inheritance:
diff --git a/doc/dbus.gobject_service.rst b/doc/dbus.gobject_service.rst
new file mode 100644
index 0000000000000000000000000000000000000000..b1ff1be1971df16e67ce05ae181e522fb4be7e45
--- /dev/null
+++ b/doc/dbus.gobject_service.rst
@@ -0,0 +1,36 @@
+.. This is not done via automodule because it cannot be imported in
+.. Python 3.
+
+dbus.gobject\_service module
+----------------------------
+
+.. py:module:: gobject_service
+
+This module is only available when using Python 2, and is deprecated.
+
+.. py:class:: ExportedGObjectType(cls, name, bases, dct)
+
+    A metaclass which inherits from both GObjectMeta and
+    `dbus.service.InterfaceType`. Used as the metaclass for
+    `ExportedGObject`.
+
+.. py:class:: ExportedGObject(self, conn=None, object_path=None, **kwargs)
+
+    A GObject which is exported on the D-Bus.
+
+    Because GObject and `dbus.service.Object` both have custom metaclasses,
+    the naive approach using simple multiple inheritance won't work. This
+    class has `ExportedGObjectType` as its metaclass, which is sufficient
+    to make it work correctly.
+
+    :param dbus.connection.Connection conn:
+         The D-Bus connection or bus
+    :param str object_path:
+         The object path at which to register this object.
+    :keyword dbus.service.BusName bus_name:
+         A bus name to be held on behalf of this object, or None.
+    :keyword dict gobject_properties:
+         GObject properties to be set on the constructed object.
+
+         Any unrecognised keyword arguments will also be interpreted
+         as GObject properties.
diff --git a/doc/dbus.lowlevel.rst b/doc/dbus.lowlevel.rst
new file mode 100644
index 0000000000000000000000000000000000000000..74e5f477fa0a23779cfdec581142b9a4cf74bf4d
--- /dev/null
+++ b/doc/dbus.lowlevel.rst
@@ -0,0 +1,7 @@
+dbus.lowlevel module
+--------------------
+
+.. automodule:: dbus.lowlevel
+    :members:
+    :undoc-members:
+    :show-inheritance:
diff --git a/doc/dbus.mainloop.rst b/doc/dbus.mainloop.rst
new file mode 100644
index 0000000000000000000000000000000000000000..14bbf147cdd9f461f1d688596701b72941225e8f
--- /dev/null
+++ b/doc/dbus.mainloop.rst
@@ -0,0 +1,18 @@
+dbus.mainloop package
+=====================
+
+Module contents
+---------------
+
+.. automodule:: dbus.mainloop
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
+dbus.mainloop.glib module
+-------------------------
+
+.. automodule:: dbus.mainloop.glib
+    :members:
+    :undoc-members:
+    :show-inheritance:
diff --git a/doc/dbus.proxies.rst b/doc/dbus.proxies.rst
new file mode 100644
index 0000000000000000000000000000000000000000..8e4e8e301d0f7201f7693d7e7c1476a6cd9777b8
--- /dev/null
+++ b/doc/dbus.proxies.rst
@@ -0,0 +1,7 @@
+dbus.proxies module
+-------------------
+
+.. automodule:: dbus.proxies
+    :members:
+    :undoc-members:
+    :show-inheritance:
diff --git a/doc/dbus.rst b/doc/dbus.rst
new file mode 100644
index 0000000000000000000000000000000000000000..2b2ab9697dd3c0c45aa2fcf37b596e2fa57850a9
--- /dev/null
+++ b/doc/dbus.rst
@@ -0,0 +1,35 @@
+dbus package API reference
+==========================
+
+Submodules
+----------
+
+.. toctree::
+
+    dbus.bus
+    dbus.connection
+    dbus.decorators
+    dbus.exceptions
+    dbus.gi_service
+    dbus.lowlevel
+    dbus.mainloop
+    dbus.proxies
+    dbus.server
+    dbus.service
+    dbus.types
+
+Deprecated submodules
+---------------------
+
+.. toctree::
+
+    dbus.glib
+    dbus.gobject_service
+
+Module contents
+---------------
+
+.. automodule:: dbus
+    :members:
+    :undoc-members:
+    :show-inheritance:
diff --git a/doc/dbus.server.rst b/doc/dbus.server.rst
new file mode 100644
index 0000000000000000000000000000000000000000..c0d94c6b3b5b20c2aa0723b7241c5b5e2f59163d
--- /dev/null
+++ b/doc/dbus.server.rst
@@ -0,0 +1,7 @@
+dbus.server module
+------------------
+
+.. automodule:: dbus.server
+    :members:
+    :undoc-members:
+    :show-inheritance:
diff --git a/doc/dbus.service.rst b/doc/dbus.service.rst
new file mode 100644
index 0000000000000000000000000000000000000000..61958d6352a4648de2a85b0e52d0ed10d5445419
--- /dev/null
+++ b/doc/dbus.service.rst
@@ -0,0 +1,7 @@
+dbus.service module
+-------------------
+
+.. automodule:: dbus.service
+    :members:
+    :undoc-members:
+    :show-inheritance:
diff --git a/doc/dbus.types.rst b/doc/dbus.types.rst
new file mode 100644
index 0000000000000000000000000000000000000000..c02c788a3eaa8f169fc3e6577e9f3b63233d31d8
--- /dev/null
+++ b/doc/dbus.types.rst
@@ -0,0 +1,7 @@
+dbus.types module
+-----------------
+
+.. automodule:: dbus.types
+    :members:
+    :undoc-members:
+    :show-inheritance:
diff --git a/doc/index.rst b/doc/index.rst
new file mode 100644
index 0000000000000000000000000000000000000000..ca754b594639e70373144fc98c4d01399319b72c
--- /dev/null
+++ b/doc/index.rst
@@ -0,0 +1,53 @@
+=======================================
+dbus-python_: Python bindings for D-Bus
+=======================================
+
+.. _dbus-python: http://www.freedesktop.org/wiki/Software/DBusBindings#python
+
+dbus-python is a Python binding for ``dbus``, the reference implementation
+of the D-Bus protocol.
+
+Problems and alternatives
+=========================
+
+dbus-python might not be the best D-Bus binding for you to use. dbus-python
+does not follow the principle of "In the face of ambiguity, refuse the
+temptation to guess", and can't be changed to not do so without seriously
+breaking compatibility.
+
+In addition, it uses libdbus (which has known problems with multi-threaded
+use) and attempts to be main-loop-agnostic (which means you have to select
+a suitable main loop for your application).
+
+Alternative ways to get your Python code onto D-Bus include:
+
+* GDBus, part of the GIO module of `GLib`_, via GObject-Introspection and
+  `PyGI`_ (uses the GLib main loop and object model)
+
+* QtDBus, part of `Qt`_, via `PyQt`_ (uses the Qt main loop and object model)
+
+.. _GLib: http://developer.gnome.org/glib/
+.. _PyGI: https://live.gnome.org/PyGObject
+.. _Qt: https://qt.nokia.com/
+.. _PyQT: http://www.riverbankcomputing.co.uk/software/pyqt/intro
+
+Documentation
+=============
+
+.. toctree::
+    :maxdepth: 2
+    :caption: Contents:
+
+    tutorial
+    dbus
+    PY3PORT
+    news
+    HACKING
+    API_CHANGES
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/doc/news.rst b/doc/news.rst
new file mode 100644
index 0000000000000000000000000000000000000000..249c431912e23af4f6c0f3315517a24f72528008
--- /dev/null
+++ b/doc/news.rst
@@ -0,0 +1,5 @@
+===============
+Release history
+===============
+
+.. include:: ../NEWS