a F `K@sddlZddlZddlmZddlmZddlmZddlmZddl m Z m Z ddl m Z ddlmZgd Zd Zd Zd d ZddZGdddeZdddZddZddZGdddeZdS)N) Parameter)get_type_hints) get_dbus_name)Signal)DBusSpecificationErrorDBusSpecification) get_dbus_type) XMLGenerator) dbus_classdbus_interface dbus_signalget_xmlaccepts_additional_arguments"are_additional_arguments_supportedZ __dbus_xml__Z-__dbus_handler_accepts_additional_arguments__cCst|td|S)a[Decorator for accepting extra arguments in a DBus method. The decorator allows the server object handler to propagate additional information about the DBus call into the decorated method. Use a dictionary of keyword arguments: .. code-block:: python @accepts_additional_arguments def Method(x: Int, y: Str, **info): pass Or use keyword only parameters: .. code-block:: python @accepts_additional_arguments def Method(x: Int, y: Str, *, call_info): pass At this moment, the library provides only the call_info argument generated by GLibServer.get_call_info, but the additional arguments can be customized in the _get_additional_arguments method of the server object handler. :param method: a DBus method :return: a DBus method with a flag T)setattr&ACCEPTS_ADDITIONAL_ARGUMENTS_ATTRIBUTEmethodr;/usr/lib/python3.9/site-packages/dasbus/server/interface.pyr5s rcCs t|tdS)zyDoes the given DBus method accept additional arguments? :param method: a DBus method :return: True or False F)getattrrrrrrrXsrc@s6eZdZdZdefddZddZddZd d ZdS) r aDBus signal. Can be used as: .. code-block:: python Signal = dbus_signal() Or as a method decorator: .. code-block:: python @dbus_signal def Signal(x: Int, y: Double): pass Signal is defined by the type hints of a decorated method. This method is accessible as: signal.definition If the signal is not defined by a method, it is expected to have no arguments and signal.definition is equal to None. NcCs||_||_d|_dS)zCreate a signal descriptor. :param definition: a definition of the emit function :param factory: a signal factory N) definitionfactoryname)selfrrrrr__init__xszdbus_signal.__init__cCs.|jdurdSdt|j||_dS)aVSet a name of the descriptor The descriptor has been assigned to the specified name. Generate a name of a private attribute that will be set to a signal in the ``__get__`` method. For example: ``__dbus_signal_my_name`` :param owner: the owning class :param name: the descriptor name Nz__{}_{})rformattype__name__lower)rownerrrrr __set_name__s  zdbus_signal.__set_name__cCs<|dur |St||jd}|dur8|}t||j||S)aGet a value of the descriptor. If the descriptor is accessed as a class attribute, return the descriptor. If the descriptor is accessed as an instance attribute, return a signal created by the signal factory. :param instance: an instance of the owning class :param owner: an owning class :return: a value of the attribute N)rrrr)rinstancer signalrrr__get__s zdbus_signal.__get__cCs tddS)zSet a value of the descriptor.zCan't set DBus signal.N)AttributeError)rr"valuerrr__set__szdbus_signal.__set__) r __module__ __qualname____doc__rrr!r$r'rrrrr as  r rcsfdd}|S)aDBus interface. A new DBus interface can be defined as: .. code-block:: python @dbus_interface class Interface(): ... The interface will be generated from the given class cls with a name interface_name and added to the DBus XML specification of the class. The XML specification is accessible as: .. code-block:: python Interface.__dbus_xml__ :param interface_name: a DBus name of the interface :param namespace: a sequence of strings cs.tgR}t||}t|t||S)N)rDBusSpecificationGeneratorgenerate_specificationrDBUS_XML_ATTRIBUTE)clsrxmlinterface_name namespacerr decorateds  z!dbus_interface..decoratedr)r1r2r3rr0rr sr cCst|}t|t||S)aDBus class. A new DBus class can be defined as: .. code-block:: python @dbus_class class Class(Interface): ... DBus class can implement DBus interfaces, but it cannot define a new interface. The DBus XML specification will be generated from implemented interfaces (inherited) and it will be accessible as: .. code-block:: python Class.__dbus_xml__ )r+r,rr-)r.r/rrrr s  r cCs&t|td}|dur"tdt|S)zReturn XML specification of an object. :param obj: an object decorated with @dbus_interface or @dbus_class :return: a string with XML specification Nz)XML specification is not defined at '{}'.)rr-rr)objZxml_specificationrrrr s r c@seZdZdZeZedZe d ddZ e ddZ e dd Z e d d Z e d d Ze ddZe ddZe ddZe ddZe ddZe ddZe ddZe ddZe ddZdS)!r+z,Class for generating DBus XML specification.z[A-Z][A-Za-z0-9]*NcCsN||}|r6|}||||||}|||<|||}|j|S)aEGenerates DBus XML specification for given class. If class defines a new interface, it will be added to the specification. :param interface_cls: class object to decorate :param str interface_name: name of the interface defined by class :return str: DBus specification in XML )_collect_interfaces_collect_standard_interfacesupdate_generate_interface_generate_node xml_generatorZelement_to_xml)r. interface_clsr1 interfacesZall_interfaces interfacenoderrrr, s   z1DBusSpecificationGenerator.generate_specificationcCs|jtj}|j|S)zCollect standard interfaces. Standard interfaces are implemented by default. :return: a dictionary of standard interfaces )r:xml_to_elementrZSTANDARD_INTERFACESget_interfaces_from_node)r.r>rrrr6'sz7DBusSpecificationGenerator._collect_standard_interfacescCsRt}tt|D]8}t|td}|s*q|j|}|j|}| |q|S)zCollect interfaces implemented by the class. Returns a dictionary that maps interface names to interface elements. :param interface_cls: a class object :return: a dictionary of implemented interfaces N) dictreversedinspectZgetmrorr-r:r?r@r7)r.r;r<memberZ member_xmlr>Znode_interfacesrrrr54s     z.DBusSpecificationGenerator._collect_interfacescCs|j|}t|D]\}}||s*q|||r8q||rP|||}n>||rh| ||}n&| |r| ||}nt d ||j||q|S)apGenerate interface defined by given class. :param interface_cls: a class object that defines the interface :param interfaces: a dictionary of implemented interfaces :param interface_name: a name of the new interface :return: a new interface element :raises DBusSpecificationError: if a class member cannot be exported z+Unsupported definition of DBus member '{}'.)r:Zcreate_interfacerCZ getmembers_is_exportable _is_defined _is_signal_generate_signal _is_property_generate_property _is_method_generate_methodrr add_child)r.r;r<r1r= member_namerDelementrrrr8Ns&      z.DBusSpecificationGenerator._generate_interfacecCst|j|S)zIs the name of a class member exportable? The name is exportable if it follows the DBus specification. Only CamelCase names are allowed. )bool NAME_PATTERN fullmatch)r.rNrrrrExsz)DBusSpecificationGenerator._is_exportablecCs@|D]2}|D](}|j|s"q|j||s2qdSqdS)zIs the member name defined in given interfaces? :param interfaces: a dictionary of interfaces :param member_name: a name of the class member :return: True if the name is defined, otherwise False TF)valuesr:Z is_memberZhas_name)r.r<rNr=rDrrrrFs   z&DBusSpecificationGenerator._is_definedcCs t|tS)z"Is the class member a DBus signal?) isinstancer r.rDrrrrGsz%DBusSpecificationGenerator._is_signalc Cst|j|}|j}|s|S||D]J\}}}|tjkrFtd|tj}|j|t ||}|j ||q$|S)zGenerate signal defined by a class member. :param member: a dbus_signal object. :param member_name: a name of the signal :return: a signal element raises DBusSpecificationError: if signal has defined return type z(Invalid return type of DBus signal '{}'.) r:Z create_signalr_iterate_parametersr DIRECTION_OUTrrcreate_parameterrrM) r.rDrNrOrr type_hint direction parameterrrrrHs&  z+DBusSpecificationGenerator._generate_signalccst|}t|}t|jddD]d}|j|j}|tjtjfvrNt |rNq$|tj kr`t d||vrvt d ||||t jfVq$|j|jurdS|jdurdSt j|jt jfVdS)zIterate over method parameters. For every parameter returns its name, a type hint and a direction. :param member: a method object :return: an iterator raises DBusSpecificationError: if parameters are invalid Nz1Only positional or keyword arguments are allowed.z!Undefined type of parameter '{}'.)rrC signaturelist parameterskindrZ VAR_KEYWORDZ KEYWORD_ONLYrZPOSITIONAL_OR_KEYWORDrrrZ DIRECTION_INZreturn_annotationemptyZRETURN_PARAMETERrW)r.rDZ type_hintsr]rr`rrrrVs2      z.DBusSpecificationGenerator._iterate_parameterscCs t|tS)z$Is the class member a DBus property?)rTpropertyrUrrrrIsz'DBusSpecificationGenerator._is_propertycCsd}d}zD|jr*||j\\}}}tj}|jrJ||j\\}}}tj}Wn"tyntd|dYn0|jr|jrtj }|durtd||j |t ||S)zGenerate DBus property defined by class member. :param member: a property object :param member_name: a property name :return: a property element raises DBusSpecificationError: if the property is invalid Nz%Undefined type of DBus property '{}'.z%DBus property '{}' is not accessible.) fsetrVrZ ACCESS_WRITEfgetZ ACCESS_READ ValueErrorrrZACCESS_READWRITEr:Zcreate_propertyr)r.rDrNaccessrY_rrrrJs4    z-DBusSpecificationGenerator._generate_propertycCst|pt|S)aIs the class member a DBus method? Ignore the difference between instance method and class method. For example: .. code-block:: python class Foo(object): def bar(cls, x): pass inspect.isfunction(Foo.bar) # True inspect.isfunction(Foo().bar) # False inspect.ismethod(Foo.bar) # False inspect.ismethod(Foo().bar) # True _is_method(Foo.bar) # True _is_method(Foo().bar) # True )rCZismethodZ isfunctionrUrrrrK'sz%DBusSpecificationGenerator._is_methodcCsH|j|}||D],\}}}|j|t||}|j||q|S)zGenerate method defined by given class member. :param member: a method object :param member_name: a name of the method :return: a method element )r:Z create_methodrVrXrrM)r.rDrNrrrYrZr[rrrrLAs  z+DBusSpecificationGenerator._generate_methodcCsH|j}|j|d|jt|D]}|j|||q,|S)zGenerate node element that specifies the given class. :param interface_cls: a class object :param interfaces: a dictionary of interfaces :return: a node element z Specifies {})r:Z create_nodeZ add_commentrrsortedkeysrM)r.r;r<r>r1rrrr9Vs  z)DBusSpecificationGenerator._generate_node)N)rr(r)r*r r:recompilerQ classmethodr,r6r5r8rErFrGrHrVrIrJrKrLr9rrrrr+s>     )    # 6  +  r+)r)rCrjrtypingrZdasbus.namespacerZ dasbus.signalrZdasbus.specificationrrZ dasbus.typingrZ dasbus.xmlr __all__r-rrrobjectr r r r r+rrrrs&       # R