Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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,718 changes: 1,714 additions & 4 deletions mdsthin/__init__.py

Large diffs are not rendered by default.

34 changes: 21 additions & 13 deletions mdsthin/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,25 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#

import os
import sys
import time
import ctypes
import socket
import getpass
import logging
import os
import socket
import sys
import time

from .message import *
from .exceptions import *
from .functions import *
from .descriptors import Descriptor, Dictionary, List, String
from .exceptions import (
STATUS_NOT_OK,
MdsException,
MDSplusException,
getException,
getExceptionFromError,
)
from .functions import dMISSING
from .internals.dtypedef import dtype_to_string
from .message import Message

INVALID_MESSAGE_ID = 0

Expand Down Expand Up @@ -453,7 +461,7 @@ def _recv(self):

return msg, data

def get(self, expr, *args):
def get(self, expr: str, *args):
"""
Evaluate an expression on the remote server and return the result. This works like
`mdsvalue()` in our other APIs.
Expand Down Expand Up @@ -495,7 +503,7 @@ def get(self, expr, *args):

return data

def getObject(self, expr, *args):
def getObject(self, expr: str, *args):
"""
Evaluate a `get()` expression, but the expression will be wrapped in 'SerializeOut'
and `deserialize()` will be called on the result. This allows you to retrieve data
Expand All @@ -513,7 +521,7 @@ def getObject(self, expr, *args):
"""
return self.get(f'SerializeOut(`({expr};))', *args).deserialize(conn=self)

def put(self, path, expr, *args):
def put(self, path: str, expr: str, *args):
"""
Put an evaluated expression into a node in the last opened MDSplus tree.

Expand Down Expand Up @@ -751,7 +759,7 @@ def __init__(self, connection: Connection):
self._queries = List()
self._result = None

def append(self, name, exp, *args):
def append(self, name: str, exp: str, *args):
"""
Add a named expression to the list to be evaluated by `execute()`.

Expand All @@ -767,7 +775,7 @@ def append(self, name, exp, *args):
'args': list(args),
}))

def remove(self, name):
def remove(self, name: str):
"""
Remove a named expression from the list

Expand Down Expand Up @@ -801,7 +809,7 @@ def execute(self):
self._result = result.deserialize()
return self._result

def get(self, name):
def get(self, name: str):
"""
Get the result of a named expression, or raise an error if the evaluation failed.

Expand Down
66 changes: 61 additions & 5 deletions mdsthin/descriptors.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,65 @@
#

import ctypes

import numpy

from .exceptions import *
from .internals.dtypedef import *
from .internals.classdef import *
from .internals.mdsdescrip import *
from .exceptions import MdsException, getExceptionFromError
from .internals.classdef import (
CLASS_A,
CLASS_APD,
CLASS_CA,
CLASS_MISSING,
CLASS_R,
CLASS_S,
class_to_string,
)
from .internals.CvtConvertFloat import convert_float, convert_float_array
from .internals.dtypedef import (
DTYPE_ACTION,
DTYPE_B,
DTYPE_BU,
DTYPE_CALL,
DTYPE_CONDITION,
DTYPE_CONGLOM,
DTYPE_D,
DTYPE_DEPENDENCY,
DTYPE_DICTIONARY,
DTYPE_DIMENSION,
DTYPE_DISPATCH,
DTYPE_F,
DTYPE_FS,
DTYPE_FT,
DTYPE_FUNCTION,
DTYPE_G,
DTYPE_IDENT,
DTYPE_L,
DTYPE_LIST,
DTYPE_LU,
DTYPE_METHOD,
DTYPE_MISSING,
DTYPE_NID,
DTYPE_OPAQUE,
DTYPE_PATH,
DTYPE_PROCEDURE,
DTYPE_PROGRAM,
DTYPE_Q,
DTYPE_QU,
DTYPE_RANGE,
DTYPE_ROUTINE,
DTYPE_SIGNAL,
DTYPE_SLOPE,
DTYPE_T,
DTYPE_TUPLE,
DTYPE_W,
DTYPE_WINDOW,
DTYPE_WITH_ERROR,
DTYPE_WITH_UNITS,
DTYPE_WU,
dtype_to_string,
get_dtype_size,
)
from .internals.mdsdescrip import mdsdsc_a_t, mdsdsc_r_t, mdsdsc_s_t, mdsdsc_t

###
### Numeric
Expand Down Expand Up @@ -492,7 +544,7 @@ def unpack(buffer, conn=None):

class DescriptorS(Descriptor):
"""
A subclass of :class:`Descriptor` representing CLASS_S types.
A subclass of :class:`Descriptor` representing CLASS_S types (static data).
"""

def __new__(cls, *args, **kwargs):
Expand Down Expand Up @@ -842,6 +894,8 @@ def __repr__(self):
###

class DescriptorA(Descriptor):
"""A subclass of :class:`Descriptor` representing CLASS_A types (array data).
"""

def __new__(cls, *args, **kwargs):
if cls is DescriptorA:
Expand Down Expand Up @@ -1224,6 +1278,7 @@ def __repr__(self):
###

class DescriptorAPD(Descriptor):
"""A subclass of :class:`Descriptor` representing CLASS_A types (`A`rray of `P`ointers to `D`ata descriptor)"""

def __new__(cls, *args, **kwargs):
if cls is DescriptorAPD:
Expand Down Expand Up @@ -1496,6 +1551,7 @@ def descs(self):
# TODO: data = Signal(); data2 = Signal(data)

class DescriptorR(Descriptor):
"""A subclass of :class:`Descriptor` representing CLASS_R types (record descriptor)."""

def __new__(cls, *args, **kwargs):
if cls is DescriptorR:
Expand Down
3 changes: 2 additions & 1 deletion mdsthin/internals/CvtConvertFloat.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@
#

import ctypes

import numpy

from .dtypedef import *
from .dtypedef import DTYPE_D, DTYPE_F, DTYPE_G, DTYPE_H

###
### VMS
Expand Down
4 changes: 2 additions & 2 deletions mdsthin/internals/mdsdescrip.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@

import ctypes

from .dtypedef import *
from .classdef import *
from .classdef import class_t, class_to_string
from .dtypedef import dtype_t, dtype_to_string

###
### mdsdsc_t
Expand Down
16 changes: 15 additions & 1 deletion mdsthin/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,21 @@

from __future__ import annotations

from .descriptors import *
import ctypes

from .descriptors import Descriptor, DescriptorA, DescriptorS
from .internals.dtypedef import (
DTYPE_D,
DTYPE_DC,
DTYPE_F,
DTYPE_FC,
DTYPE_FS,
DTYPE_FSC,
DTYPE_FT,
DTYPE_FTC,
DTYPE_MISSING,
dtype_t,
)

client_t = ctypes.c_int8

Expand Down
80 changes: 79 additions & 1 deletion mdsthin/test/connection_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,12 @@
import getpass
import unittest

import numpy

from ..connection import *
from ..descriptors import *
from ..functions import *
from ..exceptions import TreeNOT_OPEN

class ConnectionTest(unittest.TestCase):

Expand Down Expand Up @@ -61,13 +65,13 @@ def setUp(self):
def test_tdi_types(self):

tests = [
{ 'type': Int32, 'exp': '42', 'value': 42, },
{ 'type': UInt8, 'exp': '42BU', 'value': 42, },
{ 'type': UInt16, 'exp': '42WU', 'value': 42, },
{ 'type': UInt32, 'exp': '42LU', 'value': 42, },
{ 'type': UInt64, 'exp': '42QU', 'value': 42, },
{ 'type': Int8, 'exp': '42B', 'value': 42, },
{ 'type': Int16, 'exp': '42W', 'value': 42, },
{ 'type': Int32, 'exp': '42', 'value': 42, },
{ 'type': Int32, 'exp': '42L', 'value': 42, },
{ 'type': Int64, 'exp': '42Q', 'value': 42, },
{ 'type': Float32, 'exp': '3.14159', 'value': 3.14159, },
Expand All @@ -85,6 +89,8 @@ def test_tdi_types(self):
{ 'type': Int16Array, 'exp': '[ [2W, 4W], [6W, 8W] ]', 'value': numpy.array([ [2, 4], [6, 8] ], dtype=numpy.int16) },
{ 'type': Int32Array, 'exp': '[ [2L, 4L], [6L, 8L] ]', 'value': numpy.array([ [2, 4], [6, 8] ], dtype=numpy.int32) },
{ 'type': Int64Array, 'exp': '[ [2Q, 4Q], [6Q, 8Q] ]', 'value': numpy.array([ [2, 4], [6, 8] ], dtype=numpy.int64) },

## TODO: complete the tests with Float32Array, and Float64Array
]

for info in tests:
Expand All @@ -111,6 +117,78 @@ def test_tdi_types(self):
else:
self.assertEqual(data, info['value'])

def test_numpy_numerical_types(self):
"""Send a numerical numpy object to the MDSplus server and then retrieve.
For numpy numerical the received data should be **identical** the sent data.
This is perhaps more of an integration test as it serializes and deserializes the data.
"""

tests_data = [
# Static data (testing DescriptorS)
{"input_data": numpy.uint8(42)},
{"input_data": numpy.uint16(42)},
{"input_data": numpy.uint32(42)},
{"input_data": numpy.uint64(42)},
{"input_data": numpy.int8(42)},
{"input_data": numpy.int16(42)},
{"input_data": numpy.int32(42)},
{"input_data": numpy.int64(42)},
{"input_data": numpy.float32(42)},
{"input_data": numpy.float64(42)},
# Array data (testing DescriptorA)
{"input_data": numpy.array([[2, 4], [6, 8]], dtype=numpy.uint8)},
{"input_data": numpy.array([[2, 4], [6, 8]], dtype=numpy.uint16)},
{"input_data": numpy.array([[2, 4], [6, 8]], dtype=numpy.uint32)},
{"input_data": numpy.array([[2, 4], [6, 8]], dtype=numpy.uint64)},
{"input_data": numpy.array([[2, 4], [6, 8]], dtype=numpy.int8)},
{"input_data": numpy.array([[2, 4], [6, 8]], dtype=numpy.int16)},
{"input_data": numpy.array([[2, 4], [6, 8]], dtype=numpy.int32)},
{"input_data": numpy.array([[2, 4], [6, 8]], dtype=numpy.int64)},
{"input_data": numpy.array([[2, 4], [6, 8]], dtype=numpy.float32)},
{"input_data": numpy.array([[2, 4], [6, 8]], dtype=numpy.float64)},
]

for test_data in tests_data:
data_retrieved = self.conn.get("_x=$1", test_data['input_data']).data()
numpy.testing.assert_array_equal(data_retrieved, test_data['input_data'])

# Extra test with a tdi numerical addition
for test_data in tests_data:
data_retrieved = self.conn.get("_x=$1+3", test_data['input_data']).data()
numpy.testing.assert_array_equal(data_retrieved, test_data['input_data'] + 3)

def test_numpy_string_types(self):
"""Send a string numpy object to the MDSplus server and then retrieve.
This is perhaps more of an integration test as it serializes and deserializes the data.
"""
tests_data = [
# Static data (testing DescriptorS)
# {"input_data": numpy.str("hello world")}, # numpy.str has been removed from numpy
# Array data (testing DescriptorA)
{"input_data": numpy.array(["hello", "world", "with jagged array..."]), "expected_data": numpy.array(["hello", "world", "with jagged array..."])},
# are there any more numpy string types supported by MDSplus?
]

for test_data in tests_data:
data_retrieved = self.conn.get("_x=$1", test_data['input_data']).data()
numpy.testing.assert_array_equal(data_retrieved, test_data['expected_data'])

def test_python_native_types(self):
"""Send Python native objects to the MDSplus server and then retrieve.
This is perhaps more of an integration test as it serializes and deserializes the data.
"""
tests_data = [
# Static data (testing DescriptorS)
{"input_data": 42, "expected_data": numpy.int64(42)},
{"input_data": 42., "expected_data": numpy.float64(42)},
{"input_data": "hello world", "expected_data": "hello world"},
# Array data (testing DescriptorA)
]

for test_data in tests_data:
data_retrieved = self.conn.get("_x=$1", test_data['input_data']).data()
numpy.testing.assert_array_equal(data_retrieved, test_data['input_data'])

def test_getmany(self):

expected_result = Descriptor({
Expand Down
1 change: 1 addition & 0 deletions mdsthin/test/descriptors_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import unittest

from ..descriptors import *
from ..exceptions import TdiINVCLADTY

class DescriptorsTest(unittest.TestCase):

Expand Down
9 changes: 8 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,17 @@ authors = [
]
requires-python = ">= 3.7"
dependencies = [
"h5py",
"numpy",
"paramiko",
"PyYAML",
"tqdm",
]

[project.urls]
"Homepage" = "https://github.com/MDSplus/mdsthin"
"Bug Reports" = "https://github.com/MDSplus/mdsthin/issues"
"Source" = "https://github.com/MDSplus/mdsthin/"
"Source" = "https://github.com/MDSplus/mdsthin/"

[tool.setuptools.packages.find]
namespaces = true