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

Harmonise instrument name definition pattern, consistently name the instrument connection argument "adapter" #659

Merged
merged 5 commits into from
Nov 29, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ Upcoming version
New adapter and instrument mechanics
------------------------------------
- Channel class added. Instrument.channels and Instrument.ch_X (X is any channel name) are reserved for channel implementations.
- All instruments are required to accept a :code:`name` argument.

Version 0.11.0 (2022-11-19)
===========================
Expand Down
31 changes: 16 additions & 15 deletions docs/dev/adding_instruments.rst
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,10 @@ This is a minimal instrument definition:
class Extreme5000(Instrument):
"""Control the imaginary Extreme 5000 instrument."""

def __init__(self, adapter, **kwargs):
def __init__(self, adapter, name="Extreme 5000", **kwargs):
super().__init__(
adapter,
"Extreme 5000",
name,
**kwargs
)

Expand Down Expand Up @@ -211,13 +211,14 @@ As our signal values are often integers, the most appropriate enum types are :co
:code:`IntFlags` are used by many instruments for the purpose just demonstrated.

The status property could look like this:

.. testcode::

status = Instrument.measurement(
BenediktBurger marked this conversation as resolved.
Show resolved Hide resolved
"STB?",
"""Measure the status of the device as enum.""",
get_process=lambda v: ErrorCode(v),
)
)

.. _default_connection_settings:

Expand All @@ -236,10 +237,10 @@ The simplest version, suitable when the instrument connection needs default sett

.. code-block:: python

def __init__(self, adapter, **kwargs):
def __init__(self, adapter, name="Extreme 5000", **kwargs):
BenediktBurger marked this conversation as resolved.
Show resolved Hide resolved
super().__init__(
adapter,
"Extreme 5000",
name,
**kwargs
)

Expand All @@ -248,10 +249,10 @@ This is suitable when the instrument has one type of interface, or any defaults

.. code-block:: python

def __init__(self, adapter, baud_rate=2400, **kwargs):
def __init__(self, adapter, name="Extreme 5000", baud_rate=2400, **kwargs):
super().__init__(
adapter,
"Extreme 5000",
name,
baud_rate=baud_rate,
**kwargs
)
Expand All @@ -260,11 +261,11 @@ If you want to set defaults, but they don't need to be prominently exposed for r

.. code-block:: python

def __init__(self, adapter, **kwargs):
def __init__(self, adapter, name="Extreme 5000", **kwargs):
kwargs.setdefault('timeout', 1500)
super().__init__(
adapter,
"Extreme 5000",
name,
**kwargs
)

Expand All @@ -279,11 +280,11 @@ These then contain a *dictionary* with the settings specific to the respective i

.. code-block:: python

def __init__(self, adapter, baud_rate=2400, **kwargs):
def __init__(self, adapter, name="Extreme 5000", baud_rate=2400, **kwargs):
kwargs.setdefault('timeout', 1500)
super().__init__(
adapter,
"Extreme 5000",
name,
gpib=dict(enable_repeat_addressing=False,
read_termination='\r'),
asrl={'baud_rate': baud_rate,
Expand Down Expand Up @@ -1003,8 +1004,8 @@ Additionally, the device needs some time after it received a command, before it
:param address: The device address for the communication.
:param query_delay: Wait time after writing and before reading in seconds.
"""
def __init__(self, adapter, address=0, query_delay=0.1):
super().__init__(adapter, "ExtremeCommunication")
def __init__(self, adapter, name="ExtremeCommunication", address=0, query_delay=0.1):
super().__init__(adapter, name)
self.address = f"{address:03}"
self.query_delay = query_delay

Expand Down Expand Up @@ -1052,8 +1053,8 @@ Some devices do not expect ASCII strings but raw bytes. In those cases, you can

class ExtremeBytes(Instrument):
"""Control the ExtremeBytes instrument with byte-based communication."""
def __init__(self, adapter):
super().__init__(adapter, "ExtremeBytes")
def __init__(self, adapter, name="ExtremeBytes"):
super().__init__(adapter, name)

def write(self, command):
"""Write to the device according to the comma separated command.
Expand Down
4 changes: 2 additions & 2 deletions pymeasure/instruments/temptronic/temptronic_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -556,8 +556,8 @@ class ATSBase(Instrument):
dynamic=True
)

def __init__(self, adapter, **kwargs):
super().__init__(adapter, query_delay=0.05, **kwargs)
def __init__(self, adapter, name="ATSBase", **kwargs):
super().__init__(adapter, name=name, query_delay=0.05, **kwargs)

def reset(self):
"""Reset (force) the System to the Operator screen.
Expand Down
105 changes: 98 additions & 7 deletions tests/instruments/test_all_instruments.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,18 +50,109 @@

# Instruments unable to accept an Adapter instance.
proper_adapters = ["IBeamSmart", "ANC300Controller"]
# Instruments with communication in their __init__ which consequently fails.
init = ["ThorlabsPM100USB", "Keithley2700", "TC038", "Agilent34450A",
"AWG401x_AWG", "AWG401x_AFG", "VARX", "HP8116A"]
# Instruments which require more input arguments
init.extend(["Instrument", "ATSBase"])
# Instruments with communication in their __init__, which consequently fails.
need_init_communication = [
"ThorlabsPM100USB",
"Keithley2700",
"TC038",
"Agilent34450A",
"AWG401x_AWG",
"AWG401x_AFG",
"VARX",
"HP8116A",
]


@pytest.mark.parametrize("cls", devices)
def test_args(cls):
def test_adapter_arg(cls):
"Test that every instrument has adapter as their input argument"
if cls.__name__ in proper_adapters:
pytest.skip(f"{cls.__name__} does not accept an Adapter instance.")
elif cls.__name__ in init:
elif cls.__name__ in need_init_communication:
pytest.skip(f"{cls.__name__} requires communication in init.")
elif cls.__name__ == "Instrument":
pytest.skip("`Instrument` requires a `name` parameter.")
cls(adapter=MagicMock())


# Instruments which do not yet accept "name" argument
nameless_instruments = [
"AdvantestR3767CG",
"Agilent33220A",
"Agilent33500",
"Agilent33521A",
"Agilent34410A",
"Agilent4156",
"Agilent8257D",
"Agilent8722ES",
"AgilentB1500",
"AgilentE4408B",
"AgilentE4980",
"Ametek7270",
"AMI430",
"DPSeriesMotorController",
"APSIN12G",
"AnritsuMG3692C",
"AnritsuMS9740A",
"Danfysik8500",
"SM7045D",
"EurotestHPP120256",
"Fluke7341",
"FWBell5080",
"ND287",
"HP33120A",
"HP3437A",
"HP34401A",
"HP3478A",
"HP6632A",
"HP6633A",
"HP6634A",
"HP8657B",
"Keithley2000",
"Keithley2306",
"Keithley2400",
"Keithley2450",
"Keithley2600",
"Keithley2750",
"Keithley6221",
"Keithley6517B",
"KeysightDSOX1102G",
"KeysightN5767A",
"KeysightN7776C",
"LakeShore331",
"LakeShore421",
"LakeShore425",
"ESP300",
"ParkerGV6",
"razorbillRP100",
"FSL",
"SFM",
"SPD1168X",
"SPD1305X",
"DSP7265",
"SG380",
"SR510",
"SR570",
"SR830",
"SR860",
"AFG3152C",
"TDS2000",
"ATS525",
"ATS545",
"ECO560",
"Thermotron3800",
"ThorlabsPro8000",
"Yokogawa7651",
"YokogawaGS200",
]


@pytest.mark.parametrize("cls", devices)
def test_name_argument(cls):
"Test that every instrument accepts a name argument"
if cls.__name__ in (*proper_adapters, *need_init_communication):
pytest.skip(f"{cls.__name__} cannot be tested without communication.")
elif cls.__name__ in nameless_instruments:
pytest.skip(f"{cls.__name__} does not accept a name argument yet.")
inst = cls(adapter=MagicMock(), name="Name_Test")
assert inst.name == "Name_Test"