//  [python]
def __setitem__(self, args, val):
        """Write to the HDF5 dataset from a Numpy array.

        NumPy's broadcasting rules are honored, for "simple" indexing
        (slices and integers).  For advanced indexing, the shapes must
        match.
        """
        self.log.info(f"Dataset __setitem__, args: {args}")
        use_base64 = True  # may need to set this to false below for some types

        args = args if isinstance(args, tuple) else (args,)

        # get the val dtype if we're passed a numpy array
        try:
            self.log.debug(
                f"val dtype: {val.dtype}, shape: {val.shape} metadata: {val.dtype.metadata}"
            )
            if numpy.prod(val.shape) == 0:
                self.log.info("no elements in numpy array, skipping write")
        except AttributeError:
            self.log.debug("val not ndarray")
            pass  # not a numpy object, just leave dtype as None

        if self._shape is None:
            # null space dataset
            if isinstance(val, Empty):
                return  # nothing to do
            else:
                raise TypeError("Unable to assign values to dataset with null shape")

        elif isinstance(val, Empty):
            pass  # no data

        if isinstance(val, Reference):
            # h5pyd References are just strings
            self.log.info("converting Reference to string")
            val = val.tolist()

        # Sort field indices from the slicing
        names = tuple(x for x in args if isinstance(x, str))
        args = tuple(x for x in args if not isinstance(x, str))

        # Generally we try to avoid converting the arrays on the Python
        # side.  However, for compound literals this is unavoidable.
        # For h5pyd, do extra check and convert type on client side for efficiency
        vlen = check_dtype(vlen=self.dtype)
        if vlen is not None and vlen not in (bytes, str):
            try:
                val = numpy.asarray(val, dtype=vlen)

            except ValueError:
                try:
                    val = numpy.array(
                        [numpy.array(x, dtype=self.dtype) for x in val],
                        dtype=self.dtype,
                    )
                except ValueError as e:
                    self.log.debug(
                        f"valueError converting value element by element: {e} "
                    )
            if vlen == val.dtype:
                if val.ndim > 1:
                    tmp = numpy.empty(shape=val.shape[:-1], dtype=self.dtype)
                    tmp.ravel()[:] = [
                        i
                        for i in val.reshape(
                            (
                                numpy.prod(val.shape[:-1], dtype=numpy.ulonglong),
                                val.shape[-1],
                            )
                        )
                    ]
                else:
                    tmp = numpy.array([None], dtype=self.dtype)
                    tmp[0] = val
                val = tmp

        elif (
            isinstance(val, complex) or getattr(getattr(val, "dtype", None), "kind", None) == "c"
        ):
            if self.dtype.kind != "V" or self.dtype.names != ("r", "i"):
                raise TypeError(
                    f"Wrong dataset dtype for complex number values: {self.dtype.fields}"
                )
            if isinstance(val, complex):
                val = numpy.asarray(val, dtype=type(val))
            tmp = numpy.empty(shape=val.shape, dtype=self.dtype)
            tmp["r"] = val.real
            tmp["i"] = val.imag
            val = tmp

        elif self.dtype.kind == "O" or (
            self.dtype.kind == "V" and (
                               not isinstance(val, numpy.ndarray) or val.dtype.kind != "V"
            ) and (self.dtype.subdtype is None)
        ):
            # TBD: Do we need something like the following in the above if condition:
            # (self.dtype.str != val.dtype.str)
            # for cases where the val is a numpy array but different type than self?
            if len(names) == 1 and self.dtype.fields is not None:
                # Single field selected for write, from a non-array source
                if not names[0] in self.dtype.fields:
                    raise ValueError(f"No such field for indexing: {names[0]}")
                dtype = self.dtype.fields[names[0]][0]
                cast_compound = True
            else:
                dtype = self.dtype
                cast_compound = False
            val = numpy.asarray(val, dtype=dtype, order="C")
            if cast_compound:
                val = val.astype(numpy.dtype([(names[0], dtype)]))

        elif isinstance(val, numpy.ndarray):
            # convert array if needed
            # TBD - need to handle cases where the type shape is different
            self.log.debug("got numpy array")
            if val.dtype != self.dtype and val.dtype.shape == self.dtype.shape:
                self.log.info(f"converting {val.dtype} to {self.dtype}")
                # convert array
                tmp = numpy.empty(val.shape, dtype=self.dtype)
                tmp[...] = val[...]
                val = tmp
        else:
            val = numpy.asarray(val, order="C", dtype=self.dtype)

        # Check for array dtype compatibility and convert
        mshape = None
        """
        # TBD..
        if self.dtype.subdtype is not None:
            shp = self.dtype.subdtype[1]   # type shape
            valshp = val.shape[-len(shp):]
            if valshp != shp:  # Last dimension has to match
                raise TypeError(f"When writing to array types,\
                                 last N dimensions have to match (got {valshp}, but should be {shp})")
            mtype = h5t.py_create(numpy.dtype((val.dtype, shp)))
            mshape = val.shape[0:len(val.shape)-len(shp)]
        """

        # Check for field selection
        if len(names) != 0:
            # Catch common errors
            if self.dtype.fields is None:
                raise TypeError("Illegal slicing argument (not a compound dataset)")
            mismatch = [x for x in names if x not in self.dtype.fields]
            if len(mismatch) != 0:
                mismatch = ", ".join(f"{x}" for x in mismatch)
                raise ValueError(f"Illegal slicing argument (fields {mismatch} not in dataset type)")

        # Use mtype derived from array (let DatasetID.write figure it out)
        else:
            mshape = val.shape
            # mtype = None

        mshape = val.shape
        self.log.debug(f"mshape: {mshape}")
        self.log.debug(f"data dtype: {val.dtype}")

        # Perform the dataspace selection
        selection = sel.select(self, args)
        self.log.debug(f"selection.mshape: {selection.mshape}")
        if selection.nselect == 0:
            return

        # Broadcast scalars if necessary.
        if mshape == () and selection.mshape is not None and selection.mshape != ():
            self.log.debug("broadcast scalar")
            if self.dtype.subdtype is not None:
                raise TypeError("Scalar broadcasting is not supported for array dtypes")
            val2 = numpy.empty(selection.mshape, dtype=val.dtype)
            val2[...] = val
            val = val2
            mshape = val.shape

        # Perform the write, with broadcasting
        # Be careful to pad memory shape with ones to avoid HDF5 chunking
        # glitch, which kicks in for mismatched memory/file selections
        """
        # TBD: do we need this adjustment?
        if(len(mshape) < len(self._shape)):
            mshape_pad = (1,)*(len(self._shape)-len(mshape)) + mshape
        else:
            mshape_pad = mshape
        """
        req = "/datasets/" + self.id.uuid + "/value"

        params = {}
        body = {}

        format = "json"

        if use_base64:

            if self.id.uuid.startswith("d-"):
                # server is HSDS, use binary data, use param values for selection
                format = "binary"
                body = arrayToBytes(val, vlen=vlen)
                self.log.debug(f"writing binary data, {len(body)}")
            else:
                # h5serv, base64 encode, body json for selection
                # TBD - replace with above once h5serv supports binary req
                data = val.tobytes()
                data = base64.b64encode(data)
                data = data.decode("ascii")
                body["value_base64"] = data
                self.log.debug(f"writing base64 data, {len(data)} bytes")
        else:
            if type(val) is not list:
                val = val.tolist()
            val = _decode(val)
            self.log.debug(f"writing json data, {len(val)} elements")
            body["value"] = val

        if selection.select_type != sel.H5S_SELECT_ALL:
            select_param = selection.getQueryParam()
            self.log.debug(f"got select query param: {select_param}")
            params["select"] = select_param

        # Perform write to subset of named fields within compound datatype, if any
        if len(names) > 0:
            params["fields"] = ":".join(names)

        self.PUT(req, body=body, format=format, params=params)
        """
        mspace = h5s.create_simple(mshape_pad, (h5s.UNLIMITED,)*len(mshape_pad))
        for fspace in selection.broadcast(mshape):
            self.id.write(mspace, fspace, val, mtype)
        """