Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A new way of adding parameters that allows overriding inherited parameters (and sphinx auto documentation) #3098

Open
wants to merge 41 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
eb5eb2c
first prototype add_parameter decorator
caenrigen Jun 8, 2021
2466c8d
improved usability for instance attributes
caenrigen Jun 8, 2021
e6d5d76
some comments
caenrigen Jun 8, 2021
5220fb0
fix codacy
caenrigen Jun 8, 2021
1224732
Merge branch 'master' into feat/add_parameter_decorator
caenrigen Jun 8, 2021
6d3f06d
address review comments
caenrigen Jun 9, 2021
a1b9e8b
bug fix
caenrigen Jun 9, 2021
3599bb5
minor var rename
caenrigen Jun 9, 2021
918ba4b
bug fix
caenrigen Jun 9, 2021
4dc1864
Merge branch 'master' into feat/add_parameter_decorator
caenrigen Jun 9, 2021
6e23d34
proper use of Callable
caenrigen Jun 9, 2021
966a61e
Merge branch 'master' into feat/add_parameter_decorator
astafan8 Jun 14, 2021
df0b771
added sphinx extension
caenrigen Jun 16, 2021
c0e2ce4
Merge remote-tracking branch 'origin/feat/add_parameter_decorator' in…
caenrigen Jun 16, 2021
6868d38
started documenting
caenrigen Jun 16, 2021
5b0e14b
docs enh
caenrigen Jun 16, 2021
89c5968
some cleanup
caenrigen Jun 16, 2021
895d5c1
wip
caenrigen Jun 17, 2021
4c87cb9
solution to render rst in notebooks
caenrigen Jun 17, 2021
4c51987
done documenting
caenrigen Jun 17, 2021
9142fbf
missing some changes to docs
caenrigen Jun 21, 2021
44ca9a6
improve error handling
caenrigen Jun 21, 2021
9f86aa3
fix codacy issues
caenrigen Jun 21, 2021
198ded8
Merge branch 'master' into feat/add_parameter_decorator
astafan8 Jun 21, 2021
a92131e
allow decorated methods to specify return types, intended for parameters
caenrigen Jun 21, 2021
ad2ea22
Merge remote-tracking branch 'origin/feat/add_parameter_decorator' in…
caenrigen Jun 21, 2021
606c36b
Merge branch 'master' into feat/add_parameter_decorator
astafan8 Jun 23, 2021
a2a8afb
address comments
caenrigen Jun 23, 2021
5f46e5b
Merge remote-tracking branch 'origin/feat/add_parameter_decorator' in…
caenrigen Jun 23, 2021
63acaf3
address comments
caenrigen Jun 23, 2021
26ae403
remove comment
caenrigen Jun 23, 2021
99a07d1
codacy fix(?)
caenrigen Jun 23, 2021
d4118ce
Merge branch 'master' into feat/add_parameter_decorator
astafan8 Jun 23, 2021
1a29c21
Merge remote-tracking branch 'origin/feat/add_parameter_decorator' in…
caenrigen Jun 23, 2021
b07eafa
typo
caenrigen Jun 24, 2021
4436bf2
tests wip
caenrigen Jun 24, 2021
d00b149
raise errors asap
caenrigen Jun 25, 2021
5455e9d
finish test
caenrigen Jun 25, 2021
f8f1d09
fix codacy (?)
caenrigen Jun 25, 2021
9ba7bc0
fix list in sphinx
caenrigen Jul 5, 2021
b18c448
print lambda
caenrigen Jul 7, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/api/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ QCoDeS API
monitor/index
station
utils/index
sphinx_extensions/index

Legacy API
----------
Expand Down
5 changes: 5 additions & 0 deletions docs/api/sphinx_extensions/add_parameter.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
qcodes.sphinx_extensions.add_parameter
--------------------------------------

.. automodule:: qcodes.sphinx_extensions.add_parameter
:members:
19 changes: 19 additions & 0 deletions docs/api/sphinx_extensions/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.. _sphinx_extensions_api :

qcodes.sphinx_extensions
========================


.. autosummary::

qcodes.sphinx_extensions
qcodes.sphinx_extensions.add_parameter

.. automodule:: qcodes.sphinx_extensions


.. toctree::
:maxdepth: 4
:hidden:

add_parameter
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
'sphinx.ext.intersphinx', 'sphinx.ext.todo',
'sphinx.ext.coverage', 'sphinx.ext.mathjax',
'sphinx.ext.viewcode', 'sphinx.ext.githubpages',
'sphinx.ext.todo']
'sphinx.ext.todo', "qcodes.sphinx_extensions.add_parameter"]

# include special __xxx__ that DO have a docstring
# it probably means something important
Expand Down
261 changes: 236 additions & 25 deletions docs/examples/writing_drivers/Creating-Instrument-Drivers.ipynb
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
{
"cells": [
{
"cell_type": "raw",
"metadata": {
"raw_mimetype": "text/restructuredtext"
},
"source": [
".. NB This notebook contains cells like this one for technical reasons (associated with building the QCoDeS documentation). These cells will render only after nbsphinx processes it. You can safely ignore them.\n",
"\n",
".. Tip: This special cell is enabled by addind `\"celltoolbar\": \"Raw Cell Format\"` entry in the `\"metadata\"` entry of the raw `.ipynb` file."
]
},
{
"cell_type": "markdown",
"metadata": {},
Expand All @@ -18,13 +29,13 @@
"text": [
"Logging hadn't been started.\n",
"Activating auto-logging. Current session state plus future input saved.\n",
"Filename : C:\\Users\\jenielse\\.qcodes\\logs\\command_history.log\n",
"Filename : /Users/Victor/.qcodes/logs/command_history.log\n",
"Mode : append\n",
"Output logging : True\n",
"Raw input log : False\n",
"Timestamping : True\n",
"State : active\n",
"Qcodes Logfile : C:\\Users\\jenielse\\.qcodes\\logs\\200122-8548-qcodes.log\n"
"Qcodes Logfile : /Users/Victor/.qcodes/logs/210617-15348-qcodes.log\n"
]
}
],
Expand Down Expand Up @@ -57,6 +68,24 @@
"If possible, please use a `VisaInstrument`, as this allows for the creation of a simulated instrument. (See the [Creating Simulated PyVISA Instruments](Creating-Simulated-PyVISA-Instruments.ipynb) notebook) "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Logging\n",
"Every QCoDeS module should have its own logger that is named with the name of the module. So to create a logger put a line at the top of the module like this:\n",
"```\n",
"log = logging.getLogger(__name__)\n",
"```\n",
"Use this logger only to log messages that are not originating from an `Instrument` instance. For messages from within an instrument instance use the `log` member of the `Instrument` class, e.g\n",
"```\n",
"self.log.info(f\"Could not connect at {address}\")\n",
"```\n",
"This way the instrument name will be prepended to the log message and the log messages can be filtered according to the instrument they originate from. See the example notebook of the logger module for more info ([offline](../logging/logging_example.ipynb),[online](https://nbviewer.jupyter.org/github/QCoDeS/Qcodes/tree/master/docs/examples/logging/logging_example.ipynb)).\n",
"\n",
"When creating a nested `Instrument`, like e.g. something like the `InstrumentChannel` class, that has a `_parent` property, make sure that this property gets set before calling the `super().__init__` method, so that the full name of the instrument gets resolved correctly for the logging.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
Expand All @@ -83,8 +112,34 @@
" \n",
"Golden rule: if a `Parameter` is settable, it must always accept its own output as input.\n",
"\n",
"In most cases you will probably be adding parameters via the `add_parameter` method on the instrument class as shown in the example below. \n",
"### Adding parameters to intruments"
]
},
{
"cell_type": "raw",
"metadata": {
"raw_mimetype": "text/restructuredtext"
caenrigen marked this conversation as resolved.
Show resolved Hide resolved
},
"source": [
"QCoDeS provides two ways of adding parameters to intruments:\n",
"\n",
"- (Conventional style) Via the :meth:`qcodes.instrument.base.Instrument.add_parameter` method.\n",
"- (Decorator style) Via the :func:`@qcodes.instrument.base.add_parameter <qcodes.instrument.base.add_parameter>` decorator function.\n",
"\n",
"The more recent decorator style provides two key advantages:\n",
"\n",
"- Allows to override parameters when inheriting them from another :class:`qcodes.instrument.base.Instrument` class.\n",
caenrigen marked this conversation as resolved.
Show resolved Hide resolved
"- Allows to auto-document the parameters of your instrument driver using sphinx and a sphinx extension available in qcodes (for usage information see :mod:`qcodes.sphinx_extensions.add_parameter`).\n",
"\n",
"Despite its advantages it can be a bit less intuitive, for more details please see decorator :func:`@add_parameter <qcodes.instrument.base.add_parameter>` documentation.\n",
"\n",
"You should choose the style that fits your needs the best. Below you will find several examples of both styles, however note that the conventional style is the predominant one in the repository and documentation because it was the first to be implemented."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Functions\n",
"\n",
"Similar to parameters QCoDeS instruments implement the concept of functions that can be added to the instrument via `add_function`. They are meant to implement simple actions on the instrument such as resetting it. However, the functions do not add any value over normal python methods in the driver Class and we are planning to eventually remove them from QCoDeS. **We therefore encourage any driver developer to not use function in any new driver**.\n",
Expand All @@ -98,25 +153,187 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"## Logging\n",
"Every QCoDeS module should have its own logger that is named with the name of the module. So to create a logger put a line at the top of the module like this:\n",
"```\n",
"log = logging.getLogger(__name__)\n",
"```\n",
"Use this logger only to log messages that are not originating from an `Instrument` instance. For messages from within an instrument instance use the `log` member of the `Instrument` class, e.g\n",
"```\n",
"self.log.info(f\"Could not connect at {address}\")\n",
"```\n",
"This way the instrument name will be prepended to the log message and the log messages can be filtered according to the instrument they originate from. See the example notebook of the logger module for more info ([offline](../logging/logging_example.ipynb),[online](https://nbviewer.jupyter.org/github/QCoDeS/Qcodes/tree/master/docs/examples/logging/logging_example.ipynb)).\n",
"## Simple driver examples (Decorator style)"
caenrigen marked this conversation as resolved.
Show resolved Hide resolved
]
},
{
"cell_type": "raw",
"metadata": {
"raw_mimetype": "text/restructuredtext"
},
"source": [
"Here we showcase a few simple uses cases of adding parameters to an instrument using the decorator style.\n",
"\n",
"When creating a nested `Instrument`, like e.g. something like the `InstrumentChannel` class, that has a `_parent` property, make sure that this property gets set before calling the `super().__init__` method, so that the full name of the instrument gets resolved correctly for the logging.\n"
"For more advanced functionality, e.g., overriding parameters please see the examples provided in the documentation of the \n",
":func:`@qcodes.instrument.base.add_parameter <qcodes.instrument.base.add_parameter>` decorator function. (The examples below are also available there with highlighted source-code that is easier to read.)\n",
"\n",
"An instrument with a :class:`~qcodes.instrument.parameter.ManualParameter`:"
]
},
{
"cell_type": "raw",
"metadata": {
"raw_mimetype": "text/restructuredtext"
},
"source": [
".. NB code highlighting does not seem to work for literal included within a notebook\n",
"\n",
".. literalinclude:: ../../../qcodes/instrument_drivers/new_style.py\n",
" :pyobject: ManualInstrument"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"m_instr:\n",
"\tparameter value\n",
"--------------------------------------------------------------------------------\n",
"IDN :\t{'vendor': None, 'model': 'm_instr', 'serial': None, 'firmware': None} \n",
"time :\t3 (s)\n"
]
}
],
"source": [
"from qcodes.instrument_drivers.new_style import ManualInstrument\n",
"m_instr = ManualInstrument(name=\"m_instr\")\n",
"m_instr.print_readable_snapshot(update=True)"
]
},
{
"cell_type": "raw",
"metadata": {
"raw_mimetype": "text/restructuredtext"
},
"source": [
"The parameters will be added to the instrument only during its\n",
":code:`__init__`. This means that when using this style of adding parameters we\n",
caenrigen marked this conversation as resolved.
Show resolved Hide resolved
"do not have access to the :code:`self` object. As a workaround the class\n",
":class:`qcodes.instrument.base.InstanceAttr` can be used to reference an\n",
"attribute that is expected to be available when the instrument is initialized:\n",
"\n",
".. literalinclude:: ../../../qcodes/instrument_drivers/new_style.py\n",
" :pyobject: InstrumentWithCmds"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"iwc_instr:\n",
"\tparameter value\n",
"--------------------------------------------------------------------------------\n",
"IDN :\t{'vendor': None, 'model': 'iwc_instr', 'serial': None, 'firmware': None} \n",
"freq :\t1e+09 (Hz)\n"
]
}
],
"source": [
"from qcodes.instrument_drivers.new_style import InstrumentWithCmds\n",
"iwc_instr = InstrumentWithCmds(name=\"iwc_instr\")\n",
"iwc_instr.print_readable_snapshot(update=True)"
]
},
{
"cell_type": "raw",
"metadata": {
"raw_mimetype": "text/restructuredtext"
},
"source": [
"In some cases you might need more control over when exactly the parameters\n",
"should be added to the instrument, e.g., when some information needed to create\n",
"the parameters is know only when an instance of the Instrument is created:\n",
caenrigen marked this conversation as resolved.
Show resolved Hide resolved
"\n",
".. literalinclude:: ../../../qcodes/instrument_drivers/new_style.py\n",
" :pyobject: InstrumentWithInitValue"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"scrolled": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"iwv_instr:\n",
"\tparameter value\n",
"--------------------------------------------------------------------------------\n",
"IDN :\t{'vendor': None, 'model': 'iwv_instr', 'serial': None, 'firmware': None} \n",
"time :\t123 (s)\n"
]
}
],
"source": [
"from qcodes.instrument_drivers.new_style import InstrumentWithInitValue\n",
"iwv_instr = InstrumentWithInitValue(name=\"iwv_instr\", some_arg=123)\n",
"iwv_instr.print_readable_snapshot(update=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Auto-documented instrument drivers (Decorator style only)\n",
"\n",
"If you adopt the decorator style for adding parameters to an `Instrument` class you will be able to automatically build documentation for the parameters of your driver using the QCoDeS Sphinx extension."
]
},
{
"cell_type": "raw",
"metadata": {
"raw_mimetype": "text/restructuredtext"
},
"source": [
"Below you will find an example of the documentation produced by sphinx when making use\n",
"of the :mod:`qcodes.sphinx_extensions.add_parameter` and the new\n",
caenrigen marked this conversation as resolved.
Show resolved Hide resolved
":func:`@add_parameter <qcodes.instrument.base.add_parameter>` decorator style of\n",
"adding parameters to an instrument.\n",
"\n",
"For usage details on building such documentation see :mod:`qcodes.sphinx_extensions.add_parameter`\n",
"documentation.\n",
"\n",
".. _intrument_drivers_new_style_docs:\n",
"\n",
".. NB the `autoclass` would output too many inherited methods so we do some manual tricks here.\n",
caenrigen marked this conversation as resolved.
Show resolved Hide resolved
"\n",
".. py:class:: qcodes.instrument_drivers.new_style.ManualInstrument\n",
" \n",
" .. automethod:: qcodes.instrument_drivers.new_style.ManualInstrument._parameter_time\n",
" \n",
".. py:class:: qcodes.instrument_drivers.new_style.InstrumentWithCmds\n",
"\n",
" .. automethod:: qcodes.instrument_drivers.new_style.InstrumentWithCmds._parameter_freq\n",
"\n",
".. py:class:: qcodes.instrument_drivers.new_style.InstrumentWithInitValue\n",
"\n",
" .. automethod:: qcodes.instrument_drivers.new_style.InstrumentWithInitValue.__init__\n",
"\n",
" .. automethod:: qcodes.instrument_drivers.new_style.InstrumentWithInitValue._parameter_time"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## VisaInstrument: Simple example\n",
"## VisaInstrument: Simple example (Conventional style)\n",
"The Weinschel 8320 driver is about as basic a driver as you can get. It only defines one parameter, \"attenuation\". All the comments here are my additions to describe what's happening."
]
},
Expand Down Expand Up @@ -171,7 +388,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"## VisaInstrument: a more involved example\n",
"## VisaInstrument: a more involved example (Conventional style)\n",
"\n",
"The Keithley 2600 sourcemeter driver uses two channels. The actual driver is quite long, so here we show an abridged version that has:\n",
"\n",
Expand Down Expand Up @@ -307,7 +524,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"## DLL-based instruments\n",
"## DLL-based instruments (Conventional style)\n",
caenrigen marked this conversation as resolved.
Show resolved Hide resolved
"The Alazar cards use their own DLL. C interfaces tend to need a lot of boilerplate, so I'm not going to include it all. The key is: use `Instrument` directly, load the DLL, and have parameters interact with it."
]
},
Expand Down Expand Up @@ -652,16 +869,10 @@
"* An IPython notebook that documents the usage of the instrument should be added to `docs/example/driver_examples/Qcodes example with <company> <model>.ipynb` Note that we execute notebooks by default as part of the docs build. That is usually not possible for instrument examples so we want to disable the execution. This can be done as described [here](https://nbsphinx.readthedocs.io/en/latest/never-execute.html) editing the notebooks metadata accessible via `Edit/Edit Notebook Metadata` from the notebook interface.\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"celltoolbar": "Raw Cell Format",
"kernelspec": {
"display_name": "Python 3",
"language": "python",
Expand All @@ -677,7 +888,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.9"
"version": "3.8.8"
},
"toc": {
"base_numbering": 1,
Expand Down
Loading