Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
 
 
 
 

179 righe
5.6 KiB

  1. # Custom resolver API
  2. ctypedef enum _InputDocumentDataType:
  3. PARSER_DATA_INVALID
  4. PARSER_DATA_EMPTY
  5. PARSER_DATA_STRING
  6. PARSER_DATA_FILENAME
  7. PARSER_DATA_FILE
  8. @cython.final
  9. @cython.internal
  10. cdef class _InputDocument:
  11. cdef _InputDocumentDataType _type
  12. cdef bytes _data_bytes
  13. cdef object _filename
  14. cdef object _file
  15. cdef bint _close_file
  16. def __cinit__(self):
  17. self._type = PARSER_DATA_INVALID
  18. cdef class Resolver:
  19. "This is the base class of all resolvers."
  20. def resolve(self, system_url, public_id, context):
  21. """resolve(self, system_url, public_id, context)
  22. Override this method to resolve an external source by
  23. ``system_url`` and ``public_id``. The third argument is an
  24. opaque context object.
  25. Return the result of one of the ``resolve_*()`` methods.
  26. """
  27. return None
  28. def resolve_empty(self, context):
  29. """resolve_empty(self, context)
  30. Return an empty input document.
  31. Pass context as parameter.
  32. """
  33. cdef _InputDocument doc_ref
  34. doc_ref = _InputDocument()
  35. doc_ref._type = PARSER_DATA_EMPTY
  36. return doc_ref
  37. def resolve_string(self, string, context, *, base_url=None):
  38. """resolve_string(self, string, context, base_url=None)
  39. Return a parsable string as input document.
  40. Pass data string and context as parameters. You can pass the
  41. source URL or filename through the ``base_url`` keyword
  42. argument.
  43. """
  44. cdef _InputDocument doc_ref
  45. if isinstance(string, unicode):
  46. string = (<unicode>string).encode('utf8')
  47. elif not isinstance(string, bytes):
  48. raise TypeError, "argument must be a byte string or unicode string"
  49. doc_ref = _InputDocument()
  50. doc_ref._type = PARSER_DATA_STRING
  51. doc_ref._data_bytes = string
  52. if base_url is not None:
  53. doc_ref._filename = _encodeFilename(base_url)
  54. return doc_ref
  55. def resolve_filename(self, filename, context):
  56. """resolve_filename(self, filename, context)
  57. Return the name of a parsable file as input document.
  58. Pass filename and context as parameters. You can also pass a
  59. URL with an HTTP, FTP or file target.
  60. """
  61. cdef _InputDocument doc_ref
  62. doc_ref = _InputDocument()
  63. doc_ref._type = PARSER_DATA_FILENAME
  64. doc_ref._filename = _encodeFilename(filename)
  65. return doc_ref
  66. def resolve_file(self, f, context, *, base_url=None, bint close=True):
  67. """resolve_file(self, f, context, base_url=None, close=True)
  68. Return an open file-like object as input document.
  69. Pass open file and context as parameters. You can pass the
  70. base URL or filename of the file through the ``base_url``
  71. keyword argument. If the ``close`` flag is True (the
  72. default), the file will be closed after reading.
  73. Note that using ``.resolve_filename()`` is more efficient,
  74. especially in threaded environments.
  75. """
  76. cdef _InputDocument doc_ref
  77. try:
  78. f.read
  79. except AttributeError:
  80. raise TypeError, "Argument is not a file-like object"
  81. doc_ref = _InputDocument()
  82. doc_ref._type = PARSER_DATA_FILE
  83. if base_url is not None:
  84. doc_ref._filename = _encodeFilename(base_url)
  85. else:
  86. doc_ref._filename = _getFilenameForFile(f)
  87. doc_ref._close_file = close
  88. doc_ref._file = f
  89. return doc_ref
  90. @cython.final
  91. @cython.internal
  92. cdef class _ResolverRegistry:
  93. cdef object _resolvers
  94. cdef Resolver _default_resolver
  95. def __cinit__(self, Resolver default_resolver=None):
  96. self._resolvers = set()
  97. self._default_resolver = default_resolver
  98. def add(self, Resolver resolver not None):
  99. """add(self, resolver)
  100. Register a resolver.
  101. For each requested entity, the 'resolve' method of the resolver will
  102. be called and the result will be passed to the parser. If this method
  103. returns None, the request will be delegated to other resolvers or the
  104. default resolver. The resolvers will be tested in an arbitrary order
  105. until the first match is found.
  106. """
  107. self._resolvers.add(resolver)
  108. def remove(self, resolver):
  109. "remove(self, resolver)"
  110. self._resolvers.discard(resolver)
  111. cdef _ResolverRegistry _copy(self):
  112. cdef _ResolverRegistry registry
  113. registry = _ResolverRegistry(self._default_resolver)
  114. registry._resolvers = self._resolvers.copy()
  115. return registry
  116. def copy(self):
  117. "copy(self)"
  118. return self._copy()
  119. def resolve(self, system_url, public_id, context):
  120. "resolve(self, system_url, public_id, context)"
  121. for resolver in self._resolvers:
  122. result = resolver.resolve(system_url, public_id, context)
  123. if result is not None:
  124. return result
  125. if self._default_resolver is None:
  126. return None
  127. return self._default_resolver.resolve(system_url, public_id, context)
  128. def __repr__(self):
  129. return repr(self._resolvers)
  130. @cython.internal
  131. cdef class _ResolverContext(_ExceptionContext):
  132. cdef _ResolverRegistry _resolvers
  133. cdef _TempStore _storage
  134. cdef int clear(self) except -1:
  135. _ExceptionContext.clear(self)
  136. self._storage.clear()
  137. return 0
  138. cdef _initResolverContext(_ResolverContext context,
  139. _ResolverRegistry resolvers):
  140. if resolvers is None:
  141. context._resolvers = _ResolverRegistry()
  142. else:
  143. context._resolvers = resolvers
  144. context._storage = _TempStore()