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

Run mypy type checking on the codebase with Travis #824

Merged
merged 136 commits into from
Apr 4, 2018
Merged
Show file tree
Hide file tree
Changes from 124 commits
Commits
Show all changes
136 commits
Select commit Hold shift + click to select a range
c3288dc
Minimal typing config
jenshnielsen Jun 28, 2017
11e15ee
Minimal typing helpers
jenshnielsen Jun 28, 2017
fc9227d
location towards minimal typing
jenshnielsen Jun 28, 2017
e1af56f
instrument base minimal typing
jenshnielsen Jun 28, 2017
5948119
Channel improve typing
jenshnielsen Jun 28, 2017
255dc01
typing driver test
jenshnielsen Jun 28, 2017
8b21b5b
test_helpers make this valid python code
jenshnielsen Jun 28, 2017
df2bae7
improve typing parameter
jenshnielsen Jun 28, 2017
975f6aa
avoid overwriting time
jenshnielsen Jun 28, 2017
988dd89
work around for missing imports in mypy
jenshnielsen Jun 28, 2017
6df7185
keysight avoid mixing modules and None
jenshnielsen Jun 28, 2017
8221bb3
h2py fix python2 syntax
jenshnielsen Jun 28, 2017
1eb8273
Travis run mypy
jenshnielsen Jun 28, 2017
08f2bf1
data_set minimal typing
jenshnielsen Jun 28, 2017
93d9d52
location silence warning
jenshnielsen Jun 28, 2017
dfd16ef
channel silence warning
jenshnielsen Jun 28, 2017
d62023f
work around mypy issue
jenshnielsen Jun 28, 2017
05f85bd
parameter fix setpoints not defined
jenshnielsen Jun 28, 2017
f8317cb
make skipping code more robust
jenshnielsen Jun 28, 2017
4fe9dc2
Test visa fix typing issue
jenshnielsen Jun 28, 2017
9f45583
h2py minimal typing
jenshnielsen Jun 28, 2017
978a012
M3201A fix mypy import issue
jenshnielsen Jun 28, 2017
a70a293
remove unused shared_kwargs
jenshnielsen Jun 28, 2017
0ad7eb0
typo
jenshnielsen Jun 28, 2017
0049576
Annotate return type of init
jenshnielsen Jun 28, 2017
5a2a4b4
Merge remote-tracking branch 'origin/master' into mypy_exp
jenshnielsen Oct 25, 2017
f222e18
Annotate __init__ return
jenshnielsen Oct 25, 2017
bc6f504
make code easier to read by not reuseing variable name
jenshnielsen Oct 25, 2017
1fc17a9
Correct type in ka driver
jenshnielsen Oct 25, 2017
0eb8595
Better typing of parameter
jenshnielsen Oct 25, 2017
1539295
Improve typing of monitor
jenshnielsen Oct 25, 2017
e0595c3
Improve channel typing
jenshnielsen Oct 25, 2017
d34dc3f
type config
jenshnielsen Oct 26, 2017
d556e06
improve typing of dics
jenshnielsen Oct 26, 2017
5e9e750
Try to improve typing of pyqtgraph module
jenshnielsen Oct 26, 2017
ab4e8f2
According to the docs __dir__ may return any sequence so cast it to a…
jenshnielsen Oct 26, 2017
faaaf57
Explicitly check for tuple to help type inference
jenshnielsen Oct 26, 2017
c799e97
remove workaround
jenshnielsen Oct 26, 2017
0feec63
simplify typing
jenshnielsen Oct 26, 2017
86d31da
still trying to get the type of self.subplots correct
jenshnielsen Oct 26, 2017
c296e3b
Merge remote-tracking branch 'origin/master' into mypy_exp
jenshnielsen Oct 27, 2017
593904d
Type fixes and workarounds to parameter
jenshnielsen Oct 27, 2017
3ba5c30
last typing issue in parameter
jenshnielsen Oct 27, 2017
c85b093
work around mypy bug in pyqtplot
jenshnielsen Oct 27, 2017
e3341db
Better typing of CombinedParameter
jenshnielsen Oct 27, 2017
bd3af1c
remove unused imports
jenshnielsen Oct 27, 2017
97206d7
Merge branch 'master' into mypy_exp
jenshnielsen Oct 30, 2017
fd68250
Improve typing of dict
jenshnielsen Oct 30, 2017
13ac5d4
Merge branch 'master' into mypy_exp
jenshnielsen Nov 3, 2017
777d9bf
Merge branch 'master' into mypy_exp
jenshnielsen Nov 3, 2017
871429d
better way of handling get/set raw
jenshnielsen Nov 3, 2017
43a4dd0
dont leak unknow type
jenshnielsen Nov 3, 2017
da7cf53
Merge branch 'master' into mypy_exp
jenshnielsen Nov 6, 2017
3bf56b3
Merge branch 'master' into mypy_exp
jenshnielsen Nov 6, 2017
3521441
type components to help linter
jenshnielsen Nov 6, 2017
725368b
type station
jenshnielsen Nov 6, 2017
96bf530
Merge branch 'master' into mypy_exp
jenshnielsen Nov 13, 2017
5898113
Monitor is optional
jenshnielsen Nov 13, 2017
3c3b372
Merge remote-tracking branch 'origin/master' into mypy_exp
jenshnielsen Nov 14, 2017
260fcc3
no longer need to exclude deleted file
jenshnielsen Nov 14, 2017
4b5387b
dont change pyspcm
jenshnielsen Nov 14, 2017
a8b6334
Import instrument from full path
jenshnielsen Nov 15, 2017
c14da1d
no longer needed
jenshnielsen Nov 15, 2017
d0056d6
missed one
jenshnielsen Nov 15, 2017
33f28d5
correct argument to logging
jenshnielsen Nov 15, 2017
1930d8f
minimal changes to get pyspcm running with mypy
jenshnielsen Nov 15, 2017
b794b53
Merge branch 'master' into mypy_exp
jenshnielsen Nov 24, 2017
592343a
Merge branch 'master' into mypy_exp
jenshnielsen Nov 27, 2017
509a517
fix typeing for ZI driver
jenshnielsen Nov 27, 2017
cfdf926
Merge branch 'master' into mypy_exp
jenshnielsen Dec 4, 2017
fe4c9a3
Fix type annotation in gs200
jenshnielsen Dec 4, 2017
f2fbf9a
Add missing return statment
jenshnielsen Dec 4, 2017
e27ac17
remove unused attribute
jenshnielsen Dec 4, 2017
51fcabc
Merge branch 'master' into mypy_exp
jenshnielsen Dec 4, 2017
b9d0a83
Merge branch 'master' into mypy_exp
jenshnielsen Dec 5, 2017
452d48f
Merge branch 'master' into mypy_exp
jenshnielsen Dec 6, 2017
eaa3891
Merge branch 'master' into mypy_exp
jenshnielsen Dec 6, 2017
d5b1aeb
Merge branch 'master' into mypy_exp
jenshnielsen Dec 6, 2017
a831eb4
Merge branch 'master' into mypy_exp
jenshnielsen Dec 6, 2017
c7f0f71
more type annotations
jenshnielsen Dec 6, 2017
47dc5da
Merge branch 'master' into mypy_exp
jenshnielsen Dec 12, 2017
defd2e6
no longer need to change dir here
jenshnielsen Dec 12, 2017
e7a7fa3
fix typing of awg
jenshnielsen Dec 14, 2017
b54990b
channel cast to array parameter
jenshnielsen Dec 14, 2017
7f26521
mypy channel parameters
jenshnielsen Dec 14, 2017
88ad86c
restrict submodules to types that implement the correct submodule
jenshnielsen Dec 14, 2017
1f38de5
define class variables at construction time
jenshnielsen Dec 14, 2017
927f9d3
ignore
jenshnielsen Dec 14, 2017
6b88c0a
Annotate ask_raw as the subclass implementation should do
jenshnielsen Dec 14, 2017
9883c42
Merge branch 'master' into mypy_exp
jenshnielsen Dec 14, 2017
4528e9d
ignore nameerror of get_ipython
jenshnielsen Dec 14, 2017
70c9473
annotate type of flat_wfmxs
jenshnielsen Dec 14, 2017
ff47f97
Merge branch 'master' into mypy_exp
jenshnielsen Dec 18, 2017
c2b8ea4
add missing import
jenshnielsen Dec 18, 2017
54a4ecc
Merge branch 'master' into mypy_exp
jenshnielsen Dec 18, 2017
3c21716
Merge branch 'master' into mypy_exp
jenshnielsen Dec 19, 2017
f46d2f3
Merge branch 'master' into mypy_exp
jenshnielsen Dec 21, 2017
4ef3678
dont redefine type
jenshnielsen Dec 22, 2017
3edb39e
getting idn may fail
jenshnielsen Dec 22, 2017
ec62913
Annotate get_idn
jenshnielsen Dec 22, 2017
96532ac
model may be an empty string
jenshnielsen Dec 22, 2017
32600f2
annotate _mulval
jenshnielsen Dec 22, 2017
5957513
Forward instrument to baseclass instread of setting it manually
jenshnielsen Dec 22, 2017
82c2a52
better type of instrument
jenshnielsen Dec 22, 2017
b351713
dont reuse variable name
jenshnielsen Dec 22, 2017
e773ec0
check that instrument is not none
jenshnielsen Dec 22, 2017
771154e
add type to _step
jenshnielsen Dec 22, 2017
b10e938
cast instrument to inst
jenshnielsen Dec 22, 2017
ca20b8b
Check type of self_range which could be none
jenshnielsen Dec 22, 2017
b167455
Merge branch 'master' into mypy_exp
jenshnielsen Jan 17, 2018
96e58a7
Merge branch 'master' into mypy_exp
jenshnielsen Jan 26, 2018
7504c8e
Merge branch 'master' into mypy_exp
jenshnielsen Mar 6, 2018
7c28237
init returns None
jenshnielsen Mar 6, 2018
e875dfb
Correct typeing for sqlite settings
jenshnielsen Mar 6, 2018
604fd93
add missing type annotation to inits
jenshnielsen Mar 6, 2018
17dca98
fix what appears to be a real bug found by mypy
jenshnielsen Mar 6, 2018
bb7e249
dont reuse variable name
jenshnielsen Mar 9, 2018
3db5264
block mypy 0.570 due to xml issue
jenshnielsen Mar 9, 2018
616a7c8
dont reuse setpoints variable
jenshnielsen Mar 9, 2018
56547c8
Cast parameter before getting unit and label
jenshnielsen Mar 9, 2018
b362fd7
Sqlite rows can be indexed both by index and column name
jenshnielsen Mar 9, 2018
aba9deb
mypy does not like the reuse of _
jenshnielsen Mar 9, 2018
717c928
this is a list of values not a list of lists of values
jenshnielsen Mar 9, 2018
59ec2b3
Merge branch 'master' into mypy_exp
jenshnielsen Mar 9, 2018
3465ef5
Merge branch 'master' into mypy_exp
jenshnielsen Mar 14, 2018
7cebc12
Merge branch 'master' into mypy_exp
jenshnielsen Mar 28, 2018
80a436f
make sure that params_ is a list before appending
jenshnielsen Apr 4, 2018
3b2f972
there seems to be a mypy issues with alias like this
jenshnielsen Apr 4, 2018
7c771e2
help mypy a bit
jenshnielsen Apr 4, 2018
44251fc
Merge branch 'master' into mypy_exp
jenshnielsen Apr 4, 2018
1cd0264
monitor wrong exception
jenshnielsen Apr 4, 2018
8528c7f
wrong variable
jenshnielsen Apr 4, 2018
eb7b356
cast to int to help mypy
jenshnielsen Apr 4, 2018
4aaec3a
avoid using global
jenshnielsen Apr 4, 2018
e4b0fe3
python 3.6 style types
jenshnielsen Apr 4, 2018
e1003d9
Merge branch 'master' into mypy_exp
jenshnielsen Apr 4, 2018
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
5 changes: 4 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ script:
- py.test --cov=qcodes --cov-report xml --cov-config=.coveragerc
# build docs with warnings as errors
- |
cd ../docs
cd ..
mypy qcodes --ignore-missing-imports
- |
cd docs
make SPHINXOPTS="-W" html-api
- cd ..

Expand Down
4 changes: 2 additions & 2 deletions qcodes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from qcodes.config import Config

config = Config()
config = Config() # type: Config

from qcodes.version import __version__

Expand Down Expand Up @@ -84,7 +84,7 @@
del _c

try:
get_ipython() # Check if we are in iPython
get_ipython() # type: ignore # Check if we are in iPython
from qcodes.utils.magic import register_magic_class
_register_magic = config.core.get('register_magic', False)
if _register_magic is not False:
Expand Down
5 changes: 3 additions & 2 deletions qcodes/config/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from pathlib import Path

import jsonschema
from typing import Dict

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -87,8 +88,8 @@ class Config():
defaults = None
defaults_schema = None

_diff_config = {}
_diff_schema = {}
_diff_config = {} # type: Dict[str, dict]
_diff_schema = {} # type: Dict[str, dict]

def __init__(self):
self.defaults, self.defaults_schema = self.load_default()
Expand Down
3 changes: 2 additions & 1 deletion qcodes/data/data_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from traceback import format_exc
from copy import deepcopy
from collections import OrderedDict
from typing import Dict, Callable

from .gnuplot_format import GNUPlotFormat
from .io import DiskIO
Expand Down Expand Up @@ -168,7 +169,7 @@ class DataSet(DelegateAttributes):
default_formatter = GNUPlotFormat()
location_provider = FormatLocation()

background_functions = OrderedDict()
background_functions = OrderedDict() # type: Dict[str, Callable]

def __init__(self, location=None, arrays=None, formatter=None, io=None,
write_period=5):
Expand Down
4 changes: 2 additions & 2 deletions qcodes/data/location.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import re
import string

from qcodes import config
import qcodes.config

class SafeFormatter(string.Formatter):

Expand Down Expand Up @@ -83,7 +83,7 @@ class FormatLocation:
as '{date:%Y-%m-%d}' or '{counter:03}'
"""

default_fmt = config['core']['default_fmt']
default_fmt = qcodes.config['core']['default_fmt']

def __init__(self, fmt=None, fmt_date=None, fmt_time=None,
fmt_counter=None, record=None):
Expand Down
6 changes: 3 additions & 3 deletions qcodes/dataset/data_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,8 @@ def _new(self, name, exp_id, specs: SPECS = None, values=None,
Actually perform all the side effects needed for
the creation of a new dataset.
"""
_, run_id, _ = create_run(self.conn, exp_id, name,
specs, values, metadata)
_, run_id, __ = create_run(self.conn, exp_id, name,
specs, values, metadata)

# this is really the UUID (an ever increasing count in the db)
self.run_id = run_id
Expand Down Expand Up @@ -440,7 +440,7 @@ def modify_results(self, start_index: int,
flattened_keys,
flattened_values)

def add_parameter_values(self, spec: ParamSpec, values: List[VALUES]):
def add_parameter_values(self, spec: ParamSpec, values: VALUES):
"""
Add a parameter to the DataSet and associates result values with the
new parameter.
Expand Down
4 changes: 2 additions & 2 deletions qcodes/dataset/experiment_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,8 @@ def load_experiment_by_name(name: str,
for row in rows:
s = f"exp_id:{row['exp_id']} ({row['name']}-{row['sample_name']}) started at({row['start_time']})"
_repr.append(s)
_repr = "\n".join(_repr)
raise ValueError(f"Many experiments matching your request found {_repr}")
_repr_str = "\n".join(_repr)
raise ValueError(f"Many experiments matching your request found {_repr_str}")
else:
e.exp_id = rows[0]['exp_id']
return e
17 changes: 11 additions & 6 deletions qcodes/dataset/measurements.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

import qcodes as qc
from qcodes import Station
from qcodes.instrument.parameter import ArrayParameter, _BaseParameter
from qcodes.instrument.parameter import ArrayParameter, _BaseParameter, Parameter
from qcodes.dataset.experiment_container import Experiment
from qcodes.dataset.param_spec import ParamSpec
from qcodes.dataset.data_set import DataSet
Expand All @@ -37,7 +37,7 @@ def __init__(self, dataset: DataSet, write_period: float,
self._known_parameters = list(parameters.keys())
self._results: List[dict] = [] # will be filled by addResult
self._last_save_time = monotonic()
self._known_dependencies: Dict[str, str] = {}
self._known_dependencies: Dict[str, List[str]] = {}
for param, parspec in parameters.items():
if parspec.depends_on != '':
self._known_dependencies.update({str(param):
Expand Down Expand Up @@ -144,6 +144,7 @@ def add_result(self,
# For compatibility with the old Loop, setpoints are
# tuples of numbers (usually tuple(np.linspace(...))
if hasattr(value, '__len__') and not(isinstance(value, str)):
value = cast(Union[Sequence,np.ndarray], value)
res_dict.update({param: value[index]})
else:
res_dict.update({param: value})
Expand Down Expand Up @@ -390,6 +391,7 @@ def register_parameter(
name = str(parameter)

if isinstance(parameter, ArrayParameter):
parameter = cast(ArrayParameter, parameter)
if parameter.setpoint_names:
spname = (f'{parameter._instrument.name}_'
f'{parameter.setpoint_names[0]}')
Expand All @@ -408,8 +410,10 @@ def register_parameter(
label=splabel, unit=spunit)

self.parameters[spname] = sp
setpoints = setpoints if setpoints else ()
setpoints += (spname,)
my_setpoints: Tuple[Union[_BaseParameter, str], ...] = setpoints if setpoints else ()
my_setpoints += (spname,)
else:
my_setpoints = setpoints

# We currently treat ALL parameters as 'numeric' and fail to add them
# to the dataset if they can not be unraveled to fit that description
Expand All @@ -418,12 +422,13 @@ def register_parameter(
# requirement later and start saving binary blobs with the datasaver,
# but for now binary blob saving is referred to using the DataSet
# API directly
parameter = cast(Union[Parameter, ArrayParameter], parameter)
paramtype = 'numeric'
label = parameter.label
unit = parameter.unit

if setpoints:
sp_strings = [str(sp) for sp in setpoints]
if my_setpoints:
sp_strings = [str(sp) for sp in my_setpoints]
else:
sp_strings = []
if basis:
Expand Down
2 changes: 1 addition & 1 deletion qcodes/dataset/sqlite_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ def _convert_array(text: bytes) -> ndarray:
return np.load(out)


def one(curr: sqlite3.Cursor, column: str) -> Any:
def one(curr: sqlite3.Cursor, column: Union[int, str]) -> Any:
"""Get the value of one column from one row
Args:
curr: cursor to operate on
Expand Down
5 changes: 4 additions & 1 deletion qcodes/dataset/sqlite_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from typing import Tuple, Dict, Union


def _read_settings() -> Tuple[Dict[str, str],
def _read_settings() -> Tuple[Dict[str, Union[str,int]],
Dict[str, Union[bool, int, str]]]:
"""
Function to read the local SQLite settings at import time.
Expand All @@ -19,6 +19,7 @@ def _read_settings() -> Tuple[Dict[str, str],
"""
# For the limits, there are known default values
# (known from https://www.sqlite.org/limits.html)
DEFAULT_LIMITS: Dict[str, Union[str, int]]
DEFAULT_LIMITS = {'MAX_ATTACHED': 10,
'MAX_COLUMN': 2000,
'MAX_COMPOUND_SELECT': 500,
Expand All @@ -35,6 +36,7 @@ def _read_settings() -> Tuple[Dict[str, str],
opt_num = 0
resp = ''

limits: Dict[str, Union[str,int]]
limits = DEFAULT_LIMITS.copy()
settings = {}

Expand All @@ -47,6 +49,7 @@ def _read_settings() -> Tuple[Dict[str, str],
opt_num += 1
lst = resp.split('=')
if len(lst) == 2:
val: Union[str,int]
(param, val) = lst
if val.isnumeric():
val = int(val)
Expand Down
34 changes: 20 additions & 14 deletions qcodes/instrument/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@
import time
import warnings
import weakref
from typing import Sequence, Optional, Dict, Union, Callable, Any, List
from typing import Sequence, Optional, Dict, Union, Callable, Any, List, TYPE_CHECKING, cast

import numpy as np

from typing import Dict, Sequence
import numpy as np
if TYPE_CHECKING:
from qcodes.instrumet.channel import ChannelList
from qcodes.utils.helpers import DelegateAttributes, strip_attrs, full_class
from qcodes.utils.metadata import Metadatable
from qcodes.utils.validators import Anything
from .parameter import Parameter
from .parameter import Parameter, _BaseParameter
from .function import Function

log = logging.getLogger(__name__)
Expand Down Expand Up @@ -45,9 +48,9 @@ def __init__(self, name: str,
metadata: Optional[Dict]=None, **kwargs) -> None:
self.name = str(name)

self.parameters = {}
self.functions = {}
self.submodules = {}
self.parameters = {} # type: Dict[str, _BaseParameter]
self.functions = {} # type: Dict[str, Function]
self.submodules = {} # type: Dict[str, Union['InstrumentBase', 'ChannelList']]
super().__init__(**kwargs)

def add_parameter(self, name: str,
Expand Down Expand Up @@ -109,7 +112,7 @@ def add_function(self, name: str, **kwargs) -> None:
func = Function(name=name, instrument=self, **kwargs)
self.functions[name] = func

def add_submodule(self, name: str, submodule: Metadatable) -> None:
def add_submodule(self, name: str, submodule: Union['InstrumentBase', 'ChannelList']) -> None:
"""
Bind one submodule to this instrument.

Expand Down Expand Up @@ -352,7 +355,9 @@ class Instrument(InstrumentBase):

shared_kwargs = ()

_all_instruments = {}
_all_instruments = {} # type: Dict[str, weakref.ref[Instrument]]
_type = None
_instances = [] # type: List[weakref.ref]

def __init__(self, name: str,
metadata: Optional[Dict]=None, **kwargs) -> None:
Expand All @@ -369,7 +374,7 @@ def __init__(self, name: str,

self.record_instance(self)

def get_idn(self) -> Dict:
def get_idn(self) -> Dict[str, Optional[str]]:
"""
Parse a standard VISA '\*IDN?' response into an ID dict.

Expand All @@ -391,6 +396,7 @@ def get_idn(self) -> Dict:
idstr = self.ask('*IDN?')
# form is supposed to be comma-separated, but we've seen
# other separators occasionally
idparts = [] # type: List[Optional[str]]
for separator in ',;:':
# split into no more than 4 parts, so we don't lose info
idparts = [p.strip() for p in idstr.split(separator, 3)]
Expand Down Expand Up @@ -569,14 +575,14 @@ def find_instrument(cls, name: str,
if ins is None:
del cls._all_instruments[name]
raise KeyError('Instrument {} has been removed'.format(name))

inst = cast('Instrument', ins)
if instrument_class is not None:
if not isinstance(ins, instrument_class):
if not isinstance(inst, instrument_class):
raise TypeError(
'Instrument {} is {} but {} was requested'.format(
name, type(ins), instrument_class))
name, type(inst), instrument_class))

return ins
return inst

# `write_raw` and `ask_raw` are the interface to hardware #
# `write` and `ask` are standard wrappers to help with error reporting #
Expand Down Expand Up @@ -647,7 +653,7 @@ def ask(self, cmd: str) -> str:
e.args = e.args + ('asking ' + repr(cmd) + ' to ' + inst,)
raise e

def ask_raw(self, cmd: str) -> None:
def ask_raw(self, cmd: str) -> str:
"""
Low level method to write to the hardware and return a response.

Expand Down
Loading