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
Changes from 5 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
97 changes: 97 additions & 0 deletions qcodes/instrument/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
cast,
)

caenrigen marked this conversation as resolved.
Show resolved Hide resolved
from functools import wraps
import inspect

import numpy as np

from qcodes.logger.instrument_logger import get_instrument_logger
Expand Down Expand Up @@ -74,6 +77,12 @@ def __init__(self, name: str, metadata: Optional[Mapping[Any, Any]] = None) -> N

self.log = get_instrument_logger(self, __name__)

if getattr(self, "_call_add_params_from_decorated_methods", True):
# setting self._call_add_params_from_decorated_methods=False before calling
# `super().__init__()` gives more control in driver whose parameters require
# information available only in the `__init__()` of a driver.
self._add_params_from_decorated_methods()

@property
def name(self) -> str:
"""Name of the instrument"""
Expand Down Expand Up @@ -401,6 +410,94 @@ def validate_status(self, verbose: bool = False) -> None:
print(f'validate_status: param {k}: {value}')
p.validate(value)

def _add_params_from_decorated_methods(self):

def special_kwargs_to_meth(self, kwarg_value):
"""
Returns the attribute that has the name `kwarg_value.attr_name`
of this instance if the kwarg_value is of type `InstanceAttr`.
"""
if isinstance(kwarg_value, InstanceAttr):
return getattr(self, kwarg_value.attr_name)
caenrigen marked this conversation as resolved.
Show resolved Hide resolved
return kwarg_value

# decorated methods are required to have a specific prefix
has_parameter_prefix = lambda s: s.startswith(DECORATED_METHOD_PREFIX)

for obj_name in filter(has_parameter_prefix, dir(self)):
meth = getattr(self, obj_name)
# the `@add_parameter` decorator adds an attribute we check for here
if hasattr(meth, ADD_PARAMETER_ATTR_NAME):
kwargs = {
key: special_kwargs_to_meth(self, val.default)
for key, val in sorted(inspect.signature(meth).parameters.items())
caenrigen marked this conversation as resolved.
Show resolved Hide resolved
}
self.add_parameter(
name=meth.__name__[len(DECORATED_METHOD_PREFIX):],
docstring=meth.__doc__,
caenrigen marked this conversation as resolved.
Show resolved Hide resolved
**kwargs,
)


class InstanceAttr:
"""
An auxiliary class to be used together with the
:obj:`qcodes.instrument.base.add_parameter` decorator to allow adding parameters
that require information available only during the `__init__()` of an Instrument
subclass.
"""
def __init__(self, attr_name: str):
"""Instantiates the class and saves the passed in attr_name."""
self.attr_name: str = attr_name

def __repr__(self):
"""Return the class name and `self.attr_name`."""
return f"{self.__class__.__name__}('{self.attr_name}')"


DECORATED_METHOD_PREFIX = "_parameter_"
"""
A constant defining the prefix of the methods on which
:obj:`qcodes.instrument.base.add_parameter` decorator can be used. The intention is to
keep the name of these methods fairly unique and private to avoid any foreseeable clash.
"""

ADD_PARAMETER_ATTR_NAME = "_add_parameter"
"""
A constant defining the name of the attribute set by
:obj:`qcodes.instrument.base.add_parameter` decorator to flag a method that will be
converted to parameter.
"""

def add_parameter(method):
caenrigen marked this conversation as resolved.
Show resolved Hide resolved
"""
caenrigen marked this conversation as resolved.
Show resolved Hide resolved
A decorator function that wraps a method of an Instrument subclass such that the
new method will be converted into the corresponding :code:`param_class`
in the :code:`_add_params_from_decorated_methods`.

Args:
method: The method to be wrapped and flagged to be converted to parameter.
"""

if DECORATED_METHOD_PREFIX not in method.__name__:
caenrigen marked this conversation as resolved.
Show resolved Hide resolved
raise ValueError(
f"Only methods prefixed with '{DECORATED_METHOD_PREFIX}' can be decorated "
caenrigen marked this conversation as resolved.
Show resolved Hide resolved
f"with this decorator."
caenrigen marked this conversation as resolved.
Show resolved Hide resolved
)

@wraps(method) # preserves info like `__doc__` and signature
caenrigen marked this conversation as resolved.
Show resolved Hide resolved
def kwargs_and_doc_container(self, *args, **kwargs):
raise RuntimeError(
f"Method not intended to be called.\n"
f"'{method.__name__}' is a special method used as information container "
caenrigen marked this conversation as resolved.
Show resolved Hide resolved
f"for creating and assigning parameters to {self}."
)

# special attribute to flag method for conversion to parameter
setattr(kwargs_and_doc_container, ADD_PARAMETER_ATTR_NAME, True)

return kwargs_and_doc_container


class AbstractInstrument(ABC):
"""ABC that is useful for defining mixin classes for Instrument class"""
Expand Down