File indexing completed on 2024-04-21 05:01:51
0001 """Library code for language-independent D-Bus-related code generation. 0002 0003 The master copy of this library is in the telepathy-glib repository - 0004 please make any changes there. 0005 """ 0006 0007 # Copyright (C) 2006-2008 Collabora Limited 0008 # 0009 # This library is free software; you can redistribute it and/or 0010 # modify it under the terms of the GNU Lesser General Public 0011 # License as published by the Free Software Foundation; either 0012 # version 2.1 of the License, or (at your option) any later version. 0013 # 0014 # This library is distributed in the hope that it will be useful, 0015 # but WITHOUT ANY WARRANTY; without even the implied warranty of 0016 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0017 # Lesser General Public License for more details. 0018 # 0019 # You should have received a copy of the GNU Lesser General Public 0020 # License along with this library; if not, write to the Free Software 0021 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 0022 0023 import os 0024 import sys 0025 from string import ascii_letters, digits 0026 0027 0028 NS_TP = "http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0" 0029 0030 _ASCII_ALNUM = ascii_letters + digits 0031 0032 if sys.version_info[0] >= 3: 0033 def u(s): 0034 """Return s, which must be a str literal with no non-ASCII characters. 0035 This is like a more restricted form of the Python 2 u'' syntax. 0036 """ 0037 return s.encode('ascii').decode('ascii') 0038 else: 0039 def u(s): 0040 """Return a Unicode version of s, which must be a str literal 0041 (a bytestring) in which each byte is an ASCII character. 0042 This is like a more restricted form of the u'' syntax. 0043 """ 0044 return s.decode('ascii') 0045 0046 def file_set_contents(filename, contents): 0047 try: 0048 os.remove(filename) 0049 except OSError: 0050 pass 0051 try: 0052 os.remove(filename + '.tmp') 0053 except OSError: 0054 pass 0055 0056 open(filename + '.tmp', 'wb').write(contents) 0057 os.rename(filename + '.tmp', filename) 0058 0059 def cmp_by_name(node1, node2): 0060 return cmp(node1.getAttributeNode("name").nodeValue, 0061 node2.getAttributeNode("name").nodeValue) 0062 0063 def key_by_name(node): 0064 return node.getAttributeNode("name").nodeValue 0065 0066 def escape_as_identifier(identifier): 0067 """Escape the given string to be a valid D-Bus object path or service 0068 name component, using a reversible encoding to ensure uniqueness. 0069 0070 The reversible encoding is as follows: 0071 0072 * The empty string becomes '_' 0073 * Otherwise, each non-alphanumeric character is replaced by '_' plus 0074 two lower-case hex digits; the same replacement is carried out on 0075 the first character, if it's a digit 0076 """ 0077 # '' -> '_' 0078 if not identifier: 0079 return '_' 0080 0081 # A bit of a fast path for strings which are already OK. 0082 # We deliberately omit '_' because, for reversibility, that must also 0083 # be escaped. 0084 if (identifier.strip(_ASCII_ALNUM) == '' and 0085 identifier[0] in ascii_letters): 0086 return identifier 0087 0088 # The first character may not be a digit 0089 if identifier[0] not in ascii_letters: 0090 ret = ['_%02x' % ord(identifier[0])] 0091 else: 0092 ret = [identifier[0]] 0093 0094 # Subsequent characters may be digits or ASCII letters 0095 for c in identifier[1:]: 0096 if c in _ASCII_ALNUM: 0097 ret.append(c) 0098 else: 0099 ret.append('_%02x' % ord(c)) 0100 0101 return ''.join(ret) 0102 0103 0104 def get_by_path(element, path): 0105 branches = path.split('/') 0106 branch = branches[0] 0107 0108 # Is the current branch an attribute, if so, return the attribute value 0109 if branch[0] == '@': 0110 return element.getAttribute(branch[1:]) 0111 0112 # Find matching children for the branch 0113 children = [] 0114 if branch == '..': 0115 children.append(element.parentNode) 0116 else: 0117 for x in element.childNodes: 0118 if x.localName == branch: 0119 children.append(x) 0120 0121 ret = [] 0122 # If this is not the last path element, recursively gather results from 0123 # children 0124 if len(branches) > 1: 0125 for x in children: 0126 add = get_by_path(x, '/'.join(branches[1:])) 0127 if isinstance(add, list): 0128 ret += add 0129 else: 0130 return add 0131 else: 0132 ret = children 0133 0134 return ret 0135 0136 0137 def get_docstring(element): 0138 docstring = None 0139 for x in element.childNodes: 0140 if x.namespaceURI == NS_TP and x.localName == 'docstring': 0141 docstring = x 0142 if docstring is not None: 0143 docstring = docstring.toxml().replace('\n', ' ').strip() 0144 if docstring.startswith('<tp:docstring>'): 0145 docstring = docstring[14:].lstrip() 0146 if docstring.endswith('</tp:docstring>'): 0147 docstring = docstring[:-15].rstrip() 0148 if docstring in ('<tp:docstring/>', ''): 0149 docstring = '' 0150 return docstring 0151 0152 def get_deprecated(element): 0153 text = [] 0154 for x in element.childNodes: 0155 if hasattr(x, 'data'): 0156 text.append(x.data.replace('\n', ' ').strip()) 0157 else: 0158 # This caters for tp:dbus-ref elements, but little else. 0159 if x.childNodes and hasattr(x.childNodes[0], 'data'): 0160 text.append(x.childNodes[0].data.replace('\n', ' ').strip()) 0161 return ' '.join(text) 0162 0163 def get_descendant_text(element_or_elements): 0164 if not element_or_elements: 0165 return '' 0166 if isinstance(element_or_elements, list): 0167 return ''.join(map(get_descendant_text, element_or_elements)) 0168 parts = [] 0169 for x in element_or_elements.childNodes: 0170 if x.nodeType == x.TEXT_NODE: 0171 parts.append(x.nodeValue) 0172 elif x.nodeType == x.ELEMENT_NODE: 0173 parts.append(get_descendant_text(x)) 0174 else: 0175 pass 0176 return ''.join(parts) 0177 0178 0179 class _SignatureIter: 0180 """Iterator over a D-Bus signature. Copied from dbus-python 0.71 so we 0181 can run genginterface in a limited environment with only Python 0182 (like Scratchbox). 0183 """ 0184 def __init__(self, string): 0185 self.remaining = string 0186 0187 def next(self): 0188 return self.__next__() 0189 0190 def __next__(self): 0191 if self.remaining == '': 0192 raise StopIteration 0193 0194 signature = self.remaining 0195 block_depth = 0 0196 block_type = None 0197 end = len(signature) 0198 0199 for marker in range(0, end): 0200 cur_sig = signature[marker] 0201 0202 if cur_sig == 'a': 0203 pass 0204 elif cur_sig == '{' or cur_sig == '(': 0205 if block_type == None: 0206 block_type = cur_sig 0207 0208 if block_type == cur_sig: 0209 block_depth = block_depth + 1 0210 0211 elif cur_sig == '}': 0212 if block_type == '{': 0213 block_depth = block_depth - 1 0214 0215 if block_depth == 0: 0216 end = marker 0217 break 0218 0219 elif cur_sig == ')': 0220 if block_type == '(': 0221 block_depth = block_depth - 1 0222 0223 if block_depth == 0: 0224 end = marker 0225 break 0226 0227 else: 0228 if block_depth == 0: 0229 end = marker 0230 break 0231 0232 end = end + 1 0233 self.remaining = signature[end:] 0234 return Signature(signature[0:end]) 0235 0236 0237 class Signature(str): 0238 """A string, iteration over which is by D-Bus single complete types 0239 rather than characters. 0240 """ 0241 def __iter__(self): 0242 return _SignatureIter(self) 0243 0244 0245 def xml_escape(s): 0246 s = s.replace('&', '&').replace("'", ''').replace('"', '"') 0247 return s.replace('<', '<').replace('>', '>')