Skip to content

Commit

Permalink
Merge pull request #659 from bmoneke/fix-658
Browse files Browse the repository at this point in the history
Harmonise instrument name definition pattern, consistently name the instrument connection argument "adapter"
  • Loading branch information
BenediktBurger committed Nov 29, 2022
2 parents fb73c5b + cae740a commit 4aea01d
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 24 deletions.
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(
"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):
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: 4 additions & 0 deletions pymeasure/instruments/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@
from . import andeenhagerling
from . import anritsu
from . import attocube
from . import bkprecision
from . import danfysik
from . import deltaelektronika
from . import edwards
from . import eurotest
from . import fluke
from . import fwbell
Expand All @@ -54,13 +56,15 @@
from . import ni
from . import oxfordinstruments
from . import parker
from . import pendulum
from . import razorbill
from . import rohdeschwarz
from . import siglenttechnologies
from . import signalrecovery
from . import srs
from . import tektronix
from . import temptronic
from . import texio
from . import thermotron
from . import thorlabs
from . import toptica
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
109 changes: 102 additions & 7 deletions tests/instruments/test_all_instruments.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,18 +50,113 @@

# 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",
"BKPrecision9130B",
"Danfysik8500",
"SM7045D", # deltaelektronika
"Nxds", # edwards
"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",
"CNT91", # pendulum
"razorbillRP100",
"FSL",
"SFM",
"SPD1168X", # siglenttechnologies
"SPD1305X", # siglenttechnologies
"DSP7265",
"SG380",
"SR510",
"SR570",
"SR830",
"SR860",
"AFG3152C",
"TDS2000", # tectronix
"ATS525", # temptronic
"ATS545", # temptronic
"ECO560", # temptronic
"TexioPSW360L30",
"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"

0 comments on commit 4aea01d

Please sign in to comment.