You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

282 regels
8.9 KiB

  1. # module-level API for namespace implementations
  2. cdef class LxmlRegistryError(LxmlError):
  3. """Base class of lxml registry errors.
  4. """
  5. cdef class NamespaceRegistryError(LxmlRegistryError):
  6. """Error registering a namespace extension.
  7. """
  8. @cython.internal
  9. cdef class _NamespaceRegistry:
  10. "Dictionary-like namespace registry"
  11. cdef object _ns_uri
  12. cdef bytes _ns_uri_utf
  13. cdef dict _entries
  14. cdef char* _c_ns_uri_utf
  15. def __cinit__(self, ns_uri):
  16. self._ns_uri = ns_uri
  17. if ns_uri is None:
  18. self._ns_uri_utf = None
  19. self._c_ns_uri_utf = NULL
  20. else:
  21. self._ns_uri_utf = _utf8(ns_uri)
  22. self._c_ns_uri_utf = _cstr(self._ns_uri_utf)
  23. self._entries = {}
  24. def update(self, class_dict_iterable):
  25. """update(self, class_dict_iterable)
  26. Forgivingly update the registry.
  27. ``class_dict_iterable`` may be a dict or some other iterable
  28. that yields (name, value) pairs.
  29. If a value does not match the required type for this registry,
  30. or if the name starts with '_', it will be silently discarded.
  31. This allows registrations at the module or class level using
  32. vars(), globals() etc."""
  33. if hasattr(class_dict_iterable, 'items'):
  34. class_dict_iterable = class_dict_iterable.items()
  35. for name, item in class_dict_iterable:
  36. if (name is None or name[:1] != '_') and callable(item):
  37. self[name] = item
  38. def __getitem__(self, name):
  39. if name is not None:
  40. name = _utf8(name)
  41. return self._get(name)
  42. def __delitem__(self, name):
  43. if name is not None:
  44. name = _utf8(name)
  45. del self._entries[name]
  46. cdef object _get(self, object name):
  47. cdef python.PyObject* dict_result
  48. dict_result = python.PyDict_GetItem(self._entries, name)
  49. if dict_result is NULL:
  50. raise KeyError, "Name not registered."
  51. return <object>dict_result
  52. cdef object _getForString(self, char* name):
  53. cdef python.PyObject* dict_result
  54. dict_result = python.PyDict_GetItem(self._entries, name)
  55. if dict_result is NULL:
  56. raise KeyError, "Name not registered."
  57. return <object>dict_result
  58. def __iter__(self):
  59. return iter(self._entries)
  60. def items(self):
  61. return list(self._entries.items())
  62. def iteritems(self):
  63. return iter(self._entries.items())
  64. def clear(self):
  65. self._entries.clear()
  66. def __call__(self, obj):
  67. # Usage as decorator:
  68. # ns = lookup.get_namespace("...")
  69. # @ns('abc')
  70. # class element(ElementBase): pass
  71. #
  72. # @ns
  73. # class elementname(ElementBase): pass
  74. if obj is None or python._isString(obj):
  75. # @ns(None) or @ns('tag')
  76. return partial(self.__deco, obj)
  77. # plain @ns decorator
  78. self[obj.__name__] = obj
  79. return obj
  80. def __deco(self, name, obj):
  81. self[name] = obj
  82. return obj
  83. @cython.final
  84. @cython.internal
  85. cdef class _ClassNamespaceRegistry(_NamespaceRegistry):
  86. "Dictionary-like registry for namespace implementation classes"
  87. def __setitem__(self, name, item):
  88. if not isinstance(item, type) or not issubclass(item, ElementBase):
  89. raise NamespaceRegistryError, \
  90. "Registered element classes must be subtypes of ElementBase"
  91. if name is not None:
  92. name = _utf8(name)
  93. self._entries[name] = item
  94. def __repr__(self):
  95. return "Namespace(%r)" % self._ns_uri
  96. cdef class ElementNamespaceClassLookup(FallbackElementClassLookup):
  97. """ElementNamespaceClassLookup(self, fallback=None)
  98. Element class lookup scheme that searches the Element class in the
  99. Namespace registry.
  100. Usage:
  101. >>> lookup = ElementNamespaceClassLookup()
  102. >>> ns_elements = lookup.get_namespace("http://schema.org/Movie")
  103. >>> @ns_elements
  104. ... class movie(ElementBase):
  105. ... "Element implementation for 'movie' tag (using class name) in schema namespace."
  106. >>> @ns_elements("movie")
  107. ... class MovieElement(ElementBase):
  108. ... "Element implementation for 'movie' tag (explicit tag name) in schema namespace."
  109. """
  110. cdef dict _namespace_registries
  111. def __cinit__(self):
  112. self._namespace_registries = {}
  113. def __init__(self, ElementClassLookup fallback=None):
  114. FallbackElementClassLookup.__init__(self, fallback)
  115. self._lookup_function = _find_nselement_class
  116. def get_namespace(self, ns_uri):
  117. """get_namespace(self, ns_uri)
  118. Retrieve the namespace object associated with the given URI.
  119. Pass None for the empty namespace.
  120. Creates a new namespace object if it does not yet exist."""
  121. if ns_uri:
  122. ns_utf = _utf8(ns_uri)
  123. else:
  124. ns_utf = None
  125. try:
  126. return self._namespace_registries[ns_utf]
  127. except KeyError:
  128. registry = self._namespace_registries[ns_utf] = \
  129. _ClassNamespaceRegistry(ns_uri)
  130. return registry
  131. cdef object _find_nselement_class(state, _Document doc, xmlNode* c_node):
  132. cdef python.PyObject* dict_result
  133. cdef ElementNamespaceClassLookup lookup
  134. cdef _NamespaceRegistry registry
  135. if state is None:
  136. return _lookupDefaultElementClass(None, doc, c_node)
  137. lookup = <ElementNamespaceClassLookup>state
  138. if c_node.type != tree.XML_ELEMENT_NODE:
  139. return _callLookupFallback(lookup, doc, c_node)
  140. c_namespace_utf = _getNs(c_node)
  141. if c_namespace_utf is not NULL:
  142. dict_result = python.PyDict_GetItem(
  143. lookup._namespace_registries, <unsigned char*>c_namespace_utf)
  144. else:
  145. dict_result = python.PyDict_GetItem(
  146. lookup._namespace_registries, None)
  147. if dict_result is not NULL:
  148. registry = <_NamespaceRegistry>dict_result
  149. classes = registry._entries
  150. if c_node.name is not NULL:
  151. dict_result = python.PyDict_GetItem(
  152. classes, <unsigned char*>c_node.name)
  153. else:
  154. dict_result = NULL
  155. if dict_result is NULL:
  156. dict_result = python.PyDict_GetItem(classes, None)
  157. if dict_result is not NULL:
  158. return <object>dict_result
  159. return _callLookupFallback(lookup, doc, c_node)
  160. ################################################################################
  161. # XPath extension functions
  162. cdef dict __FUNCTION_NAMESPACE_REGISTRIES
  163. __FUNCTION_NAMESPACE_REGISTRIES = {}
  164. def FunctionNamespace(ns_uri):
  165. """FunctionNamespace(ns_uri)
  166. Retrieve the function namespace object associated with the given
  167. URI.
  168. Creates a new one if it does not yet exist. A function namespace
  169. can only be used to register extension functions.
  170. Usage:
  171. >>> ns_functions = FunctionNamespace("http://schema.org/Movie")
  172. >>> @ns_functions # uses function name
  173. ... def add2(x):
  174. ... return x + 2
  175. >>> @ns_functions("add3") # uses explicit name
  176. ... def add_three(x):
  177. ... return x + 3
  178. """
  179. ns_utf = _utf8(ns_uri) if ns_uri else None
  180. try:
  181. return __FUNCTION_NAMESPACE_REGISTRIES[ns_utf]
  182. except KeyError:
  183. registry = __FUNCTION_NAMESPACE_REGISTRIES[ns_utf] = \
  184. _XPathFunctionNamespaceRegistry(ns_uri)
  185. return registry
  186. @cython.internal
  187. cdef class _FunctionNamespaceRegistry(_NamespaceRegistry):
  188. def __setitem__(self, name, item):
  189. if not callable(item):
  190. raise NamespaceRegistryError, \
  191. "Registered functions must be callable."
  192. if not name:
  193. raise ValueError, \
  194. "extensions must have non empty names"
  195. self._entries[_utf8(name)] = item
  196. def __repr__(self):
  197. return "FunctionNamespace(%r)" % self._ns_uri
  198. @cython.final
  199. @cython.internal
  200. cdef class _XPathFunctionNamespaceRegistry(_FunctionNamespaceRegistry):
  201. cdef object _prefix
  202. cdef bytes _prefix_utf
  203. property prefix:
  204. "Namespace prefix for extension functions."
  205. def __del__(self):
  206. self._prefix = None # no prefix configured
  207. self._prefix_utf = None
  208. def __get__(self):
  209. if self._prefix is None:
  210. return ''
  211. else:
  212. return self._prefix
  213. def __set__(self, prefix):
  214. if prefix == '':
  215. prefix = None # empty prefix
  216. self._prefix_utf = _utf8(prefix) if prefix is not None else None
  217. self._prefix = prefix
  218. cdef list _find_all_extension_prefixes():
  219. "Internal lookup function to find all function prefixes for XSLT/XPath."
  220. cdef _XPathFunctionNamespaceRegistry registry
  221. cdef list ns_prefixes = []
  222. for registry in __FUNCTION_NAMESPACE_REGISTRIES.itervalues():
  223. if registry._prefix_utf is not None:
  224. if registry._ns_uri_utf is not None:
  225. ns_prefixes.append(
  226. (registry._prefix_utf, registry._ns_uri_utf))
  227. return ns_prefixes