"""
Wrapper classes and functions for dealing with the MMKV library, which is
used to read & write Epik, LigPrep, and ConfGen input files.
See MMKVSettings documentation for more details.
Copyright Schrodinger, LLC. All rights reserved.
"""
from schrodinger.infra import mm
from schrodinger.infra import mminit
_initializer = mminit.Initializer([mm.mmkv_initialize], [mm.mmkv_terminate])
# The following functions convert the value passed as argument to
# an array of the appropriate type. Since this value will have
# only one element, conversions are straightforward
# NOTE: These functions are also used by mmim.py
def _convert_to_float_array(value):
try:
return [float(value)]
except ValueError:
# FIXME do we really want to hide any errors this way?
return []
def _convert_to_string_array(value):
return [value]
def _convert_to_int_array(value):
try:
return [int(value)]
except ValueError:
# FIXME do we really want to hide any errors this way?
return []
def _convert_to_bool_array(value):
value = str(value).lower()
if value in ['yes', 'true', '1']:
return [True]
else:
if value not in ['no', 'false', '0']:
# FIXME We might want to raise an exception for invalid strings
print(
'WARNING: mmim.py:_convert_to_bool_array() invalid bool: "%s"' %
value)
return [False]
def _convert_to_bool(value):
"""
This function will convert a boolean represented as string to Python
"boolean" type.
"""
try:
# Treat YES/NO/TRUE/FALSE strings specially:
value = value.lower()
if value in ['yes', 'true', '1']:
value = True
elif value in ['no', 'false', '0']:
value = False
else:
# FIXME We might want to raise an exception for invalid strings
print('WARNING: mmim.py:_convert_to_bool() invalid bool: "%s"' %
value)
value = bool(value)
except AttributeError:
# <value> is not of string type (may be bool already)
value = bool(value)
return value
[docs]def get_handlers_for_key(key, arg_handlers):
"""
Find the data for the specified key in the given argument handlers
dictionary. Returns a tuple of the converter, getter, and setter
functions.
Will raise a KeyError if the wrong key value (or type) is specified,
because the exception is passed onto the getter/setter methods.
NOTE: Also used in mmim.py
"""
arg_type_endpoint = None
for endpoint in sorted(arg_handlers):
if key < endpoint:
arg_type_endpoint = endpoint
break
if arg_type_endpoint is None:
raise KeyError("Argument key %d is of an unsupported type." % key)
handlers = arg_handlers[arg_type_endpoint]
if key <= handlers[0]:
raise KeyError("Argument key %d is not in the "
"supported %s range (%d, %d)" %
(key, handlers[1], arg_type_endpoint, handlers[0]))
return handlers
[docs]class MMKVArgList:
"""
A class to provide list-like access to MMKV/MMIM array properties.
Like an actual Python list, indices start at 0, not 1. Unlike a Python
list, at this time negative indices and slices are not supported.
Note that while this class requires an MMKV/MMIM handle, it neither
initiates nor terminates the mmkv library.
"""
[docs] def __init__(self, handle, key, converter, getter, setter, get_len,
set_len):
"""
Create an instance from the MMKV/MMIM handle and key (int or string).
Takes functions to get and set indexed values, and a function to
determine the length. If these are not provided, they are looked up
based on the key provided.
get_len and set_len methods should be defined for array types only.
"""
# FIXME add ability to initialize from a Python list of strings, ints,
# floats, or booleans.
self.handle = handle
self.key = key
self._converter = converter
self._getter = getter
self._setter = setter
self._get_len = get_len
self._set_len = set_len
def __getitem__(self, index):
try:
return self._getter(self.handle, self.key, index + 1)
except mm.MmException:
# If an exception is raised, check to see if it was due to an
# index error. If it wasn't, just reraise the exception.
if index < 0 or index >= len(self):
raise IndexError("List index out of range (index "
"starts at zero)")
else:
raise
def __setitem__(self, index, value):
# Ev:67229 Attempt to convert the value to float in case it was
# specified as a string if setting indexed_arg_float type:
value = self._converter(value)[0] # Take the first element of the array
try:
return self._setter(self.handle, self.key, index + 1, value)
except mm.MmException:
# If an exception is raised, check to see if it was due to an
# index error. If it wasn't, just re-raise the exception.
if index < 0 or index >= len(self):
raise IndexError("List index out of range (index "
"starts at zero)")
else:
raise
[docs] def __len__(self):
return self._get_len(self.handle, self.key)
[docs] def setSize(self, size):
"""
Set the size of the MMKV argument list.
"""
set_count_function = self._set_len
return set_count_function(self.handle, self.key, size)
[docs] def append(self, value):
"""
Append a new value to the current MMKV arglist.
:param value: The new value to append.
:type value: str, int, bool, or float depending on the MMKV arglist
type
"""
size = len(self) + 1
self.setSize(size)
self.__setitem__(size - 1, value)
def __iter__(self):
list_len = len(self)
for i in range(list_len):
yield self.__getitem__(i)