Пример #1
0
 def testExtend(self):
     """MultiValue: Extending a list converts all to required type"""
     multival = MultiValue(IS, [1, 5, 10])
     multival.extend(['7', 42])
     assert isinstance(multival[-2], IS)
     assert isinstance(multival[-1], IS)
     assert 7 == multival[-2]
Пример #2
0
 def testAppend(self):
     """MultiValue: Append of item converts it to required type..."""
     multival = MultiValue(IS, [1, 5, 10])
     multival.append('5')
     self.assertTrue(isinstance(multival[-1], IS))
     self.assertEqual(multival[-1], 5,
                      "Item set by append is not correct value")
Пример #3
0
 def testAppend(self):
     """MultiValue: Append of item converts it to required type..."""
     multival = MultiValue(IS, [1, 5, 10])
     multival.append('5')
     self.assertTrue(isinstance(multival[-1], IS))
     self.assertEqual(multival[-1], 5,
                      "Item set by append is not correct value")
Пример #4
0
 def test_multivalue_DA(self):
     """Write DA/DT/TM data elements.........."""
     multi_DA_expected = (date(1961, 8, 4), date(1963, 11, 22))
     DA_expected = date(1961, 8, 4)
     tzinfo = tzoffset('-0600', -21600)
     multi_DT_expected = (datetime(1961, 8, 4),
                          datetime(1963, 11, 22, 12, 30, 0, 0,
                                   tzoffset('-0600', -21600)))
     multi_TM_expected = (time(1, 23, 45), time(11, 11, 11))
     TM_expected = time(11, 11, 11, 1)
     ds = read_file(datetime_name)
     # Add date/time data elements
     ds.CalibrationDate = MultiValue(DA, multi_DA_expected)
     ds.DateOfLastCalibration = DA(DA_expected)
     ds.ReferencedDateTime = MultiValue(DT, multi_DT_expected)
     ds.CalibrationTime = MultiValue(TM, multi_TM_expected)
     ds.TimeOfLastCalibration = TM(TM_expected)
     ds.save_as(datetime_out)
     # Now read it back in and check the values are as expected
     ds = read_file(datetime_out)
     self.assertSequenceEqual(
         multi_DA_expected, ds.CalibrationDate,
         "Multiple dates not written correctly (VR=DA)")
     self.assertEqual(DA_expected, ds.DateOfLastCalibration,
                      "Date not written correctly (VR=DA)")
     self.assertSequenceEqual(
         multi_DT_expected, ds.ReferencedDateTime,
         "Multiple datetimes not written correctly (VR=DT)")
     self.assertSequenceEqual(
         multi_TM_expected, ds.CalibrationTime,
         "Multiple times not written correctly (VR=TM)")
     self.assertEqual(TM_expected, ds.TimeOfLastCalibration,
                      "Time not written correctly (VR=DA)")
     if os.path.exists(datetime_out):
         os.remove(datetime_out)  # get rid of the file
Пример #5
0
 def testExtend(self):
     """MultiValue: Extending a list converts all to required type"""
     multival = MultiValue(IS, [1, 5, 10])
     multival.extend(['7', 42])
     self.assertTrue(isinstance(multival[-2], IS))
     self.assertTrue(isinstance(multival[-1], IS))
     self.assertEqual(multival[-2], 7, "Item set by extend not correct value")
Пример #6
0
 def testExtend(self):
     """MultiValue: Extending a list converts all to required type"""
     multival = MultiValue(IS, [1, 5, 10])
     multival.extend(['7', 42])
     self.assertTrue(isinstance(multival[-2], IS))
     self.assertTrue(isinstance(multival[-1], IS))
     self.assertEqual(multival[-2], 7, "Item set by extend not correct value")
Пример #7
0
 def testIssue236DeepCopy(self):
     """MultiValue: deepcopy of MultiValue does not generate an error"""
     multival = MultiValue(IS, range(7))
     deepcopy(multival)
     multival = MultiValue(DS, range(7))
     deepcopy(multival)
     multival = MultiValue(DSfloat, range(7))
     deepcopy(multival)
Пример #8
0
 def test_str_rep(self):
     """MultiValue: test print output"""
     multival = MultiValue(IS, [])
     assert '' == str(multival)
     multival = MultiValue(str, [1, 2, 3])
     assert "['1', '2', '3']" == str(multival)
     multival = MultiValue(int, [1, 2, 3])
     assert '[1, 2, 3]' == str(multival)
     multival = MultiValue(float, [1.1, 2.2, 3.3])
     assert '[1.1, 2.2, 3.3]' == str(multival)
Пример #9
0
def generate_dicom_scans(dst, num_scans=10, intercept=0, slope=1):
    spacing = (0.4 + 0.4 * np.random.rand(num_scans, 3) +
               np.array([1 + 0.5 * np.random.rand(), 0, 0]))
    origin = np.random.randint(-200, 200, (num_scans, 3))
    for i in range(num_scans):
        num_slices = np.random.randint(128, 169)
        scan_id = np.random.randint(2**16)
        scan_data = np.random.randint(0, 256, (num_slices, 128, 128))
        folder = os.path.join(dst,
                              hex(scan_id).replace('x', '').upper().zfill(8))

        if not os.path.exists(folder):
            os.makedirs(folder)

        for k in range(num_slices):
            slice_name = (hex(scan_id + k).replace('x', '').upper().zfill(8))
            filename = os.path.join(folder, slice_name)
            pixel_array = (scan_data[k, ...] - intercept) / slope
            locZ = float(origin[i, 0] + spacing[i, 0] * k)
            locY, locX = float(origin[i, 1]), float(origin[i, 2])

            file_meta = DicomDataset()
            file_meta.MediaStorageSOPClassUID = "Secondary Capture Image Storage"
            file_meta.MediaStorateSOPInstanceUID = (hex(scan_id).replace(
                'x', '').upper().zfill(8))

            file_meta.ImplementationClassUID = slice_name

            dataset = DicomFileDataset(filename, {},
                                       file_meta=file_meta,
                                       preamble=b"\0" * 128)

            dataset.PixelData = pixel_array.astype(np.uint16).tostring()
            dataset.RescaleSlope = slope
            dataset.RescaleIntercept = intercept

            dataset.ImagePositionPatient = MultiValue(
                type_constructor=float, iterable=[locZ, locY, locX])

            dataset.PixelSpacing = MultiValue(
                type_constructor=float,
                iterable=[float(spacing[i, 1]),
                          float(spacing[i, 2])])
            dataset.SliceThickness = float(spacing[i, 0])

            dataset.Modality = 'WSD'
            dataset.Columns = pixel_array.shape[0]
            dataset.Rows = pixel_array.shape[1]
            dataset.file_meta.TransferSyntaxUID = pydicom.uid.ImplicitVRLittleEndian
            dataset.PixelRepresentation = 1
            dataset.BitsAllocated = 16
            dataset.BitsStored = 16
            dataset.SamplesPerPixel = 1

            write_file(filename, dataset)
Пример #10
0
 def testEmptyElements(self):
     """MultiValue: Empty number string elements are not converted..."""
     multival = MultiValue(DSfloat, ['1.0', ''])
     self.assertEqual(1.0, multival[0])
     self.assertEqual('', multival[1])
     multival = MultiValue(IS, ['1', ''])
     self.assertEqual(1, multival[0])
     self.assertEqual('', multival[1])
     multival = MultiValue(DSdecimal, ['1', ''])
     self.assertEqual(1, multival[0])
     self.assertEqual('', multival[1])
Пример #11
0
    def testEmptyElements(self):
        """MultiValue: Empty number string elements are not converted..."""
        multival = MultiValue(DSfloat, ['1.0', ''])
        assert 1.0 == multival[0]
        assert '' == multival[1]
        multival = MultiValue(IS, ['1', ''])
        assert 1 == multival[0]
        assert '' == multival[1]
        multival = MultiValue(DSdecimal, ['1', ''])
        assert 1 == multival[0]
        assert '' == multival[1]

        multival = MultiValue(IS, [])
        assert not multival
        assert 0 == len(multival)
Пример #12
0
    def test_empty_binary_values(self):
        """Test that assigning an empty value behaves as expected for
        non-text VRs."""
        def check_empty_binary_element(value):
            setattr(ds, tag_name, value)
            elem = ds[tag_name]
            assert bool(elem.value) is False
            assert 0 == elem.VM
            assert elem.value == value
            fp = DicomBytesIO()
            filewriter.write_dataset(fp, ds)
            ds_read = dcmread(fp, force=True)
            assert ds_read[tag_name].value is None

        non_text_vrs = {
            'AT': 'OffendingElement',
            'DS': 'PatientWeight',
            'IS': 'BeamNumber',
            'SL': 'RationalNumeratorValue',
            'SS': 'SelectorSSValue',
            'UL': 'SimpleFrameList',
            'US': 'SourceAcquisitionBeamNumber',
            'FD': 'RealWorldValueLUTData',
            'FL': 'VectorAccuracy',
            'OB': 'FillPattern',
            'OD': 'DoubleFloatPixelData',
            'OF': 'UValueData',
            'OL': 'TrackPointIndexList',
            'OW': 'TrianglePointIndexList',
            'UN': 'SelectorUNValue',
        }
        ds = Dataset()
        ds.is_little_endian = True
        # set value to new element
        for tag_name in non_text_vrs.values():
            check_empty_binary_element(None)
            del ds[tag_name]
            check_empty_binary_element([])
            del ds[tag_name]
            check_empty_binary_element(MultiValue(int, []))
            del ds[tag_name]

        # set value to existing element
        for tag_name in non_text_vrs.values():
            check_empty_binary_element(None)
            check_empty_binary_element([])
            check_empty_binary_element(MultiValue(int, []))
            check_empty_binary_element(None)
Пример #13
0
def convert_ATvalue(
    byte_string: bytes,
    is_little_endian: bool,
    struct_format: Optional[str] = None
) -> Union[BaseTag, MutableSequence[BaseTag]]:
    """Return a decoded 'AT' value.

    Parameters
    ----------
    byte_string : bytes
        The encoded 'AT' element value.
    is_little_endian : bool
        ``True`` if the value is encoded as little endian, ``False`` otherwise.
    struct_format : str, optional
        Not used.

    Returns
    -------
    BaseTag or MultiValue of BaseTag
        The decoded value(s).
    """
    length = len(byte_string)
    if length == 4:
        return convert_tag(byte_string, is_little_endian)

    # length > 4
    if length % 4 != 0:
        logger.warning("Expected length to be multiple of 4 for VR 'AT', "
                       f"got length {length}")
    return MultiValue(Tag, [
        convert_tag(byte_string, is_little_endian, offset=x)
        for x in range(0, length, 4)
    ])
Пример #14
0
def convert_PN(byte_string,
               is_little_endian,
               struct_format=None,
               encoding=None):
    """Read and return string(s) as PersonName instance(s)"""
    def get_valtype(x):
        if not in_py2:
            if encoding:
                return PersonName(x, encoding).decode()
            return PersonName(x).decode()
        return PersonName(x)

    # XXX - We have to replicate MultiString functionality
    # here because we can't decode easily here since that
    # is performed in PersonNameUnicode
    ends_with1 = byte_string.endswith(b' ')
    ends_with2 = byte_string.endswith(b'\x00')
    if byte_string and (ends_with1 or ends_with2):
        byte_string = byte_string[:-1]

    splitup = byte_string.split(b"\\")

    if len(splitup) == 1:
        return get_valtype(splitup[0])
    else:
        return MultiValue(get_valtype, splitup)
Пример #15
0
 def testSetIndex(self):
     """MultiValue: Setting list item converts it to required type"""
     multival = MultiValue(IS, [1, 5, 10])
     multival[1] = '7'
     self.assertTrue(isinstance(multival[1], IS))
     self.assertEqual(multival[1], 7,
                      "Item set by index is not correct value")
Пример #16
0
 def testDeleteIndex(self):
     """MultiValue: Deleting item at index behaves as expected..."""
     multival = MultiValue(IS, [1, 5, 10])
     del multival[1]
     self.assertEqual(2, len(multival))
     self.assertEqual(multival[0], 1)
     self.assertEqual(multival[1], 10)
Пример #17
0
def MultiString(val, valtype=str):
    """Split a bytestring by delimiters if there are any

    Parameters
    ----------
    val : bytes or str
        DICOM byte string to split up.
    valtype
        Default :class:`str`, but can be e.g. :class:`~pydicom.uid.UID` to
        overwrite to a specific type.

    Returns
    -------
    valtype or list of valtype
        The split value as `valtype` or a :class:`list` of `valtype`.
    """
    # Remove trailing blank used to pad to even length
    # 2005.05.25: also check for trailing 0, error made
    # in PET files we are converting

    while val and (val.endswith(' ') or val.endswith('\x00')):
        val = val[:-1]
    splitup = val.split("\\")

    if len(splitup) == 1:
        val = splitup[0]
        return valtype(val) if val else val
    else:
        return MultiValue(valtype, splitup)
Пример #18
0
def convert_DT_string(
    byte_string: bytes,
    is_little_endian: bool,
    struct_format: Optional[str] = None
) -> Union[str, DT, SequenceType[Union[str, DT]]]:
    """Return a decoded 'DT' value.

    Parameters
    ----------
    byte_string : bytes
        The encoded 'DT' element value.
    is_little_endian : bool
        ``True`` if the value is encoded as little endian, ``False`` otherwise.
    struct_format : str, optional
        Not used.

    Returns
    -------
    str or MultiValue of str or valuerep.DT or MultiValue of DT
        If
        :attr:`~pydicom.config.datetime_conversion` is ``True`` then returns
        :class:`~pydicom.valuerep.DT` or a :class:`list` of ``DT``, otherwise
        returns :class:`str` or ``list`` of ``str``.
    """
    if config.datetime_conversion:
        splitup = byte_string.decode(default_encoding).split("\\")
        if len(splitup) == 1:
            return _DT_from_str(splitup[0])

        return MultiValue(_DT_from_str, splitup)

    return convert_string(byte_string, is_little_endian, struct_format)
Пример #19
0
def convert_PN(byte_string, encodings=None):
    """Return a decoded 'PN' value.

    Parameters
    ----------
    byte_string : bytes or str
        The encoded 'IS' element value.
    encodings : list of str, optional
        A list of the character encoding schemes used to encode the 'PN' value.

    Returns
    -------
    valuerep.PersonName or list of PersonName
        The decoded 'PN' value(s).
    """
    def get_valtype(x):
        return PersonName(x, encodings).decode()

    if byte_string.endswith((b' ', b'\x00')):
        byte_string = byte_string[:-1]

    splitup = byte_string.split(b"\\")

    if len(splitup) == 1:
        return get_valtype(splitup[0])
    else:
        return MultiValue(get_valtype, splitup)
Пример #20
0
def convert_text(byte_string, encodings=None):
    """Return a decoded text VR value, ignoring backslashes.

    Text VRs are 'SH', 'LO' and 'UC'.

    Parameters
    ----------
    byte_string : bytes or str
        The encoded text VR element value.
    encodings : list of str, optional
        A list of the character encoding schemes used to encode the value.

    Returns
    -------
    unicode or list of unicode
        The decoded value(s) if in Python 2.
    str or list of str
        The decoded value(s) if in Python 3.
    """
    values = byte_string.split(b'\\')
    values = [convert_single_string(value, encodings) for value in values]
    if len(values) == 1:
        return values[0]
    else:
        return MultiValue(compat.text_type, values)
Пример #21
0
def convert_PN(
    byte_string: bytes, encodings: Optional[List[str]] = None
) -> Union[PersonName, MutableSequence[PersonName]]:
    """Return a decoded 'PN' value.

    Parameters
    ----------
    byte_string : bytes
        The encoded 'PN' element value.
    encodings : list of str, optional
        A list of the character encoding schemes used to encode the 'PN' value.

    Returns
    -------
    valuerep.PersonName or MultiValue of PersonName
        The decoded 'PN' value(s).
    """
    def get_valtype(x: bytes) -> PersonName:
        return PersonName(x, encodings).decode()

    b_split = byte_string.rstrip(b'\x00 ').split(b'\\')
    if len(b_split) == 1:
        return get_valtype(b_split[0])

    return MultiValue(get_valtype, b_split)
Пример #22
0
 def testSlice(self):
     """MultiValue: Setting slice converts items to required type."""
     multival = MultiValue(IS, range(7))
     multival[2:7:2] = [4, 16, 36]
     for val in multival:
         assert isinstance(val, IS)
     assert 16 == multival[4]
Пример #23
0
def convert_text(
    byte_string: bytes, encodings: Optional[List[str]] = None
) -> Union[str, MutableSequence[str]]:
    """Return a decoded text VR value, ignoring backslashes.

    Text VRs are 'SH', 'LO' and 'UC'.

    Parameters
    ----------
    byte_string : bytes
        The encoded text VR element value.
    encodings : list of str, optional
        A list of the character encoding schemes used to encode the value.

    Returns
    -------
    str or list of str
        The decoded value(s).
    """
    values = byte_string.split(b'\\')
    as_strings = [convert_single_string(value, encodings) for value in values]
    if len(as_strings) == 1:
        return as_strings[0]

    return MultiValue(str, as_strings)
Пример #24
0
def convert_ATvalue(byte_string, is_little_endian, struct_format=None):
    """Return a decoded 'AT' value.

    Parameters
    ----------
    byte_string : bytes or str
        The encoded 'AT' element value.
    is_little_endian : bool
        ``True`` if the value is encoded as little endian, ``False`` otherwise.
    struct_format : str, optional
        Not used.

    Returns
    -------
    BaseTag or list of BaseTag
        The decoded value(s).
    """
    length = len(byte_string)
    if length == 4:
        return convert_tag(byte_string, is_little_endian)

    # length > 4
    if length % 4 != 0:
        logger.warn(
            "Expected length to be multiple of 4 for VR 'AT', "
            "got length %d", length)
    return MultiValue(Tag, [
        convert_tag(byte_string, is_little_endian, offset=x)
        for x in range(0, length, 4)
    ])
Пример #25
0
def convert_TM_string(
    byte_string: bytes,
    is_little_endian: bool,
    struct_format: Optional[str] = None
) -> Union[str, TM, MutableSequence[str], MutableSequence[TM]]:
    """Return a decoded 'TM' value.

    Parameters
    ----------
    byte_string : bytes
        The encoded 'TM' element value.
    is_little_endian : bool
        ``True`` if the value is encoded as little endian, ``False`` otherwise.
    struct_format : str, optional
        Not used.

    Returns
    -------
    str or list of str or valuerep.TM or list of valuerep.TM
        If
        :attr:`~pydicom.config.datetime_conversion` is ``True`` then returns
        either :class:`~pydicom.valuerep.TM` or a :class:`list` of ``TM``,
        otherwise returns :class:`str` or ``list`` of ``str``.
    """
    if config.datetime_conversion:
        splitup = byte_string.decode(default_encoding).split("\\")
        if len(splitup) == 1:
            return _TM_from_str(splitup[0])

        return MultiValue(_TM_from_str, splitup)

    return convert_string(byte_string, is_little_endian)
Пример #26
0
def convert_text(byte_string: bytes,
                 encodings: Optional[List[str]] = None,
                 vr: str = None) -> Union[str, MutableSequence[str]]:
    """Return a decoded text VR value.

    Text VRs are 'SH', 'LO' and 'UC'.

    Parameters
    ----------
    byte_string : bytes
        The encoded text VR element value.
    encodings : list of str, optional
        A list of the character encoding schemes used to encode the value.
    vr : str
        The value representation of the element. Needed for validation.

    Returns
    -------
    str or list of str
        The decoded value(s).
    """
    values = byte_string.split(b'\\')
    as_strings = [
        convert_single_string(value, encodings, vr) for value in values
    ]
    if len(as_strings) == 1:
        return as_strings[0]

    return MultiValue(str,
                      as_strings,
                      validation_mode=config.settings.reading_validation_mode)
Пример #27
0
def convert_PN(byte_string, is_little_endian, struct_format=None, encoding=None):
    """Read and return string(s) as PersonName instance(s)"""

    # XXX - We have to replicate MultiString functionality here because we can't decode
    # easily here since that is performed in PersonNameUnicode
    if byte_string and (byte_string.endswith(b' ') or byte_string.endswith(b'\x00')):
        byte_string = byte_string[:-1]

    splitup = byte_string.split(b"\\")

    if encoding and not in_py2:
        args = (encoding,)
    else:
        args = ()

    # We would like to return string literals
    if not in_py2:
        valtype = lambda x: PersonName(x, *args).decode()
    else:
        valtype = lambda x: PersonName(x, *args)

    if len(splitup) == 1:
        return valtype(splitup[0])
    else:
        return MultiValue(valtype, splitup)
Пример #28
0
def convert_PN(byte_string, encodings=None):
    """Return a decoded 'PN' value.

    Parameters
    ----------
    byte_string : bytes or str
        The encoded 'IS' element value.
    encodings : list of str, optional
        A list of the character encoding schemes used to encode the 'PN' value.

    Returns
    -------
    valuerep.PersonName3 or list of PersonName3
        The decoded 'PN' value(s) if using Python 3.
    valuerep.PersonNameUnicode or list of PersonNameUnicode
        The decoded 'PN' value(s) if using Python 2.
    """
    def get_valtype(x):
        if not in_py2:
            return PersonName(x, encodings).decode()
        return PersonName(x, encodings)

    # XXX - We have to replicate MultiString functionality
    # here because we can't decode easily here since that
    # is performed in PersonNameUnicode
    if byte_string.endswith((b' ', b'\x00')):
        byte_string = byte_string[:-1]

    splitup = byte_string.split(b"\\")

    if len(splitup) == 1:
        return get_valtype(splitup[0])
    else:
        return MultiValue(get_valtype, splitup)
Пример #29
0
def MultiString(
    val: str,
    valtype: Optional[Union[Type[_T], Callable[[object], _T]]] = None
) -> Union[_T, SequenceType[_T]]:
    """Split a bytestring by delimiters if there are any

    Parameters
    ----------
    val : str
        The string to split up.
    valtype : type or callable, optional
        Default :class:`str`, but can be e.g. :class:`~pydicom.uid.UID` to
        overwrite to a specific type.

    Returns
    -------
    valtype or MultiValue of valtype
        The split value as `valtype` or a :class:`list` of `valtype`.
    """
    valtype = str if valtype is None else valtype
    # Remove trailing blank used to pad to even length
    # 2005.05.25: also check for trailing 0, error made
    # in PET files we are converting
    while val and val.endswith((' ', '\x00')):
        val = val[:-1]

    splitup = val.split("\\")
    if len(splitup) == 1:
        val = splitup[0]
        return valtype(val) if val else val

    return MultiValue(valtype, splitup)
Пример #30
0
def convert_TM_string(byte_string, is_little_endian, struct_format=None):
    """Return a decoded 'TM' value.

    Parameters
    ----------
    byte_string : bytes or str
        The encoded 'TM' element value.
    is_little_endian : bool
        ``True`` if the value is encoded as little endian, ``False`` otherwise.
    struct_format : str, optional
        Not used.

    Returns
    -------
    str or list of str or valuerep.TM or list of valuerep.TM
        If
        :attr:`~pydicom.config.datetime_conversion` is ``True`` then returns
        either :class:`~pydicom.valuerep.TM` or a :class:`list` of ``TM``,
        otherwise returns :class:`str` or ``list`` of ``str``.
    """
    if config.datetime_conversion:
        if not in_py2:
            byte_string = byte_string.decode(default_encoding)
        splitup = byte_string.split("\\")
        if len(splitup) == 1:
            return _TM_from_byte_string(splitup[0])
        else:
            return MultiValue(_TM_from_byte_string, splitup)
    else:
        return convert_string(byte_string, is_little_endian, struct_format)
Пример #31
0
def convert_AE_string(
    byte_string: bytes,
    is_little_endian: bool,
    struct_format: Optional[str] = None
) -> Union[str, MutableSequence[str]]:
    """Return a decoded 'AE' value.

    Elements with VR of 'AE' have non-significant leading and trailing spaces.

    Parameters
    ----------
    byte_string : bytes
        The encoded 'AE' element value.
    is_little_endian : bool
        ``True`` if the value is encoded as little endian, ``False`` otherwise.
    struct_format : str, optional
        Not used.

    Returns
    -------
    str
        The decoded 'AE' value without non-significant spaces.
    """
    # Differs from convert_string because leading spaces are non-significant
    values = byte_string.decode(default_encoding).split('\\')
    values = [s.strip() for s in values]
    if len(values) == 1:
        return values[0]

    return MultiValue(str, values)
Пример #32
0
 def testDeleteIndex(self):
     """MultiValue: Deleting item at index behaves as expected..."""
     multival = MultiValue(IS, [1, 5, 10])
     del multival[1]
     assert 2 == len(multival)
     assert 1 == multival[0]
     assert 10 == multival[1]
Пример #33
0
 def testSorting(self):
     """MultiValue: allow inline sort."""
     multival = MultiValue(DS, [12, 33, 5, 7, 1])
     multival.sort()
     self.assertEqual([1, 5, 7, 12, 33], multival)
     multival.sort(reverse=True)
     self.assertEqual([33, 12, 7, 5, 1], multival)
     multival.sort(key=str)
     self.assertEqual([1, 12, 33, 5, 7], multival)