# Path: src/pubsub/core/topicutils.py
# def stringize(topicName: Sequence[str]) -> str:
#     """
#     If topicName is a string, just return it
#     as is. If it is a topic definition object (ie an object that has
#     'msgDataSpec' as data member), return the dotted name of corresponding
#     topic. Otherwise, assume topicName is a tuple and convert it to to a
#     dotted name i.e. ('a','b','c') => 'a.b.c'. Empty name is not allowed
#     (ValueError). The reverse operation is tupleize(topicName).
#     """
#     if isinstance(topicName, str):
#         return topicName
# 
#     if hasattr(topicName, "_topicNameStr"):
#         return topicName._topicNameStr
# 
#     try:
#         name = '.'.join(topicName)
#     except Exception:
#         exc = sys.exc_info()[1]
#         raise TopicNameError(topicName, str(exc))
# 
#     return name
# 
# class WeakNone:
#     """
#     Pretend to be a weak reference to nothing. Used by ArgsInfos to
#     refer to parent when None so no if-else blocks needed.
#     """
# 
#     def __call__(self):
#         return None
# 
# Path: src/pubsub/core/annotations.py
# def annotationType(sig_obj: Any):
#     """
#     Use to forward declare an annotation type, i.e. a type that will be fully defined later but is not available
#     at the annotation location in the source code. The following example shows a MyType class with a method that
#     accepts a MyType instance; annotating this cannot be done in Python::
# 
#         class MyType:
#             def copy_from(other: MyType):  # interpreter will croak
#                 ...
# 
#     The recommended approach is to use a string annotation, but this is rather unnatural in Python code::
# 
#         class MyType:
#             def copy_from(other: 'MyType'):
#                 ...
# 
#     The annotationType function allows a more pythonic syntax:
# 
#         @annotationType
#         class MyType:
#             pass
# 
#         class MyType:
#             def copy_from(other: MyType):
#                 ...
# 
#     This decorator doesn't actually do anything to its argument.
#     """
#     return sig_obj
# 
# Path: src/pubsub/core/topicexc.py
# class MessageDataSpecError(RuntimeError):
#     """
#     Raised when an attempt is made to define a topic's Message Data
#     Specification (MDS) to something that is not valid.
# 
#     The keyword names for invalid data go in the 'args' list,
#     and the msg should state the problem and contain "%s" for the
#     args, such as MessageDataSpecError('duplicate args %s', ('arg1', 'arg2')).
#     """
# 
#     def __init__(self, msg: str, args: Sequence[str]):
#         argsMsg = msg % ','.join(args)
#         RuntimeError.__init__(self, 'Invalid message data spec: ' + argsMsg)
# 
# Path: src/pubsub/core/listener.py
# class Topic:
# class Listener:
# class IListenerExcHandler:
# class Listener:
# class ListenerValidator:
#     def __call__(self, listenerID: str, topicObj: Topic):
#     def __init__(self, callable_obj: UserListener, argsInfo: CallArgsInfo, curriedArgs: Mapping[str, Any] = None,
#                  onDead: Callable[[Listener], None] = None):
#     def name(self) -> str:
#     def typeName(self) -> str:
#     def module(self) -> ModuleType:
#     def getCallable(self) -> UserListener:
#     def isDead(self) -> bool:
#     def wantsTopicObjOnCall(self) -> bool:
#     def wantsAllMessageData(self) -> bool:
#     def setCurriedArgs(self, **curriedArgs):
#     def _unlinkFromTopic_(self):
#     def _calledWhenDead(self):
#     def __notifyOnDead(self, _: WeakRef):
#     def __eq__(self, rhs: Listener):
#     def __ne__(self, rhs: Listener):
#     def __hash__(self):
#     def __str__(self):
#     def __call__(self, kwargs: Mapping[str, Any], actualTopic: Topic, allKwargs: Mapping[str, Any] = None):
#     def __init__(self, topicArgs: Sequence[str], topicKwargs: Sequence[str]):
#     def validate(self, listener: UserListener, curriedArgNames: Sequence[str] = None) -> CallArgsInfo:
#     def isValid(self, listener: UserListener, curriedArgNames: Sequence[str] = None) -> bool:
#     def __validateArgs(self, listener: UserListener, paramsInfo: CallArgsInfo, curriedArgNames: Sequence[str]):
#     AUTO_TOPIC = _AUTO_ARG


import weakref
from typing import Tuple, List, Sequence as Seq, Mapping, Dict, Callable, Any, Optional, Union
from .topicutils import stringize, WeakNone
from .annotations import annotationType
from .topicexc import MessageDataSpecError
from .listener import getArgs as getListenerArgs, UserListener

class ArgSpecGiven:
    """
    The message data specification (MDS) for a topic.

    This consists of each argument name that listener should have in its
    signature, plus which ones are required in any sendMessage(), and a
    documentation string for each argument. This instance will be transformed
    into an ArgsInfo object which is basically a superset of that information,
    needed to ensure that the arguments specifications satisfy
    pubsub policies for chosen API version.
    """

    SPEC_GIVEN_NONE = 1  # specification not given
    SPEC_GIVEN_ALL = 3  # all args specified

    def __init__(self, argsDocs: ArgsDocs = None, reqdArgs: Seq[str] = None):
        self.reqdArgs = tuple(reqdArgs or ())

        if argsDocs is None:
            self.argsSpecType = ArgSpecGiven.SPEC_GIVEN_NONE
            self.argsDocs = {}
        else:
            self.argsSpecType = ArgSpecGiven.SPEC_GIVEN_ALL
            self.argsDocs = argsDocs

            # check that all args marked as required are in argsDocs
            missingArgs = set(self.reqdArgs).difference(self.argsDocs.keys())  # py3: iter keys ok
            if missingArgs:
                msg = 'Params [%s] missing inherited required args [%%s]' % ','.join(argsDocs.keys())  # iter keys ok

# Path: src/pubsub/core/topicutils.py
# def stringize(topicName: Sequence[str]) -> str:
#     """
#     If topicName is a string, just return it
#     as is. If it is a topic definition object (ie an object that has
#     'msgDataSpec' as data member), return the dotted name of corresponding
#     topic. Otherwise, assume topicName is a tuple and convert it to to a
#     dotted name i.e. ('a','b','c') => 'a.b.c'. Empty name is not allowed
#     (ValueError). The reverse operation is tupleize(topicName).
#     """
#     if isinstance(topicName, str):
#         return topicName
# 
#     if hasattr(topicName, "_topicNameStr"):
#         return topicName._topicNameStr
# 
#     try:
#         name = '.'.join(topicName)
#     except Exception:
#         exc = sys.exc_info()[1]
#         raise TopicNameError(topicName, str(exc))
# 
#     return name
# 
# class WeakNone:
#     """
#     Pretend to be a weak reference to nothing. Used by ArgsInfos to
#     refer to parent when None so no if-else blocks needed.
#     """
# 
#     def __call__(self):
#         return None
# 
# Path: src/pubsub/core/annotations.py
# def annotationType(sig_obj: Any):
#     """
#     Use to forward declare an annotation type, i.e. a type that will be fully defined later but is not available
#     at the annotation location in the source code. The following example shows a MyType class with a method that
#     accepts a MyType instance; annotating this cannot be done in Python::
# 
#         class MyType:
#             def copy_from(other: MyType):  # interpreter will croak
#                 ...
# 
#     The recommended approach is to use a string annotation, but this is rather unnatural in Python code::
# 
#         class MyType:
#             def copy_from(other: 'MyType'):
#                 ...
# 
#     The annotationType function allows a more pythonic syntax:
# 
#         @annotationType
#         class MyType:
#             pass
# 
#         class MyType:
#             def copy_from(other: MyType):
#                 ...
# 
#     This decorator doesn't actually do anything to its argument.
#     """
#     return sig_obj
# 
# Path: src/pubsub/core/topicexc.py
# class MessageDataSpecError(RuntimeError):
#     """
#     Raised when an attempt is made to define a topic's Message Data
#     Specification (MDS) to something that is not valid.
# 
#     The keyword names for invalid data go in the 'args' list,
#     and the msg should state the problem and contain "%s" for the
#     args, such as MessageDataSpecError('duplicate args %s', ('arg1', 'arg2')).
#     """
# 
#     def __init__(self, msg: str, args: Sequence[str]):
#         argsMsg = msg % ','.join(args)
#         RuntimeError.__init__(self, 'Invalid message data spec: ' + argsMsg)
# 
# Path: src/pubsub/core/listener.py
# class Topic:
# class Listener:
# class IListenerExcHandler:
# class Listener:
# class ListenerValidator:
#     def __call__(self, listenerID: str, topicObj: Topic):
#     def __init__(self, callable_obj: UserListener, argsInfo: CallArgsInfo, curriedArgs: Mapping[str, Any] = None,
#                  onDead: Callable[[Listener], None] = None):
#     def name(self) -> str:
#     def typeName(self) -> str:
#     def module(self) -> ModuleType:
#     def getCallable(self) -> UserListener:
#     def isDead(self) -> bool:
#     def wantsTopicObjOnCall(self) -> bool:
#     def wantsAllMessageData(self) -> bool:
#     def setCurriedArgs(self, **curriedArgs):
#     def _unlinkFromTopic_(self):
#     def _calledWhenDead(self):
#     def __notifyOnDead(self, _: WeakRef):
#     def __eq__(self, rhs: Listener):
#     def __ne__(self, rhs: Listener):
#     def __hash__(self):
#     def __str__(self):
#     def __call__(self, kwargs: Mapping[str, Any], actualTopic: Topic, allKwargs: Mapping[str, Any] = None):
#     def __init__(self, topicArgs: Sequence[str], topicKwargs: Sequence[str]):
#     def validate(self, listener: UserListener, curriedArgNames: Sequence[str] = None) -> CallArgsInfo:
#     def isValid(self, listener: UserListener, curriedArgNames: Sequence[str] = None) -> bool:
#     def __validateArgs(self, listener: UserListener, paramsInfo: CallArgsInfo, curriedArgNames: Sequence[str]):
#     AUTO_TOPIC = _AUTO_ARG


# Path: src/pubsub/core/topicargspec.py
import weakref
from typing import Tuple, List, Sequence as Seq, Mapping, Dict, Callable, Any, Optional, Union
from .topicutils import stringize, WeakNone
from .annotations import annotationType
from .topicexc import MessageDataSpecError
from .listener import getArgs as getListenerArgs, UserListener


class ArgSpecGiven:
    """
    The message data specification (MDS) for a topic.

    This consists of each argument name that listener should have in its
    signature, plus which ones are required in any sendMessage(), and a
    documentation string for each argument. This instance will be transformed
    into an ArgsInfo object which is basically a superset of that information,
    needed to ensure that the arguments specifications satisfy
    pubsub policies for chosen API version.
    """

    SPEC_GIVEN_NONE = 1  # specification not given
    SPEC_GIVEN_ALL = 3  # all args specified

    def __init__(self, argsDocs: ArgsDocs = None, reqdArgs: Seq[str] = None):
        self.reqdArgs = tuple(reqdArgs or ())

        if argsDocs is None:
            self.argsSpecType = ArgSpecGiven.SPEC_GIVEN_NONE
            self.argsDocs = {}
        else:
            self.argsSpecType = ArgSpecGiven.SPEC_GIVEN_ALL
            self.argsDocs = argsDocs

            # check that all args marked as required are in argsDocs
            missingArgs = set(self.reqdArgs).difference(self.argsDocs.keys())  # py3: iter keys ok
            if missingArgs:
                msg = 'Params [%s] missing inherited required args [%%s]' % ','.join(argsDocs.keys())  # iter keys ok

                raise MessageDataSpecError(msg, missingArgs)