Repository: PennyLaneAI/pennylane (python)

Issue:
[BUG] Unable to reference some decorated top-lvl functions
### Expected behavior

The syntax
```
func:`~.pennylane.compile`
```
works for referencing any top-level functions in the docs.

### Actual behavior

```
func:`~.pennylane.compile`
```
doesn't include a hyperlink to the related documentation change, likely because `qml.compile` is decorated with `@qfunc_transform`. This behaviour was originally noted with the [`@qml.defer_measurements` transform](https://github.com/PennyLaneAI/pennylane/pull/2211#discussion_r818996929).

### Additional information

Further note, that the documentation pages for such decorated functions also differs in style:
![Screenshot from 2022-03-04 15-06-39](https://user-images.githubusercontent.com/24476053/156834715-847fe2ad-429b-4481-9912-8b658d3773cf.png)
![Screenshot from 2022-03-04 15-06-58](https://user-images.githubusercontent.com/24476053/156834725-bfcd6955-190b-47b9-bf0f-887a84630a55.png)

### Source code

_No response_

### Tracebacks

_No response_

### System information

```shell
Name: PennyLane
Version: 0.22.0.dev0
Summary: PennyLane is a Python quantum machine learning library by Xanadu Inc.
Home-page: https://github.com/XanaduAI/pennylane
Author: 
Author-email: 
License: Apache License 2.0
Location: /home/antal/xanadu/pennylane
Requires: numpy, scipy, networkx, retworkx, autograd, toml, appdirs, semantic_version, autoray, cachetools, pennylane-lightning
Required-by: PennyLane-Lightning, amazon-braket-pennylane-plugin, PennyLane-Orquestra, pennylane-qulacs, PennyLane-Honeywell, PennyLane-AQT, PennyLane-PQ, PennyLane-Forest, PennyLane-qsharp, PennyLane-Qchem, PennyLane-IonQ, PennyLane-SF, PennyLane-Cirq, PennyLane-qiskit
Platform info:           Linux-5.13.0-30-generic-x86_64-with-glibc2.10
Python version:          3.8.5
Numpy version:           1.22.1
Scipy version:           1.7.3
Installed devices:
- default.gaussian (PennyLane-0.22.0.dev0)
- default.mixed (PennyLane-0.22.0.dev0)
- default.qubit (PennyLane-0.22.0.dev0)
- default.qubit.autograd (PennyLane-0.22.0.dev0)
- default.qubit.jax (PennyLane-0.22.0.dev0)
- default.qubit.tf (PennyLane-0.22.0.dev0)
- default.qubit.torch (PennyLane-0.22.0.dev0)
- lightning.qubit (PennyLane-Lightning-0.21.0)
- braket.aws.qubit (amazon-braket-pennylane-plugin-1.5.3)
- braket.local.qubit (amazon-braket-pennylane-plugin-1.5.3)
- orquestra.forest (PennyLane-Orquestra-0.15.0)
- orquestra.ibmq (PennyLane-Orquestra-0.15.0)
- orquestra.qiskit (PennyLane-Orquestra-0.15.0)
- orquestra.qulacs (PennyLane-Orquestra-0.15.0)
- qulacs.simulator (pennylane-qulacs-0.17.0.dev0)
- honeywell.hqs (PennyLane-Honeywell-0.16.0.dev0)
- aqt.noisy_sim (PennyLane-AQT-0.18.0)
- aqt.sim (PennyLane-AQT-0.18.0)
- projectq.classical (PennyLane-PQ-0.18.0.dev0)
- projectq.ibm (PennyLane-PQ-0.18.0.dev0)
- projectq.simulator (PennyLane-PQ-0.18.0.dev0)
- forest.numpy_wavefunction (PennyLane-Forest-0.18.0.dev0)
- forest.qvm (PennyLane-Forest-0.18.0.dev0)
- forest.wavefunction (PennyLane-Forest-0.18.0.dev0)
- microsoft.QuantumSimulator (PennyLane-qsharp-0.19.0)
- ionq.qpu (PennyLane-IonQ-0.17.0.dev0)
- ionq.simulator (PennyLane-IonQ-0.17.0.dev0)
- strawberryfields.fock (PennyLane-SF-0.20.0.dev0)
- strawberryfields.gaussian (PennyLane-SF-0.20.0.dev0)
- strawberryfields.gbs (PennyLane-SF-0.20.0.dev0)
- strawberryfields.remote (PennyLane-SF-0.20.0.dev0)
- strawberryfields.tf (PennyLane-SF-0.20.0.dev0)
- cirq.mixedsimulator (PennyLane-Cirq-0.20.0.dev0)
- cirq.pasqal (PennyLane-Cirq-0.20.0.dev0)
- cirq.qsim (PennyLane-Cirq-0.20.0.dev0)
- cirq.qsimh (PennyLane-Cirq-0.20.0.dev0)
- cirq.simulator (PennyLane-Cirq-0.20.0.dev0)
- qiskit.aer (PennyLane-qiskit-0.22.0.dev0)
- qiskit.basicaer (PennyLane-qiskit-0.22.0.dev0)
- qiskit.ibmq (PennyLane-qiskit-0.22.0.dev0)
- qiskit.ibmq.circuit_runner (PennyLane-qiskit-0.22.0.dev0)
- qiskit.ibmq.sampler (PennyLane-qiskit-0.22.0.dev0)
```


### Existing GitHub issues

- [X] I have searched existing GitHub issues to make sure the issue does not already exist.

Pull request description:
Ensures that `@qfunc_transform` decorated functions display their signatures in the docs
**Context:** Decorated functions do not correctly display their signatures in the docs

**Description of the Change:** If a Sphinx build is detected, the `@qfunc_transform` decorator will simply return the function unchanged, allowing Sphinx to correctly introspect the signature.

**Benefits:** As above.

**Possible Drawbacks:** None

**Related GitHub Issues:** Fixes #2283


Patch:
diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index ad9cf8295..25eff427e 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -405,6 +405,10 @@
   usage of the passed `substep_optimizer` and its keyword arguments.
   [(#2160)](https://github.com/PennyLaneAI/pennylane/pull/2160)
 
+* Ensures that signatures of `@qml.qfunc_transform` decorated functions
+  display correctly in the docs.
+  [(#2286)](https://github.com/PennyLaneAI/pennylane/pull/2286)
+
 <h3>Operator class refactor</h3>
 
 The Operator class has undergone a major refactor with the following changes:
diff --git a/pennylane/transforms/qfunc_transforms.py b/pennylane/transforms/qfunc_transforms.py
index 5825aaaf6..40e056b96 100644
--- a/pennylane/transforms/qfunc_transforms.py
+++ b/pennylane/transforms/qfunc_transforms.py
@@ -16,6 +16,8 @@
 from copy import deepcopy
 import functools
 import inspect
+import os
+import warnings
 
 import pennylane as qml
 
@@ -374,6 +376,21 @@ def qfunc_transform(tape_transform):
         the queueing logic required under steps (1) and (3), so that it does not need to be
         repeated and tested for every new qfunc transform.
     """
+    if os.environ.get("SPHINX_BUILD") == "1":
+        # If called during a Sphinx documentation build,
+        # simply return the original function rather than
+        # instantiating the object. This allows the signature to
+        # be correctly displayed in the documentation.
+
+        warnings.warn(
+            "qfunc transformations have been disabled, as a Sphinx "
+            "build has been detected via SPHINX_BUILD='1'. If this is not the "
+            "case, please set the environment variable SPHINX_BUILD='0'.",
+            UserWarning,
+        )
+
+        return tape_transform
+
     if not callable(tape_transform):
         raise ValueError(
             "The qfunc_transform decorator can only be applied "


Test patch:
diff --git a/tests/transforms/test_qfunc_transform.py b/tests/transforms/test_qfunc_transform.py
index 4879d2fae..fccdcb81a 100644
--- a/tests/transforms/test_qfunc_transform.py
+++ b/tests/transforms/test_qfunc_transform.py
@@ -302,6 +302,27 @@ class TestQFuncTransforms:
         assert np.allclose(normal_result, transformed_result)
         assert normal_result.shape == transformed_result.shape
 
+    def test_sphinx_build(self, monkeypatch):
+        """Test that qfunc transforms are not created during Sphinx builds"""
+
+        def original_fn(tape):
+            for op in tape.operations + tape.measurements:
+                if op.name == "Hadamard":
+                    qml.RZ(np.pi, wires=op.wires)
+                    qml.RY(np.pi / 2, wires=op.wires)
+                else:
+                    op.queue()
+
+        decorated_transform = qml.qfunc_transform(original_fn)
+        assert original_fn is not decorated_transform
+
+        monkeypatch.setenv("SPHINX_BUILD", "1")
+
+        with pytest.warns(UserWarning, match="qfunc transformations have been disabled"):
+            decorated_transform = qml.qfunc_transform(original_fn)
+
+        assert original_fn is decorated_transform
+
 
 ############################################
 # Test transform, ansatz, and qfunc function