1 """Library code for language-independent D-Bus-related code generation.
3 The master copy of this library is in the telepathy-glib repository -
4 please make any changes there.
7 # Copyright (C) 2006-2008 Collabora Limited
9 # This library is free software; you can redistribute it and/or
10 # modify it under the terms of the GNU Lesser General Public
11 # License as published by the Free Software Foundation; either
12 # version 2.1 of the License, or (at your option) any later version.
14 # This library is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 # Lesser General Public License for more details.
19 # You should have received a copy of the GNU Lesser General Public
20 # License along with this library; if not, write to the Free Software
21 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 from string
import ascii_letters
, digits
28 NS_TP
= "http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
30 _ASCII_ALNUM
= ascii_letters
+ digits
32 if sys
.version_info
[0] >= 3:
34 """Return s, which must be a str literal with no non-ASCII characters.
35 This is like a more restricted form of the Python 2 u'' syntax.
37 return s
.encode('ascii').decode('ascii')
40 """Return a Unicode version of s, which must be a str literal
41 (a bytestring) in which each byte is an ASCII character.
42 This is like a more restricted form of the u'' syntax.
44 return s
.decode('ascii')
46 def file_set_contents(filename
, contents
):
52 os
.remove(filename
+ '.tmp')
56 open(filename
+ '.tmp', 'wb').write(contents
)
57 os
.rename(filename
+ '.tmp', filename
)
59 def cmp_by_name(node1
, node2
):
60 return cmp(node1
.getAttributeNode("name").nodeValue
,
61 node2
.getAttributeNode("name").nodeValue
)
63 def key_by_name(node
):
64 return node
.getAttributeNode("name").nodeValue
66 def escape_as_identifier(identifier
):
67 """Escape the given string to be a valid D-Bus object path or service
68 name component, using a reversible encoding to ensure uniqueness.
70 The reversible encoding is as follows:
72 * The empty string becomes '_'
73 * Otherwise, each non-alphanumeric character is replaced by '_' plus
74 two lower-case hex digits; the same replacement is carried out on
75 the first character, if it's a digit
81 # A bit of a fast path for strings which are already OK.
82 # We deliberately omit '_' because, for reversibility, that must also
84 if (identifier
.strip(_ASCII_ALNUM
) == '' and
85 identifier
[0] in ascii_letters
):
88 # The first character may not be a digit
89 if identifier
[0] not in ascii_letters
:
90 ret
= ['_%02x' % ord(identifier
[0])]
94 # Subsequent characters may be digits or ASCII letters
95 for c
in identifier
[1:]:
99 ret
.append('_%02x' % ord(c
))
104 def get_by_path(element
, path
):
105 branches
= path
.split('/')
108 # Is the current branch an attribute, if so, return the attribute value
110 return element
.getAttribute(branch
[1:])
112 # Find matching children for the branch
115 children
.append(element
.parentNode
)
117 for x
in element
.childNodes
:
118 if x
.localName
== branch
:
122 # If this is not the last path element, recursively gather results from
124 if len(branches
) > 1:
126 add
= get_by_path(x
, '/'.join(branches
[1:]))
127 if isinstance(add
, list):
137 def get_docstring(element
):
139 for x
in element
.childNodes
:
140 if x
.namespaceURI
== NS_TP
and x
.localName
== 'docstring':
142 if docstring
is not None:
143 docstring
= docstring
.toxml().replace('\n', ' ').strip()
144 if docstring
.startswith('<tp:docstring>'):
145 docstring
= docstring
[14:].lstrip()
146 if docstring
.endswith('</tp:docstring>'):
147 docstring
= docstring
[:-15].rstrip()
148 if docstring
in ('<tp:docstring/>', ''):
152 def get_deprecated(element
):
154 for x
in element
.childNodes
:
155 if hasattr(x
, 'data'):
156 text
.append(x
.data
.replace('\n', ' ').strip())
158 # This caters for tp:dbus-ref elements, but little else.
159 if x
.childNodes
and hasattr(x
.childNodes
[0], 'data'):
160 text
.append(x
.childNodes
[0].data
.replace('\n', ' ').strip())
161 return ' '.join(text
)
163 def get_descendant_text(element_or_elements
):
164 if not element_or_elements
:
166 if isinstance(element_or_elements
, list):
167 return ''.join(map(get_descendant_text
, element_or_elements
))
169 for x
in element_or_elements
.childNodes
:
170 if x
.nodeType
== x
.TEXT_NODE
:
171 parts
.append(x
.nodeValue
)
172 elif x
.nodeType
== x
.ELEMENT_NODE
:
173 parts
.append(get_descendant_text(x
))
176 return ''.join(parts
)
179 class _SignatureIter
:
180 """Iterator over a D-Bus signature. Copied from dbus-python 0.71 so we
181 can run genginterface in a limited environment with only Python
184 def __init__(self
, string
):
185 self
.remaining
= string
188 return self
.__next
__()
191 if self
.remaining
== '':
194 signature
= self
.remaining
199 for marker
in range(0, end
):
200 cur_sig
= signature
[marker
]
204 elif cur_sig
== '{' or cur_sig
== '(':
205 if block_type
== None:
208 if block_type
== cur_sig
:
209 block_depth
= block_depth
+ 1
212 if block_type
== '{':
213 block_depth
= block_depth
- 1
220 if block_type
== '(':
221 block_depth
= block_depth
- 1
233 self
.remaining
= signature
[end
:]
234 return Signature(signature
[0:end
])
237 class Signature(str):
238 """A string, iteration over which is by D-Bus single complete types
239 rather than characters.
242 return _SignatureIter(self
)
246 s
= s
.replace('&', '&').replace("'", ''').replace('"', '"')
247 return s
.replace('<', '<').replace('>', '>')