<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 async def query( cls, *, hostnames: typing.Iterable[str] = None, domains: typing.Iterable[str] = None, zones: typing.Iterable[str] = None, macs: typing.Iterable[str] = None, system_ids: typing.Iterable[str] = None, agent_name: str = None, level: typing.Union[Level, int, str] = None, before: int = None, after: int = None, limit: int = None, owner: typing.Union[User, str] = None):
 """Query MAAS for matching events."""


        if before is not None and after is not None:
            raise ValueError("Specify either `before` or `after`, not both.")

        params = {}

        if hostnames is not None:
            params["hostname"] = list(hostnames)
        if domains is not None:
            params["domain"] = list(domains)
        if zones is not None:
            params["zone"] = list(zones)
        if macs is not None:
            params["mac_address"] = list(macs)
        if system_ids is not None:
            params["id"] = list(system_ids)
        if agent_name is not None:
            params["agent_name"] = [agent_name]
        if level is not None:
            level = Level.normalise(level)
            params["level"] = [level.name]
        if before is not None:
            params["before"] = ["{:d}".format(before)]
        if after is not None:
            params["after"] = ["{:d}".format(after)]
        if limit is not None:
            params["limit"] = ["{:d}".format(limit)]
        if owner is not None:
            if isinstance(owner, User):
                params["owner"] = [owner.username]
            elif isinstance(owner, str):
                params["owner"] = [owner]
            else:
                raise TypeError(
                    "owner must be either User or str, not %s" % (
                        type(owner).__name__))

        data = await cls._handler.query(**params)
        return cls(data)