"""This module models a read-write object representation of theX-Globus-Client-Info header.The spec for X-Globus-Client-Info is documented, in brief, in the GlobusClientInfo classdocstring."""from__future__importannotationsimporttypingastfromglobus_sdkimport__version__,exc_RESERVED_CHARS=";,="
[docs]classGlobusClientInfo:""" An implementation of X-Globus-Client-Info as an object. This header encodes a mapping of multiple products to versions and potentially other information. Values can be added to a clientinfo object via the ``add()`` method. The object always initializes itself to start with product=python-sdk,version=... using the current package version information. .. rubric:: ``X-Globus-Client-Info`` Specification Header Name: ``X-Globus-Client-Info`` Header Value: - A semicolon (``;``) separated list of client information. - Client information is a comma-separated list of ``=`` delimited key-value pairs. Well-known values for client-information are: - ``product``: A unique identifier of the product. - ``version``: Relevant version information for the product. - Based on the above, the characters ``;,=`` should be considered reserved and should NOT be included in client information values to ensure proper parsing. .. rubric:: Example Headers .. code-block:: none X-Globus-Client-Info: product=python-sdk,version=3.32.1 X-Globus-Client-Info: product=python-sdk,version=3.32.1;product=cli,version=4.0.0a1 .. note:: The ``GlobusClientInfo`` object is not guaranteed to reject all invalid usages. For example, ``product`` is required to be unique per header, and users are expected to enforce this in their usage. :param update_callback: A callback function to be invoked each time the content of the GlobusClientInfo changes via ``add()`` or ``clear()``. """# noqa: E501def__init__(self,*,update_callback:t.Callable[[GlobusClientInfo],None]|None=None)->None:self.infos:list[str]=[]# set `update_callback` to `None` at first so that `add()` can run without# triggering it during `__init__`self.update_callback=Noneself.add({"product":"python-sdk","version":__version__})self.update_callback=update_callbackdef__bool__(self)->bool:"""Check if there are any values present."""returnbool(self.infos)
[docs]defformat(self)->str:"""Format as a header value."""return";".join(self.infos)
[docs]defadd(self,value:str|dict[str,str])->None:""" Add an item to the clientinfo. The item is either already formatted as a string, or is a dict containing values to format. :param value: The element to add to the client-info. If it is a dict, it may not contain reserved characters in any keys or values. If it is a string, it cannot contain the ``;`` separator. """ifnotisinstance(value,str):value=",".join(_format_items(value))elif";"invalue:raiseexc.GlobusSDKUsageError("GlobusClientInfo.add() cannot be used to add multiple items in ""an already-joined string. Add items separately instead. "f"Bad usage: '{value}'")self.infos.append(value)ifself.update_callbackisnotNone:self.update_callback(self)
[docs]defclear(self)->None:"""Empty the list of info strings and trigger the update callback."""self.infos=[]ifself.update_callbackisnotNone:self.update_callback(self)
def_format_items(info:dict[str,str])->t.Iterable[str]:"""Format the items in a dict, yielding the contents as an iterable."""forkey,valueininfo.items():_check_reserved_chars(key,value)yieldf"{key}={value}"def_check_reserved_chars(key:str,value:str)->None:"""Check a key-value pair to see if it uses reserved chars."""ifany(cinxforcin_RESERVED_CHARSforxin(key,value)):raiseexc.GlobusSDKUsageError("X-Globus-Client-Info reserved characters cannot be used in keys or "f"values. Bad usage: '{key}: {value}'")