Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/ElementInclude.py +244 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/__init__.py +22 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/_elementpath.py +343 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/apihelpers.pxi +1801 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/builder.py +243 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/classlookup.pxi +580 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/cleanup.pxi +215 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/cssselect.py +101 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/doctestcompare.py +488 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/etree.h +244 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/etree.pyx +0 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/etree_api.h +204 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/extensions.pxi +830 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/lxml.etree.h +244 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/objectpath.pxi +332 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/proxy.pxi +622 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/public-api.pxi +178 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/pyclasslookup.py +3 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/relaxng.pxi +165 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/sax.py +285 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/schematron.pxi +173 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/serializer.pxi +1849 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/xinclude.pxi +67 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/xmlerror.pxi +1662 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/xpath.pxi +487 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/xsltext.pxi +242 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/__init__.py +975 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/activations.py +356 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/activations_tf.py +147 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/audio_utils.py +1224 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/cache_utils.py +1493 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/convert_tf_hub_seq_to_seq_bert_to_pytorch.py +86 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/debug_utils.py +346 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/dependency_versions_table.py +108 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/dynamic_module_utils.py +843 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/feature_extraction_sequence_utils.py +371 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/feature_extraction_utils.py +697 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/file_utils.py +130 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/hyperparameter_search.py +141 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/image_processing_base.py +543 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/image_processing_utils.py +317 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/image_utils.py +969 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/keras_callbacks.py +413 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/model_debugging_utils.py +456 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/modelcard.py +914 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/modeling_attn_mask_utils.py +487 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/modeling_flash_attention_utils.py +668 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/modeling_flax_outputs.py +700 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/modeling_flax_utils.py +1274 -0
- Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/modeling_gguf_pytorch_utils.py +532 -0
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/ElementInclude.py
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#
|
| 2 |
+
# ElementTree
|
| 3 |
+
# $Id: ElementInclude.py 1862 2004-06-18 07:31:02Z Fredrik $
|
| 4 |
+
#
|
| 5 |
+
# limited xinclude support for element trees
|
| 6 |
+
#
|
| 7 |
+
# history:
|
| 8 |
+
# 2003-08-15 fl created
|
| 9 |
+
# 2003-11-14 fl fixed default loader
|
| 10 |
+
#
|
| 11 |
+
# Copyright (c) 2003-2004 by Fredrik Lundh. All rights reserved.
|
| 12 |
+
#
|
| 13 |
+
# fredrik@pythonware.com
|
| 14 |
+
# http://www.pythonware.com
|
| 15 |
+
#
|
| 16 |
+
# --------------------------------------------------------------------
|
| 17 |
+
# The ElementTree toolkit is
|
| 18 |
+
#
|
| 19 |
+
# Copyright (c) 1999-2004 by Fredrik Lundh
|
| 20 |
+
#
|
| 21 |
+
# By obtaining, using, and/or copying this software and/or its
|
| 22 |
+
# associated documentation, you agree that you have read, understood,
|
| 23 |
+
# and will comply with the following terms and conditions:
|
| 24 |
+
#
|
| 25 |
+
# Permission to use, copy, modify, and distribute this software and
|
| 26 |
+
# its associated documentation for any purpose and without fee is
|
| 27 |
+
# hereby granted, provided that the above copyright notice appears in
|
| 28 |
+
# all copies, and that both that copyright notice and this permission
|
| 29 |
+
# notice appear in supporting documentation, and that the name of
|
| 30 |
+
# Secret Labs AB or the author not be used in advertising or publicity
|
| 31 |
+
# pertaining to distribution of the software without specific, written
|
| 32 |
+
# prior permission.
|
| 33 |
+
#
|
| 34 |
+
# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
|
| 35 |
+
# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
|
| 36 |
+
# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR
|
| 37 |
+
# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
|
| 38 |
+
# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
| 39 |
+
# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
| 40 |
+
# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
| 41 |
+
# OF THIS SOFTWARE.
|
| 42 |
+
# --------------------------------------------------------------------
|
| 43 |
+
|
| 44 |
+
"""
|
| 45 |
+
Limited XInclude support for the ElementTree package.
|
| 46 |
+
|
| 47 |
+
While lxml.etree has full support for XInclude (see
|
| 48 |
+
`etree.ElementTree.xinclude()`), this module provides a simpler, pure
|
| 49 |
+
Python, ElementTree compatible implementation that supports a simple
|
| 50 |
+
form of custom URL resolvers.
|
| 51 |
+
"""
|
| 52 |
+
|
| 53 |
+
from lxml import etree
|
| 54 |
+
try:
|
| 55 |
+
from urlparse import urljoin
|
| 56 |
+
from urllib2 import urlopen
|
| 57 |
+
except ImportError:
|
| 58 |
+
# Python 3
|
| 59 |
+
from urllib.parse import urljoin
|
| 60 |
+
from urllib.request import urlopen
|
| 61 |
+
|
| 62 |
+
XINCLUDE = "{http://www.w3.org/2001/XInclude}"
|
| 63 |
+
|
| 64 |
+
XINCLUDE_INCLUDE = XINCLUDE + "include"
|
| 65 |
+
XINCLUDE_FALLBACK = XINCLUDE + "fallback"
|
| 66 |
+
XINCLUDE_ITER_TAG = XINCLUDE + "*"
|
| 67 |
+
|
| 68 |
+
# For security reasons, the inclusion depth is limited to this read-only value by default.
|
| 69 |
+
DEFAULT_MAX_INCLUSION_DEPTH = 6
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
##
|
| 73 |
+
# Fatal include error.
|
| 74 |
+
|
| 75 |
+
class FatalIncludeError(etree.LxmlSyntaxError):
|
| 76 |
+
pass
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
class LimitedRecursiveIncludeError(FatalIncludeError):
|
| 80 |
+
pass
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
##
|
| 84 |
+
# ET compatible default loader.
|
| 85 |
+
# This loader reads an included resource from disk.
|
| 86 |
+
#
|
| 87 |
+
# @param href Resource reference.
|
| 88 |
+
# @param parse Parse mode. Either "xml" or "text".
|
| 89 |
+
# @param encoding Optional text encoding.
|
| 90 |
+
# @return The expanded resource. If the parse mode is "xml", this
|
| 91 |
+
# is an ElementTree instance. If the parse mode is "text", this
|
| 92 |
+
# is a Unicode string. If the loader fails, it can return None
|
| 93 |
+
# or raise an IOError exception.
|
| 94 |
+
# @throws IOError If the loader fails to load the resource.
|
| 95 |
+
|
| 96 |
+
def default_loader(href, parse, encoding=None):
|
| 97 |
+
file = open(href, 'rb')
|
| 98 |
+
if parse == "xml":
|
| 99 |
+
data = etree.parse(file).getroot()
|
| 100 |
+
else:
|
| 101 |
+
data = file.read()
|
| 102 |
+
if not encoding:
|
| 103 |
+
encoding = 'utf-8'
|
| 104 |
+
data = data.decode(encoding)
|
| 105 |
+
file.close()
|
| 106 |
+
return data
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
##
|
| 110 |
+
# Default loader used by lxml.etree - handles custom resolvers properly
|
| 111 |
+
#
|
| 112 |
+
|
| 113 |
+
def _lxml_default_loader(href, parse, encoding=None, parser=None):
|
| 114 |
+
if parse == "xml":
|
| 115 |
+
data = etree.parse(href, parser).getroot()
|
| 116 |
+
else:
|
| 117 |
+
if "://" in href:
|
| 118 |
+
f = urlopen(href)
|
| 119 |
+
else:
|
| 120 |
+
f = open(href, 'rb')
|
| 121 |
+
data = f.read()
|
| 122 |
+
f.close()
|
| 123 |
+
if not encoding:
|
| 124 |
+
encoding = 'utf-8'
|
| 125 |
+
data = data.decode(encoding)
|
| 126 |
+
return data
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
##
|
| 130 |
+
# Wrapper for ET compatibility - drops the parser
|
| 131 |
+
|
| 132 |
+
def _wrap_et_loader(loader):
|
| 133 |
+
def load(href, parse, encoding=None, parser=None):
|
| 134 |
+
return loader(href, parse, encoding)
|
| 135 |
+
return load
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
##
|
| 139 |
+
# Expand XInclude directives.
|
| 140 |
+
#
|
| 141 |
+
# @param elem Root element.
|
| 142 |
+
# @param loader Optional resource loader. If omitted, it defaults
|
| 143 |
+
# to {@link default_loader}. If given, it should be a callable
|
| 144 |
+
# that implements the same interface as <b>default_loader</b>.
|
| 145 |
+
# @param base_url The base URL of the original file, to resolve
|
| 146 |
+
# relative include file references.
|
| 147 |
+
# @param max_depth The maximum number of recursive inclusions.
|
| 148 |
+
# Limited to reduce the risk of malicious content explosion.
|
| 149 |
+
# Pass None to disable the limitation.
|
| 150 |
+
# @throws LimitedRecursiveIncludeError If the {@link max_depth} was exceeded.
|
| 151 |
+
# @throws FatalIncludeError If the function fails to include a given
|
| 152 |
+
# resource, or if the tree contains malformed XInclude elements.
|
| 153 |
+
# @throws IOError If the function fails to load a given resource.
|
| 154 |
+
# @returns the node or its replacement if it was an XInclude node
|
| 155 |
+
|
| 156 |
+
def include(elem, loader=None, base_url=None,
|
| 157 |
+
max_depth=DEFAULT_MAX_INCLUSION_DEPTH):
|
| 158 |
+
if max_depth is None:
|
| 159 |
+
max_depth = -1
|
| 160 |
+
elif max_depth < 0:
|
| 161 |
+
raise ValueError("expected non-negative depth or None for 'max_depth', got %r" % max_depth)
|
| 162 |
+
|
| 163 |
+
if base_url is None:
|
| 164 |
+
if hasattr(elem, 'getroot'):
|
| 165 |
+
tree = elem
|
| 166 |
+
elem = elem.getroot()
|
| 167 |
+
else:
|
| 168 |
+
tree = elem.getroottree()
|
| 169 |
+
if hasattr(tree, 'docinfo'):
|
| 170 |
+
base_url = tree.docinfo.URL
|
| 171 |
+
elif hasattr(elem, 'getroot'):
|
| 172 |
+
elem = elem.getroot()
|
| 173 |
+
_include(elem, loader, base_url, max_depth)
|
| 174 |
+
|
| 175 |
+
|
| 176 |
+
def _include(elem, loader=None, base_url=None,
|
| 177 |
+
max_depth=DEFAULT_MAX_INCLUSION_DEPTH, _parent_hrefs=None):
|
| 178 |
+
if loader is not None:
|
| 179 |
+
load_include = _wrap_et_loader(loader)
|
| 180 |
+
else:
|
| 181 |
+
load_include = _lxml_default_loader
|
| 182 |
+
|
| 183 |
+
if _parent_hrefs is None:
|
| 184 |
+
_parent_hrefs = set()
|
| 185 |
+
|
| 186 |
+
parser = elem.getroottree().parser
|
| 187 |
+
|
| 188 |
+
include_elements = list(
|
| 189 |
+
elem.iter(XINCLUDE_ITER_TAG))
|
| 190 |
+
|
| 191 |
+
for e in include_elements:
|
| 192 |
+
if e.tag == XINCLUDE_INCLUDE:
|
| 193 |
+
# process xinclude directive
|
| 194 |
+
href = urljoin(base_url, e.get("href"))
|
| 195 |
+
parse = e.get("parse", "xml")
|
| 196 |
+
parent = e.getparent()
|
| 197 |
+
if parse == "xml":
|
| 198 |
+
if href in _parent_hrefs:
|
| 199 |
+
raise FatalIncludeError(
|
| 200 |
+
"recursive include of %r detected" % href
|
| 201 |
+
)
|
| 202 |
+
if max_depth == 0:
|
| 203 |
+
raise LimitedRecursiveIncludeError(
|
| 204 |
+
"maximum xinclude depth reached when including file %s" % href)
|
| 205 |
+
node = load_include(href, parse, parser=parser)
|
| 206 |
+
if node is None:
|
| 207 |
+
raise FatalIncludeError(
|
| 208 |
+
"cannot load %r as %r" % (href, parse)
|
| 209 |
+
)
|
| 210 |
+
node = _include(node, loader, href, max_depth - 1, {href} | _parent_hrefs)
|
| 211 |
+
if e.tail:
|
| 212 |
+
node.tail = (node.tail or "") + e.tail
|
| 213 |
+
if parent is None:
|
| 214 |
+
return node # replaced the root node!
|
| 215 |
+
parent.replace(e, node)
|
| 216 |
+
elif parse == "text":
|
| 217 |
+
text = load_include(href, parse, encoding=e.get("encoding"))
|
| 218 |
+
if text is None:
|
| 219 |
+
raise FatalIncludeError(
|
| 220 |
+
"cannot load %r as %r" % (href, parse)
|
| 221 |
+
)
|
| 222 |
+
predecessor = e.getprevious()
|
| 223 |
+
if predecessor is not None:
|
| 224 |
+
predecessor.tail = (predecessor.tail or "") + text
|
| 225 |
+
elif parent is None:
|
| 226 |
+
return text # replaced the root node!
|
| 227 |
+
else:
|
| 228 |
+
parent.text = (parent.text or "") + text + (e.tail or "")
|
| 229 |
+
parent.remove(e)
|
| 230 |
+
else:
|
| 231 |
+
raise FatalIncludeError(
|
| 232 |
+
"unknown parse type in xi:include tag (%r)" % parse
|
| 233 |
+
)
|
| 234 |
+
elif e.tag == XINCLUDE_FALLBACK:
|
| 235 |
+
parent = e.getparent()
|
| 236 |
+
if parent is not None and parent.tag != XINCLUDE_INCLUDE:
|
| 237 |
+
raise FatalIncludeError(
|
| 238 |
+
"xi:fallback tag must be child of xi:include (%r)" % e.tag
|
| 239 |
+
)
|
| 240 |
+
else:
|
| 241 |
+
raise FatalIncludeError(
|
| 242 |
+
"Invalid element found in XInclude namespace (%r)" % e.tag
|
| 243 |
+
)
|
| 244 |
+
return elem
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/__init__.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# this is a package
|
| 2 |
+
|
| 3 |
+
__version__ = "6.0.2"
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def get_include():
|
| 7 |
+
"""
|
| 8 |
+
Returns a list of header include paths (for lxml itself, libxml2
|
| 9 |
+
and libxslt) needed to compile C code against lxml if it was built
|
| 10 |
+
with statically linked libraries.
|
| 11 |
+
"""
|
| 12 |
+
import os
|
| 13 |
+
lxml_path = __path__[0]
|
| 14 |
+
include_path = os.path.join(lxml_path, 'includes')
|
| 15 |
+
includes = [include_path, lxml_path]
|
| 16 |
+
|
| 17 |
+
for name in os.listdir(include_path):
|
| 18 |
+
path = os.path.join(include_path, name)
|
| 19 |
+
if os.path.isdir(path):
|
| 20 |
+
includes.append(path)
|
| 21 |
+
|
| 22 |
+
return includes
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/_elementpath.py
ADDED
|
@@ -0,0 +1,343 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# cython: language_level=3
|
| 2 |
+
|
| 3 |
+
#
|
| 4 |
+
# ElementTree
|
| 5 |
+
# $Id: ElementPath.py 3375 2008-02-13 08:05:08Z fredrik $
|
| 6 |
+
#
|
| 7 |
+
# limited xpath support for element trees
|
| 8 |
+
#
|
| 9 |
+
# history:
|
| 10 |
+
# 2003-05-23 fl created
|
| 11 |
+
# 2003-05-28 fl added support for // etc
|
| 12 |
+
# 2003-08-27 fl fixed parsing of periods in element names
|
| 13 |
+
# 2007-09-10 fl new selection engine
|
| 14 |
+
# 2007-09-12 fl fixed parent selector
|
| 15 |
+
# 2007-09-13 fl added iterfind; changed findall to return a list
|
| 16 |
+
# 2007-11-30 fl added namespaces support
|
| 17 |
+
# 2009-10-30 fl added child element value filter
|
| 18 |
+
#
|
| 19 |
+
# Copyright (c) 2003-2009 by Fredrik Lundh. All rights reserved.
|
| 20 |
+
#
|
| 21 |
+
# fredrik@pythonware.com
|
| 22 |
+
# http://www.pythonware.com
|
| 23 |
+
#
|
| 24 |
+
# --------------------------------------------------------------------
|
| 25 |
+
# The ElementTree toolkit is
|
| 26 |
+
#
|
| 27 |
+
# Copyright (c) 1999-2009 by Fredrik Lundh
|
| 28 |
+
#
|
| 29 |
+
# By obtaining, using, and/or copying this software and/or its
|
| 30 |
+
# associated documentation, you agree that you have read, understood,
|
| 31 |
+
# and will comply with the following terms and conditions:
|
| 32 |
+
#
|
| 33 |
+
# Permission to use, copy, modify, and distribute this software and
|
| 34 |
+
# its associated documentation for any purpose and without fee is
|
| 35 |
+
# hereby granted, provided that the above copyright notice appears in
|
| 36 |
+
# all copies, and that both that copyright notice and this permission
|
| 37 |
+
# notice appear in supporting documentation, and that the name of
|
| 38 |
+
# Secret Labs AB or the author not be used in advertising or publicity
|
| 39 |
+
# pertaining to distribution of the software without specific, written
|
| 40 |
+
# prior permission.
|
| 41 |
+
#
|
| 42 |
+
# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
|
| 43 |
+
# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
|
| 44 |
+
# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR
|
| 45 |
+
# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
|
| 46 |
+
# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
| 47 |
+
# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
| 48 |
+
# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
| 49 |
+
# OF THIS SOFTWARE.
|
| 50 |
+
# --------------------------------------------------------------------
|
| 51 |
+
|
| 52 |
+
##
|
| 53 |
+
# Implementation module for XPath support. There's usually no reason
|
| 54 |
+
# to import this module directly; the <b>ElementTree</b> does this for
|
| 55 |
+
# you, if needed.
|
| 56 |
+
##
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
import re
|
| 60 |
+
|
| 61 |
+
xpath_tokenizer_re = re.compile(
|
| 62 |
+
"("
|
| 63 |
+
"'[^']*'|\"[^\"]*\"|"
|
| 64 |
+
"::|"
|
| 65 |
+
"//?|"
|
| 66 |
+
r"\.\.|"
|
| 67 |
+
r"\(\)|"
|
| 68 |
+
r"[/.*:\[\]\(\)@=])|"
|
| 69 |
+
r"((?:\{[^}]+\})?[^/\[\]\(\)@=\s]+)|"
|
| 70 |
+
r"\s+"
|
| 71 |
+
)
|
| 72 |
+
|
| 73 |
+
def xpath_tokenizer(pattern, namespaces=None, with_prefixes=True):
|
| 74 |
+
# ElementTree uses '', lxml used None originally.
|
| 75 |
+
default_namespace = (namespaces.get(None) or namespaces.get('')) if namespaces else None
|
| 76 |
+
parsing_attribute = False
|
| 77 |
+
for token in xpath_tokenizer_re.findall(pattern):
|
| 78 |
+
ttype, tag = token
|
| 79 |
+
if tag and tag[0] != "{":
|
| 80 |
+
if ":" in tag and with_prefixes:
|
| 81 |
+
prefix, uri = tag.split(":", 1)
|
| 82 |
+
try:
|
| 83 |
+
if not namespaces:
|
| 84 |
+
raise KeyError
|
| 85 |
+
yield ttype, "{%s}%s" % (namespaces[prefix], uri)
|
| 86 |
+
except KeyError:
|
| 87 |
+
raise SyntaxError("prefix %r not found in prefix map" % prefix)
|
| 88 |
+
elif tag.isdecimal():
|
| 89 |
+
yield token # index
|
| 90 |
+
elif default_namespace and not parsing_attribute:
|
| 91 |
+
yield ttype, "{%s}%s" % (default_namespace, tag)
|
| 92 |
+
else:
|
| 93 |
+
yield token
|
| 94 |
+
parsing_attribute = False
|
| 95 |
+
else:
|
| 96 |
+
yield token
|
| 97 |
+
parsing_attribute = ttype == '@'
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
def prepare_child(next, token):
|
| 101 |
+
tag = token[1]
|
| 102 |
+
def select(result):
|
| 103 |
+
for elem in result:
|
| 104 |
+
yield from elem.iterchildren(tag)
|
| 105 |
+
return select
|
| 106 |
+
|
| 107 |
+
def prepare_star(next, token):
|
| 108 |
+
def select(result):
|
| 109 |
+
for elem in result:
|
| 110 |
+
yield from elem.iterchildren('*')
|
| 111 |
+
return select
|
| 112 |
+
|
| 113 |
+
def prepare_self(next, token):
|
| 114 |
+
def select(result):
|
| 115 |
+
return result
|
| 116 |
+
return select
|
| 117 |
+
|
| 118 |
+
def prepare_descendant(next, token):
|
| 119 |
+
token = next()
|
| 120 |
+
if token[0] == "*":
|
| 121 |
+
tag = "*"
|
| 122 |
+
elif not token[0]:
|
| 123 |
+
tag = token[1]
|
| 124 |
+
else:
|
| 125 |
+
raise SyntaxError("invalid descendant")
|
| 126 |
+
def select(result):
|
| 127 |
+
for elem in result:
|
| 128 |
+
yield from elem.iterdescendants(tag)
|
| 129 |
+
return select
|
| 130 |
+
|
| 131 |
+
def prepare_parent(next, token):
|
| 132 |
+
def select(result):
|
| 133 |
+
for elem in result:
|
| 134 |
+
parent = elem.getparent()
|
| 135 |
+
if parent is not None:
|
| 136 |
+
yield parent
|
| 137 |
+
return select
|
| 138 |
+
|
| 139 |
+
def prepare_predicate(next, token):
|
| 140 |
+
# FIXME: replace with real parser!!! refs:
|
| 141 |
+
# http://effbot.org/zone/simple-iterator-parser.htm
|
| 142 |
+
# http://javascript.crockford.com/tdop/tdop.html
|
| 143 |
+
signature = ''
|
| 144 |
+
predicate = []
|
| 145 |
+
while 1:
|
| 146 |
+
token = next()
|
| 147 |
+
if token[0] == "]":
|
| 148 |
+
break
|
| 149 |
+
if token == ('', ''):
|
| 150 |
+
# ignore whitespace
|
| 151 |
+
continue
|
| 152 |
+
if token[0] and token[0][:1] in "'\"":
|
| 153 |
+
token = "'", token[0][1:-1]
|
| 154 |
+
signature += token[0] or "-"
|
| 155 |
+
predicate.append(token[1])
|
| 156 |
+
|
| 157 |
+
# use signature to determine predicate type
|
| 158 |
+
if signature == "@-":
|
| 159 |
+
# [@attribute] predicate
|
| 160 |
+
key = predicate[1]
|
| 161 |
+
def select(result):
|
| 162 |
+
for elem in result:
|
| 163 |
+
if elem.get(key) is not None:
|
| 164 |
+
yield elem
|
| 165 |
+
return select
|
| 166 |
+
if signature == "@-='":
|
| 167 |
+
# [@attribute='value']
|
| 168 |
+
key = predicate[1]
|
| 169 |
+
value = predicate[-1]
|
| 170 |
+
def select(result):
|
| 171 |
+
for elem in result:
|
| 172 |
+
if elem.get(key) == value:
|
| 173 |
+
yield elem
|
| 174 |
+
return select
|
| 175 |
+
if signature == "-" and not re.match(r"-?\d+$", predicate[0]):
|
| 176 |
+
# [tag]
|
| 177 |
+
tag = predicate[0]
|
| 178 |
+
def select(result):
|
| 179 |
+
for elem in result:
|
| 180 |
+
for _ in elem.iterchildren(tag):
|
| 181 |
+
yield elem
|
| 182 |
+
break
|
| 183 |
+
return select
|
| 184 |
+
if signature == ".='" or (signature == "-='" and not re.match(r"-?\d+$", predicate[0])):
|
| 185 |
+
# [.='value'] or [tag='value']
|
| 186 |
+
tag = predicate[0]
|
| 187 |
+
value = predicate[-1]
|
| 188 |
+
if tag:
|
| 189 |
+
def select(result):
|
| 190 |
+
for elem in result:
|
| 191 |
+
for e in elem.iterchildren(tag):
|
| 192 |
+
if "".join(e.itertext()) == value:
|
| 193 |
+
yield elem
|
| 194 |
+
break
|
| 195 |
+
else:
|
| 196 |
+
def select(result):
|
| 197 |
+
for elem in result:
|
| 198 |
+
if "".join(elem.itertext()) == value:
|
| 199 |
+
yield elem
|
| 200 |
+
return select
|
| 201 |
+
if signature == "-" or signature == "-()" or signature == "-()-":
|
| 202 |
+
# [index] or [last()] or [last()-index]
|
| 203 |
+
if signature == "-":
|
| 204 |
+
# [index]
|
| 205 |
+
index = int(predicate[0]) - 1
|
| 206 |
+
if index < 0:
|
| 207 |
+
if index == -1:
|
| 208 |
+
raise SyntaxError(
|
| 209 |
+
"indices in path predicates are 1-based, not 0-based")
|
| 210 |
+
else:
|
| 211 |
+
raise SyntaxError("path index >= 1 expected")
|
| 212 |
+
else:
|
| 213 |
+
if predicate[0] != "last":
|
| 214 |
+
raise SyntaxError("unsupported function")
|
| 215 |
+
if signature == "-()-":
|
| 216 |
+
try:
|
| 217 |
+
index = int(predicate[2]) - 1
|
| 218 |
+
except ValueError:
|
| 219 |
+
raise SyntaxError("unsupported expression")
|
| 220 |
+
else:
|
| 221 |
+
index = -1
|
| 222 |
+
def select(result):
|
| 223 |
+
for elem in result:
|
| 224 |
+
parent = elem.getparent()
|
| 225 |
+
if parent is None:
|
| 226 |
+
continue
|
| 227 |
+
try:
|
| 228 |
+
# FIXME: what if the selector is "*" ?
|
| 229 |
+
elems = list(parent.iterchildren(elem.tag))
|
| 230 |
+
if elems[index] is elem:
|
| 231 |
+
yield elem
|
| 232 |
+
except IndexError:
|
| 233 |
+
pass
|
| 234 |
+
return select
|
| 235 |
+
raise SyntaxError("invalid predicate")
|
| 236 |
+
|
| 237 |
+
ops = {
|
| 238 |
+
"": prepare_child,
|
| 239 |
+
"*": prepare_star,
|
| 240 |
+
".": prepare_self,
|
| 241 |
+
"..": prepare_parent,
|
| 242 |
+
"//": prepare_descendant,
|
| 243 |
+
"[": prepare_predicate,
|
| 244 |
+
}
|
| 245 |
+
|
| 246 |
+
|
| 247 |
+
# --------------------------------------------------------------------
|
| 248 |
+
|
| 249 |
+
_cache = {}
|
| 250 |
+
|
| 251 |
+
|
| 252 |
+
def _build_path_iterator(path, namespaces, with_prefixes=True):
|
| 253 |
+
"""compile selector pattern"""
|
| 254 |
+
if path[-1:] == "/":
|
| 255 |
+
path += "*" # implicit all (FIXME: keep this?)
|
| 256 |
+
|
| 257 |
+
cache_key = (path,)
|
| 258 |
+
if namespaces:
|
| 259 |
+
# lxml originally used None for the default namespace but ElementTree uses the
|
| 260 |
+
# more convenient (all-strings-dict) empty string, so we support both here,
|
| 261 |
+
# preferring the more convenient '', as long as they aren't ambiguous.
|
| 262 |
+
if None in namespaces:
|
| 263 |
+
if '' in namespaces and namespaces[None] != namespaces['']:
|
| 264 |
+
raise ValueError("Ambiguous default namespace provided: %r versus %r" % (
|
| 265 |
+
namespaces[None], namespaces['']))
|
| 266 |
+
cache_key += (namespaces[None],) + tuple(sorted(
|
| 267 |
+
item for item in namespaces.items() if item[0] is not None))
|
| 268 |
+
else:
|
| 269 |
+
cache_key += tuple(sorted(namespaces.items()))
|
| 270 |
+
|
| 271 |
+
try:
|
| 272 |
+
return _cache[cache_key]
|
| 273 |
+
except KeyError:
|
| 274 |
+
pass
|
| 275 |
+
if len(_cache) > 100:
|
| 276 |
+
_cache.clear()
|
| 277 |
+
|
| 278 |
+
if path[:1] == "/":
|
| 279 |
+
raise SyntaxError("cannot use absolute path on element")
|
| 280 |
+
stream = iter(xpath_tokenizer(path, namespaces, with_prefixes=with_prefixes))
|
| 281 |
+
try:
|
| 282 |
+
_next = stream.next
|
| 283 |
+
except AttributeError:
|
| 284 |
+
# Python 3
|
| 285 |
+
_next = stream.__next__
|
| 286 |
+
try:
|
| 287 |
+
token = _next()
|
| 288 |
+
except StopIteration:
|
| 289 |
+
raise SyntaxError("empty path expression")
|
| 290 |
+
selector = []
|
| 291 |
+
while 1:
|
| 292 |
+
try:
|
| 293 |
+
selector.append(ops[token[0]](_next, token))
|
| 294 |
+
except StopIteration:
|
| 295 |
+
raise SyntaxError("invalid path")
|
| 296 |
+
try:
|
| 297 |
+
token = _next()
|
| 298 |
+
if token[0] == "/":
|
| 299 |
+
token = _next()
|
| 300 |
+
except StopIteration:
|
| 301 |
+
break
|
| 302 |
+
_cache[cache_key] = selector
|
| 303 |
+
return selector
|
| 304 |
+
|
| 305 |
+
|
| 306 |
+
##
|
| 307 |
+
# Iterate over the matching nodes
|
| 308 |
+
|
| 309 |
+
def iterfind(elem, path, namespaces=None, with_prefixes=True):
|
| 310 |
+
selector = _build_path_iterator(path, namespaces, with_prefixes=with_prefixes)
|
| 311 |
+
result = iter((elem,))
|
| 312 |
+
for select in selector:
|
| 313 |
+
result = select(result)
|
| 314 |
+
return result
|
| 315 |
+
|
| 316 |
+
|
| 317 |
+
##
|
| 318 |
+
# Find first matching object.
|
| 319 |
+
|
| 320 |
+
def find(elem, path, namespaces=None, with_prefixes=True):
|
| 321 |
+
it = iterfind(elem, path, namespaces, with_prefixes=with_prefixes)
|
| 322 |
+
try:
|
| 323 |
+
return next(it)
|
| 324 |
+
except StopIteration:
|
| 325 |
+
return None
|
| 326 |
+
|
| 327 |
+
|
| 328 |
+
##
|
| 329 |
+
# Find all matching objects.
|
| 330 |
+
|
| 331 |
+
def findall(elem, path, namespaces=None, with_prefixes=True):
|
| 332 |
+
return list(iterfind(elem, path, namespaces))
|
| 333 |
+
|
| 334 |
+
|
| 335 |
+
##
|
| 336 |
+
# Find text for first matching object.
|
| 337 |
+
|
| 338 |
+
def findtext(elem, path, default=None, namespaces=None, with_prefixes=True):
|
| 339 |
+
el = find(elem, path, namespaces, with_prefixes=with_prefixes)
|
| 340 |
+
if el is None:
|
| 341 |
+
return default
|
| 342 |
+
else:
|
| 343 |
+
return el.text or ''
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/apihelpers.pxi
ADDED
|
@@ -0,0 +1,1801 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Private/public helper functions for API functions
|
| 2 |
+
|
| 3 |
+
from lxml.includes cimport uri
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
cdef void displayNode(xmlNode* c_node, indent) noexcept:
|
| 7 |
+
# to help with debugging
|
| 8 |
+
cdef xmlNode* c_child
|
| 9 |
+
try:
|
| 10 |
+
print(indent * ' ', <long>c_node)
|
| 11 |
+
c_child = c_node.children
|
| 12 |
+
while c_child is not NULL:
|
| 13 |
+
displayNode(c_child, indent + 1)
|
| 14 |
+
c_child = c_child.next
|
| 15 |
+
finally:
|
| 16 |
+
return # swallow any exceptions
|
| 17 |
+
|
| 18 |
+
cdef inline bint _isHtmlDocument(_Element element) except -1:
|
| 19 |
+
cdef xmlNode* c_node = element._c_node
|
| 20 |
+
return (
|
| 21 |
+
c_node is not NULL and c_node.doc is not NULL and
|
| 22 |
+
c_node.doc.properties & tree.XML_DOC_HTML != 0
|
| 23 |
+
)
|
| 24 |
+
|
| 25 |
+
cdef inline int _assertValidNode(_Element element) except -1:
|
| 26 |
+
assert element._c_node is not NULL, "invalid Element proxy at %s" % id(element)
|
| 27 |
+
|
| 28 |
+
cdef inline int _assertValidDoc(_Document doc) except -1:
|
| 29 |
+
assert doc._c_doc is not NULL, "invalid Document proxy at %s" % id(doc)
|
| 30 |
+
|
| 31 |
+
cdef _Document _documentOrRaise(object input):
|
| 32 |
+
"""Call this to get the document of a _Document, _ElementTree or _Element
|
| 33 |
+
object, or to raise an exception if it can't be determined.
|
| 34 |
+
|
| 35 |
+
Should be used in all API functions for consistency.
|
| 36 |
+
"""
|
| 37 |
+
cdef _Document doc
|
| 38 |
+
if isinstance(input, _ElementTree):
|
| 39 |
+
if (<_ElementTree>input)._context_node is not None:
|
| 40 |
+
doc = (<_ElementTree>input)._context_node._doc
|
| 41 |
+
else:
|
| 42 |
+
doc = None
|
| 43 |
+
elif isinstance(input, _Element):
|
| 44 |
+
doc = (<_Element>input)._doc
|
| 45 |
+
elif isinstance(input, _Document):
|
| 46 |
+
doc = <_Document>input
|
| 47 |
+
else:
|
| 48 |
+
raise TypeError, f"Invalid input object: {python._fqtypename(input).decode('utf8')}"
|
| 49 |
+
if doc is None:
|
| 50 |
+
raise ValueError, f"Input object has no document: {python._fqtypename(input).decode('utf8')}"
|
| 51 |
+
_assertValidDoc(doc)
|
| 52 |
+
return doc
|
| 53 |
+
|
| 54 |
+
cdef _Element _rootNodeOrRaise(object input):
|
| 55 |
+
"""Call this to get the root node of a _Document, _ElementTree or
|
| 56 |
+
_Element object, or to raise an exception if it can't be determined.
|
| 57 |
+
|
| 58 |
+
Should be used in all API functions for consistency.
|
| 59 |
+
"""
|
| 60 |
+
cdef _Element node
|
| 61 |
+
if isinstance(input, _ElementTree):
|
| 62 |
+
node = (<_ElementTree>input)._context_node
|
| 63 |
+
elif isinstance(input, _Element):
|
| 64 |
+
node = <_Element>input
|
| 65 |
+
elif isinstance(input, _Document):
|
| 66 |
+
node = (<_Document>input).getroot()
|
| 67 |
+
else:
|
| 68 |
+
raise TypeError, f"Invalid input object: {python._fqtypename(input).decode('utf8')}"
|
| 69 |
+
if (node is None or not node._c_node or
|
| 70 |
+
node._c_node.type != tree.XML_ELEMENT_NODE):
|
| 71 |
+
raise ValueError, f"Input object is not an XML element: {python._fqtypename(input).decode('utf8')}"
|
| 72 |
+
_assertValidNode(node)
|
| 73 |
+
return node
|
| 74 |
+
|
| 75 |
+
cdef bint _isAncestorOrSame(xmlNode* c_ancestor, xmlNode* c_node) noexcept:
|
| 76 |
+
while c_node:
|
| 77 |
+
if c_node is c_ancestor:
|
| 78 |
+
return True
|
| 79 |
+
c_node = c_node.parent
|
| 80 |
+
return False
|
| 81 |
+
|
| 82 |
+
cdef _Element _makeElement(tag, xmlDoc* c_doc, _Document doc,
|
| 83 |
+
_BaseParser parser, text, tail, attrib, nsmap,
|
| 84 |
+
dict extra_attrs):
|
| 85 |
+
"""Create a new element and initialize text content, namespaces and
|
| 86 |
+
attributes.
|
| 87 |
+
|
| 88 |
+
This helper function will reuse as much of the existing document as
|
| 89 |
+
possible:
|
| 90 |
+
|
| 91 |
+
If 'parser' is None, the parser will be inherited from 'doc' or the
|
| 92 |
+
default parser will be used.
|
| 93 |
+
|
| 94 |
+
If 'doc' is None, 'c_doc' is used to create a new _Document and the new
|
| 95 |
+
element is made its root node.
|
| 96 |
+
|
| 97 |
+
If 'c_doc' is also NULL, a new xmlDoc will be created.
|
| 98 |
+
"""
|
| 99 |
+
cdef xmlNode* c_node
|
| 100 |
+
if doc is not None:
|
| 101 |
+
c_doc = doc._c_doc
|
| 102 |
+
ns_utf, name_utf = _getNsTag(tag)
|
| 103 |
+
if parser is not None and parser._for_html:
|
| 104 |
+
_htmlTagValidOrRaise(name_utf)
|
| 105 |
+
if c_doc is NULL:
|
| 106 |
+
c_doc = _newHTMLDoc()
|
| 107 |
+
else:
|
| 108 |
+
_tagValidOrRaise(name_utf)
|
| 109 |
+
if c_doc is NULL:
|
| 110 |
+
c_doc = _newXMLDoc()
|
| 111 |
+
c_node = _createElement(c_doc, name_utf)
|
| 112 |
+
if c_node is NULL:
|
| 113 |
+
if doc is None and c_doc is not NULL:
|
| 114 |
+
tree.xmlFreeDoc(c_doc)
|
| 115 |
+
raise MemoryError()
|
| 116 |
+
try:
|
| 117 |
+
if doc is None:
|
| 118 |
+
tree.xmlDocSetRootElement(c_doc, c_node)
|
| 119 |
+
doc = _documentFactory(c_doc, parser)
|
| 120 |
+
if text is not None:
|
| 121 |
+
_setNodeText(c_node, text)
|
| 122 |
+
if tail is not None:
|
| 123 |
+
_setTailText(c_node, tail)
|
| 124 |
+
# add namespaces to node if necessary
|
| 125 |
+
_setNodeNamespaces(c_node, doc, ns_utf, nsmap)
|
| 126 |
+
_initNodeAttributes(c_node, doc, attrib, extra_attrs)
|
| 127 |
+
return _elementFactory(doc, c_node)
|
| 128 |
+
except:
|
| 129 |
+
# free allocated c_node/c_doc unless Python does it for us
|
| 130 |
+
if c_node.doc is not c_doc:
|
| 131 |
+
# node not yet in document => will not be freed by document
|
| 132 |
+
if tail is not None:
|
| 133 |
+
_removeText(c_node.next) # tail
|
| 134 |
+
tree.xmlFreeNode(c_node)
|
| 135 |
+
if doc is None:
|
| 136 |
+
# c_doc will not be freed by doc
|
| 137 |
+
tree.xmlFreeDoc(c_doc)
|
| 138 |
+
raise
|
| 139 |
+
|
| 140 |
+
cdef int _initNewElement(_Element element, bint is_html, name_utf, ns_utf,
|
| 141 |
+
_BaseParser parser, attrib, nsmap, dict extra_attrs) except -1:
|
| 142 |
+
"""Initialise a new Element object.
|
| 143 |
+
|
| 144 |
+
This is used when users instantiate a Python Element subclass
|
| 145 |
+
directly, without it being mapped to an existing XML node.
|
| 146 |
+
"""
|
| 147 |
+
cdef xmlDoc* c_doc
|
| 148 |
+
cdef xmlNode* c_node
|
| 149 |
+
cdef _Document doc
|
| 150 |
+
if is_html:
|
| 151 |
+
_htmlTagValidOrRaise(name_utf)
|
| 152 |
+
c_doc = _newHTMLDoc()
|
| 153 |
+
else:
|
| 154 |
+
_tagValidOrRaise(name_utf)
|
| 155 |
+
c_doc = _newXMLDoc()
|
| 156 |
+
c_node = _createElement(c_doc, name_utf)
|
| 157 |
+
if c_node is NULL:
|
| 158 |
+
if c_doc is not NULL:
|
| 159 |
+
tree.xmlFreeDoc(c_doc)
|
| 160 |
+
raise MemoryError()
|
| 161 |
+
tree.xmlDocSetRootElement(c_doc, c_node)
|
| 162 |
+
doc = _documentFactory(c_doc, parser)
|
| 163 |
+
# add namespaces to node if necessary
|
| 164 |
+
_setNodeNamespaces(c_node, doc, ns_utf, nsmap)
|
| 165 |
+
_initNodeAttributes(c_node, doc, attrib, extra_attrs)
|
| 166 |
+
_registerProxy(element, doc, c_node)
|
| 167 |
+
element._init()
|
| 168 |
+
return 0
|
| 169 |
+
|
| 170 |
+
cdef _Element _makeSubElement(_Element parent, tag, text, tail,
|
| 171 |
+
attrib, nsmap, dict extra_attrs):
|
| 172 |
+
"""Create a new child element and initialize text content, namespaces and
|
| 173 |
+
attributes.
|
| 174 |
+
"""
|
| 175 |
+
cdef xmlNode* c_node
|
| 176 |
+
cdef xmlDoc* c_doc
|
| 177 |
+
if parent is None or parent._doc is None:
|
| 178 |
+
return None
|
| 179 |
+
_assertValidNode(parent)
|
| 180 |
+
ns_utf, name_utf = _getNsTag(tag)
|
| 181 |
+
c_doc = parent._doc._c_doc
|
| 182 |
+
|
| 183 |
+
if parent._doc._parser is not None and parent._doc._parser._for_html:
|
| 184 |
+
_htmlTagValidOrRaise(name_utf)
|
| 185 |
+
else:
|
| 186 |
+
_tagValidOrRaise(name_utf)
|
| 187 |
+
|
| 188 |
+
c_node = _createElement(c_doc, name_utf)
|
| 189 |
+
if c_node is NULL:
|
| 190 |
+
raise MemoryError()
|
| 191 |
+
tree.xmlAddChild(parent._c_node, c_node)
|
| 192 |
+
|
| 193 |
+
try:
|
| 194 |
+
if text is not None:
|
| 195 |
+
_setNodeText(c_node, text)
|
| 196 |
+
if tail is not None:
|
| 197 |
+
_setTailText(c_node, tail)
|
| 198 |
+
|
| 199 |
+
# add namespaces to node if necessary
|
| 200 |
+
_setNodeNamespaces(c_node, parent._doc, ns_utf, nsmap)
|
| 201 |
+
_initNodeAttributes(c_node, parent._doc, attrib, extra_attrs)
|
| 202 |
+
return _elementFactory(parent._doc, c_node)
|
| 203 |
+
except:
|
| 204 |
+
# make sure we clean up in case of an error
|
| 205 |
+
_removeNode(parent._doc, c_node)
|
| 206 |
+
raise
|
| 207 |
+
|
| 208 |
+
|
| 209 |
+
cdef int _setNodeNamespaces(xmlNode* c_node, _Document doc,
|
| 210 |
+
object node_ns_utf, object nsmap) except -1:
|
| 211 |
+
"""Lookup current namespace prefixes, then set namespace structure for
|
| 212 |
+
node (if 'node_ns_utf' was provided) and register new ns-prefix mappings.
|
| 213 |
+
|
| 214 |
+
'node_ns_utf' should only be passed for a newly created node.
|
| 215 |
+
"""
|
| 216 |
+
cdef xmlNs* c_ns
|
| 217 |
+
cdef list nsdefs
|
| 218 |
+
|
| 219 |
+
if nsmap:
|
| 220 |
+
for prefix, href in _iter_nsmap(nsmap):
|
| 221 |
+
href_utf = _utf8(href)
|
| 222 |
+
_uriValidOrRaise(href_utf)
|
| 223 |
+
c_href = _xcstr(href_utf)
|
| 224 |
+
if prefix is not None:
|
| 225 |
+
prefix_utf = _utf8(prefix)
|
| 226 |
+
_prefixValidOrRaise(prefix_utf)
|
| 227 |
+
c_prefix = _xcstr(prefix_utf)
|
| 228 |
+
else:
|
| 229 |
+
c_prefix = <const_xmlChar*>NULL
|
| 230 |
+
# add namespace with prefix if it is not already known
|
| 231 |
+
c_ns = tree.xmlSearchNs(doc._c_doc, c_node, c_prefix)
|
| 232 |
+
if c_ns is NULL or \
|
| 233 |
+
c_ns.href is NULL or \
|
| 234 |
+
tree.xmlStrcmp(c_ns.href, c_href) != 0:
|
| 235 |
+
c_ns = tree.xmlNewNs(c_node, c_href, c_prefix)
|
| 236 |
+
if href_utf == node_ns_utf:
|
| 237 |
+
tree.xmlSetNs(c_node, c_ns)
|
| 238 |
+
node_ns_utf = None
|
| 239 |
+
|
| 240 |
+
if node_ns_utf is not None:
|
| 241 |
+
_uriValidOrRaise(node_ns_utf)
|
| 242 |
+
doc._setNodeNs(c_node, _xcstr(node_ns_utf))
|
| 243 |
+
return 0
|
| 244 |
+
|
| 245 |
+
|
| 246 |
+
cdef dict _build_nsmap(xmlNode* c_node):
|
| 247 |
+
"""
|
| 248 |
+
Namespace prefix->URI mapping known in the context of this Element.
|
| 249 |
+
This includes all namespace declarations of the parents.
|
| 250 |
+
"""
|
| 251 |
+
cdef xmlNs* c_ns
|
| 252 |
+
nsmap = {}
|
| 253 |
+
while c_node is not NULL and c_node.type == tree.XML_ELEMENT_NODE:
|
| 254 |
+
c_ns = c_node.nsDef
|
| 255 |
+
while c_ns is not NULL:
|
| 256 |
+
if c_ns.prefix or c_ns.href:
|
| 257 |
+
prefix = funicodeOrNone(c_ns.prefix)
|
| 258 |
+
if prefix not in nsmap:
|
| 259 |
+
nsmap[prefix] = funicodeOrNone(c_ns.href)
|
| 260 |
+
c_ns = c_ns.next
|
| 261 |
+
c_node = c_node.parent
|
| 262 |
+
return nsmap
|
| 263 |
+
|
| 264 |
+
|
| 265 |
+
cdef _iter_nsmap(nsmap):
|
| 266 |
+
"""
|
| 267 |
+
Create a reproducibly ordered iterable from an nsmap mapping.
|
| 268 |
+
Tries to preserve an existing order and sorts if it assumes no order.
|
| 269 |
+
|
| 270 |
+
The difference to _iter_attrib() is that None doesn't sort with strings
|
| 271 |
+
in Py3.x.
|
| 272 |
+
"""
|
| 273 |
+
if isinstance(nsmap, dict):
|
| 274 |
+
# dicts are insertion-ordered in Py3.6+ => keep the user provided order.
|
| 275 |
+
return nsmap.items()
|
| 276 |
+
if len(nsmap) <= 1:
|
| 277 |
+
return nsmap.items()
|
| 278 |
+
# nsmap will usually be a plain unordered dict => avoid type checking overhead
|
| 279 |
+
if type(nsmap) is not dict and isinstance(nsmap, OrderedDict):
|
| 280 |
+
return nsmap.items() # keep existing order
|
| 281 |
+
if None not in nsmap:
|
| 282 |
+
return sorted(nsmap.items())
|
| 283 |
+
|
| 284 |
+
# Move the default namespace to the end. This makes sure libxml2
|
| 285 |
+
# prefers a prefix if the ns is defined redundantly on the same
|
| 286 |
+
# element. That way, users can work around a problem themselves
|
| 287 |
+
# where default namespace attributes on non-default namespaced
|
| 288 |
+
# elements serialise without prefix (i.e. into the non-default
|
| 289 |
+
# namespace).
|
| 290 |
+
default_ns = nsmap[None]
|
| 291 |
+
nsdefs = [(k, v) for k, v in nsmap.items() if k is not None]
|
| 292 |
+
nsdefs.sort()
|
| 293 |
+
nsdefs.append((None, default_ns))
|
| 294 |
+
return nsdefs
|
| 295 |
+
|
| 296 |
+
|
| 297 |
+
cdef _iter_attrib(attrib):
|
| 298 |
+
"""
|
| 299 |
+
Create a reproducibly ordered iterable from an attrib mapping.
|
| 300 |
+
Tries to preserve an existing order and sorts if it assumes no order.
|
| 301 |
+
"""
|
| 302 |
+
# dicts are insertion-ordered in Py3.6+ => keep the user provided order.
|
| 303 |
+
if isinstance(attrib, (dict, _Attrib, OrderedDict)):
|
| 304 |
+
return attrib.items()
|
| 305 |
+
# assume it's an unordered mapping of some kind
|
| 306 |
+
return sorted(attrib.items())
|
| 307 |
+
|
| 308 |
+
|
| 309 |
+
cdef _initNodeAttributes(xmlNode* c_node, _Document doc, attrib, dict extra):
|
| 310 |
+
"""Initialise the attributes of an element node.
|
| 311 |
+
"""
|
| 312 |
+
cdef bint is_html
|
| 313 |
+
cdef xmlNs* c_ns
|
| 314 |
+
if attrib is not None and not hasattr(attrib, 'items'):
|
| 315 |
+
raise TypeError, f"Invalid attribute dictionary: {python._fqtypename(attrib).decode('utf8')}"
|
| 316 |
+
if not attrib and not extra:
|
| 317 |
+
return # nothing to do
|
| 318 |
+
is_html = doc._parser._for_html
|
| 319 |
+
seen = set()
|
| 320 |
+
if extra:
|
| 321 |
+
for name, value in extra.items():
|
| 322 |
+
_addAttributeToNode(c_node, doc, is_html, name, value, seen)
|
| 323 |
+
if attrib:
|
| 324 |
+
for name, value in _iter_attrib(attrib):
|
| 325 |
+
_addAttributeToNode(c_node, doc, is_html, name, value, seen)
|
| 326 |
+
|
| 327 |
+
|
| 328 |
+
cdef int _addAttributeToNode(xmlNode* c_node, _Document doc, bint is_html,
|
| 329 |
+
name, value, set seen_tags) except -1:
|
| 330 |
+
ns_utf, name_utf = tag = _getNsTag(name)
|
| 331 |
+
if tag in seen_tags:
|
| 332 |
+
return 0
|
| 333 |
+
seen_tags.add(tag)
|
| 334 |
+
if not is_html:
|
| 335 |
+
_attributeValidOrRaise(name_utf)
|
| 336 |
+
value_utf = _utf8(value)
|
| 337 |
+
if ns_utf is None:
|
| 338 |
+
tree.xmlNewProp(c_node, _xcstr(name_utf), _xcstr(value_utf))
|
| 339 |
+
else:
|
| 340 |
+
_uriValidOrRaise(ns_utf)
|
| 341 |
+
c_ns = doc._findOrBuildNodeNs(c_node, _xcstr(ns_utf), NULL, 1)
|
| 342 |
+
tree.xmlNewNsProp(c_node, c_ns,
|
| 343 |
+
_xcstr(name_utf), _xcstr(value_utf))
|
| 344 |
+
return 0
|
| 345 |
+
|
| 346 |
+
|
| 347 |
+
ctypedef struct _ns_node_ref:
|
| 348 |
+
xmlNs* ns
|
| 349 |
+
xmlNode* node
|
| 350 |
+
|
| 351 |
+
|
| 352 |
+
cdef int _collectNsDefs(xmlNode* c_element, _ns_node_ref **_c_ns_list,
|
| 353 |
+
size_t *_c_ns_list_len, size_t *_c_ns_list_size) except -1:
|
| 354 |
+
c_ns_list = _c_ns_list[0]
|
| 355 |
+
cdef size_t c_ns_list_len = _c_ns_list_len[0]
|
| 356 |
+
cdef size_t c_ns_list_size = _c_ns_list_size[0]
|
| 357 |
+
|
| 358 |
+
c_nsdef = c_element.nsDef
|
| 359 |
+
while c_nsdef is not NULL:
|
| 360 |
+
if c_ns_list_len >= c_ns_list_size:
|
| 361 |
+
if c_ns_list is NULL:
|
| 362 |
+
c_ns_list_size = 20
|
| 363 |
+
else:
|
| 364 |
+
c_ns_list_size *= 2
|
| 365 |
+
c_nsref_ptr = <_ns_node_ref*> python.lxml_realloc(
|
| 366 |
+
c_ns_list, c_ns_list_size, sizeof(_ns_node_ref))
|
| 367 |
+
if c_nsref_ptr is NULL:
|
| 368 |
+
if c_ns_list is not NULL:
|
| 369 |
+
python.lxml_free(c_ns_list)
|
| 370 |
+
_c_ns_list[0] = NULL
|
| 371 |
+
raise MemoryError()
|
| 372 |
+
c_ns_list = c_nsref_ptr
|
| 373 |
+
|
| 374 |
+
c_ns_list[c_ns_list_len] = _ns_node_ref(c_nsdef, c_element)
|
| 375 |
+
c_ns_list_len += 1
|
| 376 |
+
c_nsdef = c_nsdef.next
|
| 377 |
+
|
| 378 |
+
_c_ns_list_size[0] = c_ns_list_size
|
| 379 |
+
_c_ns_list_len[0] = c_ns_list_len
|
| 380 |
+
_c_ns_list[0] = c_ns_list
|
| 381 |
+
|
| 382 |
+
|
| 383 |
+
cdef int _removeUnusedNamespaceDeclarations(xmlNode* c_element, set prefixes_to_keep) except -1:
|
| 384 |
+
"""Remove any namespace declarations from a subtree that are not used by
|
| 385 |
+
any of its elements (or attributes).
|
| 386 |
+
|
| 387 |
+
If a 'prefixes_to_keep' is provided, it must be a set of prefixes.
|
| 388 |
+
Any corresponding namespace mappings will not be removed as part of the cleanup.
|
| 389 |
+
"""
|
| 390 |
+
cdef xmlNode* c_node
|
| 391 |
+
cdef _ns_node_ref* c_ns_list = NULL
|
| 392 |
+
cdef size_t c_ns_list_size = 0
|
| 393 |
+
cdef size_t c_ns_list_len = 0
|
| 394 |
+
cdef size_t i
|
| 395 |
+
|
| 396 |
+
if c_element.parent and c_element.parent.type == tree.XML_DOCUMENT_NODE:
|
| 397 |
+
# include declarations on the document node
|
| 398 |
+
_collectNsDefs(c_element.parent, &c_ns_list, &c_ns_list_len, &c_ns_list_size)
|
| 399 |
+
|
| 400 |
+
tree.BEGIN_FOR_EACH_ELEMENT_FROM(c_element, c_element, 1)
|
| 401 |
+
# collect all new namespace declarations into the ns list
|
| 402 |
+
if c_element.nsDef:
|
| 403 |
+
_collectNsDefs(c_element, &c_ns_list, &c_ns_list_len, &c_ns_list_size)
|
| 404 |
+
|
| 405 |
+
# remove all namespace declarations from the list that are referenced
|
| 406 |
+
if c_ns_list_len and c_element.type == tree.XML_ELEMENT_NODE:
|
| 407 |
+
c_node = c_element
|
| 408 |
+
while c_node and c_ns_list_len:
|
| 409 |
+
if c_node.ns:
|
| 410 |
+
for i in range(c_ns_list_len):
|
| 411 |
+
if c_node.ns is c_ns_list[i].ns:
|
| 412 |
+
c_ns_list_len -= 1
|
| 413 |
+
c_ns_list[i] = c_ns_list[c_ns_list_len]
|
| 414 |
+
#c_ns_list[c_ns_list_len] = _ns_node_ref(NULL, NULL)
|
| 415 |
+
break
|
| 416 |
+
if c_node is c_element:
|
| 417 |
+
# continue with attributes
|
| 418 |
+
c_node = <xmlNode*>c_element.properties
|
| 419 |
+
else:
|
| 420 |
+
c_node = c_node.next
|
| 421 |
+
tree.END_FOR_EACH_ELEMENT_FROM(c_element)
|
| 422 |
+
|
| 423 |
+
if c_ns_list is NULL:
|
| 424 |
+
return 0
|
| 425 |
+
|
| 426 |
+
# free all namespace declarations that remained in the list,
|
| 427 |
+
# except for those we should keep explicitly
|
| 428 |
+
cdef xmlNs* c_nsdef
|
| 429 |
+
for i in range(c_ns_list_len):
|
| 430 |
+
if prefixes_to_keep is not None:
|
| 431 |
+
if c_ns_list[i].ns.prefix and c_ns_list[i].ns.prefix in prefixes_to_keep:
|
| 432 |
+
continue
|
| 433 |
+
c_node = c_ns_list[i].node
|
| 434 |
+
c_nsdef = c_node.nsDef
|
| 435 |
+
if c_nsdef is c_ns_list[i].ns:
|
| 436 |
+
c_node.nsDef = c_node.nsDef.next
|
| 437 |
+
else:
|
| 438 |
+
while c_nsdef.next is not c_ns_list[i].ns:
|
| 439 |
+
c_nsdef = c_nsdef.next
|
| 440 |
+
c_nsdef.next = c_nsdef.next.next
|
| 441 |
+
tree.xmlFreeNs(c_ns_list[i].ns)
|
| 442 |
+
|
| 443 |
+
if c_ns_list is not NULL:
|
| 444 |
+
python.lxml_free(c_ns_list)
|
| 445 |
+
return 0
|
| 446 |
+
|
| 447 |
+
cdef xmlNs* _searchNsByHref(xmlNode* c_node, const_xmlChar* c_href, bint is_attribute) noexcept:
|
| 448 |
+
"""Search a namespace declaration that covers a node (element or
|
| 449 |
+
attribute).
|
| 450 |
+
|
| 451 |
+
For attributes, try to find a prefixed namespace declaration
|
| 452 |
+
instead of the default namespaces. This helps in supporting
|
| 453 |
+
round-trips for attributes on elements with a different namespace.
|
| 454 |
+
"""
|
| 455 |
+
cdef xmlNs* c_ns
|
| 456 |
+
cdef xmlNs* c_default_ns = NULL
|
| 457 |
+
cdef xmlNode* c_element
|
| 458 |
+
if c_href is NULL or c_node is NULL or c_node.type == tree.XML_ENTITY_REF_NODE:
|
| 459 |
+
return NULL
|
| 460 |
+
if tree.xmlStrcmp(c_href, tree.XML_XML_NAMESPACE) == 0:
|
| 461 |
+
# no special cases here, let libxml2 handle this
|
| 462 |
+
return tree.xmlSearchNsByHref(c_node.doc, c_node, c_href)
|
| 463 |
+
if c_node.type == tree.XML_ATTRIBUTE_NODE:
|
| 464 |
+
is_attribute = 1
|
| 465 |
+
while c_node is not NULL and c_node.type != tree.XML_ELEMENT_NODE:
|
| 466 |
+
c_node = c_node.parent
|
| 467 |
+
c_element = c_node
|
| 468 |
+
while c_node is not NULL:
|
| 469 |
+
if c_node.type == tree.XML_ELEMENT_NODE:
|
| 470 |
+
c_ns = c_node.nsDef
|
| 471 |
+
while c_ns is not NULL:
|
| 472 |
+
if c_ns.href is not NULL and tree.xmlStrcmp(c_href, c_ns.href) == 0:
|
| 473 |
+
if c_ns.prefix is NULL and is_attribute:
|
| 474 |
+
# for attributes, continue searching a named
|
| 475 |
+
# prefix, but keep the first default namespace
|
| 476 |
+
# declaration that we found
|
| 477 |
+
if c_default_ns is NULL:
|
| 478 |
+
c_default_ns = c_ns
|
| 479 |
+
elif tree.xmlSearchNs(
|
| 480 |
+
c_element.doc, c_element, c_ns.prefix) is c_ns:
|
| 481 |
+
# start node is in namespace scope => found!
|
| 482 |
+
return c_ns
|
| 483 |
+
c_ns = c_ns.next
|
| 484 |
+
if c_node is not c_element and c_node.ns is not NULL:
|
| 485 |
+
# optimise: the node may have the namespace itself
|
| 486 |
+
c_ns = c_node.ns
|
| 487 |
+
if c_ns.href is not NULL and tree.xmlStrcmp(c_href, c_ns.href) == 0:
|
| 488 |
+
if c_ns.prefix is NULL and is_attribute:
|
| 489 |
+
# for attributes, continue searching a named
|
| 490 |
+
# prefix, but keep the first default namespace
|
| 491 |
+
# declaration that we found
|
| 492 |
+
if c_default_ns is NULL:
|
| 493 |
+
c_default_ns = c_ns
|
| 494 |
+
elif tree.xmlSearchNs(
|
| 495 |
+
c_element.doc, c_element, c_ns.prefix) is c_ns:
|
| 496 |
+
# start node is in namespace scope => found!
|
| 497 |
+
return c_ns
|
| 498 |
+
c_node = c_node.parent
|
| 499 |
+
# nothing found => use a matching default namespace or fail
|
| 500 |
+
if c_default_ns is not NULL:
|
| 501 |
+
if tree.xmlSearchNs(c_element.doc, c_element, NULL) is c_default_ns:
|
| 502 |
+
return c_default_ns
|
| 503 |
+
return NULL
|
| 504 |
+
|
| 505 |
+
cdef int _replaceNodeByChildren(_Document doc, xmlNode* c_node) except -1:
|
| 506 |
+
# NOTE: this does not deallocate the node, just unlink it!
|
| 507 |
+
cdef xmlNode* c_parent
|
| 508 |
+
cdef xmlNode* c_child
|
| 509 |
+
if c_node.children is NULL:
|
| 510 |
+
tree.xmlUnlinkNode(c_node)
|
| 511 |
+
return 0
|
| 512 |
+
|
| 513 |
+
c_parent = c_node.parent
|
| 514 |
+
# fix parent links of children
|
| 515 |
+
c_child = c_node.children
|
| 516 |
+
while c_child is not NULL:
|
| 517 |
+
c_child.parent = c_parent
|
| 518 |
+
c_child = c_child.next
|
| 519 |
+
|
| 520 |
+
# fix namespace references of children if their parent's namespace
|
| 521 |
+
# declarations get lost
|
| 522 |
+
if c_node.nsDef is not NULL:
|
| 523 |
+
c_child = c_node.children
|
| 524 |
+
while c_child is not NULL:
|
| 525 |
+
moveNodeToDocument(doc, doc._c_doc, c_child)
|
| 526 |
+
c_child = c_child.next
|
| 527 |
+
|
| 528 |
+
# fix sibling links to/from child slice
|
| 529 |
+
if c_node.prev is NULL:
|
| 530 |
+
c_parent.children = c_node.children
|
| 531 |
+
else:
|
| 532 |
+
c_node.prev.next = c_node.children
|
| 533 |
+
c_node.children.prev = c_node.prev
|
| 534 |
+
if c_node.next is NULL:
|
| 535 |
+
c_parent.last = c_node.last
|
| 536 |
+
else:
|
| 537 |
+
c_node.next.prev = c_node.last
|
| 538 |
+
c_node.last.next = c_node.next
|
| 539 |
+
|
| 540 |
+
# unlink c_node
|
| 541 |
+
c_node.children = c_node.last = NULL
|
| 542 |
+
c_node.parent = c_node.next = c_node.prev = NULL
|
| 543 |
+
return 0
|
| 544 |
+
|
| 545 |
+
cdef unicode _attributeValue(xmlNode* c_element, xmlAttr* c_attrib_node):
|
| 546 |
+
c_href = _getNs(<xmlNode*>c_attrib_node)
|
| 547 |
+
value = tree.xmlGetNsProp(c_element, c_attrib_node.name, c_href)
|
| 548 |
+
try:
|
| 549 |
+
result = funicode(value)
|
| 550 |
+
finally:
|
| 551 |
+
tree.xmlFree(value)
|
| 552 |
+
return result
|
| 553 |
+
|
| 554 |
+
cdef unicode _attributeValueFromNsName(xmlNode* c_element,
|
| 555 |
+
const_xmlChar* c_href, const_xmlChar* c_name):
|
| 556 |
+
c_result = tree.xmlGetNsProp(c_element, c_name, c_href)
|
| 557 |
+
if c_result is NULL:
|
| 558 |
+
return None
|
| 559 |
+
try:
|
| 560 |
+
result = funicode(c_result)
|
| 561 |
+
finally:
|
| 562 |
+
tree.xmlFree(c_result)
|
| 563 |
+
return result
|
| 564 |
+
|
| 565 |
+
cdef object _getNodeAttributeValue(xmlNode* c_node, key, default):
|
| 566 |
+
ns, tag = _getNsTag(key)
|
| 567 |
+
c_href = <const_xmlChar*>NULL if ns is None else _xcstr(ns)
|
| 568 |
+
c_result = tree.xmlGetNsProp(c_node, _xcstr(tag), c_href)
|
| 569 |
+
if c_result is NULL:
|
| 570 |
+
# XXX free namespace that is not in use..?
|
| 571 |
+
return default
|
| 572 |
+
try:
|
| 573 |
+
result = funicode(c_result)
|
| 574 |
+
finally:
|
| 575 |
+
tree.xmlFree(c_result)
|
| 576 |
+
return result
|
| 577 |
+
|
| 578 |
+
cdef inline object _getAttributeValue(_Element element, key, default):
|
| 579 |
+
return _getNodeAttributeValue(element._c_node, key, default)
|
| 580 |
+
|
| 581 |
+
cdef int _setAttributeValue(_Element element, key, value) except -1:
|
| 582 |
+
cdef const_xmlChar* c_value
|
| 583 |
+
cdef xmlNs* c_ns
|
| 584 |
+
ns, tag = _getNsTag(key)
|
| 585 |
+
is_html = element._doc._parser._for_html
|
| 586 |
+
if not is_html:
|
| 587 |
+
_attributeValidOrRaise(tag)
|
| 588 |
+
c_tag = _xcstr(tag)
|
| 589 |
+
if value is None and is_html:
|
| 590 |
+
c_value = NULL
|
| 591 |
+
else:
|
| 592 |
+
if isinstance(value, QName):
|
| 593 |
+
value = _resolveQNameText(element, value)
|
| 594 |
+
else:
|
| 595 |
+
value = _utf8(value)
|
| 596 |
+
c_value = _xcstr(value)
|
| 597 |
+
if ns is None:
|
| 598 |
+
c_ns = NULL
|
| 599 |
+
else:
|
| 600 |
+
c_ns = element._doc._findOrBuildNodeNs(element._c_node, _xcstr(ns), NULL, 1)
|
| 601 |
+
tree.xmlSetNsProp(element._c_node, c_ns, c_tag, c_value)
|
| 602 |
+
return 0
|
| 603 |
+
|
| 604 |
+
cdef int _delAttribute(_Element element, key) except -1:
|
| 605 |
+
ns, tag = _getNsTag(key)
|
| 606 |
+
c_href = <const_xmlChar*>NULL if ns is None else _xcstr(ns)
|
| 607 |
+
if _delAttributeFromNsName(element._c_node, c_href, _xcstr(tag)):
|
| 608 |
+
raise KeyError, key
|
| 609 |
+
return 0
|
| 610 |
+
|
| 611 |
+
cdef int _delAttributeFromNsName(xmlNode* c_node, const_xmlChar* c_href, const_xmlChar* c_name) noexcept:
|
| 612 |
+
c_attr = tree.xmlHasNsProp(c_node, c_name, c_href)
|
| 613 |
+
if c_attr is NULL:
|
| 614 |
+
# XXX free namespace that is not in use..?
|
| 615 |
+
return -1
|
| 616 |
+
tree.xmlRemoveProp(c_attr)
|
| 617 |
+
return 0
|
| 618 |
+
|
| 619 |
+
cdef list _collectAttributes(xmlNode* c_node, int collecttype):
|
| 620 |
+
"""Collect all attributes of a node in a list. Depending on collecttype,
|
| 621 |
+
it collects either the name (1), the value (2) or the name-value tuples.
|
| 622 |
+
"""
|
| 623 |
+
cdef Py_ssize_t count
|
| 624 |
+
c_attr = c_node.properties
|
| 625 |
+
count = 0
|
| 626 |
+
while c_attr is not NULL:
|
| 627 |
+
if c_attr.type == tree.XML_ATTRIBUTE_NODE:
|
| 628 |
+
count += 1
|
| 629 |
+
c_attr = c_attr.next
|
| 630 |
+
|
| 631 |
+
if not count:
|
| 632 |
+
return []
|
| 633 |
+
|
| 634 |
+
attributes = [None] * count
|
| 635 |
+
c_attr = c_node.properties
|
| 636 |
+
count = 0
|
| 637 |
+
while c_attr is not NULL:
|
| 638 |
+
if c_attr.type == tree.XML_ATTRIBUTE_NODE:
|
| 639 |
+
if collecttype == 1:
|
| 640 |
+
item = _namespacedName(<xmlNode*>c_attr)
|
| 641 |
+
elif collecttype == 2:
|
| 642 |
+
item = _attributeValue(c_node, c_attr)
|
| 643 |
+
else:
|
| 644 |
+
item = (_namespacedName(<xmlNode*>c_attr),
|
| 645 |
+
_attributeValue(c_node, c_attr))
|
| 646 |
+
attributes[count] = item
|
| 647 |
+
count += 1
|
| 648 |
+
c_attr = c_attr.next
|
| 649 |
+
return attributes
|
| 650 |
+
|
| 651 |
+
cdef object __RE_XML_ENCODING = re.compile(
|
| 652 |
+
r'^(<\?xml[^>]+)\s+encoding\s*=\s*["\'][^"\']*["\'](\s*\?>|)', re.U)
|
| 653 |
+
|
| 654 |
+
cdef object __REPLACE_XML_ENCODING = __RE_XML_ENCODING.sub
|
| 655 |
+
cdef object __HAS_XML_ENCODING = __RE_XML_ENCODING.match
|
| 656 |
+
|
| 657 |
+
cdef object _stripEncodingDeclaration(object xml_string):
|
| 658 |
+
# this is a hack to remove the XML encoding declaration from unicode
|
| 659 |
+
return __REPLACE_XML_ENCODING(r'\g<1>\g<2>', xml_string)
|
| 660 |
+
|
| 661 |
+
cdef bint _hasEncodingDeclaration(object xml_string) except -1:
|
| 662 |
+
# check if a (unicode) string has an XML encoding declaration
|
| 663 |
+
return __HAS_XML_ENCODING(xml_string) is not None
|
| 664 |
+
|
| 665 |
+
cdef inline bint _hasText(xmlNode* c_node) noexcept:
|
| 666 |
+
return c_node is not NULL and _textNodeOrSkip(c_node.children) is not NULL
|
| 667 |
+
|
| 668 |
+
cdef inline bint _hasTail(xmlNode* c_node) noexcept:
|
| 669 |
+
return c_node is not NULL and _textNodeOrSkip(c_node.next) is not NULL
|
| 670 |
+
|
| 671 |
+
cdef inline bint _hasNonWhitespaceTail(xmlNode* c_node) except -1:
|
| 672 |
+
return _hasNonWhitespaceText(c_node, tail=True)
|
| 673 |
+
|
| 674 |
+
cdef bint _hasNonWhitespaceText(xmlNode* c_node, bint tail=False) except -1:
|
| 675 |
+
c_text_node = c_node and _textNodeOrSkip(c_node.next if tail else c_node.children)
|
| 676 |
+
if c_text_node is NULL:
|
| 677 |
+
return False
|
| 678 |
+
while c_text_node is not NULL:
|
| 679 |
+
if c_text_node.content[0] != c'\0' and not _collectText(c_text_node).isspace():
|
| 680 |
+
return True
|
| 681 |
+
c_text_node = _textNodeOrSkip(c_text_node.next)
|
| 682 |
+
return False
|
| 683 |
+
|
| 684 |
+
cdef unicode _collectText(xmlNode* c_node):
|
| 685 |
+
"""Collect all text nodes and return them as a unicode string.
|
| 686 |
+
|
| 687 |
+
Start collecting at c_node.
|
| 688 |
+
|
| 689 |
+
If there was no text to collect, return None
|
| 690 |
+
"""
|
| 691 |
+
cdef Py_ssize_t scount
|
| 692 |
+
cdef xmlChar* c_text
|
| 693 |
+
cdef xmlNode* c_node_cur
|
| 694 |
+
# check for multiple text nodes
|
| 695 |
+
scount = 0
|
| 696 |
+
c_text = NULL
|
| 697 |
+
c_node_cur = c_node = _textNodeOrSkip(c_node)
|
| 698 |
+
while c_node_cur is not NULL:
|
| 699 |
+
if c_node_cur.content[0] != c'\0':
|
| 700 |
+
c_text = c_node_cur.content
|
| 701 |
+
scount += 1
|
| 702 |
+
c_node_cur = _textNodeOrSkip(c_node_cur.next)
|
| 703 |
+
|
| 704 |
+
# handle two most common cases first
|
| 705 |
+
if c_text is NULL:
|
| 706 |
+
return '' if scount > 0 else None
|
| 707 |
+
if scount == 1:
|
| 708 |
+
return funicode(c_text)
|
| 709 |
+
|
| 710 |
+
# the rest is not performance critical anymore
|
| 711 |
+
result = b''
|
| 712 |
+
while c_node is not NULL:
|
| 713 |
+
result += <unsigned char*>c_node.content
|
| 714 |
+
c_node = _textNodeOrSkip(c_node.next)
|
| 715 |
+
return funicode(<const_xmlChar*><unsigned char*>result)
|
| 716 |
+
|
| 717 |
+
cdef void _removeText(xmlNode* c_node) noexcept:
|
| 718 |
+
"""Remove all text nodes.
|
| 719 |
+
|
| 720 |
+
Start removing at c_node.
|
| 721 |
+
"""
|
| 722 |
+
cdef xmlNode* c_next
|
| 723 |
+
c_node = _textNodeOrSkip(c_node)
|
| 724 |
+
while c_node is not NULL:
|
| 725 |
+
c_next = _textNodeOrSkip(c_node.next)
|
| 726 |
+
tree.xmlUnlinkNode(c_node)
|
| 727 |
+
tree.xmlFreeNode(c_node)
|
| 728 |
+
c_node = c_next
|
| 729 |
+
|
| 730 |
+
cdef xmlNode* _createTextNode(xmlDoc* doc, value) except NULL:
|
| 731 |
+
cdef xmlNode* c_text_node
|
| 732 |
+
if isinstance(value, CDATA):
|
| 733 |
+
c_text_node = tree.xmlNewCDataBlock(
|
| 734 |
+
doc, _xcstr((<CDATA>value)._utf8_data),
|
| 735 |
+
python.PyBytes_GET_SIZE((<CDATA>value)._utf8_data))
|
| 736 |
+
else:
|
| 737 |
+
text = _utf8(value)
|
| 738 |
+
c_text_node = tree.xmlNewDocText(doc, _xcstr(text))
|
| 739 |
+
if not c_text_node:
|
| 740 |
+
raise MemoryError()
|
| 741 |
+
return c_text_node
|
| 742 |
+
|
| 743 |
+
cdef int _setNodeText(xmlNode* c_node, value) except -1:
|
| 744 |
+
# remove all text nodes at the start first
|
| 745 |
+
_removeText(c_node.children)
|
| 746 |
+
if value is None:
|
| 747 |
+
return 0
|
| 748 |
+
# now add new text node with value at start
|
| 749 |
+
c_text_node = _createTextNode(c_node.doc, value)
|
| 750 |
+
if c_node.children is NULL:
|
| 751 |
+
tree.xmlAddChild(c_node, c_text_node)
|
| 752 |
+
else:
|
| 753 |
+
tree.xmlAddPrevSibling(c_node.children, c_text_node)
|
| 754 |
+
return 0
|
| 755 |
+
|
| 756 |
+
cdef int _setTailText(xmlNode* c_node, value) except -1:
|
| 757 |
+
# remove all text nodes at the start first
|
| 758 |
+
_removeText(c_node.next)
|
| 759 |
+
if value is None:
|
| 760 |
+
return 0
|
| 761 |
+
# now append new text node with value
|
| 762 |
+
c_text_node = _createTextNode(c_node.doc, value)
|
| 763 |
+
tree.xmlAddNextSibling(c_node, c_text_node)
|
| 764 |
+
return 0
|
| 765 |
+
|
| 766 |
+
cdef bytes _resolveQNameText(_Element element, value):
|
| 767 |
+
cdef xmlNs* c_ns
|
| 768 |
+
ns, tag = _getNsTag(value)
|
| 769 |
+
if ns is None:
|
| 770 |
+
return tag
|
| 771 |
+
else:
|
| 772 |
+
c_ns = element._doc._findOrBuildNodeNs(
|
| 773 |
+
element._c_node, _xcstr(ns), NULL, 0)
|
| 774 |
+
return python.PyBytes_FromFormat('%s:%s', c_ns.prefix, _cstr(tag))
|
| 775 |
+
|
| 776 |
+
cdef inline bint _hasChild(xmlNode* c_node) noexcept:
|
| 777 |
+
return c_node is not NULL and _findChildForwards(c_node, 0) is not NULL
|
| 778 |
+
|
| 779 |
+
cdef inline Py_ssize_t _countElements(xmlNode* c_node) noexcept:
|
| 780 |
+
"Counts the elements within the following siblings and the node itself."
|
| 781 |
+
cdef Py_ssize_t count
|
| 782 |
+
count = 0
|
| 783 |
+
while c_node is not NULL:
|
| 784 |
+
if _isElement(c_node):
|
| 785 |
+
count += 1
|
| 786 |
+
c_node = c_node.next
|
| 787 |
+
return count
|
| 788 |
+
|
| 789 |
+
cdef int _findChildSlice(
|
| 790 |
+
slice sliceobject, xmlNode* c_parent,
|
| 791 |
+
xmlNode** c_start_node, Py_ssize_t* c_step, Py_ssize_t* c_length) except -1:
|
| 792 |
+
"""Resolve a children slice.
|
| 793 |
+
|
| 794 |
+
Returns the start node, step size and the slice length in the
|
| 795 |
+
pointer arguments.
|
| 796 |
+
"""
|
| 797 |
+
cdef Py_ssize_t start = 0, stop = 0, childcount
|
| 798 |
+
childcount = _countElements(c_parent.children)
|
| 799 |
+
if childcount == 0:
|
| 800 |
+
c_start_node[0] = NULL
|
| 801 |
+
c_length[0] = 0
|
| 802 |
+
if sliceobject.step is None:
|
| 803 |
+
c_step[0] = 1
|
| 804 |
+
else:
|
| 805 |
+
python._PyEval_SliceIndex(sliceobject.step, c_step)
|
| 806 |
+
return 0
|
| 807 |
+
python.PySlice_GetIndicesEx(
|
| 808 |
+
sliceobject, childcount, &start, &stop, c_step, c_length)
|
| 809 |
+
if start > childcount // 2:
|
| 810 |
+
c_start_node[0] = _findChildBackwards(c_parent, childcount - start - 1)
|
| 811 |
+
else:
|
| 812 |
+
c_start_node[0] = _findChild(c_parent, start)
|
| 813 |
+
return 0
|
| 814 |
+
|
| 815 |
+
cdef bint _isFullSlice(slice sliceobject) except -1:
|
| 816 |
+
"""Conservative guess if this slice is a full slice as in ``s[:]``.
|
| 817 |
+
"""
|
| 818 |
+
cdef Py_ssize_t step = 0
|
| 819 |
+
if sliceobject is None:
|
| 820 |
+
return 0
|
| 821 |
+
if sliceobject.start is None and \
|
| 822 |
+
sliceobject.stop is None:
|
| 823 |
+
if sliceobject.step is None:
|
| 824 |
+
return 1
|
| 825 |
+
python._PyEval_SliceIndex(sliceobject.step, &step)
|
| 826 |
+
if step == 1:
|
| 827 |
+
return 1
|
| 828 |
+
return 0
|
| 829 |
+
return 0
|
| 830 |
+
|
| 831 |
+
cdef _collectChildren(_Element element):
|
| 832 |
+
cdef xmlNode* c_node
|
| 833 |
+
cdef list result = []
|
| 834 |
+
c_node = element._c_node.children
|
| 835 |
+
if c_node is not NULL:
|
| 836 |
+
if not _isElement(c_node):
|
| 837 |
+
c_node = _nextElement(c_node)
|
| 838 |
+
while c_node is not NULL:
|
| 839 |
+
result.append(_elementFactory(element._doc, c_node))
|
| 840 |
+
c_node = _nextElement(c_node)
|
| 841 |
+
return result
|
| 842 |
+
|
| 843 |
+
cdef inline xmlNode* _findChild(xmlNode* c_node, Py_ssize_t index) noexcept:
|
| 844 |
+
if index < 0:
|
| 845 |
+
return _findChildBackwards(c_node, -index - 1)
|
| 846 |
+
else:
|
| 847 |
+
return _findChildForwards(c_node, index)
|
| 848 |
+
|
| 849 |
+
cdef inline xmlNode* _findChildForwards(xmlNode* c_node, Py_ssize_t index) noexcept:
|
| 850 |
+
"""Return child element of c_node with index, or return NULL if not found.
|
| 851 |
+
"""
|
| 852 |
+
cdef xmlNode* c_child
|
| 853 |
+
cdef Py_ssize_t c
|
| 854 |
+
c_child = c_node.children
|
| 855 |
+
c = 0
|
| 856 |
+
while c_child is not NULL:
|
| 857 |
+
if _isElement(c_child):
|
| 858 |
+
if c == index:
|
| 859 |
+
return c_child
|
| 860 |
+
c += 1
|
| 861 |
+
c_child = c_child.next
|
| 862 |
+
return NULL
|
| 863 |
+
|
| 864 |
+
cdef inline xmlNode* _findChildBackwards(xmlNode* c_node, Py_ssize_t index) noexcept:
|
| 865 |
+
"""Return child element of c_node with index, or return NULL if not found.
|
| 866 |
+
Search from the end.
|
| 867 |
+
"""
|
| 868 |
+
cdef xmlNode* c_child
|
| 869 |
+
cdef Py_ssize_t c
|
| 870 |
+
c_child = c_node.last
|
| 871 |
+
c = 0
|
| 872 |
+
while c_child is not NULL:
|
| 873 |
+
if _isElement(c_child):
|
| 874 |
+
if c == index:
|
| 875 |
+
return c_child
|
| 876 |
+
c += 1
|
| 877 |
+
c_child = c_child.prev
|
| 878 |
+
return NULL
|
| 879 |
+
|
| 880 |
+
cdef inline xmlNode* _textNodeOrSkip(xmlNode* c_node) noexcept nogil:
|
| 881 |
+
"""Return the node if it's a text node. Skip over ignorable nodes in a
|
| 882 |
+
series of text nodes. Return NULL if a non-ignorable node is found.
|
| 883 |
+
|
| 884 |
+
This is used to skip over XInclude nodes when collecting adjacent text
|
| 885 |
+
nodes.
|
| 886 |
+
"""
|
| 887 |
+
while c_node is not NULL:
|
| 888 |
+
if c_node.type == tree.XML_TEXT_NODE or \
|
| 889 |
+
c_node.type == tree.XML_CDATA_SECTION_NODE:
|
| 890 |
+
return c_node
|
| 891 |
+
elif c_node.type == tree.XML_XINCLUDE_START or \
|
| 892 |
+
c_node.type == tree.XML_XINCLUDE_END:
|
| 893 |
+
c_node = c_node.next
|
| 894 |
+
else:
|
| 895 |
+
return NULL
|
| 896 |
+
return NULL
|
| 897 |
+
|
| 898 |
+
cdef inline xmlNode* _nextElement(xmlNode* c_node) noexcept:
|
| 899 |
+
"""Given a node, find the next sibling that is an element.
|
| 900 |
+
"""
|
| 901 |
+
if c_node is NULL:
|
| 902 |
+
return NULL
|
| 903 |
+
c_node = c_node.next
|
| 904 |
+
while c_node is not NULL:
|
| 905 |
+
if _isElement(c_node):
|
| 906 |
+
return c_node
|
| 907 |
+
c_node = c_node.next
|
| 908 |
+
return NULL
|
| 909 |
+
|
| 910 |
+
cdef inline xmlNode* _previousElement(xmlNode* c_node) noexcept:
|
| 911 |
+
"""Given a node, find the next sibling that is an element.
|
| 912 |
+
"""
|
| 913 |
+
if c_node is NULL:
|
| 914 |
+
return NULL
|
| 915 |
+
c_node = c_node.prev
|
| 916 |
+
while c_node is not NULL:
|
| 917 |
+
if _isElement(c_node):
|
| 918 |
+
return c_node
|
| 919 |
+
c_node = c_node.prev
|
| 920 |
+
return NULL
|
| 921 |
+
|
| 922 |
+
cdef inline xmlNode* _parentElement(xmlNode* c_node) noexcept:
|
| 923 |
+
"Given a node, find the parent element."
|
| 924 |
+
if c_node is NULL or not _isElement(c_node):
|
| 925 |
+
return NULL
|
| 926 |
+
c_node = c_node.parent
|
| 927 |
+
if c_node is NULL or not _isElement(c_node):
|
| 928 |
+
return NULL
|
| 929 |
+
return c_node
|
| 930 |
+
|
| 931 |
+
cdef inline bint _tagMatches(xmlNode* c_node, const_xmlChar* c_href, const_xmlChar* c_name) noexcept:
|
| 932 |
+
"""Tests if the node matches namespace URI and tag name.
|
| 933 |
+
|
| 934 |
+
A node matches if it matches both c_href and c_name.
|
| 935 |
+
|
| 936 |
+
A node matches c_href if any of the following is true:
|
| 937 |
+
* c_href is NULL
|
| 938 |
+
* its namespace is NULL and c_href is the empty string
|
| 939 |
+
* its namespace string equals the c_href string
|
| 940 |
+
|
| 941 |
+
A node matches c_name if any of the following is true:
|
| 942 |
+
* c_name is NULL
|
| 943 |
+
* its name string equals the c_name string
|
| 944 |
+
"""
|
| 945 |
+
if c_node is NULL:
|
| 946 |
+
return 0
|
| 947 |
+
if c_node.type != tree.XML_ELEMENT_NODE:
|
| 948 |
+
# not an element, only succeed if we match everything
|
| 949 |
+
return c_name is NULL and c_href is NULL
|
| 950 |
+
if c_name is NULL:
|
| 951 |
+
if c_href is NULL:
|
| 952 |
+
# always match
|
| 953 |
+
return 1
|
| 954 |
+
else:
|
| 955 |
+
c_node_href = _getNs(c_node)
|
| 956 |
+
if c_node_href is NULL:
|
| 957 |
+
return c_href[0] == c'\0'
|
| 958 |
+
else:
|
| 959 |
+
return tree.xmlStrcmp(c_node_href, c_href) == 0
|
| 960 |
+
elif c_href is NULL:
|
| 961 |
+
if _getNs(c_node) is not NULL:
|
| 962 |
+
return 0
|
| 963 |
+
return c_node.name == c_name or tree.xmlStrcmp(c_node.name, c_name) == 0
|
| 964 |
+
elif c_node.name == c_name or tree.xmlStrcmp(c_node.name, c_name) == 0:
|
| 965 |
+
c_node_href = _getNs(c_node)
|
| 966 |
+
if c_node_href is NULL:
|
| 967 |
+
return c_href[0] == c'\0'
|
| 968 |
+
else:
|
| 969 |
+
return tree.xmlStrcmp(c_node_href, c_href) == 0
|
| 970 |
+
else:
|
| 971 |
+
return 0
|
| 972 |
+
|
| 973 |
+
cdef inline bint _tagMatchesExactly(xmlNode* c_node, qname* c_qname) noexcept:
|
| 974 |
+
"""Tests if the node matches namespace URI and tag name.
|
| 975 |
+
|
| 976 |
+
This differs from _tagMatches() in that it does not consider a
|
| 977 |
+
NULL value in qname.href a wildcard, and that it expects the c_name
|
| 978 |
+
to be taken from the doc dict, i.e. it only compares the names by
|
| 979 |
+
address.
|
| 980 |
+
|
| 981 |
+
A node matches if it matches both href and c_name of the qname.
|
| 982 |
+
|
| 983 |
+
A node matches c_href if any of the following is true:
|
| 984 |
+
* its namespace is NULL and c_href is the empty string
|
| 985 |
+
* its namespace string equals the c_href string
|
| 986 |
+
|
| 987 |
+
A node matches c_name if any of the following is true:
|
| 988 |
+
* c_name is NULL
|
| 989 |
+
* its name string points to the same address (!) as c_name
|
| 990 |
+
"""
|
| 991 |
+
return _nsTagMatchesExactly(_getNs(c_node), c_node.name, c_qname)
|
| 992 |
+
|
| 993 |
+
cdef inline bint _nsTagMatchesExactly(const_xmlChar* c_node_href,
|
| 994 |
+
const_xmlChar* c_node_name,
|
| 995 |
+
qname* c_qname) noexcept:
|
| 996 |
+
"""Tests if name and namespace URI match those of c_qname.
|
| 997 |
+
|
| 998 |
+
This differs from _tagMatches() in that it does not consider a
|
| 999 |
+
NULL value in qname.href a wildcard, and that it expects the c_name
|
| 1000 |
+
to be taken from the doc dict, i.e. it only compares the names by
|
| 1001 |
+
address.
|
| 1002 |
+
|
| 1003 |
+
A node matches if it matches both href and c_name of the qname.
|
| 1004 |
+
|
| 1005 |
+
A node matches c_href if any of the following is true:
|
| 1006 |
+
* its namespace is NULL and c_href is the empty string
|
| 1007 |
+
* its namespace string equals the c_href string
|
| 1008 |
+
|
| 1009 |
+
A node matches c_name if any of the following is true:
|
| 1010 |
+
* c_name is NULL
|
| 1011 |
+
* its name string points to the same address (!) as c_name
|
| 1012 |
+
"""
|
| 1013 |
+
cdef char* c_href
|
| 1014 |
+
if c_qname.c_name is not NULL and c_qname.c_name is not c_node_name:
|
| 1015 |
+
return 0
|
| 1016 |
+
if c_qname.href is NULL:
|
| 1017 |
+
return 1
|
| 1018 |
+
c_href = python.__cstr(c_qname.href)
|
| 1019 |
+
if c_href[0] == b'\0':
|
| 1020 |
+
return c_node_href is NULL or c_node_href[0] == b'\0'
|
| 1021 |
+
elif c_node_href is NULL:
|
| 1022 |
+
return 0
|
| 1023 |
+
else:
|
| 1024 |
+
return tree.xmlStrcmp(<const_xmlChar*>c_href, c_node_href) == 0
|
| 1025 |
+
|
| 1026 |
+
cdef Py_ssize_t _mapTagsToQnameMatchArray(xmlDoc* c_doc, list ns_tags,
|
| 1027 |
+
qname* c_ns_tags, bint force_into_dict) except -1:
|
| 1028 |
+
"""Map a sequence of (name, namespace) pairs to a qname array for efficient
|
| 1029 |
+
matching with _tagMatchesExactly() above.
|
| 1030 |
+
|
| 1031 |
+
Note that each qname struct in the array owns its href byte string object
|
| 1032 |
+
if it is not NULL.
|
| 1033 |
+
"""
|
| 1034 |
+
cdef Py_ssize_t count = 0, i, c_tag_len
|
| 1035 |
+
cdef bytes ns, tag
|
| 1036 |
+
cdef const_xmlChar* c_tag
|
| 1037 |
+
|
| 1038 |
+
for ns, tag in ns_tags:
|
| 1039 |
+
if tag is None:
|
| 1040 |
+
c_tag = <const_xmlChar*> NULL
|
| 1041 |
+
else:
|
| 1042 |
+
c_tag_len = len(tag)
|
| 1043 |
+
if c_tag_len > limits.INT_MAX:
|
| 1044 |
+
# too long, not in the dict => not in the document
|
| 1045 |
+
continue
|
| 1046 |
+
elif force_into_dict:
|
| 1047 |
+
c_tag = tree.xmlDictLookup(c_doc.dict, _xcstr(tag), <int> c_tag_len)
|
| 1048 |
+
if c_tag is NULL:
|
| 1049 |
+
# clean up before raising the error
|
| 1050 |
+
for i in xrange(count):
|
| 1051 |
+
cpython.ref.Py_XDECREF(c_ns_tags[i].href)
|
| 1052 |
+
raise MemoryError()
|
| 1053 |
+
else:
|
| 1054 |
+
c_tag = tree.xmlDictExists(c_doc.dict, _xcstr(tag), <int> c_tag_len)
|
| 1055 |
+
if c_tag is NULL:
|
| 1056 |
+
# not in the dict => not in the document
|
| 1057 |
+
continue
|
| 1058 |
+
|
| 1059 |
+
c_ns_tags[count].c_name = c_tag
|
| 1060 |
+
if ns is None:
|
| 1061 |
+
c_ns_tags[count].href = NULL
|
| 1062 |
+
else:
|
| 1063 |
+
cpython.ref.Py_INCREF(ns) # keep an owned reference!
|
| 1064 |
+
c_ns_tags[count].href = <python.PyObject*>ns
|
| 1065 |
+
count += 1
|
| 1066 |
+
return count
|
| 1067 |
+
|
| 1068 |
+
cdef int _removeNode(_Document doc, xmlNode* c_node) except -1:
|
| 1069 |
+
"""Unlink and free a node and subnodes if possible. Otherwise, make sure
|
| 1070 |
+
it's self-contained.
|
| 1071 |
+
"""
|
| 1072 |
+
cdef xmlNode* c_next
|
| 1073 |
+
c_next = c_node.next
|
| 1074 |
+
tree.xmlUnlinkNode(c_node)
|
| 1075 |
+
_moveTail(c_next, c_node)
|
| 1076 |
+
if not attemptDeallocation(c_node):
|
| 1077 |
+
# make namespaces absolute
|
| 1078 |
+
moveNodeToDocument(doc, c_node.doc, c_node)
|
| 1079 |
+
return 0
|
| 1080 |
+
|
| 1081 |
+
cdef int _removeSiblings(xmlNode* c_element, tree.xmlElementType node_type, bint with_tail) except -1:
|
| 1082 |
+
cdef xmlNode* c_node
|
| 1083 |
+
cdef xmlNode* c_next
|
| 1084 |
+
c_node = c_element.next
|
| 1085 |
+
while c_node is not NULL:
|
| 1086 |
+
c_next = _nextElement(c_node)
|
| 1087 |
+
if c_node.type == node_type:
|
| 1088 |
+
if with_tail:
|
| 1089 |
+
_removeText(c_node.next)
|
| 1090 |
+
tree.xmlUnlinkNode(c_node)
|
| 1091 |
+
attemptDeallocation(c_node)
|
| 1092 |
+
c_node = c_next
|
| 1093 |
+
c_node = c_element.prev
|
| 1094 |
+
while c_node is not NULL:
|
| 1095 |
+
c_next = _previousElement(c_node)
|
| 1096 |
+
if c_node.type == node_type:
|
| 1097 |
+
if with_tail:
|
| 1098 |
+
_removeText(c_node.next)
|
| 1099 |
+
tree.xmlUnlinkNode(c_node)
|
| 1100 |
+
attemptDeallocation(c_node)
|
| 1101 |
+
c_node = c_next
|
| 1102 |
+
return 0
|
| 1103 |
+
|
| 1104 |
+
cdef void _moveTail(xmlNode* c_tail, xmlNode* c_target) noexcept:
|
| 1105 |
+
cdef xmlNode* c_next
|
| 1106 |
+
# tail support: look for any text nodes trailing this node and
|
| 1107 |
+
# move them too
|
| 1108 |
+
c_tail = _textNodeOrSkip(c_tail)
|
| 1109 |
+
while c_tail is not NULL:
|
| 1110 |
+
c_next = _textNodeOrSkip(c_tail.next)
|
| 1111 |
+
c_target = tree.xmlAddNextSibling(c_target, c_tail)
|
| 1112 |
+
c_tail = c_next
|
| 1113 |
+
|
| 1114 |
+
cdef int _copyTail(xmlNode* c_tail, xmlNode* c_target) except -1:
|
| 1115 |
+
cdef xmlNode* c_new_tail
|
| 1116 |
+
# tail copying support: look for any text nodes trailing this node and
|
| 1117 |
+
# copy it to the target node
|
| 1118 |
+
c_tail = _textNodeOrSkip(c_tail)
|
| 1119 |
+
while c_tail is not NULL:
|
| 1120 |
+
if c_target.doc is not c_tail.doc:
|
| 1121 |
+
c_new_tail = tree.xmlDocCopyNode(c_tail, c_target.doc, 0)
|
| 1122 |
+
else:
|
| 1123 |
+
c_new_tail = tree.xmlCopyNode(c_tail, 0)
|
| 1124 |
+
if c_new_tail is NULL:
|
| 1125 |
+
raise MemoryError()
|
| 1126 |
+
c_target = tree.xmlAddNextSibling(c_target, c_new_tail)
|
| 1127 |
+
c_tail = _textNodeOrSkip(c_tail.next)
|
| 1128 |
+
return 0
|
| 1129 |
+
|
| 1130 |
+
cdef int _copyNonElementSiblings(xmlNode* c_node, xmlNode* c_target) except -1:
|
| 1131 |
+
cdef xmlNode* c_copy
|
| 1132 |
+
cdef xmlNode* c_sibling = c_node
|
| 1133 |
+
while c_sibling.prev != NULL and \
|
| 1134 |
+
(c_sibling.prev.type == tree.XML_PI_NODE or
|
| 1135 |
+
c_sibling.prev.type == tree.XML_COMMENT_NODE or
|
| 1136 |
+
c_sibling.prev.type == tree.XML_DTD_NODE):
|
| 1137 |
+
c_sibling = c_sibling.prev
|
| 1138 |
+
while c_sibling != c_node:
|
| 1139 |
+
if c_sibling.type == tree.XML_DTD_NODE:
|
| 1140 |
+
c_copy = <xmlNode*>_copyDtd(<tree.xmlDtd*>c_sibling)
|
| 1141 |
+
if c_sibling == <xmlNode*>c_node.doc.intSubset:
|
| 1142 |
+
c_target.doc.intSubset = <tree.xmlDtd*>c_copy
|
| 1143 |
+
else: # c_sibling == c_node.doc.extSubset
|
| 1144 |
+
c_target.doc.extSubset = <tree.xmlDtd*>c_copy
|
| 1145 |
+
else:
|
| 1146 |
+
c_copy = tree.xmlDocCopyNode(c_sibling, c_target.doc, 1)
|
| 1147 |
+
if c_copy is NULL:
|
| 1148 |
+
raise MemoryError()
|
| 1149 |
+
tree.xmlAddPrevSibling(c_target, c_copy)
|
| 1150 |
+
c_sibling = c_sibling.next
|
| 1151 |
+
while c_sibling.next != NULL and \
|
| 1152 |
+
(c_sibling.next.type == tree.XML_PI_NODE or
|
| 1153 |
+
c_sibling.next.type == tree.XML_COMMENT_NODE):
|
| 1154 |
+
c_sibling = c_sibling.next
|
| 1155 |
+
c_copy = tree.xmlDocCopyNode(c_sibling, c_target.doc, 1)
|
| 1156 |
+
if c_copy is NULL:
|
| 1157 |
+
raise MemoryError()
|
| 1158 |
+
tree.xmlAddNextSibling(c_target, c_copy)
|
| 1159 |
+
|
| 1160 |
+
cdef int _deleteSlice(_Document doc, xmlNode* c_node,
|
| 1161 |
+
Py_ssize_t count, Py_ssize_t step) except -1:
|
| 1162 |
+
"""Delete slice, ``count`` items starting with ``c_node`` with a step
|
| 1163 |
+
width of ``step``.
|
| 1164 |
+
"""
|
| 1165 |
+
cdef xmlNode* c_next
|
| 1166 |
+
cdef Py_ssize_t c, i
|
| 1167 |
+
cdef _node_to_node_function next_element
|
| 1168 |
+
if c_node is NULL:
|
| 1169 |
+
return 0
|
| 1170 |
+
if step > 0:
|
| 1171 |
+
next_element = _nextElement
|
| 1172 |
+
else:
|
| 1173 |
+
step = -step
|
| 1174 |
+
next_element = _previousElement
|
| 1175 |
+
# now start deleting nodes
|
| 1176 |
+
c = 0
|
| 1177 |
+
c_next = c_node
|
| 1178 |
+
while c_node is not NULL and c < count:
|
| 1179 |
+
for i in range(step):
|
| 1180 |
+
c_next = next_element(c_next)
|
| 1181 |
+
if c_next is NULL:
|
| 1182 |
+
break
|
| 1183 |
+
_removeNode(doc, c_node)
|
| 1184 |
+
c += 1
|
| 1185 |
+
c_node = c_next
|
| 1186 |
+
return 0
|
| 1187 |
+
|
| 1188 |
+
cdef int _replaceSlice(_Element parent, xmlNode* c_node,
|
| 1189 |
+
Py_ssize_t slicelength, Py_ssize_t step,
|
| 1190 |
+
bint left_to_right, elements) except -1:
|
| 1191 |
+
"""Replace the slice of ``count`` elements starting at ``c_node`` with
|
| 1192 |
+
positive step width ``step`` by the Elements in ``elements``. The
|
| 1193 |
+
direction is given by the boolean argument ``left_to_right``.
|
| 1194 |
+
|
| 1195 |
+
``c_node`` may be NULL to indicate the end of the children list.
|
| 1196 |
+
"""
|
| 1197 |
+
cdef xmlNode* c_orig_neighbour
|
| 1198 |
+
cdef xmlNode* c_next
|
| 1199 |
+
cdef xmlDoc* c_source_doc
|
| 1200 |
+
cdef _Element element
|
| 1201 |
+
cdef Py_ssize_t seqlength, i, c
|
| 1202 |
+
cdef _node_to_node_function next_element
|
| 1203 |
+
assert step > 0
|
| 1204 |
+
if left_to_right:
|
| 1205 |
+
next_element = _nextElement
|
| 1206 |
+
else:
|
| 1207 |
+
next_element = _previousElement
|
| 1208 |
+
|
| 1209 |
+
if not isinstance(elements, (list, tuple)):
|
| 1210 |
+
elements = list(elements)
|
| 1211 |
+
|
| 1212 |
+
if step != 1 or not left_to_right:
|
| 1213 |
+
# *replacing* children stepwise with list => check size!
|
| 1214 |
+
seqlength = len(elements)
|
| 1215 |
+
if seqlength != slicelength:
|
| 1216 |
+
raise ValueError, f"attempt to assign sequence of size {seqlength} " \
|
| 1217 |
+
f"to extended slice of size {slicelength}"
|
| 1218 |
+
|
| 1219 |
+
if c_node is NULL:
|
| 1220 |
+
# no children yet => add all elements straight away
|
| 1221 |
+
if left_to_right:
|
| 1222 |
+
for element in elements:
|
| 1223 |
+
assert element is not None, "Node must not be None"
|
| 1224 |
+
_appendChild(parent, element)
|
| 1225 |
+
else:
|
| 1226 |
+
for element in elements:
|
| 1227 |
+
assert element is not None, "Node must not be None"
|
| 1228 |
+
_prependChild(parent, element)
|
| 1229 |
+
return 0
|
| 1230 |
+
|
| 1231 |
+
# remove the elements first as some might be re-added
|
| 1232 |
+
if left_to_right:
|
| 1233 |
+
# L->R, remember left neighbour
|
| 1234 |
+
c_orig_neighbour = _previousElement(c_node)
|
| 1235 |
+
else:
|
| 1236 |
+
# R->L, remember right neighbour
|
| 1237 |
+
c_orig_neighbour = _nextElement(c_node)
|
| 1238 |
+
|
| 1239 |
+
# We remove the original slice elements one by one. Since we hold
|
| 1240 |
+
# a Python reference to all elements that we will insert, it is
|
| 1241 |
+
# safe to let _removeNode() try (and fail) to free them even if
|
| 1242 |
+
# the element itself or one of its descendents will be reinserted.
|
| 1243 |
+
c = 0
|
| 1244 |
+
c_next = c_node
|
| 1245 |
+
while c_node is not NULL and c < slicelength:
|
| 1246 |
+
for i in range(step):
|
| 1247 |
+
c_next = next_element(c_next)
|
| 1248 |
+
if c_next is NULL:
|
| 1249 |
+
break
|
| 1250 |
+
_removeNode(parent._doc, c_node)
|
| 1251 |
+
c += 1
|
| 1252 |
+
c_node = c_next
|
| 1253 |
+
|
| 1254 |
+
# make sure each element is inserted only once
|
| 1255 |
+
elements = iter(elements)
|
| 1256 |
+
|
| 1257 |
+
# find the first node right of the new insertion point
|
| 1258 |
+
if left_to_right:
|
| 1259 |
+
if c_orig_neighbour is not NULL:
|
| 1260 |
+
c_node = next_element(c_orig_neighbour)
|
| 1261 |
+
else:
|
| 1262 |
+
# before the first element
|
| 1263 |
+
c_node = _findChildForwards(parent._c_node, 0)
|
| 1264 |
+
elif c_orig_neighbour is NULL:
|
| 1265 |
+
# at the end, but reversed stepping
|
| 1266 |
+
# append one element and go to the next insertion point
|
| 1267 |
+
for element in elements:
|
| 1268 |
+
assert element is not None, "Node must not be None"
|
| 1269 |
+
_appendChild(parent, element)
|
| 1270 |
+
c_node = element._c_node
|
| 1271 |
+
if slicelength > 0:
|
| 1272 |
+
slicelength -= 1
|
| 1273 |
+
for i in range(1, step):
|
| 1274 |
+
c_node = next_element(c_node)
|
| 1275 |
+
if c_node is NULL:
|
| 1276 |
+
break
|
| 1277 |
+
break
|
| 1278 |
+
else:
|
| 1279 |
+
c_node = c_orig_neighbour
|
| 1280 |
+
|
| 1281 |
+
if left_to_right:
|
| 1282 |
+
# adjust step size after removing slice as we are not stepping
|
| 1283 |
+
# over the newly inserted elements
|
| 1284 |
+
step -= 1
|
| 1285 |
+
|
| 1286 |
+
# now insert elements where we removed them
|
| 1287 |
+
if c_node is not NULL:
|
| 1288 |
+
for element in elements:
|
| 1289 |
+
assert element is not None, "Node must not be None"
|
| 1290 |
+
_assertValidNode(element)
|
| 1291 |
+
# move element and tail over
|
| 1292 |
+
c_source_doc = element._c_node.doc
|
| 1293 |
+
c_next = element._c_node.next
|
| 1294 |
+
tree.xmlAddPrevSibling(c_node, element._c_node)
|
| 1295 |
+
_moveTail(c_next, element._c_node)
|
| 1296 |
+
|
| 1297 |
+
# integrate element into new document
|
| 1298 |
+
moveNodeToDocument(parent._doc, c_source_doc, element._c_node)
|
| 1299 |
+
|
| 1300 |
+
# stop at the end of the slice
|
| 1301 |
+
if slicelength > 0:
|
| 1302 |
+
slicelength -= 1
|
| 1303 |
+
for i in range(step):
|
| 1304 |
+
c_node = next_element(c_node)
|
| 1305 |
+
if c_node is NULL:
|
| 1306 |
+
break
|
| 1307 |
+
if c_node is NULL:
|
| 1308 |
+
break
|
| 1309 |
+
else:
|
| 1310 |
+
# everything inserted
|
| 1311 |
+
return 0
|
| 1312 |
+
|
| 1313 |
+
# append the remaining elements at the respective end
|
| 1314 |
+
if left_to_right:
|
| 1315 |
+
for element in elements:
|
| 1316 |
+
assert element is not None, "Node must not be None"
|
| 1317 |
+
_assertValidNode(element)
|
| 1318 |
+
_appendChild(parent, element)
|
| 1319 |
+
else:
|
| 1320 |
+
for element in elements:
|
| 1321 |
+
assert element is not None, "Node must not be None"
|
| 1322 |
+
_assertValidNode(element)
|
| 1323 |
+
_prependChild(parent, element)
|
| 1324 |
+
|
| 1325 |
+
return 0
|
| 1326 |
+
|
| 1327 |
+
|
| 1328 |
+
cdef int _linkChild(xmlNode* c_parent, xmlNode* c_node) except -1:
|
| 1329 |
+
"""Adaptation of 'xmlAddChild()' that deep-fix the document links iteratively.
|
| 1330 |
+
"""
|
| 1331 |
+
assert _isElement(c_node)
|
| 1332 |
+
c_node.parent = c_parent
|
| 1333 |
+
if c_parent.children is NULL:
|
| 1334 |
+
c_parent.children = c_parent.last = c_node
|
| 1335 |
+
else:
|
| 1336 |
+
c_node.prev = c_parent.last
|
| 1337 |
+
c_parent.last.next = c_node
|
| 1338 |
+
c_parent.last = c_node
|
| 1339 |
+
|
| 1340 |
+
_setTreeDoc(c_node, c_parent.doc)
|
| 1341 |
+
return 0
|
| 1342 |
+
|
| 1343 |
+
|
| 1344 |
+
cdef int _appendChild(_Element parent, _Element child) except -1:
|
| 1345 |
+
"""Append a new child to a parent element.
|
| 1346 |
+
"""
|
| 1347 |
+
c_node = child._c_node
|
| 1348 |
+
c_source_doc = c_node.doc
|
| 1349 |
+
# prevent cycles
|
| 1350 |
+
if _isAncestorOrSame(c_node, parent._c_node):
|
| 1351 |
+
raise ValueError("cannot append parent to itself")
|
| 1352 |
+
# store possible text node
|
| 1353 |
+
c_next = c_node.next
|
| 1354 |
+
# move node itself
|
| 1355 |
+
tree.xmlUnlinkNode(c_node)
|
| 1356 |
+
# do not call xmlAddChild() here since it would deep-traverse the tree
|
| 1357 |
+
_linkChild(parent._c_node, c_node)
|
| 1358 |
+
_moveTail(c_next, c_node)
|
| 1359 |
+
# uh oh, elements may be pointing to different doc when
|
| 1360 |
+
# parent element has moved; change them too..
|
| 1361 |
+
moveNodeToDocument(parent._doc, c_source_doc, c_node)
|
| 1362 |
+
return 0
|
| 1363 |
+
|
| 1364 |
+
cdef int _prependChild(_Element parent, _Element child) except -1:
|
| 1365 |
+
"""Prepend a new child to a parent element.
|
| 1366 |
+
"""
|
| 1367 |
+
c_node = child._c_node
|
| 1368 |
+
c_source_doc = c_node.doc
|
| 1369 |
+
# prevent cycles
|
| 1370 |
+
if _isAncestorOrSame(c_node, parent._c_node):
|
| 1371 |
+
raise ValueError("cannot append parent to itself")
|
| 1372 |
+
# store possible text node
|
| 1373 |
+
c_next = c_node.next
|
| 1374 |
+
# move node itself
|
| 1375 |
+
c_child = _findChildForwards(parent._c_node, 0)
|
| 1376 |
+
if c_child is NULL:
|
| 1377 |
+
tree.xmlUnlinkNode(c_node)
|
| 1378 |
+
# do not call xmlAddChild() here since it would deep-traverse the tree
|
| 1379 |
+
_linkChild(parent._c_node, c_node)
|
| 1380 |
+
else:
|
| 1381 |
+
tree.xmlAddPrevSibling(c_child, c_node)
|
| 1382 |
+
_moveTail(c_next, c_node)
|
| 1383 |
+
# uh oh, elements may be pointing to different doc when
|
| 1384 |
+
# parent element has moved; change them too..
|
| 1385 |
+
moveNodeToDocument(parent._doc, c_source_doc, c_node)
|
| 1386 |
+
return 0
|
| 1387 |
+
|
| 1388 |
+
cdef int _appendSibling(_Element element, _Element sibling) except -1:
|
| 1389 |
+
"""Add a new sibling behind an element.
|
| 1390 |
+
"""
|
| 1391 |
+
return _addSibling(element, sibling, as_next=True)
|
| 1392 |
+
|
| 1393 |
+
cdef int _prependSibling(_Element element, _Element sibling) except -1:
|
| 1394 |
+
"""Add a new sibling before an element.
|
| 1395 |
+
"""
|
| 1396 |
+
return _addSibling(element, sibling, as_next=False)
|
| 1397 |
+
|
| 1398 |
+
cdef int _addSibling(_Element element, _Element sibling, bint as_next) except -1:
|
| 1399 |
+
c_node = sibling._c_node
|
| 1400 |
+
c_source_doc = c_node.doc
|
| 1401 |
+
# prevent cycles
|
| 1402 |
+
if _isAncestorOrSame(c_node, element._c_node):
|
| 1403 |
+
if element._c_node is c_node:
|
| 1404 |
+
return 0 # nothing to do
|
| 1405 |
+
raise ValueError("cannot add ancestor as sibling, please break cycle first")
|
| 1406 |
+
# store possible text node
|
| 1407 |
+
c_next = c_node.next
|
| 1408 |
+
# move node itself
|
| 1409 |
+
if as_next:
|
| 1410 |
+
# must insert after any tail text
|
| 1411 |
+
c_next_node = _nextElement(element._c_node)
|
| 1412 |
+
if c_next_node is NULL:
|
| 1413 |
+
c_next_node = element._c_node
|
| 1414 |
+
while c_next_node.next:
|
| 1415 |
+
c_next_node = c_next_node.next
|
| 1416 |
+
tree.xmlAddNextSibling(c_next_node, c_node)
|
| 1417 |
+
else:
|
| 1418 |
+
tree.xmlAddPrevSibling(c_next_node, c_node)
|
| 1419 |
+
else:
|
| 1420 |
+
tree.xmlAddPrevSibling(element._c_node, c_node)
|
| 1421 |
+
_moveTail(c_next, c_node)
|
| 1422 |
+
# uh oh, elements may be pointing to different doc when
|
| 1423 |
+
# parent element has moved; change them too..
|
| 1424 |
+
moveNodeToDocument(element._doc, c_source_doc, c_node)
|
| 1425 |
+
return 0
|
| 1426 |
+
|
| 1427 |
+
cdef inline bint isutf8(const_xmlChar* s) noexcept:
|
| 1428 |
+
cdef xmlChar c = s[0]
|
| 1429 |
+
while c != c'\0':
|
| 1430 |
+
if c & 0x80:
|
| 1431 |
+
return True
|
| 1432 |
+
s += 1
|
| 1433 |
+
c = s[0]
|
| 1434 |
+
return False
|
| 1435 |
+
|
| 1436 |
+
cdef bint isutf8l(const_xmlChar* s, size_t length) noexcept:
|
| 1437 |
+
"""
|
| 1438 |
+
Search for non-ASCII characters in the string, knowing its length in advance.
|
| 1439 |
+
"""
|
| 1440 |
+
cdef unsigned int i
|
| 1441 |
+
cdef unsigned long non_ascii_mask
|
| 1442 |
+
cdef const unsigned long *lptr = <const unsigned long*> s
|
| 1443 |
+
|
| 1444 |
+
cdef const unsigned long *end = lptr + length // sizeof(unsigned long)
|
| 1445 |
+
if length >= sizeof(non_ascii_mask):
|
| 1446 |
+
# Build constant 0x80808080... mask (and let the C compiler fold it).
|
| 1447 |
+
non_ascii_mask = 0
|
| 1448 |
+
for i in range(sizeof(non_ascii_mask) // 2):
|
| 1449 |
+
non_ascii_mask = (non_ascii_mask << 16) | 0x8080
|
| 1450 |
+
|
| 1451 |
+
# Advance to long-aligned character before we start reading longs.
|
| 1452 |
+
while (<size_t>s) % sizeof(unsigned long) and s < <const_xmlChar *>end:
|
| 1453 |
+
if s[0] & 0x80:
|
| 1454 |
+
return True
|
| 1455 |
+
s += 1
|
| 1456 |
+
|
| 1457 |
+
# Read one long at a time
|
| 1458 |
+
lptr = <const unsigned long*> s
|
| 1459 |
+
while lptr < end:
|
| 1460 |
+
if lptr[0] & non_ascii_mask:
|
| 1461 |
+
return True
|
| 1462 |
+
lptr += 1
|
| 1463 |
+
s = <const_xmlChar *>lptr
|
| 1464 |
+
|
| 1465 |
+
while s < (<const_xmlChar *>end + length % sizeof(unsigned long)):
|
| 1466 |
+
if s[0] & 0x80:
|
| 1467 |
+
return True
|
| 1468 |
+
s += 1
|
| 1469 |
+
|
| 1470 |
+
return False
|
| 1471 |
+
|
| 1472 |
+
cdef int _is_valid_xml_ascii(bytes pystring) except -1:
|
| 1473 |
+
"""Check if a string is XML ascii content."""
|
| 1474 |
+
cdef signed char ch
|
| 1475 |
+
# When ch is a *signed* char, non-ascii characters are negative integers
|
| 1476 |
+
# and xmlIsChar_ch does not accept them.
|
| 1477 |
+
for ch in pystring:
|
| 1478 |
+
if not tree.xmlIsChar_ch(ch):
|
| 1479 |
+
return 0
|
| 1480 |
+
return 1
|
| 1481 |
+
|
| 1482 |
+
cdef bint _is_valid_xml_utf8(bytes pystring) except -1:
|
| 1483 |
+
"""Check if a string is like valid UTF-8 XML content."""
|
| 1484 |
+
cdef const_xmlChar* s = _xcstr(pystring)
|
| 1485 |
+
cdef const_xmlChar* c_end = s + len(pystring)
|
| 1486 |
+
cdef unsigned long next3 = 0
|
| 1487 |
+
if s < c_end - 2:
|
| 1488 |
+
next3 = (s[0] << 8) | (s[1])
|
| 1489 |
+
|
| 1490 |
+
while s < c_end - 2:
|
| 1491 |
+
next3 = 0x00ffffff & ((next3 << 8) | s[2])
|
| 1492 |
+
if s[0] & 0x80:
|
| 1493 |
+
# 0xefbfbe and 0xefbfbf are utf-8 encodings of
|
| 1494 |
+
# forbidden characters \ufffe and \uffff
|
| 1495 |
+
if next3 == 0x00efbfbe or next3 == 0x00efbfbf:
|
| 1496 |
+
return 0
|
| 1497 |
+
# 0xeda080 and 0xedbfbf are utf-8 encodings of
|
| 1498 |
+
# \ud800 and \udfff. Anything between them (inclusive)
|
| 1499 |
+
# is forbidden, because they are surrogate blocks in utf-16.
|
| 1500 |
+
if 0x00eda080 <= next3 <= 0x00edbfbf:
|
| 1501 |
+
return 0
|
| 1502 |
+
elif not tree.xmlIsChar_ch(s[0]):
|
| 1503 |
+
return 0 # invalid ascii char
|
| 1504 |
+
s += 1
|
| 1505 |
+
|
| 1506 |
+
while s < c_end:
|
| 1507 |
+
if not s[0] & 0x80 and not tree.xmlIsChar_ch(s[0]):
|
| 1508 |
+
return 0 # invalid ascii char
|
| 1509 |
+
s += 1
|
| 1510 |
+
|
| 1511 |
+
return 1
|
| 1512 |
+
|
| 1513 |
+
cdef inline unicode funicodeOrNone(const_xmlChar* s):
|
| 1514 |
+
return funicode(s) if s is not NULL else None
|
| 1515 |
+
|
| 1516 |
+
cdef inline unicode funicodeOrEmpty(const_xmlChar* s):
|
| 1517 |
+
return funicode(s) if s is not NULL else ''
|
| 1518 |
+
|
| 1519 |
+
cdef unicode funicode(const_xmlChar* s):
|
| 1520 |
+
return s.decode('UTF-8')
|
| 1521 |
+
|
| 1522 |
+
cdef bytes _utf8(object s):
|
| 1523 |
+
"""Test if a string is valid user input and encode it to UTF-8.
|
| 1524 |
+
Reject all bytes/unicode input that contains non-XML characters.
|
| 1525 |
+
Reject all bytes input that contains non-ASCII characters.
|
| 1526 |
+
"""
|
| 1527 |
+
cdef int valid
|
| 1528 |
+
cdef bytes utf8_string
|
| 1529 |
+
if isinstance(s, unicode):
|
| 1530 |
+
utf8_string = (<unicode>s).encode('utf8')
|
| 1531 |
+
valid = _is_valid_xml_utf8(utf8_string)
|
| 1532 |
+
elif isinstance(s, (bytes, bytearray)):
|
| 1533 |
+
utf8_string = s if type(s) is bytes else bytes(s)
|
| 1534 |
+
valid = _is_valid_xml_ascii(utf8_string)
|
| 1535 |
+
else:
|
| 1536 |
+
raise TypeError("Argument must be bytes or unicode, got '%.200s'" % type(s).__name__)
|
| 1537 |
+
if not valid:
|
| 1538 |
+
raise ValueError(
|
| 1539 |
+
"All strings must be XML compatible: Unicode or ASCII, no NULL bytes or control characters")
|
| 1540 |
+
return utf8_string
|
| 1541 |
+
|
| 1542 |
+
|
| 1543 |
+
cdef bytes _utf8orNone(object s):
|
| 1544 |
+
return _utf8(s) if s is not None else None
|
| 1545 |
+
|
| 1546 |
+
|
| 1547 |
+
cdef enum:
|
| 1548 |
+
NO_FILE_PATH = 0
|
| 1549 |
+
ABS_UNIX_FILE_PATH = 1
|
| 1550 |
+
ABS_WIN_FILE_PATH = 2
|
| 1551 |
+
REL_FILE_PATH = 3
|
| 1552 |
+
|
| 1553 |
+
|
| 1554 |
+
cdef bint _isFilePath(const_xmlChar* c_path) noexcept:
|
| 1555 |
+
"simple heuristic to see if a path is a filename"
|
| 1556 |
+
cdef xmlChar c
|
| 1557 |
+
# test if it looks like an absolute Unix path or a Windows network path
|
| 1558 |
+
if c_path[0] == c'/':
|
| 1559 |
+
return ABS_UNIX_FILE_PATH
|
| 1560 |
+
|
| 1561 |
+
# test if it looks like an absolute Windows path or URL
|
| 1562 |
+
if c'a' <= c_path[0] <= c'z' or c'A' <= c_path[0] <= c'Z':
|
| 1563 |
+
c_path += 1
|
| 1564 |
+
if c_path[0] == c':' and c_path[1] in b'\0\\':
|
| 1565 |
+
return ABS_WIN_FILE_PATH # C: or C:\...
|
| 1566 |
+
|
| 1567 |
+
# test if it looks like a URL with scheme://
|
| 1568 |
+
while c'a' <= c_path[0] <= c'z' or c'A' <= c_path[0] <= c'Z':
|
| 1569 |
+
c_path += 1
|
| 1570 |
+
if c_path[0] == c':' and c_path[1] == c'/' and c_path[2] == c'/':
|
| 1571 |
+
return NO_FILE_PATH
|
| 1572 |
+
|
| 1573 |
+
# assume it's a relative path
|
| 1574 |
+
return REL_FILE_PATH
|
| 1575 |
+
|
| 1576 |
+
|
| 1577 |
+
cdef object _getFSPathOrObject(object obj):
|
| 1578 |
+
"""
|
| 1579 |
+
Get the __fspath__ attribute of an object if it exists.
|
| 1580 |
+
Otherwise, the original object is returned.
|
| 1581 |
+
"""
|
| 1582 |
+
if _isString(obj):
|
| 1583 |
+
return obj
|
| 1584 |
+
try:
|
| 1585 |
+
return python.PyOS_FSPath(obj)
|
| 1586 |
+
except TypeError:
|
| 1587 |
+
return obj
|
| 1588 |
+
|
| 1589 |
+
|
| 1590 |
+
cdef object _encodeFilename(object filename):
|
| 1591 |
+
"""Make sure a filename is 8-bit encoded (or None).
|
| 1592 |
+
"""
|
| 1593 |
+
if filename is None:
|
| 1594 |
+
return None
|
| 1595 |
+
elif isinstance(filename, bytes):
|
| 1596 |
+
return filename
|
| 1597 |
+
elif isinstance(filename, unicode):
|
| 1598 |
+
filename8 = (<unicode>filename).encode('utf8')
|
| 1599 |
+
if _isFilePath(<unsigned char*>filename8):
|
| 1600 |
+
try:
|
| 1601 |
+
return python.PyUnicode_AsEncodedString(
|
| 1602 |
+
filename, _C_FILENAME_ENCODING, NULL)
|
| 1603 |
+
except UnicodeEncodeError:
|
| 1604 |
+
pass
|
| 1605 |
+
return filename8
|
| 1606 |
+
else:
|
| 1607 |
+
raise TypeError("Argument must be string or unicode.")
|
| 1608 |
+
|
| 1609 |
+
cdef object _decodeFilename(const_xmlChar* c_path):
|
| 1610 |
+
"""Make the filename a unicode string if we are in Py3.
|
| 1611 |
+
"""
|
| 1612 |
+
return _decodeFilenameWithLength(c_path, tree.xmlStrlen(c_path))
|
| 1613 |
+
|
| 1614 |
+
cdef object _decodeFilenameWithLength(const_xmlChar* c_path, size_t c_len):
|
| 1615 |
+
"""Make the filename a unicode string if we are in Py3.
|
| 1616 |
+
"""
|
| 1617 |
+
if _isFilePath(c_path):
|
| 1618 |
+
try:
|
| 1619 |
+
return python.PyUnicode_Decode(
|
| 1620 |
+
<const_char*>c_path, c_len, _C_FILENAME_ENCODING, NULL)
|
| 1621 |
+
except UnicodeDecodeError:
|
| 1622 |
+
pass
|
| 1623 |
+
try:
|
| 1624 |
+
return (<unsigned char*>c_path)[:c_len].decode('UTF-8')
|
| 1625 |
+
except UnicodeDecodeError:
|
| 1626 |
+
# this is a stupid fallback, but it might still work...
|
| 1627 |
+
return (<unsigned char*>c_path)[:c_len].decode('latin-1', 'replace')
|
| 1628 |
+
|
| 1629 |
+
cdef object _encodeFilenameUTF8(object filename):
|
| 1630 |
+
"""Recode filename as UTF-8. Tries ASCII, local filesystem encoding and
|
| 1631 |
+
UTF-8 as source encoding.
|
| 1632 |
+
"""
|
| 1633 |
+
cdef char* c_filename
|
| 1634 |
+
if filename is None:
|
| 1635 |
+
return None
|
| 1636 |
+
elif isinstance(filename, bytes):
|
| 1637 |
+
if not isutf8l(<bytes>filename, len(<bytes>filename)):
|
| 1638 |
+
# plain ASCII!
|
| 1639 |
+
return filename
|
| 1640 |
+
c_filename = _cstr(<bytes>filename)
|
| 1641 |
+
try:
|
| 1642 |
+
# try to decode with default encoding
|
| 1643 |
+
filename = python.PyUnicode_Decode(
|
| 1644 |
+
c_filename, len(<bytes>filename),
|
| 1645 |
+
_C_FILENAME_ENCODING, NULL)
|
| 1646 |
+
except UnicodeDecodeError as decode_exc:
|
| 1647 |
+
try:
|
| 1648 |
+
# try if it's proper UTF-8
|
| 1649 |
+
(<bytes>filename).decode('utf8')
|
| 1650 |
+
return filename
|
| 1651 |
+
except UnicodeDecodeError:
|
| 1652 |
+
raise decode_exc # otherwise re-raise original exception
|
| 1653 |
+
if isinstance(filename, unicode):
|
| 1654 |
+
return (<unicode>filename).encode('utf8')
|
| 1655 |
+
else:
|
| 1656 |
+
raise TypeError("Argument must be string or unicode.")
|
| 1657 |
+
|
| 1658 |
+
cdef tuple _getNsTag(tag):
|
| 1659 |
+
"""Given a tag, find namespace URI and tag name.
|
| 1660 |
+
Return None for NS uri if no namespace URI provided.
|
| 1661 |
+
"""
|
| 1662 |
+
return __getNsTag(tag, 0)
|
| 1663 |
+
|
| 1664 |
+
cdef tuple _getNsTagWithEmptyNs(tag):
|
| 1665 |
+
"""Given a tag, find namespace URI and tag name. Return None for NS uri
|
| 1666 |
+
if no namespace URI provided, or the empty string if namespace
|
| 1667 |
+
part is '{}'.
|
| 1668 |
+
"""
|
| 1669 |
+
return __getNsTag(tag, 1)
|
| 1670 |
+
|
| 1671 |
+
cdef tuple __getNsTag(tag, bint empty_ns):
|
| 1672 |
+
cdef char* c_tag
|
| 1673 |
+
cdef char* c_ns_end
|
| 1674 |
+
cdef Py_ssize_t taglen
|
| 1675 |
+
cdef Py_ssize_t nslen
|
| 1676 |
+
cdef bytes ns = None
|
| 1677 |
+
# _isString() is much faster than isinstance()
|
| 1678 |
+
if not _isString(tag) and isinstance(tag, QName):
|
| 1679 |
+
tag = (<QName>tag).text
|
| 1680 |
+
tag = _utf8(tag)
|
| 1681 |
+
c_tag = _cstr(tag)
|
| 1682 |
+
if c_tag[0] == c'{':
|
| 1683 |
+
c_tag += 1
|
| 1684 |
+
c_ns_end = cstring_h.strchr(c_tag, c'}')
|
| 1685 |
+
if c_ns_end is NULL:
|
| 1686 |
+
raise ValueError, "Invalid tag name"
|
| 1687 |
+
nslen = c_ns_end - c_tag
|
| 1688 |
+
taglen = python.PyBytes_GET_SIZE(tag) - nslen - 2
|
| 1689 |
+
if taglen == 0:
|
| 1690 |
+
raise ValueError, "Empty tag name"
|
| 1691 |
+
if nslen > 0:
|
| 1692 |
+
ns = <bytes>c_tag[:nslen]
|
| 1693 |
+
elif empty_ns:
|
| 1694 |
+
ns = b''
|
| 1695 |
+
tag = <bytes>c_ns_end[1:taglen+1]
|
| 1696 |
+
elif python.PyBytes_GET_SIZE(tag) == 0:
|
| 1697 |
+
raise ValueError, "Empty tag name"
|
| 1698 |
+
return ns, tag
|
| 1699 |
+
|
| 1700 |
+
cdef inline int _pyXmlNameIsValid(name_utf8):
|
| 1701 |
+
return _xmlNameIsValid(_xcstr(name_utf8)) and b':' not in name_utf8
|
| 1702 |
+
|
| 1703 |
+
cdef inline int _pyHtmlNameIsValid(name_utf8):
|
| 1704 |
+
return _htmlNameIsValid(_xcstr(name_utf8))
|
| 1705 |
+
|
| 1706 |
+
cdef inline int _xmlNameIsValid(const_xmlChar* c_name) noexcept:
|
| 1707 |
+
return tree.xmlValidateNameValue(c_name)
|
| 1708 |
+
|
| 1709 |
+
cdef int _htmlNameIsValid(const_xmlChar* c_name) noexcept:
|
| 1710 |
+
if c_name is NULL or c_name[0] == c'\0':
|
| 1711 |
+
return 0
|
| 1712 |
+
while c_name[0] != c'\0':
|
| 1713 |
+
if c_name[0] in b'&<>/"\'\t\n\x0B\x0C\r ':
|
| 1714 |
+
return 0
|
| 1715 |
+
c_name += 1
|
| 1716 |
+
return 1
|
| 1717 |
+
|
| 1718 |
+
cdef bint _characterReferenceIsValid(const_xmlChar* c_name) noexcept:
|
| 1719 |
+
cdef bint is_hex
|
| 1720 |
+
if c_name[0] == c'x':
|
| 1721 |
+
c_name += 1
|
| 1722 |
+
is_hex = 1
|
| 1723 |
+
else:
|
| 1724 |
+
is_hex = 0
|
| 1725 |
+
if c_name[0] == c'\0':
|
| 1726 |
+
return 0
|
| 1727 |
+
while c_name[0] != c'\0':
|
| 1728 |
+
if c_name[0] < c'0' or c_name[0] > c'9':
|
| 1729 |
+
if not is_hex:
|
| 1730 |
+
return 0
|
| 1731 |
+
if not (c'a' <= c_name[0] <= c'f'):
|
| 1732 |
+
if not (c'A' <= c_name[0] <= c'F'):
|
| 1733 |
+
return 0
|
| 1734 |
+
c_name += 1
|
| 1735 |
+
return 1
|
| 1736 |
+
|
| 1737 |
+
cdef int _tagValidOrRaise(tag_utf) except -1:
|
| 1738 |
+
if not _pyXmlNameIsValid(tag_utf):
|
| 1739 |
+
raise ValueError(f"Invalid tag name {(<bytes>tag_utf).decode('utf8')!r}")
|
| 1740 |
+
return 0
|
| 1741 |
+
|
| 1742 |
+
cdef int _htmlTagValidOrRaise(tag_utf) except -1:
|
| 1743 |
+
if not _pyHtmlNameIsValid(tag_utf):
|
| 1744 |
+
raise ValueError(f"Invalid HTML tag name {(<bytes>tag_utf).decode('utf8')!r}")
|
| 1745 |
+
return 0
|
| 1746 |
+
|
| 1747 |
+
cdef int _attributeValidOrRaise(name_utf) except -1:
|
| 1748 |
+
if not _pyXmlNameIsValid(name_utf):
|
| 1749 |
+
raise ValueError(f"Invalid attribute name {(<bytes>name_utf).decode('utf8')!r}")
|
| 1750 |
+
return 0
|
| 1751 |
+
|
| 1752 |
+
cdef int _prefixValidOrRaise(tag_utf) except -1:
|
| 1753 |
+
if not _pyXmlNameIsValid(tag_utf):
|
| 1754 |
+
raise ValueError(f"Invalid namespace prefix {(<bytes>tag_utf).decode('utf8')!r}")
|
| 1755 |
+
return 0
|
| 1756 |
+
|
| 1757 |
+
cdef int _uriValidOrRaise(uri_utf) except -1:
|
| 1758 |
+
cdef uri.xmlURI* c_uri = uri.xmlParseURI(_cstr(uri_utf))
|
| 1759 |
+
if c_uri is NULL:
|
| 1760 |
+
raise ValueError(f"Invalid namespace URI {(<bytes>uri_utf).decode('utf8')!r}")
|
| 1761 |
+
uri.xmlFreeURI(c_uri)
|
| 1762 |
+
return 0
|
| 1763 |
+
|
| 1764 |
+
cdef inline unicode _namespacedName(xmlNode* c_node):
|
| 1765 |
+
return _namespacedNameFromNsName(_getNs(c_node), c_node.name)
|
| 1766 |
+
|
| 1767 |
+
|
| 1768 |
+
cdef unicode _namespacedNameFromNsName(const_xmlChar* c_href, const_xmlChar* c_name):
|
| 1769 |
+
name = funicode(c_name)
|
| 1770 |
+
if c_href is NULL:
|
| 1771 |
+
return name
|
| 1772 |
+
href = funicode(c_href)
|
| 1773 |
+
return f"{{{href}}}{name}"
|
| 1774 |
+
|
| 1775 |
+
|
| 1776 |
+
cdef _getFilenameForFile(source):
|
| 1777 |
+
"""Given a Python File or Gzip object, give filename back.
|
| 1778 |
+
|
| 1779 |
+
Returns None if not a file object.
|
| 1780 |
+
"""
|
| 1781 |
+
# urllib2 provides a geturl() method
|
| 1782 |
+
try:
|
| 1783 |
+
return source.geturl()
|
| 1784 |
+
except:
|
| 1785 |
+
pass
|
| 1786 |
+
# file instances have a name attribute
|
| 1787 |
+
try:
|
| 1788 |
+
filename = source.name
|
| 1789 |
+
if _isString(filename):
|
| 1790 |
+
return os_path_abspath(filename)
|
| 1791 |
+
except:
|
| 1792 |
+
pass
|
| 1793 |
+
# gzip file instances have a filename attribute (before Py3k)
|
| 1794 |
+
try:
|
| 1795 |
+
filename = source.filename
|
| 1796 |
+
if _isString(filename):
|
| 1797 |
+
return os_path_abspath(filename)
|
| 1798 |
+
except:
|
| 1799 |
+
pass
|
| 1800 |
+
# can't determine filename
|
| 1801 |
+
return None
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/builder.py
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# cython: language_level=2
|
| 2 |
+
|
| 3 |
+
#
|
| 4 |
+
# Element generator factory by Fredrik Lundh.
|
| 5 |
+
#
|
| 6 |
+
# Source:
|
| 7 |
+
# http://online.effbot.org/2006_11_01_archive.htm#et-builder
|
| 8 |
+
# http://effbot.python-hosting.com/file/stuff/sandbox/elementlib/builder.py
|
| 9 |
+
#
|
| 10 |
+
# --------------------------------------------------------------------
|
| 11 |
+
# The ElementTree toolkit is
|
| 12 |
+
#
|
| 13 |
+
# Copyright (c) 1999-2004 by Fredrik Lundh
|
| 14 |
+
#
|
| 15 |
+
# By obtaining, using, and/or copying this software and/or its
|
| 16 |
+
# associated documentation, you agree that you have read, understood,
|
| 17 |
+
# and will comply with the following terms and conditions:
|
| 18 |
+
#
|
| 19 |
+
# Permission to use, copy, modify, and distribute this software and
|
| 20 |
+
# its associated documentation for any purpose and without fee is
|
| 21 |
+
# hereby granted, provided that the above copyright notice appears in
|
| 22 |
+
# all copies, and that both that copyright notice and this permission
|
| 23 |
+
# notice appear in supporting documentation, and that the name of
|
| 24 |
+
# Secret Labs AB or the author not be used in advertising or publicity
|
| 25 |
+
# pertaining to distribution of the software without specific, written
|
| 26 |
+
# prior permission.
|
| 27 |
+
#
|
| 28 |
+
# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
|
| 29 |
+
# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
|
| 30 |
+
# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR
|
| 31 |
+
# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
|
| 32 |
+
# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
| 33 |
+
# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
| 34 |
+
# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
| 35 |
+
# OF THIS SOFTWARE.
|
| 36 |
+
# --------------------------------------------------------------------
|
| 37 |
+
|
| 38 |
+
"""
|
| 39 |
+
The ``E`` Element factory for generating XML documents.
|
| 40 |
+
"""
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
import lxml.etree as ET
|
| 44 |
+
_QName = ET.QName
|
| 45 |
+
|
| 46 |
+
from functools import partial
|
| 47 |
+
|
| 48 |
+
try:
|
| 49 |
+
from types import GenericAlias as _GenericAlias
|
| 50 |
+
except ImportError:
|
| 51 |
+
# Python 3.8 - we only need this as return value from "__class_getitem__"
|
| 52 |
+
def _GenericAlias(cls, item):
|
| 53 |
+
return f"{cls.__name__}[{item.__name__}]"
|
| 54 |
+
|
| 55 |
+
try:
|
| 56 |
+
basestring
|
| 57 |
+
except NameError:
|
| 58 |
+
basestring = str
|
| 59 |
+
|
| 60 |
+
try:
|
| 61 |
+
unicode
|
| 62 |
+
except NameError:
|
| 63 |
+
unicode = str
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
class ElementMaker:
|
| 67 |
+
"""Element generator factory.
|
| 68 |
+
|
| 69 |
+
Unlike the ordinary Element factory, the E factory allows you to pass in
|
| 70 |
+
more than just a tag and some optional attributes; you can also pass in
|
| 71 |
+
text and other elements. The text is added as either text or tail
|
| 72 |
+
attributes, and elements are inserted at the right spot. Some small
|
| 73 |
+
examples::
|
| 74 |
+
|
| 75 |
+
>>> from lxml import etree as ET
|
| 76 |
+
>>> from lxml.builder import E
|
| 77 |
+
|
| 78 |
+
>>> ET.tostring(E("tag"))
|
| 79 |
+
'<tag/>'
|
| 80 |
+
>>> ET.tostring(E("tag", "text"))
|
| 81 |
+
'<tag>text</tag>'
|
| 82 |
+
>>> ET.tostring(E("tag", "text", key="value"))
|
| 83 |
+
'<tag key="value">text</tag>'
|
| 84 |
+
>>> ET.tostring(E("tag", E("subtag", "text"), "tail"))
|
| 85 |
+
'<tag><subtag>text</subtag>tail</tag>'
|
| 86 |
+
|
| 87 |
+
For simple tags, the factory also allows you to write ``E.tag(...)`` instead
|
| 88 |
+
of ``E('tag', ...)``::
|
| 89 |
+
|
| 90 |
+
>>> ET.tostring(E.tag())
|
| 91 |
+
'<tag/>'
|
| 92 |
+
>>> ET.tostring(E.tag("text"))
|
| 93 |
+
'<tag>text</tag>'
|
| 94 |
+
>>> ET.tostring(E.tag(E.subtag("text"), "tail"))
|
| 95 |
+
'<tag><subtag>text</subtag>tail</tag>'
|
| 96 |
+
|
| 97 |
+
Here's a somewhat larger example; this shows how to generate HTML
|
| 98 |
+
documents, using a mix of prepared factory functions for inline elements,
|
| 99 |
+
nested ``E.tag`` calls, and embedded XHTML fragments::
|
| 100 |
+
|
| 101 |
+
# some common inline elements
|
| 102 |
+
A = E.a
|
| 103 |
+
I = E.i
|
| 104 |
+
B = E.b
|
| 105 |
+
|
| 106 |
+
def CLASS(v):
|
| 107 |
+
# helper function, 'class' is a reserved word
|
| 108 |
+
return {'class': v}
|
| 109 |
+
|
| 110 |
+
page = (
|
| 111 |
+
E.html(
|
| 112 |
+
E.head(
|
| 113 |
+
E.title("This is a sample document")
|
| 114 |
+
),
|
| 115 |
+
E.body(
|
| 116 |
+
E.h1("Hello!", CLASS("title")),
|
| 117 |
+
E.p("This is a paragraph with ", B("bold"), " text in it!"),
|
| 118 |
+
E.p("This is another paragraph, with a ",
|
| 119 |
+
A("link", href="http://www.python.org"), "."),
|
| 120 |
+
E.p("Here are some reserved characters: <spam&egg>."),
|
| 121 |
+
ET.XML("<p>And finally, here is an embedded XHTML fragment.</p>"),
|
| 122 |
+
)
|
| 123 |
+
)
|
| 124 |
+
)
|
| 125 |
+
|
| 126 |
+
print ET.tostring(page)
|
| 127 |
+
|
| 128 |
+
Here's a prettyprinted version of the output from the above script::
|
| 129 |
+
|
| 130 |
+
<html>
|
| 131 |
+
<head>
|
| 132 |
+
<title>This is a sample document</title>
|
| 133 |
+
</head>
|
| 134 |
+
<body>
|
| 135 |
+
<h1 class="title">Hello!</h1>
|
| 136 |
+
<p>This is a paragraph with <b>bold</b> text in it!</p>
|
| 137 |
+
<p>This is another paragraph, with <a href="http://www.python.org">link</a>.</p>
|
| 138 |
+
<p>Here are some reserved characters: <spam&egg>.</p>
|
| 139 |
+
<p>And finally, here is an embedded XHTML fragment.</p>
|
| 140 |
+
</body>
|
| 141 |
+
</html>
|
| 142 |
+
|
| 143 |
+
For namespace support, you can pass a namespace map (``nsmap``)
|
| 144 |
+
and/or a specific target ``namespace`` to the ElementMaker class::
|
| 145 |
+
|
| 146 |
+
>>> E = ElementMaker(namespace="http://my.ns/")
|
| 147 |
+
>>> print(ET.tostring( E.test ))
|
| 148 |
+
<test xmlns="http://my.ns/"/>
|
| 149 |
+
|
| 150 |
+
>>> E = ElementMaker(namespace="http://my.ns/", nsmap={'p':'http://my.ns/'})
|
| 151 |
+
>>> print(ET.tostring( E.test ))
|
| 152 |
+
<p:test xmlns:p="http://my.ns/"/>
|
| 153 |
+
"""
|
| 154 |
+
|
| 155 |
+
def __init__(self, typemap=None,
|
| 156 |
+
namespace=None, nsmap=None, makeelement=None):
|
| 157 |
+
self._namespace = '{' + namespace + '}' if namespace is not None else None
|
| 158 |
+
self._nsmap = dict(nsmap) if nsmap else None
|
| 159 |
+
|
| 160 |
+
assert makeelement is None or callable(makeelement)
|
| 161 |
+
self._makeelement = makeelement if makeelement is not None else ET.Element
|
| 162 |
+
|
| 163 |
+
# initialize the default type map functions for this element factory
|
| 164 |
+
typemap = dict(typemap) if typemap else {}
|
| 165 |
+
|
| 166 |
+
def add_text(elem, item):
|
| 167 |
+
try:
|
| 168 |
+
last_child = elem[-1]
|
| 169 |
+
except IndexError:
|
| 170 |
+
elem.text = (elem.text or "") + item
|
| 171 |
+
else:
|
| 172 |
+
last_child.tail = (last_child.tail or "") + item
|
| 173 |
+
|
| 174 |
+
def add_cdata(elem, cdata):
|
| 175 |
+
if elem.text:
|
| 176 |
+
raise ValueError("Can't add a CDATA section. Element already has some text: %r" % elem.text)
|
| 177 |
+
elem.text = cdata
|
| 178 |
+
|
| 179 |
+
if str not in typemap:
|
| 180 |
+
typemap[str] = add_text
|
| 181 |
+
if unicode not in typemap:
|
| 182 |
+
typemap[unicode] = add_text
|
| 183 |
+
if ET.CDATA not in typemap:
|
| 184 |
+
typemap[ET.CDATA] = add_cdata
|
| 185 |
+
|
| 186 |
+
def add_dict(elem, item):
|
| 187 |
+
attrib = elem.attrib
|
| 188 |
+
for k, v in item.items():
|
| 189 |
+
if isinstance(v, basestring):
|
| 190 |
+
attrib[k] = v
|
| 191 |
+
else:
|
| 192 |
+
attrib[k] = typemap[type(v)](None, v)
|
| 193 |
+
|
| 194 |
+
if dict not in typemap:
|
| 195 |
+
typemap[dict] = add_dict
|
| 196 |
+
|
| 197 |
+
self._typemap = typemap
|
| 198 |
+
|
| 199 |
+
def __call__(self, tag, *children, **attrib):
|
| 200 |
+
typemap = self._typemap
|
| 201 |
+
|
| 202 |
+
# We'll usually get a 'str', and the compiled type check is very fast.
|
| 203 |
+
if not isinstance(tag, str) and isinstance(tag, _QName):
|
| 204 |
+
# A QName is explicitly qualified, do not look at self._namespace.
|
| 205 |
+
tag = tag.text
|
| 206 |
+
elif self._namespace is not None and tag[0] != '{':
|
| 207 |
+
tag = self._namespace + tag
|
| 208 |
+
elem = self._makeelement(tag, nsmap=self._nsmap)
|
| 209 |
+
if attrib:
|
| 210 |
+
typemap[dict](elem, attrib)
|
| 211 |
+
|
| 212 |
+
for item in children:
|
| 213 |
+
if callable(item):
|
| 214 |
+
item = item()
|
| 215 |
+
t = typemap.get(type(item))
|
| 216 |
+
if t is None:
|
| 217 |
+
if ET.iselement(item):
|
| 218 |
+
elem.append(item)
|
| 219 |
+
continue
|
| 220 |
+
for basetype in type(item).__mro__:
|
| 221 |
+
# See if the typemap knows of any of this type's bases.
|
| 222 |
+
t = typemap.get(basetype)
|
| 223 |
+
if t is not None:
|
| 224 |
+
break
|
| 225 |
+
else:
|
| 226 |
+
raise TypeError("bad argument type: %s(%r)" %
|
| 227 |
+
(type(item).__name__, item))
|
| 228 |
+
v = t(elem, item)
|
| 229 |
+
if v:
|
| 230 |
+
typemap.get(type(v))(elem, v)
|
| 231 |
+
|
| 232 |
+
return elem
|
| 233 |
+
|
| 234 |
+
def __getattr__(self, tag):
|
| 235 |
+
return partial(self, tag)
|
| 236 |
+
|
| 237 |
+
# Allow subscripting ElementMaker in type annotions (PEP 560)
|
| 238 |
+
def __class_getitem__(cls, item):
|
| 239 |
+
return _GenericAlias(cls, item)
|
| 240 |
+
|
| 241 |
+
|
| 242 |
+
# create factory object
|
| 243 |
+
E = ElementMaker()
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/classlookup.pxi
ADDED
|
@@ -0,0 +1,580 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Configurable Element class lookup
|
| 2 |
+
|
| 3 |
+
################################################################################
|
| 4 |
+
# Custom Element classes
|
| 5 |
+
|
| 6 |
+
cdef public class ElementBase(_Element) [ type LxmlElementBaseType,
|
| 7 |
+
object LxmlElementBase ]:
|
| 8 |
+
"""ElementBase(*children, attrib=None, nsmap=None, **_extra)
|
| 9 |
+
|
| 10 |
+
The public Element class. All custom Element classes must inherit
|
| 11 |
+
from this one. To create an Element, use the `Element()` factory.
|
| 12 |
+
|
| 13 |
+
BIG FAT WARNING: Subclasses *must not* override __init__ or
|
| 14 |
+
__new__ as it is absolutely undefined when these objects will be
|
| 15 |
+
created or destroyed. All persistent state of Elements must be
|
| 16 |
+
stored in the underlying XML. If you really need to initialize
|
| 17 |
+
the object after creation, you can implement an ``_init(self)``
|
| 18 |
+
method that will be called directly after object creation.
|
| 19 |
+
|
| 20 |
+
Subclasses of this class can be instantiated to create a new
|
| 21 |
+
Element. By default, the tag name will be the class name and the
|
| 22 |
+
namespace will be empty. You can modify this with the following
|
| 23 |
+
class attributes:
|
| 24 |
+
|
| 25 |
+
* TAG - the tag name, possibly containing a namespace in Clark
|
| 26 |
+
notation
|
| 27 |
+
|
| 28 |
+
* NAMESPACE - the default namespace URI, unless provided as part
|
| 29 |
+
of the TAG attribute.
|
| 30 |
+
|
| 31 |
+
* HTML - flag if the class is an HTML tag, as opposed to an XML
|
| 32 |
+
tag. This only applies to un-namespaced tags and defaults to
|
| 33 |
+
false (i.e. XML).
|
| 34 |
+
|
| 35 |
+
* PARSER - the parser that provides the configuration for the
|
| 36 |
+
newly created document. Providing an HTML parser here will
|
| 37 |
+
default to creating an HTML element.
|
| 38 |
+
|
| 39 |
+
In user code, the latter three are commonly inherited in class
|
| 40 |
+
hierarchies that implement a common namespace.
|
| 41 |
+
"""
|
| 42 |
+
def __init__(self, *children, attrib=None, nsmap=None, **_extra):
|
| 43 |
+
"""ElementBase(*children, attrib=None, nsmap=None, **_extra)
|
| 44 |
+
"""
|
| 45 |
+
cdef bint is_html = 0
|
| 46 |
+
cdef _BaseParser parser
|
| 47 |
+
cdef _Element last_child
|
| 48 |
+
# don't use normal attribute access as it might be overridden
|
| 49 |
+
_getattr = object.__getattribute__
|
| 50 |
+
try:
|
| 51 |
+
namespace = _utf8(_getattr(self, 'NAMESPACE'))
|
| 52 |
+
except AttributeError:
|
| 53 |
+
namespace = None
|
| 54 |
+
try:
|
| 55 |
+
ns, tag = _getNsTag(_getattr(self, 'TAG'))
|
| 56 |
+
if ns is not None:
|
| 57 |
+
namespace = ns
|
| 58 |
+
except AttributeError:
|
| 59 |
+
tag = _utf8(_getattr(_getattr(self, '__class__'), '__name__'))
|
| 60 |
+
if b'.' in tag:
|
| 61 |
+
tag = tag.split(b'.')[-1]
|
| 62 |
+
try:
|
| 63 |
+
parser = _getattr(self, 'PARSER')
|
| 64 |
+
except AttributeError:
|
| 65 |
+
parser = None
|
| 66 |
+
for child in children:
|
| 67 |
+
if isinstance(child, _Element):
|
| 68 |
+
parser = (<_Element>child)._doc._parser
|
| 69 |
+
break
|
| 70 |
+
if isinstance(parser, HTMLParser):
|
| 71 |
+
is_html = 1
|
| 72 |
+
if namespace is None:
|
| 73 |
+
try:
|
| 74 |
+
is_html = _getattr(self, 'HTML')
|
| 75 |
+
except AttributeError:
|
| 76 |
+
pass
|
| 77 |
+
_initNewElement(self, is_html, tag, namespace, parser,
|
| 78 |
+
attrib, nsmap, _extra)
|
| 79 |
+
last_child = None
|
| 80 |
+
for child in children:
|
| 81 |
+
if _isString(child):
|
| 82 |
+
if last_child is None:
|
| 83 |
+
_setNodeText(self._c_node,
|
| 84 |
+
(_collectText(self._c_node.children) or '') + child)
|
| 85 |
+
else:
|
| 86 |
+
_setTailText(last_child._c_node,
|
| 87 |
+
(_collectText(last_child._c_node.next) or '') + child)
|
| 88 |
+
elif isinstance(child, _Element):
|
| 89 |
+
last_child = child
|
| 90 |
+
_appendChild(self, last_child)
|
| 91 |
+
elif isinstance(child, type) and issubclass(child, ElementBase):
|
| 92 |
+
last_child = child()
|
| 93 |
+
_appendChild(self, last_child)
|
| 94 |
+
else:
|
| 95 |
+
raise TypeError, f"Invalid child type: {type(child)!r}"
|
| 96 |
+
|
| 97 |
+
cdef class CommentBase(_Comment):
|
| 98 |
+
"""All custom Comment classes must inherit from this one.
|
| 99 |
+
|
| 100 |
+
To create an XML Comment instance, use the ``Comment()`` factory.
|
| 101 |
+
|
| 102 |
+
Subclasses *must not* override __init__ or __new__ as it is
|
| 103 |
+
absolutely undefined when these objects will be created or
|
| 104 |
+
destroyed. All persistent state of Comments must be stored in the
|
| 105 |
+
underlying XML. If you really need to initialize the object after
|
| 106 |
+
creation, you can implement an ``_init(self)`` method that will be
|
| 107 |
+
called after object creation.
|
| 108 |
+
"""
|
| 109 |
+
def __init__(self, text):
|
| 110 |
+
# copied from Comment() factory
|
| 111 |
+
cdef _Document doc
|
| 112 |
+
cdef xmlDoc* c_doc
|
| 113 |
+
if text is None:
|
| 114 |
+
text = b''
|
| 115 |
+
else:
|
| 116 |
+
text = _utf8(text)
|
| 117 |
+
c_doc = _newXMLDoc()
|
| 118 |
+
doc = _documentFactory(c_doc, None)
|
| 119 |
+
self._c_node = _createComment(c_doc, _xcstr(text))
|
| 120 |
+
if self._c_node is NULL:
|
| 121 |
+
raise MemoryError()
|
| 122 |
+
tree.xmlAddChild(<xmlNode*>c_doc, self._c_node)
|
| 123 |
+
_registerProxy(self, doc, self._c_node)
|
| 124 |
+
self._init()
|
| 125 |
+
|
| 126 |
+
cdef class PIBase(_ProcessingInstruction):
|
| 127 |
+
"""All custom Processing Instruction classes must inherit from this one.
|
| 128 |
+
|
| 129 |
+
To create an XML ProcessingInstruction instance, use the ``PI()``
|
| 130 |
+
factory.
|
| 131 |
+
|
| 132 |
+
Subclasses *must not* override __init__ or __new__ as it is
|
| 133 |
+
absolutely undefined when these objects will be created or
|
| 134 |
+
destroyed. All persistent state of PIs must be stored in the
|
| 135 |
+
underlying XML. If you really need to initialize the object after
|
| 136 |
+
creation, you can implement an ``_init(self)`` method that will be
|
| 137 |
+
called after object creation.
|
| 138 |
+
"""
|
| 139 |
+
def __init__(self, target, text=None):
|
| 140 |
+
# copied from PI() factory
|
| 141 |
+
cdef _Document doc
|
| 142 |
+
cdef xmlDoc* c_doc
|
| 143 |
+
target = _utf8(target)
|
| 144 |
+
if text is None:
|
| 145 |
+
text = b''
|
| 146 |
+
else:
|
| 147 |
+
text = _utf8(text)
|
| 148 |
+
c_doc = _newXMLDoc()
|
| 149 |
+
doc = _documentFactory(c_doc, None)
|
| 150 |
+
self._c_node = _createPI(c_doc, _xcstr(target), _xcstr(text))
|
| 151 |
+
if self._c_node is NULL:
|
| 152 |
+
raise MemoryError()
|
| 153 |
+
tree.xmlAddChild(<xmlNode*>c_doc, self._c_node)
|
| 154 |
+
_registerProxy(self, doc, self._c_node)
|
| 155 |
+
self._init()
|
| 156 |
+
|
| 157 |
+
cdef class EntityBase(_Entity):
|
| 158 |
+
"""All custom Entity classes must inherit from this one.
|
| 159 |
+
|
| 160 |
+
To create an XML Entity instance, use the ``Entity()`` factory.
|
| 161 |
+
|
| 162 |
+
Subclasses *must not* override __init__ or __new__ as it is
|
| 163 |
+
absolutely undefined when these objects will be created or
|
| 164 |
+
destroyed. All persistent state of Entities must be stored in the
|
| 165 |
+
underlying XML. If you really need to initialize the object after
|
| 166 |
+
creation, you can implement an ``_init(self)`` method that will be
|
| 167 |
+
called after object creation.
|
| 168 |
+
"""
|
| 169 |
+
def __init__(self, name):
|
| 170 |
+
cdef _Document doc
|
| 171 |
+
cdef xmlDoc* c_doc
|
| 172 |
+
name_utf = _utf8(name)
|
| 173 |
+
c_name = _xcstr(name_utf)
|
| 174 |
+
if c_name[0] == c'#':
|
| 175 |
+
if not _characterReferenceIsValid(c_name + 1):
|
| 176 |
+
raise ValueError, f"Invalid character reference: '{name}'"
|
| 177 |
+
elif not _xmlNameIsValid(c_name):
|
| 178 |
+
raise ValueError, f"Invalid entity reference: '{name}'"
|
| 179 |
+
c_doc = _newXMLDoc()
|
| 180 |
+
doc = _documentFactory(c_doc, None)
|
| 181 |
+
self._c_node = _createEntity(c_doc, c_name)
|
| 182 |
+
if self._c_node is NULL:
|
| 183 |
+
raise MemoryError()
|
| 184 |
+
tree.xmlAddChild(<xmlNode*>c_doc, self._c_node)
|
| 185 |
+
_registerProxy(self, doc, self._c_node)
|
| 186 |
+
self._init()
|
| 187 |
+
|
| 188 |
+
|
| 189 |
+
cdef int _validateNodeClass(xmlNode* c_node, cls) except -1:
|
| 190 |
+
if c_node.type == tree.XML_ELEMENT_NODE:
|
| 191 |
+
expected = ElementBase
|
| 192 |
+
elif c_node.type == tree.XML_COMMENT_NODE:
|
| 193 |
+
expected = CommentBase
|
| 194 |
+
elif c_node.type == tree.XML_ENTITY_REF_NODE:
|
| 195 |
+
expected = EntityBase
|
| 196 |
+
elif c_node.type == tree.XML_PI_NODE:
|
| 197 |
+
expected = PIBase
|
| 198 |
+
else:
|
| 199 |
+
assert False, f"Unknown node type: {c_node.type}"
|
| 200 |
+
|
| 201 |
+
if not (isinstance(cls, type) and issubclass(cls, expected)):
|
| 202 |
+
raise TypeError(
|
| 203 |
+
f"result of class lookup must be subclass of {type(expected)}, got {type(cls)}")
|
| 204 |
+
return 0
|
| 205 |
+
|
| 206 |
+
|
| 207 |
+
################################################################################
|
| 208 |
+
# Element class lookup
|
| 209 |
+
|
| 210 |
+
ctypedef public object (*_element_class_lookup_function)(object, _Document, xmlNode*)
|
| 211 |
+
|
| 212 |
+
# class to store element class lookup functions
|
| 213 |
+
cdef public class ElementClassLookup [ type LxmlElementClassLookupType,
|
| 214 |
+
object LxmlElementClassLookup ]:
|
| 215 |
+
"""ElementClassLookup(self)
|
| 216 |
+
Superclass of Element class lookups.
|
| 217 |
+
"""
|
| 218 |
+
cdef _element_class_lookup_function _lookup_function
|
| 219 |
+
|
| 220 |
+
|
| 221 |
+
cdef public class FallbackElementClassLookup(ElementClassLookup) \
|
| 222 |
+
[ type LxmlFallbackElementClassLookupType,
|
| 223 |
+
object LxmlFallbackElementClassLookup ]:
|
| 224 |
+
"""FallbackElementClassLookup(self, fallback=None)
|
| 225 |
+
|
| 226 |
+
Superclass of Element class lookups with additional fallback.
|
| 227 |
+
"""
|
| 228 |
+
cdef readonly ElementClassLookup fallback
|
| 229 |
+
cdef _element_class_lookup_function _fallback_function
|
| 230 |
+
def __cinit__(self):
|
| 231 |
+
# fall back to default lookup
|
| 232 |
+
self._fallback_function = _lookupDefaultElementClass
|
| 233 |
+
|
| 234 |
+
def __init__(self, ElementClassLookup fallback=None):
|
| 235 |
+
if fallback is not None:
|
| 236 |
+
self._setFallback(fallback)
|
| 237 |
+
else:
|
| 238 |
+
self._fallback_function = _lookupDefaultElementClass
|
| 239 |
+
|
| 240 |
+
cdef void _setFallback(self, ElementClassLookup lookup):
|
| 241 |
+
"""Sets the fallback scheme for this lookup method.
|
| 242 |
+
"""
|
| 243 |
+
self.fallback = lookup
|
| 244 |
+
self._fallback_function = lookup._lookup_function
|
| 245 |
+
if self._fallback_function is NULL:
|
| 246 |
+
self._fallback_function = _lookupDefaultElementClass
|
| 247 |
+
|
| 248 |
+
def set_fallback(self, ElementClassLookup lookup not None):
|
| 249 |
+
"""set_fallback(self, lookup)
|
| 250 |
+
|
| 251 |
+
Sets the fallback scheme for this lookup method.
|
| 252 |
+
"""
|
| 253 |
+
self._setFallback(lookup)
|
| 254 |
+
|
| 255 |
+
cdef inline object _callLookupFallback(FallbackElementClassLookup lookup,
|
| 256 |
+
_Document doc, xmlNode* c_node):
|
| 257 |
+
return lookup._fallback_function(lookup.fallback, doc, c_node)
|
| 258 |
+
|
| 259 |
+
|
| 260 |
+
################################################################################
|
| 261 |
+
# default lookup scheme
|
| 262 |
+
|
| 263 |
+
cdef class ElementDefaultClassLookup(ElementClassLookup):
|
| 264 |
+
"""ElementDefaultClassLookup(self, element=None, comment=None, pi=None, entity=None)
|
| 265 |
+
Element class lookup scheme that always returns the default Element
|
| 266 |
+
class.
|
| 267 |
+
|
| 268 |
+
The keyword arguments ``element``, ``comment``, ``pi`` and ``entity``
|
| 269 |
+
accept the respective Element classes.
|
| 270 |
+
"""
|
| 271 |
+
cdef readonly object element_class
|
| 272 |
+
cdef readonly object comment_class
|
| 273 |
+
cdef readonly object pi_class
|
| 274 |
+
cdef readonly object entity_class
|
| 275 |
+
def __cinit__(self):
|
| 276 |
+
self._lookup_function = _lookupDefaultElementClass
|
| 277 |
+
|
| 278 |
+
def __init__(self, element=None, comment=None, pi=None, entity=None):
|
| 279 |
+
if element is None:
|
| 280 |
+
self.element_class = _Element
|
| 281 |
+
elif issubclass(element, ElementBase):
|
| 282 |
+
self.element_class = element
|
| 283 |
+
else:
|
| 284 |
+
raise TypeError, "element class must be subclass of ElementBase"
|
| 285 |
+
|
| 286 |
+
if comment is None:
|
| 287 |
+
self.comment_class = _Comment
|
| 288 |
+
elif issubclass(comment, CommentBase):
|
| 289 |
+
self.comment_class = comment
|
| 290 |
+
else:
|
| 291 |
+
raise TypeError, "comment class must be subclass of CommentBase"
|
| 292 |
+
|
| 293 |
+
if entity is None:
|
| 294 |
+
self.entity_class = _Entity
|
| 295 |
+
elif issubclass(entity, EntityBase):
|
| 296 |
+
self.entity_class = entity
|
| 297 |
+
else:
|
| 298 |
+
raise TypeError, "Entity class must be subclass of EntityBase"
|
| 299 |
+
|
| 300 |
+
if pi is None:
|
| 301 |
+
self.pi_class = None # special case, see below
|
| 302 |
+
elif issubclass(pi, PIBase):
|
| 303 |
+
self.pi_class = pi
|
| 304 |
+
else:
|
| 305 |
+
raise TypeError, "PI class must be subclass of PIBase"
|
| 306 |
+
|
| 307 |
+
cdef object _lookupDefaultElementClass(state, _Document _doc, xmlNode* c_node):
|
| 308 |
+
"Trivial class lookup function that always returns the default class."
|
| 309 |
+
if c_node.type == tree.XML_ELEMENT_NODE:
|
| 310 |
+
if state is not None:
|
| 311 |
+
return (<ElementDefaultClassLookup>state).element_class
|
| 312 |
+
else:
|
| 313 |
+
return _Element
|
| 314 |
+
elif c_node.type == tree.XML_COMMENT_NODE:
|
| 315 |
+
if state is not None:
|
| 316 |
+
return (<ElementDefaultClassLookup>state).comment_class
|
| 317 |
+
else:
|
| 318 |
+
return _Comment
|
| 319 |
+
elif c_node.type == tree.XML_ENTITY_REF_NODE:
|
| 320 |
+
if state is not None:
|
| 321 |
+
return (<ElementDefaultClassLookup>state).entity_class
|
| 322 |
+
else:
|
| 323 |
+
return _Entity
|
| 324 |
+
elif c_node.type == tree.XML_PI_NODE:
|
| 325 |
+
if state is None or (<ElementDefaultClassLookup>state).pi_class is None:
|
| 326 |
+
# special case XSLT-PI
|
| 327 |
+
if c_node.name is not NULL and c_node.content is not NULL:
|
| 328 |
+
if tree.xmlStrcmp(c_node.name, <unsigned char*>"xml-stylesheet") == 0:
|
| 329 |
+
if tree.xmlStrstr(c_node.content, <unsigned char*>"text/xsl") is not NULL or \
|
| 330 |
+
tree.xmlStrstr(c_node.content, <unsigned char*>"text/xml") is not NULL:
|
| 331 |
+
return _XSLTProcessingInstruction
|
| 332 |
+
return _ProcessingInstruction
|
| 333 |
+
else:
|
| 334 |
+
return (<ElementDefaultClassLookup>state).pi_class
|
| 335 |
+
else:
|
| 336 |
+
assert False, f"Unknown node type: {c_node.type}"
|
| 337 |
+
|
| 338 |
+
|
| 339 |
+
################################################################################
|
| 340 |
+
# attribute based lookup scheme
|
| 341 |
+
|
| 342 |
+
cdef class AttributeBasedElementClassLookup(FallbackElementClassLookup):
|
| 343 |
+
"""AttributeBasedElementClassLookup(self, attribute_name, class_mapping, fallback=None)
|
| 344 |
+
Checks an attribute of an Element and looks up the value in a
|
| 345 |
+
class dictionary.
|
| 346 |
+
|
| 347 |
+
Arguments:
|
| 348 |
+
- attribute name - '{ns}name' style string
|
| 349 |
+
- class mapping - Python dict mapping attribute values to Element classes
|
| 350 |
+
- fallback - optional fallback lookup mechanism
|
| 351 |
+
|
| 352 |
+
A None key in the class mapping will be checked if the attribute is
|
| 353 |
+
missing.
|
| 354 |
+
"""
|
| 355 |
+
cdef object _class_mapping
|
| 356 |
+
cdef tuple _pytag
|
| 357 |
+
cdef const_xmlChar* _c_ns
|
| 358 |
+
cdef const_xmlChar* _c_name
|
| 359 |
+
def __cinit__(self):
|
| 360 |
+
self._lookup_function = _attribute_class_lookup
|
| 361 |
+
|
| 362 |
+
def __init__(self, attribute_name, class_mapping,
|
| 363 |
+
ElementClassLookup fallback=None):
|
| 364 |
+
self._pytag = _getNsTag(attribute_name)
|
| 365 |
+
ns, name = self._pytag
|
| 366 |
+
if ns is None:
|
| 367 |
+
self._c_ns = NULL
|
| 368 |
+
else:
|
| 369 |
+
self._c_ns = _xcstr(ns)
|
| 370 |
+
self._c_name = _xcstr(name)
|
| 371 |
+
self._class_mapping = dict(class_mapping)
|
| 372 |
+
|
| 373 |
+
FallbackElementClassLookup.__init__(self, fallback)
|
| 374 |
+
|
| 375 |
+
cdef object _attribute_class_lookup(state, _Document doc, xmlNode* c_node):
|
| 376 |
+
cdef AttributeBasedElementClassLookup lookup
|
| 377 |
+
cdef python.PyObject* dict_result
|
| 378 |
+
|
| 379 |
+
lookup = <AttributeBasedElementClassLookup>state
|
| 380 |
+
if c_node.type == tree.XML_ELEMENT_NODE:
|
| 381 |
+
value = _attributeValueFromNsName(
|
| 382 |
+
c_node, lookup._c_ns, lookup._c_name)
|
| 383 |
+
dict_result = python.PyDict_GetItem(lookup._class_mapping, value)
|
| 384 |
+
if dict_result is not NULL:
|
| 385 |
+
cls = <object>dict_result
|
| 386 |
+
_validateNodeClass(c_node, cls)
|
| 387 |
+
return cls
|
| 388 |
+
return _callLookupFallback(lookup, doc, c_node)
|
| 389 |
+
|
| 390 |
+
|
| 391 |
+
################################################################################
|
| 392 |
+
# per-parser lookup scheme
|
| 393 |
+
|
| 394 |
+
cdef class ParserBasedElementClassLookup(FallbackElementClassLookup):
|
| 395 |
+
"""ParserBasedElementClassLookup(self, fallback=None)
|
| 396 |
+
Element class lookup based on the XML parser.
|
| 397 |
+
"""
|
| 398 |
+
def __cinit__(self):
|
| 399 |
+
self._lookup_function = _parser_class_lookup
|
| 400 |
+
|
| 401 |
+
cdef object _parser_class_lookup(state, _Document doc, xmlNode* c_node):
|
| 402 |
+
if doc._parser._class_lookup is not None:
|
| 403 |
+
return doc._parser._class_lookup._lookup_function(
|
| 404 |
+
doc._parser._class_lookup, doc, c_node)
|
| 405 |
+
return _callLookupFallback(<FallbackElementClassLookup>state, doc, c_node)
|
| 406 |
+
|
| 407 |
+
|
| 408 |
+
################################################################################
|
| 409 |
+
# custom class lookup based on node type, namespace, name
|
| 410 |
+
|
| 411 |
+
cdef class CustomElementClassLookup(FallbackElementClassLookup):
|
| 412 |
+
"""CustomElementClassLookup(self, fallback=None)
|
| 413 |
+
Element class lookup based on a subclass method.
|
| 414 |
+
|
| 415 |
+
You can inherit from this class and override the method::
|
| 416 |
+
|
| 417 |
+
lookup(self, type, doc, namespace, name)
|
| 418 |
+
|
| 419 |
+
to lookup the element class for a node. Arguments of the method:
|
| 420 |
+
* type: one of 'element', 'comment', 'PI', 'entity'
|
| 421 |
+
* doc: document that the node is in
|
| 422 |
+
* namespace: namespace URI of the node (or None for comments/PIs/entities)
|
| 423 |
+
* name: name of the element/entity, None for comments, target for PIs
|
| 424 |
+
|
| 425 |
+
If you return None from this method, the fallback will be called.
|
| 426 |
+
"""
|
| 427 |
+
def __cinit__(self):
|
| 428 |
+
self._lookup_function = _custom_class_lookup
|
| 429 |
+
|
| 430 |
+
def lookup(self, type, doc, namespace, name):
|
| 431 |
+
"lookup(self, type, doc, namespace, name)"
|
| 432 |
+
return None
|
| 433 |
+
|
| 434 |
+
cdef object _custom_class_lookup(state, _Document doc, xmlNode* c_node):
|
| 435 |
+
cdef CustomElementClassLookup lookup
|
| 436 |
+
|
| 437 |
+
lookup = <CustomElementClassLookup>state
|
| 438 |
+
|
| 439 |
+
if c_node.type == tree.XML_ELEMENT_NODE:
|
| 440 |
+
element_type = "element"
|
| 441 |
+
elif c_node.type == tree.XML_COMMENT_NODE:
|
| 442 |
+
element_type = "comment"
|
| 443 |
+
elif c_node.type == tree.XML_PI_NODE:
|
| 444 |
+
element_type = "PI"
|
| 445 |
+
elif c_node.type == tree.XML_ENTITY_REF_NODE:
|
| 446 |
+
element_type = "entity"
|
| 447 |
+
else:
|
| 448 |
+
element_type = "element"
|
| 449 |
+
if c_node.name is NULL:
|
| 450 |
+
name = None
|
| 451 |
+
else:
|
| 452 |
+
name = funicode(c_node.name)
|
| 453 |
+
c_str = tree._getNs(c_node)
|
| 454 |
+
ns = funicode(c_str) if c_str is not NULL else None
|
| 455 |
+
|
| 456 |
+
cls = lookup.lookup(element_type, doc, ns, name)
|
| 457 |
+
if cls is not None:
|
| 458 |
+
_validateNodeClass(c_node, cls)
|
| 459 |
+
return cls
|
| 460 |
+
return _callLookupFallback(lookup, doc, c_node)
|
| 461 |
+
|
| 462 |
+
|
| 463 |
+
################################################################################
|
| 464 |
+
# read-only tree based class lookup
|
| 465 |
+
|
| 466 |
+
cdef class PythonElementClassLookup(FallbackElementClassLookup):
|
| 467 |
+
"""PythonElementClassLookup(self, fallback=None)
|
| 468 |
+
Element class lookup based on a subclass method.
|
| 469 |
+
|
| 470 |
+
This class lookup scheme allows access to the entire XML tree in
|
| 471 |
+
read-only mode. To use it, re-implement the ``lookup(self, doc,
|
| 472 |
+
root)`` method in a subclass::
|
| 473 |
+
|
| 474 |
+
from lxml import etree, pyclasslookup
|
| 475 |
+
|
| 476 |
+
class MyElementClass(etree.ElementBase):
|
| 477 |
+
honkey = True
|
| 478 |
+
|
| 479 |
+
class MyLookup(pyclasslookup.PythonElementClassLookup):
|
| 480 |
+
def lookup(self, doc, root):
|
| 481 |
+
if root.tag == "sometag":
|
| 482 |
+
return MyElementClass
|
| 483 |
+
else:
|
| 484 |
+
for child in root:
|
| 485 |
+
if child.tag == "someothertag":
|
| 486 |
+
return MyElementClass
|
| 487 |
+
# delegate to default
|
| 488 |
+
return None
|
| 489 |
+
|
| 490 |
+
If you return None from this method, the fallback will be called.
|
| 491 |
+
|
| 492 |
+
The first argument is the opaque document instance that contains
|
| 493 |
+
the Element. The second argument is a lightweight Element proxy
|
| 494 |
+
implementation that is only valid during the lookup. Do not try
|
| 495 |
+
to keep a reference to it. Once the lookup is done, the proxy
|
| 496 |
+
will be invalid.
|
| 497 |
+
|
| 498 |
+
Also, you cannot wrap such a read-only Element in an ElementTree,
|
| 499 |
+
and you must take care not to keep a reference to them outside of
|
| 500 |
+
the `lookup()` method.
|
| 501 |
+
|
| 502 |
+
Note that the API of the Element objects is not complete. It is
|
| 503 |
+
purely read-only and does not support all features of the normal
|
| 504 |
+
`lxml.etree` API (such as XPath, extended slicing or some
|
| 505 |
+
iteration methods).
|
| 506 |
+
|
| 507 |
+
See https://lxml.de/element_classes.html
|
| 508 |
+
"""
|
| 509 |
+
def __cinit__(self):
|
| 510 |
+
self._lookup_function = _python_class_lookup
|
| 511 |
+
|
| 512 |
+
def lookup(self, doc, element):
|
| 513 |
+
"""lookup(self, doc, element)
|
| 514 |
+
|
| 515 |
+
Override this method to implement your own lookup scheme.
|
| 516 |
+
"""
|
| 517 |
+
return None
|
| 518 |
+
|
| 519 |
+
cdef object _python_class_lookup(state, _Document doc, tree.xmlNode* c_node):
|
| 520 |
+
cdef PythonElementClassLookup lookup
|
| 521 |
+
cdef _ReadOnlyProxy proxy
|
| 522 |
+
lookup = <PythonElementClassLookup>state
|
| 523 |
+
|
| 524 |
+
proxy = _newReadOnlyProxy(None, c_node)
|
| 525 |
+
cls = lookup.lookup(doc, proxy)
|
| 526 |
+
_freeReadOnlyProxies(proxy)
|
| 527 |
+
|
| 528 |
+
if cls is not None:
|
| 529 |
+
_validateNodeClass(c_node, cls)
|
| 530 |
+
return cls
|
| 531 |
+
return _callLookupFallback(lookup, doc, c_node)
|
| 532 |
+
|
| 533 |
+
################################################################################
|
| 534 |
+
# Global setup
|
| 535 |
+
|
| 536 |
+
cdef _element_class_lookup_function LOOKUP_ELEMENT_CLASS
|
| 537 |
+
cdef object ELEMENT_CLASS_LOOKUP_STATE
|
| 538 |
+
|
| 539 |
+
cdef void _setElementClassLookupFunction(
|
| 540 |
+
_element_class_lookup_function function, object state):
|
| 541 |
+
global LOOKUP_ELEMENT_CLASS, ELEMENT_CLASS_LOOKUP_STATE
|
| 542 |
+
if function is NULL:
|
| 543 |
+
state = DEFAULT_ELEMENT_CLASS_LOOKUP
|
| 544 |
+
function = DEFAULT_ELEMENT_CLASS_LOOKUP._lookup_function
|
| 545 |
+
|
| 546 |
+
ELEMENT_CLASS_LOOKUP_STATE = state
|
| 547 |
+
LOOKUP_ELEMENT_CLASS = function
|
| 548 |
+
|
| 549 |
+
def set_element_class_lookup(ElementClassLookup lookup = None):
|
| 550 |
+
"""set_element_class_lookup(lookup = None)
|
| 551 |
+
|
| 552 |
+
Set the global element class lookup method.
|
| 553 |
+
|
| 554 |
+
This defines the main entry point for looking up element implementations.
|
| 555 |
+
The standard implementation uses the :class:`ParserBasedElementClassLookup`
|
| 556 |
+
to delegate to different lookup schemes for each parser.
|
| 557 |
+
|
| 558 |
+
.. warning::
|
| 559 |
+
|
| 560 |
+
This should only be changed by applications, not by library packages.
|
| 561 |
+
In most cases, parser specific lookups should be preferred,
|
| 562 |
+
which can be configured via
|
| 563 |
+
:meth:`~lxml.etree.XMLParser.set_element_class_lookup`
|
| 564 |
+
(and the same for HTML parsers).
|
| 565 |
+
|
| 566 |
+
Globally replacing the element class lookup by something other than a
|
| 567 |
+
:class:`ParserBasedElementClassLookup` will prevent parser specific lookup
|
| 568 |
+
schemes from working. Several tools rely on parser specific lookups,
|
| 569 |
+
including :mod:`lxml.html` and :mod:`lxml.objectify`.
|
| 570 |
+
"""
|
| 571 |
+
if lookup is None or lookup._lookup_function is NULL:
|
| 572 |
+
_setElementClassLookupFunction(NULL, None)
|
| 573 |
+
else:
|
| 574 |
+
_setElementClassLookupFunction(lookup._lookup_function, lookup)
|
| 575 |
+
|
| 576 |
+
# default setup: parser delegation
|
| 577 |
+
cdef ParserBasedElementClassLookup DEFAULT_ELEMENT_CLASS_LOOKUP
|
| 578 |
+
DEFAULT_ELEMENT_CLASS_LOOKUP = ParserBasedElementClassLookup()
|
| 579 |
+
|
| 580 |
+
set_element_class_lookup(DEFAULT_ELEMENT_CLASS_LOOKUP)
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/cleanup.pxi
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# functions for tree cleanup and removing elements from subtrees
|
| 2 |
+
|
| 3 |
+
def cleanup_namespaces(tree_or_element, top_nsmap=None, keep_ns_prefixes=None):
|
| 4 |
+
"""cleanup_namespaces(tree_or_element, top_nsmap=None, keep_ns_prefixes=None)
|
| 5 |
+
|
| 6 |
+
Remove all namespace declarations from a subtree that are not used
|
| 7 |
+
by any of the elements or attributes in that tree.
|
| 8 |
+
|
| 9 |
+
If a 'top_nsmap' is provided, it must be a mapping from prefixes
|
| 10 |
+
to namespace URIs. These namespaces will be declared on the top
|
| 11 |
+
element of the subtree before running the cleanup, which allows
|
| 12 |
+
moving namespace declarations to the top of the tree.
|
| 13 |
+
|
| 14 |
+
If a 'keep_ns_prefixes' is provided, it must be a list of prefixes.
|
| 15 |
+
These prefixes will not be removed as part of the cleanup.
|
| 16 |
+
"""
|
| 17 |
+
element = _rootNodeOrRaise(tree_or_element)
|
| 18 |
+
c_element = element._c_node
|
| 19 |
+
|
| 20 |
+
if top_nsmap:
|
| 21 |
+
doc = element._doc
|
| 22 |
+
# declare namespaces from nsmap, then apply them to the subtree
|
| 23 |
+
_setNodeNamespaces(c_element, doc, None, top_nsmap)
|
| 24 |
+
moveNodeToDocument(doc, c_element.doc, c_element)
|
| 25 |
+
|
| 26 |
+
keep_ns_prefixes = (
|
| 27 |
+
set([_utf8(prefix) for prefix in keep_ns_prefixes])
|
| 28 |
+
if keep_ns_prefixes else None)
|
| 29 |
+
|
| 30 |
+
_removeUnusedNamespaceDeclarations(c_element, keep_ns_prefixes)
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def strip_attributes(tree_or_element, *attribute_names):
|
| 34 |
+
"""strip_attributes(tree_or_element, *attribute_names)
|
| 35 |
+
|
| 36 |
+
Delete all attributes with the provided attribute names from an
|
| 37 |
+
Element (or ElementTree) and its descendants.
|
| 38 |
+
|
| 39 |
+
Attribute names can contain wildcards as in `_Element.iter`.
|
| 40 |
+
|
| 41 |
+
Example usage::
|
| 42 |
+
|
| 43 |
+
strip_attributes(root_element,
|
| 44 |
+
'simpleattr',
|
| 45 |
+
'{http://some/ns}attrname',
|
| 46 |
+
'{http://other/ns}*')
|
| 47 |
+
"""
|
| 48 |
+
cdef _MultiTagMatcher matcher
|
| 49 |
+
element = _rootNodeOrRaise(tree_or_element)
|
| 50 |
+
if not attribute_names:
|
| 51 |
+
return
|
| 52 |
+
|
| 53 |
+
matcher = _MultiTagMatcher.__new__(_MultiTagMatcher, attribute_names)
|
| 54 |
+
matcher.cacheTags(element._doc)
|
| 55 |
+
if matcher.rejectsAllAttributes():
|
| 56 |
+
return
|
| 57 |
+
_strip_attributes(element._c_node, matcher)
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
cdef _strip_attributes(xmlNode* c_node, _MultiTagMatcher matcher):
|
| 61 |
+
cdef xmlAttr* c_attr
|
| 62 |
+
cdef xmlAttr* c_next_attr
|
| 63 |
+
tree.BEGIN_FOR_EACH_ELEMENT_FROM(c_node, c_node, 1)
|
| 64 |
+
if c_node.type == tree.XML_ELEMENT_NODE:
|
| 65 |
+
c_attr = c_node.properties
|
| 66 |
+
while c_attr is not NULL:
|
| 67 |
+
c_next_attr = c_attr.next
|
| 68 |
+
if matcher.matchesAttribute(c_attr):
|
| 69 |
+
tree.xmlRemoveProp(c_attr)
|
| 70 |
+
c_attr = c_next_attr
|
| 71 |
+
tree.END_FOR_EACH_ELEMENT_FROM(c_node)
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
def strip_elements(tree_or_element, *tag_names, bint with_tail=True):
|
| 75 |
+
"""strip_elements(tree_or_element, *tag_names, with_tail=True)
|
| 76 |
+
|
| 77 |
+
Delete all elements with the provided tag names from a tree or
|
| 78 |
+
subtree. This will remove the elements and their entire subtree,
|
| 79 |
+
including all their attributes, text content and descendants. It
|
| 80 |
+
will also remove the tail text of the element unless you
|
| 81 |
+
explicitly set the ``with_tail`` keyword argument option to False.
|
| 82 |
+
|
| 83 |
+
Tag names can contain wildcards as in `_Element.iter`.
|
| 84 |
+
|
| 85 |
+
Note that this will not delete the element (or ElementTree root
|
| 86 |
+
element) that you passed even if it matches. It will only treat
|
| 87 |
+
its descendants. If you want to include the root element, check
|
| 88 |
+
its tag name directly before even calling this function.
|
| 89 |
+
|
| 90 |
+
Example usage::
|
| 91 |
+
|
| 92 |
+
strip_elements(some_element,
|
| 93 |
+
'simpletagname', # non-namespaced tag
|
| 94 |
+
'{http://some/ns}tagname', # namespaced tag
|
| 95 |
+
'{http://some/other/ns}*' # any tag from a namespace
|
| 96 |
+
lxml.etree.Comment # comments
|
| 97 |
+
)
|
| 98 |
+
"""
|
| 99 |
+
cdef _MultiTagMatcher matcher
|
| 100 |
+
doc = _documentOrRaise(tree_or_element)
|
| 101 |
+
element = _rootNodeOrRaise(tree_or_element)
|
| 102 |
+
if not tag_names:
|
| 103 |
+
return
|
| 104 |
+
|
| 105 |
+
matcher = _MultiTagMatcher.__new__(_MultiTagMatcher, tag_names)
|
| 106 |
+
matcher.cacheTags(doc)
|
| 107 |
+
if matcher.rejectsAll():
|
| 108 |
+
return
|
| 109 |
+
|
| 110 |
+
if isinstance(tree_or_element, _ElementTree):
|
| 111 |
+
# include PIs and comments next to the root node
|
| 112 |
+
if matcher.matchesType(tree.XML_COMMENT_NODE):
|
| 113 |
+
_removeSiblings(element._c_node, tree.XML_COMMENT_NODE, with_tail)
|
| 114 |
+
if matcher.matchesType(tree.XML_PI_NODE):
|
| 115 |
+
_removeSiblings(element._c_node, tree.XML_PI_NODE, with_tail)
|
| 116 |
+
_strip_elements(doc, element._c_node, matcher, with_tail)
|
| 117 |
+
|
| 118 |
+
cdef _strip_elements(_Document doc, xmlNode* c_node, _MultiTagMatcher matcher,
|
| 119 |
+
bint with_tail):
|
| 120 |
+
cdef xmlNode* c_child
|
| 121 |
+
cdef xmlNode* c_next
|
| 122 |
+
|
| 123 |
+
tree.BEGIN_FOR_EACH_ELEMENT_FROM(c_node, c_node, 1)
|
| 124 |
+
if c_node.type == tree.XML_ELEMENT_NODE:
|
| 125 |
+
# we run through the children here to prevent any problems
|
| 126 |
+
# with the tree iteration which would occur if we unlinked the
|
| 127 |
+
# c_node itself
|
| 128 |
+
c_child = _findChildForwards(c_node, 0)
|
| 129 |
+
while c_child is not NULL:
|
| 130 |
+
c_next = _nextElement(c_child)
|
| 131 |
+
if matcher.matches(c_child):
|
| 132 |
+
if c_child.type == tree.XML_ELEMENT_NODE:
|
| 133 |
+
if not with_tail:
|
| 134 |
+
tree.xmlUnlinkNode(c_child)
|
| 135 |
+
_removeNode(doc, c_child)
|
| 136 |
+
else:
|
| 137 |
+
if with_tail:
|
| 138 |
+
_removeText(c_child.next)
|
| 139 |
+
tree.xmlUnlinkNode(c_child)
|
| 140 |
+
attemptDeallocation(c_child)
|
| 141 |
+
c_child = c_next
|
| 142 |
+
tree.END_FOR_EACH_ELEMENT_FROM(c_node)
|
| 143 |
+
|
| 144 |
+
|
| 145 |
+
def strip_tags(tree_or_element, *tag_names):
|
| 146 |
+
"""strip_tags(tree_or_element, *tag_names)
|
| 147 |
+
|
| 148 |
+
Delete all elements with the provided tag names from a tree or
|
| 149 |
+
subtree. This will remove the elements and their attributes, but
|
| 150 |
+
*not* their text/tail content or descendants. Instead, it will
|
| 151 |
+
merge the text content and children of the element into its
|
| 152 |
+
parent.
|
| 153 |
+
|
| 154 |
+
Tag names can contain wildcards as in `_Element.iter`.
|
| 155 |
+
|
| 156 |
+
Note that this will not delete the element (or ElementTree root
|
| 157 |
+
element) that you passed even if it matches. It will only treat
|
| 158 |
+
its descendants.
|
| 159 |
+
|
| 160 |
+
Example usage::
|
| 161 |
+
|
| 162 |
+
strip_tags(some_element,
|
| 163 |
+
'simpletagname', # non-namespaced tag
|
| 164 |
+
'{http://some/ns}tagname', # namespaced tag
|
| 165 |
+
'{http://some/other/ns}*' # any tag from a namespace
|
| 166 |
+
Comment # comments (including their text!)
|
| 167 |
+
)
|
| 168 |
+
"""
|
| 169 |
+
cdef _MultiTagMatcher matcher
|
| 170 |
+
doc = _documentOrRaise(tree_or_element)
|
| 171 |
+
element = _rootNodeOrRaise(tree_or_element)
|
| 172 |
+
if not tag_names:
|
| 173 |
+
return
|
| 174 |
+
|
| 175 |
+
matcher = _MultiTagMatcher.__new__(_MultiTagMatcher, tag_names)
|
| 176 |
+
matcher.cacheTags(doc)
|
| 177 |
+
if matcher.rejectsAll():
|
| 178 |
+
return
|
| 179 |
+
|
| 180 |
+
if isinstance(tree_or_element, _ElementTree):
|
| 181 |
+
# include PIs and comments next to the root node
|
| 182 |
+
if matcher.matchesType(tree.XML_COMMENT_NODE):
|
| 183 |
+
_removeSiblings(element._c_node, tree.XML_COMMENT_NODE, 0)
|
| 184 |
+
if matcher.matchesType(tree.XML_PI_NODE):
|
| 185 |
+
_removeSiblings(element._c_node, tree.XML_PI_NODE, 0)
|
| 186 |
+
_strip_tags(doc, element._c_node, matcher)
|
| 187 |
+
|
| 188 |
+
cdef _strip_tags(_Document doc, xmlNode* c_node, _MultiTagMatcher matcher):
|
| 189 |
+
cdef xmlNode* c_child
|
| 190 |
+
cdef xmlNode* c_next
|
| 191 |
+
|
| 192 |
+
tree.BEGIN_FOR_EACH_ELEMENT_FROM(c_node, c_node, 1)
|
| 193 |
+
if c_node.type == tree.XML_ELEMENT_NODE:
|
| 194 |
+
# we run through the children here to prevent any problems
|
| 195 |
+
# with the tree iteration which would occur if we unlinked the
|
| 196 |
+
# c_node itself
|
| 197 |
+
c_child = _findChildForwards(c_node, 0)
|
| 198 |
+
while c_child is not NULL:
|
| 199 |
+
if not matcher.matches(c_child):
|
| 200 |
+
c_child = _nextElement(c_child)
|
| 201 |
+
continue
|
| 202 |
+
if c_child.type == tree.XML_ELEMENT_NODE:
|
| 203 |
+
c_next = _findChildForwards(c_child, 0) or _nextElement(c_child)
|
| 204 |
+
_replaceNodeByChildren(doc, c_child)
|
| 205 |
+
if not attemptDeallocation(c_child):
|
| 206 |
+
if c_child.nsDef is not NULL:
|
| 207 |
+
# make namespaces absolute
|
| 208 |
+
moveNodeToDocument(doc, doc._c_doc, c_child)
|
| 209 |
+
c_child = c_next
|
| 210 |
+
else:
|
| 211 |
+
c_next = _nextElement(c_child)
|
| 212 |
+
tree.xmlUnlinkNode(c_child)
|
| 213 |
+
attemptDeallocation(c_child)
|
| 214 |
+
c_child = c_next
|
| 215 |
+
tree.END_FOR_EACH_ELEMENT_FROM(c_node)
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/cssselect.py
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""CSS Selectors based on XPath.
|
| 2 |
+
|
| 3 |
+
This module supports selecting XML/HTML tags based on CSS selectors.
|
| 4 |
+
See the `CSSSelector` class for details.
|
| 5 |
+
|
| 6 |
+
This is a thin wrapper around cssselect 0.7 or later.
|
| 7 |
+
"""
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
from . import etree
|
| 11 |
+
try:
|
| 12 |
+
import cssselect as external_cssselect
|
| 13 |
+
except ImportError:
|
| 14 |
+
raise ImportError(
|
| 15 |
+
'cssselect does not seem to be installed. '
|
| 16 |
+
'See https://pypi.org/project/cssselect/')
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
SelectorSyntaxError = external_cssselect.SelectorSyntaxError
|
| 20 |
+
ExpressionError = external_cssselect.ExpressionError
|
| 21 |
+
SelectorError = external_cssselect.SelectorError
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
__all__ = ['SelectorSyntaxError', 'ExpressionError', 'SelectorError',
|
| 25 |
+
'CSSSelector']
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
class LxmlTranslator(external_cssselect.GenericTranslator):
|
| 29 |
+
"""
|
| 30 |
+
A custom CSS selector to XPath translator with lxml-specific extensions.
|
| 31 |
+
"""
|
| 32 |
+
def xpath_contains_function(self, xpath, function):
|
| 33 |
+
# Defined there, removed in later drafts:
|
| 34 |
+
# http://www.w3.org/TR/2001/CR-css3-selectors-20011113/#content-selectors
|
| 35 |
+
if function.argument_types() not in (['STRING'], ['IDENT']):
|
| 36 |
+
raise ExpressionError(
|
| 37 |
+
"Expected a single string or ident for :contains(), got %r"
|
| 38 |
+
% function.arguments)
|
| 39 |
+
value = function.arguments[0].value
|
| 40 |
+
return xpath.add_condition(
|
| 41 |
+
'contains(__lxml_internal_css:lower-case(string(.)), %s)'
|
| 42 |
+
% self.xpath_literal(value.lower()))
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
class LxmlHTMLTranslator(LxmlTranslator, external_cssselect.HTMLTranslator):
|
| 46 |
+
"""
|
| 47 |
+
lxml extensions + HTML support.
|
| 48 |
+
"""
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
def _make_lower_case(context, s):
|
| 52 |
+
return s.lower()
|
| 53 |
+
|
| 54 |
+
ns = etree.FunctionNamespace('http://codespeak.net/lxml/css/')
|
| 55 |
+
ns.prefix = '__lxml_internal_css'
|
| 56 |
+
ns['lower-case'] = _make_lower_case
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
class CSSSelector(etree.XPath):
|
| 60 |
+
"""A CSS selector.
|
| 61 |
+
|
| 62 |
+
Usage::
|
| 63 |
+
|
| 64 |
+
>>> from lxml import etree, cssselect
|
| 65 |
+
>>> select = cssselect.CSSSelector("a tag > child")
|
| 66 |
+
|
| 67 |
+
>>> root = etree.XML("<a><b><c/><tag><child>TEXT</child></tag></b></a>")
|
| 68 |
+
>>> [ el.tag for el in select(root) ]
|
| 69 |
+
['child']
|
| 70 |
+
|
| 71 |
+
To use CSS namespaces, you need to pass a prefix-to-namespace
|
| 72 |
+
mapping as ``namespaces`` keyword argument::
|
| 73 |
+
|
| 74 |
+
>>> rdfns = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'
|
| 75 |
+
>>> select_ns = cssselect.CSSSelector('root > rdf|Description',
|
| 76 |
+
... namespaces={'rdf': rdfns})
|
| 77 |
+
|
| 78 |
+
>>> rdf = etree.XML((
|
| 79 |
+
... '<root xmlns:rdf="%s">'
|
| 80 |
+
... '<rdf:Description>blah</rdf:Description>'
|
| 81 |
+
... '</root>') % rdfns)
|
| 82 |
+
>>> [(el.tag, el.text) for el in select_ns(rdf)]
|
| 83 |
+
[('{http://www.w3.org/1999/02/22-rdf-syntax-ns#}Description', 'blah')]
|
| 84 |
+
|
| 85 |
+
"""
|
| 86 |
+
def __init__(self, css, namespaces=None, translator='xml'):
|
| 87 |
+
if translator == 'xml':
|
| 88 |
+
translator = LxmlTranslator()
|
| 89 |
+
elif translator == 'html':
|
| 90 |
+
translator = LxmlHTMLTranslator()
|
| 91 |
+
elif translator == 'xhtml':
|
| 92 |
+
translator = LxmlHTMLTranslator(xhtml=True)
|
| 93 |
+
path = translator.css_to_xpath(css)
|
| 94 |
+
super().__init__(path, namespaces=namespaces)
|
| 95 |
+
self.css = css
|
| 96 |
+
|
| 97 |
+
def __repr__(self):
|
| 98 |
+
return '<%s %x for %r>' % (
|
| 99 |
+
self.__class__.__name__,
|
| 100 |
+
abs(id(self)),
|
| 101 |
+
self.css)
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/doctestcompare.py
ADDED
|
@@ -0,0 +1,488 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
lxml-based doctest output comparison.
|
| 3 |
+
|
| 4 |
+
Note: normally, you should just import the `lxml.usedoctest` and
|
| 5 |
+
`lxml.html.usedoctest` modules from within a doctest, instead of this
|
| 6 |
+
one::
|
| 7 |
+
|
| 8 |
+
>>> import lxml.usedoctest # for XML output
|
| 9 |
+
|
| 10 |
+
>>> import lxml.html.usedoctest # for HTML output
|
| 11 |
+
|
| 12 |
+
To use this module directly, you must call ``lxmldoctest.install()``,
|
| 13 |
+
which will cause doctest to use this in all subsequent calls.
|
| 14 |
+
|
| 15 |
+
This changes the way output is checked and comparisons are made for
|
| 16 |
+
XML or HTML-like content.
|
| 17 |
+
|
| 18 |
+
XML or HTML content is noticed because the example starts with ``<``
|
| 19 |
+
(it's HTML if it starts with ``<html``). You can also use the
|
| 20 |
+
``PARSE_HTML`` and ``PARSE_XML`` flags to force parsing.
|
| 21 |
+
|
| 22 |
+
Some rough wildcard-like things are allowed. Whitespace is generally
|
| 23 |
+
ignored (except in attributes). In text (attributes and text in the
|
| 24 |
+
body) you can use ``...`` as a wildcard. In an example it also
|
| 25 |
+
matches any trailing tags in the element, though it does not match
|
| 26 |
+
leading tags. You may create a tag ``<any>`` or include an ``any``
|
| 27 |
+
attribute in the tag. An ``any`` tag matches any tag, while the
|
| 28 |
+
attribute matches any and all attributes.
|
| 29 |
+
|
| 30 |
+
When a match fails, the reformatted example and gotten text is
|
| 31 |
+
displayed (indented), and a rough diff-like output is given. Anything
|
| 32 |
+
marked with ``+`` is in the output but wasn't supposed to be, and
|
| 33 |
+
similarly ``-`` means its in the example but wasn't in the output.
|
| 34 |
+
|
| 35 |
+
You can disable parsing on one line with ``# doctest:+NOPARSE_MARKUP``
|
| 36 |
+
"""
|
| 37 |
+
|
| 38 |
+
from lxml import etree
|
| 39 |
+
import sys
|
| 40 |
+
import re
|
| 41 |
+
import doctest
|
| 42 |
+
try:
|
| 43 |
+
from html import escape as html_escape
|
| 44 |
+
except ImportError:
|
| 45 |
+
from cgi import escape as html_escape
|
| 46 |
+
|
| 47 |
+
__all__ = ['PARSE_HTML', 'PARSE_XML', 'NOPARSE_MARKUP', 'LXMLOutputChecker',
|
| 48 |
+
'LHTMLOutputChecker', 'install', 'temp_install']
|
| 49 |
+
|
| 50 |
+
PARSE_HTML = doctest.register_optionflag('PARSE_HTML')
|
| 51 |
+
PARSE_XML = doctest.register_optionflag('PARSE_XML')
|
| 52 |
+
NOPARSE_MARKUP = doctest.register_optionflag('NOPARSE_MARKUP')
|
| 53 |
+
|
| 54 |
+
OutputChecker = doctest.OutputChecker
|
| 55 |
+
|
| 56 |
+
def strip(v):
|
| 57 |
+
if v is None:
|
| 58 |
+
return None
|
| 59 |
+
else:
|
| 60 |
+
return v.strip()
|
| 61 |
+
|
| 62 |
+
def norm_whitespace(v):
|
| 63 |
+
return _norm_whitespace_re.sub(' ', v)
|
| 64 |
+
|
| 65 |
+
_html_parser = etree.HTMLParser(recover=False, remove_blank_text=True)
|
| 66 |
+
|
| 67 |
+
def html_fromstring(html):
|
| 68 |
+
return etree.fromstring(html, _html_parser)
|
| 69 |
+
|
| 70 |
+
# We use this to distinguish repr()s from elements:
|
| 71 |
+
_repr_re = re.compile(r'^<[^>]+ (at|object) ')
|
| 72 |
+
_norm_whitespace_re = re.compile(r'[ \t\n][ \t\n]+')
|
| 73 |
+
|
| 74 |
+
class LXMLOutputChecker(OutputChecker):
|
| 75 |
+
|
| 76 |
+
empty_tags = (
|
| 77 |
+
'param', 'img', 'area', 'br', 'basefont', 'input',
|
| 78 |
+
'base', 'meta', 'link', 'col')
|
| 79 |
+
|
| 80 |
+
def get_default_parser(self):
|
| 81 |
+
return etree.XML
|
| 82 |
+
|
| 83 |
+
def check_output(self, want, got, optionflags):
|
| 84 |
+
alt_self = getattr(self, '_temp_override_self', None)
|
| 85 |
+
if alt_self is not None:
|
| 86 |
+
super_method = self._temp_call_super_check_output
|
| 87 |
+
self = alt_self
|
| 88 |
+
else:
|
| 89 |
+
super_method = OutputChecker.check_output
|
| 90 |
+
parser = self.get_parser(want, got, optionflags)
|
| 91 |
+
if not parser:
|
| 92 |
+
return super_method(
|
| 93 |
+
self, want, got, optionflags)
|
| 94 |
+
try:
|
| 95 |
+
want_doc = parser(want)
|
| 96 |
+
except etree.XMLSyntaxError:
|
| 97 |
+
return False
|
| 98 |
+
try:
|
| 99 |
+
got_doc = parser(got)
|
| 100 |
+
except etree.XMLSyntaxError:
|
| 101 |
+
return False
|
| 102 |
+
return self.compare_docs(want_doc, got_doc)
|
| 103 |
+
|
| 104 |
+
def get_parser(self, want, got, optionflags):
|
| 105 |
+
parser = None
|
| 106 |
+
if NOPARSE_MARKUP & optionflags:
|
| 107 |
+
return None
|
| 108 |
+
if PARSE_HTML & optionflags:
|
| 109 |
+
parser = html_fromstring
|
| 110 |
+
elif PARSE_XML & optionflags:
|
| 111 |
+
parser = etree.XML
|
| 112 |
+
elif (want.strip().lower().startswith('<html')
|
| 113 |
+
and got.strip().startswith('<html')):
|
| 114 |
+
parser = html_fromstring
|
| 115 |
+
elif (self._looks_like_markup(want)
|
| 116 |
+
and self._looks_like_markup(got)):
|
| 117 |
+
parser = self.get_default_parser()
|
| 118 |
+
return parser
|
| 119 |
+
|
| 120 |
+
def _looks_like_markup(self, s):
|
| 121 |
+
s = s.strip()
|
| 122 |
+
return (s.startswith('<')
|
| 123 |
+
and not _repr_re.search(s))
|
| 124 |
+
|
| 125 |
+
def compare_docs(self, want, got):
|
| 126 |
+
if not self.tag_compare(want.tag, got.tag):
|
| 127 |
+
return False
|
| 128 |
+
if not self.text_compare(want.text, got.text, True):
|
| 129 |
+
return False
|
| 130 |
+
if not self.text_compare(want.tail, got.tail, True):
|
| 131 |
+
return False
|
| 132 |
+
if 'any' not in want.attrib:
|
| 133 |
+
want_keys = sorted(want.attrib.keys())
|
| 134 |
+
got_keys = sorted(got.attrib.keys())
|
| 135 |
+
if want_keys != got_keys:
|
| 136 |
+
return False
|
| 137 |
+
for key in want_keys:
|
| 138 |
+
if not self.text_compare(want.attrib[key], got.attrib[key], False):
|
| 139 |
+
return False
|
| 140 |
+
if want.text != '...' or len(want):
|
| 141 |
+
want_children = list(want)
|
| 142 |
+
got_children = list(got)
|
| 143 |
+
while want_children or got_children:
|
| 144 |
+
if not want_children or not got_children:
|
| 145 |
+
return False
|
| 146 |
+
want_first = want_children.pop(0)
|
| 147 |
+
got_first = got_children.pop(0)
|
| 148 |
+
if not self.compare_docs(want_first, got_first):
|
| 149 |
+
return False
|
| 150 |
+
if not got_children and want_first.tail == '...':
|
| 151 |
+
break
|
| 152 |
+
return True
|
| 153 |
+
|
| 154 |
+
def text_compare(self, want, got, strip):
|
| 155 |
+
want = want or ''
|
| 156 |
+
got = got or ''
|
| 157 |
+
if strip:
|
| 158 |
+
want = norm_whitespace(want).strip()
|
| 159 |
+
got = norm_whitespace(got).strip()
|
| 160 |
+
want = '^%s$' % re.escape(want)
|
| 161 |
+
want = want.replace(r'\.\.\.', '.*')
|
| 162 |
+
if re.search(want, got):
|
| 163 |
+
return True
|
| 164 |
+
else:
|
| 165 |
+
return False
|
| 166 |
+
|
| 167 |
+
def tag_compare(self, want, got):
|
| 168 |
+
if want == 'any':
|
| 169 |
+
return True
|
| 170 |
+
if (not isinstance(want, (str, bytes))
|
| 171 |
+
or not isinstance(got, (str, bytes))):
|
| 172 |
+
return want == got
|
| 173 |
+
want = want or ''
|
| 174 |
+
got = got or ''
|
| 175 |
+
if want.startswith('{...}'):
|
| 176 |
+
# Ellipsis on the namespace
|
| 177 |
+
return want.split('}')[-1] == got.split('}')[-1]
|
| 178 |
+
else:
|
| 179 |
+
return want == got
|
| 180 |
+
|
| 181 |
+
def output_difference(self, example, got, optionflags):
|
| 182 |
+
want = example.want
|
| 183 |
+
parser = self.get_parser(want, got, optionflags)
|
| 184 |
+
errors = []
|
| 185 |
+
if parser is not None:
|
| 186 |
+
try:
|
| 187 |
+
want_doc = parser(want)
|
| 188 |
+
except etree.XMLSyntaxError:
|
| 189 |
+
e = sys.exc_info()[1]
|
| 190 |
+
errors.append('In example: %s' % e)
|
| 191 |
+
try:
|
| 192 |
+
got_doc = parser(got)
|
| 193 |
+
except etree.XMLSyntaxError:
|
| 194 |
+
e = sys.exc_info()[1]
|
| 195 |
+
errors.append('In actual output: %s' % e)
|
| 196 |
+
if parser is None or errors:
|
| 197 |
+
value = OutputChecker.output_difference(
|
| 198 |
+
self, example, got, optionflags)
|
| 199 |
+
if errors:
|
| 200 |
+
errors.append(value)
|
| 201 |
+
return '\n'.join(errors)
|
| 202 |
+
else:
|
| 203 |
+
return value
|
| 204 |
+
html = parser is html_fromstring
|
| 205 |
+
diff_parts = ['Expected:',
|
| 206 |
+
self.format_doc(want_doc, html, 2),
|
| 207 |
+
'Got:',
|
| 208 |
+
self.format_doc(got_doc, html, 2),
|
| 209 |
+
'Diff:',
|
| 210 |
+
self.collect_diff(want_doc, got_doc, html, 2)]
|
| 211 |
+
return '\n'.join(diff_parts)
|
| 212 |
+
|
| 213 |
+
def html_empty_tag(self, el, html=True):
|
| 214 |
+
if not html:
|
| 215 |
+
return False
|
| 216 |
+
if el.tag not in self.empty_tags:
|
| 217 |
+
return False
|
| 218 |
+
if el.text or len(el):
|
| 219 |
+
# This shouldn't happen (contents in an empty tag)
|
| 220 |
+
return False
|
| 221 |
+
return True
|
| 222 |
+
|
| 223 |
+
def format_doc(self, doc, html, indent, prefix=''):
|
| 224 |
+
parts = []
|
| 225 |
+
if not len(doc):
|
| 226 |
+
# No children...
|
| 227 |
+
parts.append(' '*indent)
|
| 228 |
+
parts.append(prefix)
|
| 229 |
+
parts.append(self.format_tag(doc))
|
| 230 |
+
if not self.html_empty_tag(doc, html):
|
| 231 |
+
if strip(doc.text):
|
| 232 |
+
parts.append(self.format_text(doc.text))
|
| 233 |
+
parts.append(self.format_end_tag(doc))
|
| 234 |
+
if strip(doc.tail):
|
| 235 |
+
parts.append(self.format_text(doc.tail))
|
| 236 |
+
parts.append('\n')
|
| 237 |
+
return ''.join(parts)
|
| 238 |
+
parts.append(' '*indent)
|
| 239 |
+
parts.append(prefix)
|
| 240 |
+
parts.append(self.format_tag(doc))
|
| 241 |
+
if not self.html_empty_tag(doc, html):
|
| 242 |
+
parts.append('\n')
|
| 243 |
+
if strip(doc.text):
|
| 244 |
+
parts.append(' '*indent)
|
| 245 |
+
parts.append(self.format_text(doc.text))
|
| 246 |
+
parts.append('\n')
|
| 247 |
+
for el in doc:
|
| 248 |
+
parts.append(self.format_doc(el, html, indent+2))
|
| 249 |
+
parts.append(' '*indent)
|
| 250 |
+
parts.append(self.format_end_tag(doc))
|
| 251 |
+
parts.append('\n')
|
| 252 |
+
if strip(doc.tail):
|
| 253 |
+
parts.append(' '*indent)
|
| 254 |
+
parts.append(self.format_text(doc.tail))
|
| 255 |
+
parts.append('\n')
|
| 256 |
+
return ''.join(parts)
|
| 257 |
+
|
| 258 |
+
def format_text(self, text, strip=True):
|
| 259 |
+
if text is None:
|
| 260 |
+
return ''
|
| 261 |
+
if strip:
|
| 262 |
+
text = text.strip()
|
| 263 |
+
return html_escape(text, 1)
|
| 264 |
+
|
| 265 |
+
def format_tag(self, el):
|
| 266 |
+
attrs = []
|
| 267 |
+
if isinstance(el, etree.CommentBase):
|
| 268 |
+
# FIXME: probably PIs should be handled specially too?
|
| 269 |
+
return '<!--'
|
| 270 |
+
for name, value in sorted(el.attrib.items()):
|
| 271 |
+
attrs.append('%s="%s"' % (name, self.format_text(value, False)))
|
| 272 |
+
if not attrs:
|
| 273 |
+
return '<%s>' % el.tag
|
| 274 |
+
return '<%s %s>' % (el.tag, ' '.join(attrs))
|
| 275 |
+
|
| 276 |
+
def format_end_tag(self, el):
|
| 277 |
+
if isinstance(el, etree.CommentBase):
|
| 278 |
+
# FIXME: probably PIs should be handled specially too?
|
| 279 |
+
return '-->'
|
| 280 |
+
return '</%s>' % el.tag
|
| 281 |
+
|
| 282 |
+
def collect_diff(self, want, got, html, indent):
|
| 283 |
+
parts = []
|
| 284 |
+
if not len(want) and not len(got):
|
| 285 |
+
parts.append(' '*indent)
|
| 286 |
+
parts.append(self.collect_diff_tag(want, got))
|
| 287 |
+
if not self.html_empty_tag(got, html):
|
| 288 |
+
parts.append(self.collect_diff_text(want.text, got.text))
|
| 289 |
+
parts.append(self.collect_diff_end_tag(want, got))
|
| 290 |
+
parts.append(self.collect_diff_text(want.tail, got.tail))
|
| 291 |
+
parts.append('\n')
|
| 292 |
+
return ''.join(parts)
|
| 293 |
+
parts.append(' '*indent)
|
| 294 |
+
parts.append(self.collect_diff_tag(want, got))
|
| 295 |
+
parts.append('\n')
|
| 296 |
+
if strip(want.text) or strip(got.text):
|
| 297 |
+
parts.append(' '*indent)
|
| 298 |
+
parts.append(self.collect_diff_text(want.text, got.text))
|
| 299 |
+
parts.append('\n')
|
| 300 |
+
want_children = list(want)
|
| 301 |
+
got_children = list(got)
|
| 302 |
+
while want_children or got_children:
|
| 303 |
+
if not want_children:
|
| 304 |
+
parts.append(self.format_doc(got_children.pop(0), html, indent+2, '+'))
|
| 305 |
+
continue
|
| 306 |
+
if not got_children:
|
| 307 |
+
parts.append(self.format_doc(want_children.pop(0), html, indent+2, '-'))
|
| 308 |
+
continue
|
| 309 |
+
parts.append(self.collect_diff(
|
| 310 |
+
want_children.pop(0), got_children.pop(0), html, indent+2))
|
| 311 |
+
parts.append(' '*indent)
|
| 312 |
+
parts.append(self.collect_diff_end_tag(want, got))
|
| 313 |
+
parts.append('\n')
|
| 314 |
+
if strip(want.tail) or strip(got.tail):
|
| 315 |
+
parts.append(' '*indent)
|
| 316 |
+
parts.append(self.collect_diff_text(want.tail, got.tail))
|
| 317 |
+
parts.append('\n')
|
| 318 |
+
return ''.join(parts)
|
| 319 |
+
|
| 320 |
+
def collect_diff_tag(self, want, got):
|
| 321 |
+
if not self.tag_compare(want.tag, got.tag):
|
| 322 |
+
tag = '%s (got: %s)' % (want.tag, got.tag)
|
| 323 |
+
else:
|
| 324 |
+
tag = got.tag
|
| 325 |
+
attrs = []
|
| 326 |
+
any = want.tag == 'any' or 'any' in want.attrib
|
| 327 |
+
for name, value in sorted(got.attrib.items()):
|
| 328 |
+
if name not in want.attrib and not any:
|
| 329 |
+
attrs.append('+%s="%s"' % (name, self.format_text(value, False)))
|
| 330 |
+
else:
|
| 331 |
+
if name in want.attrib:
|
| 332 |
+
text = self.collect_diff_text(want.attrib[name], value, False)
|
| 333 |
+
else:
|
| 334 |
+
text = self.format_text(value, False)
|
| 335 |
+
attrs.append('%s="%s"' % (name, text))
|
| 336 |
+
if not any:
|
| 337 |
+
for name, value in sorted(want.attrib.items()):
|
| 338 |
+
if name in got.attrib:
|
| 339 |
+
continue
|
| 340 |
+
attrs.append('-%s="%s"' % (name, self.format_text(value, False)))
|
| 341 |
+
if attrs:
|
| 342 |
+
tag = '<%s %s>' % (tag, ' '.join(attrs))
|
| 343 |
+
else:
|
| 344 |
+
tag = '<%s>' % tag
|
| 345 |
+
return tag
|
| 346 |
+
|
| 347 |
+
def collect_diff_end_tag(self, want, got):
|
| 348 |
+
if want.tag != got.tag:
|
| 349 |
+
tag = '%s (got: %s)' % (want.tag, got.tag)
|
| 350 |
+
else:
|
| 351 |
+
tag = got.tag
|
| 352 |
+
return '</%s>' % tag
|
| 353 |
+
|
| 354 |
+
def collect_diff_text(self, want, got, strip=True):
|
| 355 |
+
if self.text_compare(want, got, strip):
|
| 356 |
+
if not got:
|
| 357 |
+
return ''
|
| 358 |
+
return self.format_text(got, strip)
|
| 359 |
+
text = '%s (got: %s)' % (want, got)
|
| 360 |
+
return self.format_text(text, strip)
|
| 361 |
+
|
| 362 |
+
class LHTMLOutputChecker(LXMLOutputChecker):
|
| 363 |
+
def get_default_parser(self):
|
| 364 |
+
return html_fromstring
|
| 365 |
+
|
| 366 |
+
def install(html=False):
|
| 367 |
+
"""
|
| 368 |
+
Install doctestcompare for all future doctests.
|
| 369 |
+
|
| 370 |
+
If html is true, then by default the HTML parser will be used;
|
| 371 |
+
otherwise the XML parser is used.
|
| 372 |
+
"""
|
| 373 |
+
if html:
|
| 374 |
+
doctest.OutputChecker = LHTMLOutputChecker
|
| 375 |
+
else:
|
| 376 |
+
doctest.OutputChecker = LXMLOutputChecker
|
| 377 |
+
|
| 378 |
+
def temp_install(html=False, del_module=None):
|
| 379 |
+
"""
|
| 380 |
+
Use this *inside* a doctest to enable this checker for this
|
| 381 |
+
doctest only.
|
| 382 |
+
|
| 383 |
+
If html is true, then by default the HTML parser will be used;
|
| 384 |
+
otherwise the XML parser is used.
|
| 385 |
+
"""
|
| 386 |
+
if html:
|
| 387 |
+
Checker = LHTMLOutputChecker
|
| 388 |
+
else:
|
| 389 |
+
Checker = LXMLOutputChecker
|
| 390 |
+
frame = _find_doctest_frame()
|
| 391 |
+
dt_self = frame.f_locals['self']
|
| 392 |
+
checker = Checker()
|
| 393 |
+
old_checker = dt_self._checker
|
| 394 |
+
dt_self._checker = checker
|
| 395 |
+
# The unfortunate thing is that there is a local variable 'check'
|
| 396 |
+
# in the function that runs the doctests, that is a bound method
|
| 397 |
+
# into the output checker. We have to update that. We can't
|
| 398 |
+
# modify the frame, so we have to modify the object in place. The
|
| 399 |
+
# only way to do this is to actually change the func_code
|
| 400 |
+
# attribute of the method. We change it, and then wait for
|
| 401 |
+
# __record_outcome to be run, which signals the end of the __run
|
| 402 |
+
# method, at which point we restore the previous check_output
|
| 403 |
+
# implementation.
|
| 404 |
+
check_func = frame.f_locals['check'].__func__
|
| 405 |
+
checker_check_func = checker.check_output.__func__
|
| 406 |
+
# Because we can't patch up func_globals, this is the only global
|
| 407 |
+
# in check_output that we care about:
|
| 408 |
+
doctest.etree = etree
|
| 409 |
+
_RestoreChecker(dt_self, old_checker, checker,
|
| 410 |
+
check_func, checker_check_func,
|
| 411 |
+
del_module)
|
| 412 |
+
|
| 413 |
+
class _RestoreChecker:
|
| 414 |
+
def __init__(self, dt_self, old_checker, new_checker, check_func, clone_func,
|
| 415 |
+
del_module):
|
| 416 |
+
self.dt_self = dt_self
|
| 417 |
+
self.checker = old_checker
|
| 418 |
+
self.checker._temp_call_super_check_output = self.call_super
|
| 419 |
+
self.checker._temp_override_self = new_checker
|
| 420 |
+
self.check_func = check_func
|
| 421 |
+
self.clone_func = clone_func
|
| 422 |
+
self.del_module = del_module
|
| 423 |
+
self.install_clone()
|
| 424 |
+
self.install_dt_self()
|
| 425 |
+
def install_clone(self):
|
| 426 |
+
self.func_code = self.check_func.__code__
|
| 427 |
+
self.func_globals = self.check_func.__globals__
|
| 428 |
+
self.check_func.__code__ = self.clone_func.__code__
|
| 429 |
+
def uninstall_clone(self):
|
| 430 |
+
self.check_func.__code__ = self.func_code
|
| 431 |
+
def install_dt_self(self):
|
| 432 |
+
self.prev_func = self.dt_self._DocTestRunner__record_outcome
|
| 433 |
+
self.dt_self._DocTestRunner__record_outcome = self
|
| 434 |
+
def uninstall_dt_self(self):
|
| 435 |
+
self.dt_self._DocTestRunner__record_outcome = self.prev_func
|
| 436 |
+
def uninstall_module(self):
|
| 437 |
+
if self.del_module:
|
| 438 |
+
import sys
|
| 439 |
+
del sys.modules[self.del_module]
|
| 440 |
+
if '.' in self.del_module:
|
| 441 |
+
package, module = self.del_module.rsplit('.', 1)
|
| 442 |
+
package_mod = sys.modules[package]
|
| 443 |
+
delattr(package_mod, module)
|
| 444 |
+
def __call__(self, *args, **kw):
|
| 445 |
+
self.uninstall_clone()
|
| 446 |
+
self.uninstall_dt_self()
|
| 447 |
+
del self.checker._temp_override_self
|
| 448 |
+
del self.checker._temp_call_super_check_output
|
| 449 |
+
result = self.prev_func(*args, **kw)
|
| 450 |
+
self.uninstall_module()
|
| 451 |
+
return result
|
| 452 |
+
def call_super(self, *args, **kw):
|
| 453 |
+
self.uninstall_clone()
|
| 454 |
+
try:
|
| 455 |
+
return self.check_func(*args, **kw)
|
| 456 |
+
finally:
|
| 457 |
+
self.install_clone()
|
| 458 |
+
|
| 459 |
+
def _find_doctest_frame():
|
| 460 |
+
import sys
|
| 461 |
+
frame = sys._getframe(1)
|
| 462 |
+
while frame:
|
| 463 |
+
l = frame.f_locals
|
| 464 |
+
if 'BOOM' in l:
|
| 465 |
+
# Sign of doctest
|
| 466 |
+
return frame
|
| 467 |
+
frame = frame.f_back
|
| 468 |
+
raise LookupError(
|
| 469 |
+
"Could not find doctest (only use this function *inside* a doctest)")
|
| 470 |
+
|
| 471 |
+
__test__ = {
|
| 472 |
+
'basic': '''
|
| 473 |
+
>>> temp_install()
|
| 474 |
+
>>> print """<xml a="1" b="2">stuff</xml>"""
|
| 475 |
+
<xml b="2" a="1">...</xml>
|
| 476 |
+
>>> print """<xml xmlns="http://example.com"><tag attr="bar" /></xml>"""
|
| 477 |
+
<xml xmlns="...">
|
| 478 |
+
<tag attr="..." />
|
| 479 |
+
</xml>
|
| 480 |
+
>>> print """<xml>blahblahblah<foo /></xml>""" # doctest: +NOPARSE_MARKUP, +ELLIPSIS
|
| 481 |
+
<xml>...foo /></xml>
|
| 482 |
+
'''}
|
| 483 |
+
|
| 484 |
+
if __name__ == '__main__':
|
| 485 |
+
import doctest
|
| 486 |
+
doctest.testmod()
|
| 487 |
+
|
| 488 |
+
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/etree.h
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* Generated by Cython 3.1.4 */
|
| 2 |
+
|
| 3 |
+
#ifndef __PYX_HAVE__lxml__etree
|
| 4 |
+
#define __PYX_HAVE__lxml__etree
|
| 5 |
+
|
| 6 |
+
#include "Python.h"
|
| 7 |
+
struct LxmlDocument;
|
| 8 |
+
struct LxmlElement;
|
| 9 |
+
struct LxmlElementTree;
|
| 10 |
+
struct LxmlElementTagMatcher;
|
| 11 |
+
struct LxmlElementIterator;
|
| 12 |
+
struct LxmlElementBase;
|
| 13 |
+
struct LxmlElementClassLookup;
|
| 14 |
+
struct LxmlFallbackElementClassLookup;
|
| 15 |
+
|
| 16 |
+
/* "lxml/etree.pyx":451
|
| 17 |
+
*
|
| 18 |
+
* # type of a function that steps from node to node
|
| 19 |
+
* ctypedef public xmlNode* (*_node_to_node_function)(xmlNode*) # <<<<<<<<<<<<<<
|
| 20 |
+
*
|
| 21 |
+
*
|
| 22 |
+
*/
|
| 23 |
+
typedef xmlNode *(*_node_to_node_function)(xmlNode *);
|
| 24 |
+
|
| 25 |
+
/* "lxml/etree.pyx":465
|
| 26 |
+
* # Public Python API
|
| 27 |
+
*
|
| 28 |
+
* @cython.final # <<<<<<<<<<<<<<
|
| 29 |
+
* @cython.freelist(8)
|
| 30 |
+
* cdef public class _Document [ type LxmlDocumentType, object LxmlDocument ]:
|
| 31 |
+
*/
|
| 32 |
+
struct LxmlDocument {
|
| 33 |
+
PyObject_HEAD
|
| 34 |
+
struct __pyx_vtabstruct_4lxml_5etree__Document *__pyx_vtab;
|
| 35 |
+
int _ns_counter;
|
| 36 |
+
PyObject *_prefix_tail;
|
| 37 |
+
xmlDoc *_c_doc;
|
| 38 |
+
struct __pyx_obj_4lxml_5etree__BaseParser *_parser;
|
| 39 |
+
};
|
| 40 |
+
|
| 41 |
+
/* "lxml/etree.pyx":817
|
| 42 |
+
*
|
| 43 |
+
*
|
| 44 |
+
* @cython.no_gc_clear # <<<<<<<<<<<<<<
|
| 45 |
+
* cdef public class _Element [ type LxmlElementType, object LxmlElement ]:
|
| 46 |
+
* """Element class.
|
| 47 |
+
*/
|
| 48 |
+
struct LxmlElement {
|
| 49 |
+
PyObject_HEAD
|
| 50 |
+
struct LxmlDocument *_doc;
|
| 51 |
+
xmlNode *_c_node;
|
| 52 |
+
PyObject *_tag;
|
| 53 |
+
};
|
| 54 |
+
|
| 55 |
+
/* "lxml/etree.pyx":1991
|
| 56 |
+
*
|
| 57 |
+
*
|
| 58 |
+
* cdef public class _ElementTree [ type LxmlElementTreeType, # <<<<<<<<<<<<<<
|
| 59 |
+
* object LxmlElementTree ]:
|
| 60 |
+
* cdef _Document _doc
|
| 61 |
+
*/
|
| 62 |
+
struct LxmlElementTree {
|
| 63 |
+
PyObject_HEAD
|
| 64 |
+
struct __pyx_vtabstruct_4lxml_5etree__ElementTree *__pyx_vtab;
|
| 65 |
+
struct LxmlDocument *_doc;
|
| 66 |
+
struct LxmlElement *_context_node;
|
| 67 |
+
};
|
| 68 |
+
|
| 69 |
+
/* "lxml/etree.pyx":2765
|
| 70 |
+
*
|
| 71 |
+
*
|
| 72 |
+
* cdef public class _ElementTagMatcher [ object LxmlElementTagMatcher, # <<<<<<<<<<<<<<
|
| 73 |
+
* type LxmlElementTagMatcherType ]:
|
| 74 |
+
* """
|
| 75 |
+
*/
|
| 76 |
+
struct LxmlElementTagMatcher {
|
| 77 |
+
PyObject_HEAD
|
| 78 |
+
struct __pyx_vtabstruct_4lxml_5etree__ElementTagMatcher *__pyx_vtab;
|
| 79 |
+
PyObject *_pystrings;
|
| 80 |
+
int _node_type;
|
| 81 |
+
char *_href;
|
| 82 |
+
char *_name;
|
| 83 |
+
};
|
| 84 |
+
|
| 85 |
+
/* "lxml/etree.pyx":2796
|
| 86 |
+
* self._name = NULL
|
| 87 |
+
*
|
| 88 |
+
* cdef public class _ElementIterator(_ElementTagMatcher) [ # <<<<<<<<<<<<<<
|
| 89 |
+
* object LxmlElementIterator, type LxmlElementIteratorType ]:
|
| 90 |
+
* """
|
| 91 |
+
*/
|
| 92 |
+
struct LxmlElementIterator {
|
| 93 |
+
struct LxmlElementTagMatcher __pyx_base;
|
| 94 |
+
struct LxmlElement *_node;
|
| 95 |
+
_node_to_node_function _next_element;
|
| 96 |
+
};
|
| 97 |
+
|
| 98 |
+
/* "src/lxml/classlookup.pxi":6
|
| 99 |
+
* # Custom Element classes
|
| 100 |
+
*
|
| 101 |
+
* cdef public class ElementBase(_Element) [ type LxmlElementBaseType, # <<<<<<<<<<<<<<
|
| 102 |
+
* object LxmlElementBase ]:
|
| 103 |
+
* """ElementBase(*children, attrib=None, nsmap=None, **_extra)
|
| 104 |
+
*/
|
| 105 |
+
struct LxmlElementBase {
|
| 106 |
+
struct LxmlElement __pyx_base;
|
| 107 |
+
};
|
| 108 |
+
|
| 109 |
+
/* "src/lxml/classlookup.pxi":210
|
| 110 |
+
* # Element class lookup
|
| 111 |
+
*
|
| 112 |
+
* ctypedef public object (*_element_class_lookup_function)(object, _Document, xmlNode*) # <<<<<<<<<<<<<<
|
| 113 |
+
*
|
| 114 |
+
* # class to store element class lookup functions
|
| 115 |
+
*/
|
| 116 |
+
typedef PyObject *(*_element_class_lookup_function)(PyObject *, struct LxmlDocument *, xmlNode *);
|
| 117 |
+
|
| 118 |
+
/* "src/lxml/classlookup.pxi":213
|
| 119 |
+
*
|
| 120 |
+
* # class to store element class lookup functions
|
| 121 |
+
* cdef public class ElementClassLookup [ type LxmlElementClassLookupType, # <<<<<<<<<<<<<<
|
| 122 |
+
* object LxmlElementClassLookup ]:
|
| 123 |
+
* """ElementClassLookup(self)
|
| 124 |
+
*/
|
| 125 |
+
struct LxmlElementClassLookup {
|
| 126 |
+
PyObject_HEAD
|
| 127 |
+
_element_class_lookup_function _lookup_function;
|
| 128 |
+
};
|
| 129 |
+
|
| 130 |
+
/* "src/lxml/classlookup.pxi":221
|
| 131 |
+
*
|
| 132 |
+
*
|
| 133 |
+
* cdef public class FallbackElementClassLookup(ElementClassLookup) \ # <<<<<<<<<<<<<<
|
| 134 |
+
* [ type LxmlFallbackElementClassLookupType,
|
| 135 |
+
* object LxmlFallbackElementClassLookup ]:
|
| 136 |
+
*/
|
| 137 |
+
struct LxmlFallbackElementClassLookup {
|
| 138 |
+
struct LxmlElementClassLookup __pyx_base;
|
| 139 |
+
struct __pyx_vtabstruct_4lxml_5etree_FallbackElementClassLookup *__pyx_vtab;
|
| 140 |
+
struct LxmlElementClassLookup *fallback;
|
| 141 |
+
_element_class_lookup_function _fallback_function;
|
| 142 |
+
};
|
| 143 |
+
|
| 144 |
+
#ifndef __PYX_HAVE_API__lxml__etree
|
| 145 |
+
|
| 146 |
+
#ifdef CYTHON_EXTERN_C
|
| 147 |
+
#undef __PYX_EXTERN_C
|
| 148 |
+
#define __PYX_EXTERN_C CYTHON_EXTERN_C
|
| 149 |
+
#elif defined(__PYX_EXTERN_C)
|
| 150 |
+
#ifdef _MSC_VER
|
| 151 |
+
#pragma message ("Please do not define the '__PYX_EXTERN_C' macro externally. Use 'CYTHON_EXTERN_C' instead.")
|
| 152 |
+
#else
|
| 153 |
+
#warning Please do not define the '__PYX_EXTERN_C' macro externally. Use 'CYTHON_EXTERN_C' instead.
|
| 154 |
+
#endif
|
| 155 |
+
#else
|
| 156 |
+
#ifdef __cplusplus
|
| 157 |
+
#define __PYX_EXTERN_C extern "C"
|
| 158 |
+
#else
|
| 159 |
+
#define __PYX_EXTERN_C extern
|
| 160 |
+
#endif
|
| 161 |
+
#endif
|
| 162 |
+
|
| 163 |
+
#ifndef DL_IMPORT
|
| 164 |
+
#define DL_IMPORT(_T) _T
|
| 165 |
+
#endif
|
| 166 |
+
|
| 167 |
+
__PYX_EXTERN_C DL_IMPORT(PyTypeObject) LxmlDocumentType;
|
| 168 |
+
__PYX_EXTERN_C DL_IMPORT(PyTypeObject) LxmlElementType;
|
| 169 |
+
__PYX_EXTERN_C DL_IMPORT(PyTypeObject) LxmlElementTreeType;
|
| 170 |
+
__PYX_EXTERN_C DL_IMPORT(PyTypeObject) LxmlElementTagMatcherType;
|
| 171 |
+
__PYX_EXTERN_C DL_IMPORT(PyTypeObject) LxmlElementIteratorType;
|
| 172 |
+
__PYX_EXTERN_C DL_IMPORT(PyTypeObject) LxmlElementBaseType;
|
| 173 |
+
__PYX_EXTERN_C DL_IMPORT(PyTypeObject) LxmlElementClassLookupType;
|
| 174 |
+
__PYX_EXTERN_C DL_IMPORT(PyTypeObject) LxmlFallbackElementClassLookupType;
|
| 175 |
+
|
| 176 |
+
__PYX_EXTERN_C struct LxmlElement *deepcopyNodeToDocument(struct LxmlDocument *, xmlNode *);
|
| 177 |
+
__PYX_EXTERN_C struct LxmlElementTree *elementTreeFactory(struct LxmlElement *);
|
| 178 |
+
__PYX_EXTERN_C struct LxmlElementTree *newElementTree(struct LxmlElement *, PyObject *);
|
| 179 |
+
__PYX_EXTERN_C struct LxmlElementTree *adoptExternalDocument(xmlDoc *, PyObject *, int);
|
| 180 |
+
__PYX_EXTERN_C struct LxmlElement *elementFactory(struct LxmlDocument *, xmlNode *);
|
| 181 |
+
__PYX_EXTERN_C struct LxmlElement *makeElement(PyObject *, struct LxmlDocument *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *);
|
| 182 |
+
__PYX_EXTERN_C struct LxmlElement *makeSubElement(struct LxmlElement *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *);
|
| 183 |
+
__PYX_EXTERN_C void setElementClassLookupFunction(_element_class_lookup_function, PyObject *);
|
| 184 |
+
__PYX_EXTERN_C PyObject *lookupDefaultElementClass(PyObject *, PyObject *, xmlNode *);
|
| 185 |
+
__PYX_EXTERN_C PyObject *lookupNamespaceElementClass(PyObject *, PyObject *, xmlNode *);
|
| 186 |
+
__PYX_EXTERN_C PyObject *callLookupFallback(struct LxmlFallbackElementClassLookup *, struct LxmlDocument *, xmlNode *);
|
| 187 |
+
__PYX_EXTERN_C int tagMatches(xmlNode *, const xmlChar *, const xmlChar *);
|
| 188 |
+
__PYX_EXTERN_C struct LxmlDocument *documentOrRaise(PyObject *);
|
| 189 |
+
__PYX_EXTERN_C struct LxmlElement *rootNodeOrRaise(PyObject *);
|
| 190 |
+
__PYX_EXTERN_C int hasText(xmlNode *);
|
| 191 |
+
__PYX_EXTERN_C int hasTail(xmlNode *);
|
| 192 |
+
__PYX_EXTERN_C PyObject *textOf(xmlNode *);
|
| 193 |
+
__PYX_EXTERN_C PyObject *tailOf(xmlNode *);
|
| 194 |
+
__PYX_EXTERN_C int setNodeText(xmlNode *, PyObject *);
|
| 195 |
+
__PYX_EXTERN_C int setTailText(xmlNode *, PyObject *);
|
| 196 |
+
__PYX_EXTERN_C PyObject *attributeValue(xmlNode *, xmlAttr *);
|
| 197 |
+
__PYX_EXTERN_C PyObject *attributeValueFromNsName(xmlNode *, const xmlChar *, const xmlChar *);
|
| 198 |
+
__PYX_EXTERN_C PyObject *getAttributeValue(struct LxmlElement *, PyObject *, PyObject *);
|
| 199 |
+
__PYX_EXTERN_C PyObject *iterattributes(struct LxmlElement *, int);
|
| 200 |
+
__PYX_EXTERN_C PyObject *collectAttributes(xmlNode *, int);
|
| 201 |
+
__PYX_EXTERN_C int setAttributeValue(struct LxmlElement *, PyObject *, PyObject *);
|
| 202 |
+
__PYX_EXTERN_C int delAttribute(struct LxmlElement *, PyObject *);
|
| 203 |
+
__PYX_EXTERN_C int delAttributeFromNsName(xmlNode *, const xmlChar *, const xmlChar *);
|
| 204 |
+
__PYX_EXTERN_C int hasChild(xmlNode *);
|
| 205 |
+
__PYX_EXTERN_C xmlNode *findChild(xmlNode *, Py_ssize_t);
|
| 206 |
+
__PYX_EXTERN_C xmlNode *findChildForwards(xmlNode *, Py_ssize_t);
|
| 207 |
+
__PYX_EXTERN_C xmlNode *findChildBackwards(xmlNode *, Py_ssize_t);
|
| 208 |
+
__PYX_EXTERN_C xmlNode *nextElement(xmlNode *);
|
| 209 |
+
__PYX_EXTERN_C xmlNode *previousElement(xmlNode *);
|
| 210 |
+
__PYX_EXTERN_C void appendChild(struct LxmlElement *, struct LxmlElement *);
|
| 211 |
+
__PYX_EXTERN_C int appendChildToElement(struct LxmlElement *, struct LxmlElement *);
|
| 212 |
+
__PYX_EXTERN_C PyObject *pyunicode(const xmlChar *);
|
| 213 |
+
__PYX_EXTERN_C PyObject *utf8(PyObject *);
|
| 214 |
+
__PYX_EXTERN_C PyObject *getNsTag(PyObject *);
|
| 215 |
+
__PYX_EXTERN_C PyObject *getNsTagWithEmptyNs(PyObject *);
|
| 216 |
+
__PYX_EXTERN_C PyObject *namespacedName(xmlNode *);
|
| 217 |
+
__PYX_EXTERN_C PyObject *namespacedNameFromNsName(const xmlChar *, const xmlChar *);
|
| 218 |
+
__PYX_EXTERN_C void iteratorStoreNext(struct LxmlElementIterator *, struct LxmlElement *);
|
| 219 |
+
__PYX_EXTERN_C void initTagMatch(struct LxmlElementTagMatcher *, PyObject *);
|
| 220 |
+
__PYX_EXTERN_C xmlNs *findOrBuildNodeNsPrefix(struct LxmlDocument *, xmlNode *, const xmlChar *, const xmlChar *);
|
| 221 |
+
|
| 222 |
+
#endif /* !__PYX_HAVE_API__lxml__etree */
|
| 223 |
+
|
| 224 |
+
/* WARNING: the interface of the module init function changed in CPython 3.5. */
|
| 225 |
+
/* It now returns a PyModuleDef instance instead of a PyModule instance. */
|
| 226 |
+
|
| 227 |
+
/* WARNING: Use PyImport_AppendInittab("etree", PyInit_etree) instead of calling PyInit_etree directly from Python 3.5 */
|
| 228 |
+
PyMODINIT_FUNC PyInit_etree(void);
|
| 229 |
+
|
| 230 |
+
#if PY_VERSION_HEX >= 0x03050000 && (defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER) || (defined(__cplusplus) && __cplusplus >= 201402L))
|
| 231 |
+
#if defined(__cplusplus) && __cplusplus >= 201402L
|
| 232 |
+
[[deprecated("Use PyImport_AppendInittab(\"etree\", PyInit_etree) instead of calling PyInit_etree directly.")]] inline
|
| 233 |
+
#elif defined(__GNUC__) || defined(__clang__)
|
| 234 |
+
__attribute__ ((__deprecated__("Use PyImport_AppendInittab(\"etree\", PyInit_etree) instead of calling PyInit_etree directly."), __unused__)) __inline__
|
| 235 |
+
#elif defined(_MSC_VER)
|
| 236 |
+
__declspec(deprecated("Use PyImport_AppendInittab(\"etree\", PyInit_etree) instead of calling PyInit_etree directly.")) __inline
|
| 237 |
+
#endif
|
| 238 |
+
static PyObject* __PYX_WARN_IF_PyInit_etree_INIT_CALLED(PyObject* res) {
|
| 239 |
+
return res;
|
| 240 |
+
}
|
| 241 |
+
#define PyInit_etree() __PYX_WARN_IF_PyInit_etree_INIT_CALLED(PyInit_etree())
|
| 242 |
+
#endif
|
| 243 |
+
|
| 244 |
+
#endif /* !__PYX_HAVE__lxml__etree */
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/etree.pyx
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/etree_api.h
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* Generated by Cython 3.1.4 */
|
| 2 |
+
|
| 3 |
+
#ifndef __PYX_HAVE_API__lxml__etree
|
| 4 |
+
#define __PYX_HAVE_API__lxml__etree
|
| 5 |
+
#ifdef __MINGW64__
|
| 6 |
+
#define MS_WIN64
|
| 7 |
+
#endif
|
| 8 |
+
#include "Python.h"
|
| 9 |
+
#include "etree.h"
|
| 10 |
+
|
| 11 |
+
static struct LxmlElement *(*__pyx_api_f_4lxml_5etree_deepcopyNodeToDocument)(struct LxmlDocument *, xmlNode *) = 0;
|
| 12 |
+
#define deepcopyNodeToDocument __pyx_api_f_4lxml_5etree_deepcopyNodeToDocument
|
| 13 |
+
static struct LxmlElementTree *(*__pyx_api_f_4lxml_5etree_elementTreeFactory)(struct LxmlElement *) = 0;
|
| 14 |
+
#define elementTreeFactory __pyx_api_f_4lxml_5etree_elementTreeFactory
|
| 15 |
+
static struct LxmlElementTree *(*__pyx_api_f_4lxml_5etree_newElementTree)(struct LxmlElement *, PyObject *) = 0;
|
| 16 |
+
#define newElementTree __pyx_api_f_4lxml_5etree_newElementTree
|
| 17 |
+
static struct LxmlElementTree *(*__pyx_api_f_4lxml_5etree_adoptExternalDocument)(xmlDoc *, PyObject *, int) = 0;
|
| 18 |
+
#define adoptExternalDocument __pyx_api_f_4lxml_5etree_adoptExternalDocument
|
| 19 |
+
static struct LxmlElement *(*__pyx_api_f_4lxml_5etree_elementFactory)(struct LxmlDocument *, xmlNode *) = 0;
|
| 20 |
+
#define elementFactory __pyx_api_f_4lxml_5etree_elementFactory
|
| 21 |
+
static struct LxmlElement *(*__pyx_api_f_4lxml_5etree_makeElement)(PyObject *, struct LxmlDocument *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *) = 0;
|
| 22 |
+
#define makeElement __pyx_api_f_4lxml_5etree_makeElement
|
| 23 |
+
static struct LxmlElement *(*__pyx_api_f_4lxml_5etree_makeSubElement)(struct LxmlElement *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *) = 0;
|
| 24 |
+
#define makeSubElement __pyx_api_f_4lxml_5etree_makeSubElement
|
| 25 |
+
static void (*__pyx_api_f_4lxml_5etree_setElementClassLookupFunction)(_element_class_lookup_function, PyObject *) = 0;
|
| 26 |
+
#define setElementClassLookupFunction __pyx_api_f_4lxml_5etree_setElementClassLookupFunction
|
| 27 |
+
static PyObject *(*__pyx_api_f_4lxml_5etree_lookupDefaultElementClass)(PyObject *, PyObject *, xmlNode *) = 0;
|
| 28 |
+
#define lookupDefaultElementClass __pyx_api_f_4lxml_5etree_lookupDefaultElementClass
|
| 29 |
+
static PyObject *(*__pyx_api_f_4lxml_5etree_lookupNamespaceElementClass)(PyObject *, PyObject *, xmlNode *) = 0;
|
| 30 |
+
#define lookupNamespaceElementClass __pyx_api_f_4lxml_5etree_lookupNamespaceElementClass
|
| 31 |
+
static PyObject *(*__pyx_api_f_4lxml_5etree_callLookupFallback)(struct LxmlFallbackElementClassLookup *, struct LxmlDocument *, xmlNode *) = 0;
|
| 32 |
+
#define callLookupFallback __pyx_api_f_4lxml_5etree_callLookupFallback
|
| 33 |
+
static int (*__pyx_api_f_4lxml_5etree_tagMatches)(xmlNode *, const xmlChar *, const xmlChar *) = 0;
|
| 34 |
+
#define tagMatches __pyx_api_f_4lxml_5etree_tagMatches
|
| 35 |
+
static struct LxmlDocument *(*__pyx_api_f_4lxml_5etree_documentOrRaise)(PyObject *) = 0;
|
| 36 |
+
#define documentOrRaise __pyx_api_f_4lxml_5etree_documentOrRaise
|
| 37 |
+
static struct LxmlElement *(*__pyx_api_f_4lxml_5etree_rootNodeOrRaise)(PyObject *) = 0;
|
| 38 |
+
#define rootNodeOrRaise __pyx_api_f_4lxml_5etree_rootNodeOrRaise
|
| 39 |
+
static int (*__pyx_api_f_4lxml_5etree_hasText)(xmlNode *) = 0;
|
| 40 |
+
#define hasText __pyx_api_f_4lxml_5etree_hasText
|
| 41 |
+
static int (*__pyx_api_f_4lxml_5etree_hasTail)(xmlNode *) = 0;
|
| 42 |
+
#define hasTail __pyx_api_f_4lxml_5etree_hasTail
|
| 43 |
+
static PyObject *(*__pyx_api_f_4lxml_5etree_textOf)(xmlNode *) = 0;
|
| 44 |
+
#define textOf __pyx_api_f_4lxml_5etree_textOf
|
| 45 |
+
static PyObject *(*__pyx_api_f_4lxml_5etree_tailOf)(xmlNode *) = 0;
|
| 46 |
+
#define tailOf __pyx_api_f_4lxml_5etree_tailOf
|
| 47 |
+
static int (*__pyx_api_f_4lxml_5etree_setNodeText)(xmlNode *, PyObject *) = 0;
|
| 48 |
+
#define setNodeText __pyx_api_f_4lxml_5etree_setNodeText
|
| 49 |
+
static int (*__pyx_api_f_4lxml_5etree_setTailText)(xmlNode *, PyObject *) = 0;
|
| 50 |
+
#define setTailText __pyx_api_f_4lxml_5etree_setTailText
|
| 51 |
+
static PyObject *(*__pyx_api_f_4lxml_5etree_attributeValue)(xmlNode *, xmlAttr *) = 0;
|
| 52 |
+
#define attributeValue __pyx_api_f_4lxml_5etree_attributeValue
|
| 53 |
+
static PyObject *(*__pyx_api_f_4lxml_5etree_attributeValueFromNsName)(xmlNode *, const xmlChar *, const xmlChar *) = 0;
|
| 54 |
+
#define attributeValueFromNsName __pyx_api_f_4lxml_5etree_attributeValueFromNsName
|
| 55 |
+
static PyObject *(*__pyx_api_f_4lxml_5etree_getAttributeValue)(struct LxmlElement *, PyObject *, PyObject *) = 0;
|
| 56 |
+
#define getAttributeValue __pyx_api_f_4lxml_5etree_getAttributeValue
|
| 57 |
+
static PyObject *(*__pyx_api_f_4lxml_5etree_iterattributes)(struct LxmlElement *, int) = 0;
|
| 58 |
+
#define iterattributes __pyx_api_f_4lxml_5etree_iterattributes
|
| 59 |
+
static PyObject *(*__pyx_api_f_4lxml_5etree_collectAttributes)(xmlNode *, int) = 0;
|
| 60 |
+
#define collectAttributes __pyx_api_f_4lxml_5etree_collectAttributes
|
| 61 |
+
static int (*__pyx_api_f_4lxml_5etree_setAttributeValue)(struct LxmlElement *, PyObject *, PyObject *) = 0;
|
| 62 |
+
#define setAttributeValue __pyx_api_f_4lxml_5etree_setAttributeValue
|
| 63 |
+
static int (*__pyx_api_f_4lxml_5etree_delAttribute)(struct LxmlElement *, PyObject *) = 0;
|
| 64 |
+
#define delAttribute __pyx_api_f_4lxml_5etree_delAttribute
|
| 65 |
+
static int (*__pyx_api_f_4lxml_5etree_delAttributeFromNsName)(xmlNode *, const xmlChar *, const xmlChar *) = 0;
|
| 66 |
+
#define delAttributeFromNsName __pyx_api_f_4lxml_5etree_delAttributeFromNsName
|
| 67 |
+
static int (*__pyx_api_f_4lxml_5etree_hasChild)(xmlNode *) = 0;
|
| 68 |
+
#define hasChild __pyx_api_f_4lxml_5etree_hasChild
|
| 69 |
+
static xmlNode *(*__pyx_api_f_4lxml_5etree_findChild)(xmlNode *, Py_ssize_t) = 0;
|
| 70 |
+
#define findChild __pyx_api_f_4lxml_5etree_findChild
|
| 71 |
+
static xmlNode *(*__pyx_api_f_4lxml_5etree_findChildForwards)(xmlNode *, Py_ssize_t) = 0;
|
| 72 |
+
#define findChildForwards __pyx_api_f_4lxml_5etree_findChildForwards
|
| 73 |
+
static xmlNode *(*__pyx_api_f_4lxml_5etree_findChildBackwards)(xmlNode *, Py_ssize_t) = 0;
|
| 74 |
+
#define findChildBackwards __pyx_api_f_4lxml_5etree_findChildBackwards
|
| 75 |
+
static xmlNode *(*__pyx_api_f_4lxml_5etree_nextElement)(xmlNode *) = 0;
|
| 76 |
+
#define nextElement __pyx_api_f_4lxml_5etree_nextElement
|
| 77 |
+
static xmlNode *(*__pyx_api_f_4lxml_5etree_previousElement)(xmlNode *) = 0;
|
| 78 |
+
#define previousElement __pyx_api_f_4lxml_5etree_previousElement
|
| 79 |
+
static void (*__pyx_api_f_4lxml_5etree_appendChild)(struct LxmlElement *, struct LxmlElement *) = 0;
|
| 80 |
+
#define appendChild __pyx_api_f_4lxml_5etree_appendChild
|
| 81 |
+
static int (*__pyx_api_f_4lxml_5etree_appendChildToElement)(struct LxmlElement *, struct LxmlElement *) = 0;
|
| 82 |
+
#define appendChildToElement __pyx_api_f_4lxml_5etree_appendChildToElement
|
| 83 |
+
static PyObject *(*__pyx_api_f_4lxml_5etree_pyunicode)(const xmlChar *) = 0;
|
| 84 |
+
#define pyunicode __pyx_api_f_4lxml_5etree_pyunicode
|
| 85 |
+
static PyObject *(*__pyx_api_f_4lxml_5etree_utf8)(PyObject *) = 0;
|
| 86 |
+
#define utf8 __pyx_api_f_4lxml_5etree_utf8
|
| 87 |
+
static PyObject *(*__pyx_api_f_4lxml_5etree_getNsTag)(PyObject *) = 0;
|
| 88 |
+
#define getNsTag __pyx_api_f_4lxml_5etree_getNsTag
|
| 89 |
+
static PyObject *(*__pyx_api_f_4lxml_5etree_getNsTagWithEmptyNs)(PyObject *) = 0;
|
| 90 |
+
#define getNsTagWithEmptyNs __pyx_api_f_4lxml_5etree_getNsTagWithEmptyNs
|
| 91 |
+
static PyObject *(*__pyx_api_f_4lxml_5etree_namespacedName)(xmlNode *) = 0;
|
| 92 |
+
#define namespacedName __pyx_api_f_4lxml_5etree_namespacedName
|
| 93 |
+
static PyObject *(*__pyx_api_f_4lxml_5etree_namespacedNameFromNsName)(const xmlChar *, const xmlChar *) = 0;
|
| 94 |
+
#define namespacedNameFromNsName __pyx_api_f_4lxml_5etree_namespacedNameFromNsName
|
| 95 |
+
static void (*__pyx_api_f_4lxml_5etree_iteratorStoreNext)(struct LxmlElementIterator *, struct LxmlElement *) = 0;
|
| 96 |
+
#define iteratorStoreNext __pyx_api_f_4lxml_5etree_iteratorStoreNext
|
| 97 |
+
static void (*__pyx_api_f_4lxml_5etree_initTagMatch)(struct LxmlElementTagMatcher *, PyObject *) = 0;
|
| 98 |
+
#define initTagMatch __pyx_api_f_4lxml_5etree_initTagMatch
|
| 99 |
+
static xmlNs *(*__pyx_api_f_4lxml_5etree_findOrBuildNodeNsPrefix)(struct LxmlDocument *, xmlNode *, const xmlChar *, const xmlChar *) = 0;
|
| 100 |
+
#define findOrBuildNodeNsPrefix __pyx_api_f_4lxml_5etree_findOrBuildNodeNsPrefix
|
| 101 |
+
static int __Pyx_ImportFunction_3_1_4(PyObject *module, const char *funcname, void (**f)(void), const char *sig);
|
| 102 |
+
|
| 103 |
+
#ifndef __PYX_HAVE_RT_ImportFunction_3_1_4
|
| 104 |
+
#define __PYX_HAVE_RT_ImportFunction_3_1_4
|
| 105 |
+
static int __Pyx_ImportFunction_3_1_4(PyObject *module, const char *funcname, void (**f)(void), const char *sig) {
|
| 106 |
+
PyObject *d = 0;
|
| 107 |
+
PyObject *cobj = 0;
|
| 108 |
+
union {
|
| 109 |
+
void (*fp)(void);
|
| 110 |
+
void *p;
|
| 111 |
+
} tmp;
|
| 112 |
+
d = PyObject_GetAttrString(module, "__pyx_capi__");
|
| 113 |
+
if (!d)
|
| 114 |
+
goto bad;
|
| 115 |
+
#if (defined(Py_LIMITED_API) && Py_LIMITED_API >= 0x030d0000) || (!defined(Py_LIMITED_API) && PY_VERSION_HEX >= 0x030d0000)
|
| 116 |
+
PyDict_GetItemStringRef(d, funcname, &cobj);
|
| 117 |
+
#else
|
| 118 |
+
cobj = PyDict_GetItemString(d, funcname);
|
| 119 |
+
Py_XINCREF(cobj);
|
| 120 |
+
#endif
|
| 121 |
+
if (!cobj) {
|
| 122 |
+
PyErr_Format(PyExc_ImportError,
|
| 123 |
+
"%.200s does not export expected C function %.200s",
|
| 124 |
+
PyModule_GetName(module), funcname);
|
| 125 |
+
goto bad;
|
| 126 |
+
}
|
| 127 |
+
if (!PyCapsule_IsValid(cobj, sig)) {
|
| 128 |
+
PyErr_Format(PyExc_TypeError,
|
| 129 |
+
"C function %.200s.%.200s has wrong signature (expected %.500s, got %.500s)",
|
| 130 |
+
PyModule_GetName(module), funcname, sig, PyCapsule_GetName(cobj));
|
| 131 |
+
goto bad;
|
| 132 |
+
}
|
| 133 |
+
tmp.p = PyCapsule_GetPointer(cobj, sig);
|
| 134 |
+
*f = tmp.fp;
|
| 135 |
+
if (!(*f))
|
| 136 |
+
goto bad;
|
| 137 |
+
Py_DECREF(d);
|
| 138 |
+
Py_DECREF(cobj);
|
| 139 |
+
return 0;
|
| 140 |
+
bad:
|
| 141 |
+
Py_XDECREF(d);
|
| 142 |
+
Py_XDECREF(cobj);
|
| 143 |
+
return -1;
|
| 144 |
+
}
|
| 145 |
+
#endif
|
| 146 |
+
|
| 147 |
+
|
| 148 |
+
static int import_lxml__etree(void) {
|
| 149 |
+
PyObject *module = 0;
|
| 150 |
+
module = PyImport_ImportModule("lxml.etree");
|
| 151 |
+
if (!module) goto bad;
|
| 152 |
+
if (__Pyx_ImportFunction_3_1_4(module, "deepcopyNodeToDocument", (void (**)(void))&__pyx_api_f_4lxml_5etree_deepcopyNodeToDocument, "struct LxmlElement *(struct LxmlDocument *, xmlNode *)") < 0) goto bad;
|
| 153 |
+
if (__Pyx_ImportFunction_3_1_4(module, "elementTreeFactory", (void (**)(void))&__pyx_api_f_4lxml_5etree_elementTreeFactory, "struct LxmlElementTree *(struct LxmlElement *)") < 0) goto bad;
|
| 154 |
+
if (__Pyx_ImportFunction_3_1_4(module, "newElementTree", (void (**)(void))&__pyx_api_f_4lxml_5etree_newElementTree, "struct LxmlElementTree *(struct LxmlElement *, PyObject *)") < 0) goto bad;
|
| 155 |
+
if (__Pyx_ImportFunction_3_1_4(module, "adoptExternalDocument", (void (**)(void))&__pyx_api_f_4lxml_5etree_adoptExternalDocument, "struct LxmlElementTree *(xmlDoc *, PyObject *, int)") < 0) goto bad;
|
| 156 |
+
if (__Pyx_ImportFunction_3_1_4(module, "elementFactory", (void (**)(void))&__pyx_api_f_4lxml_5etree_elementFactory, "struct LxmlElement *(struct LxmlDocument *, xmlNode *)") < 0) goto bad;
|
| 157 |
+
if (__Pyx_ImportFunction_3_1_4(module, "makeElement", (void (**)(void))&__pyx_api_f_4lxml_5etree_makeElement, "struct LxmlElement *(PyObject *, struct LxmlDocument *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *)") < 0) goto bad;
|
| 158 |
+
if (__Pyx_ImportFunction_3_1_4(module, "makeSubElement", (void (**)(void))&__pyx_api_f_4lxml_5etree_makeSubElement, "struct LxmlElement *(struct LxmlElement *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *)") < 0) goto bad;
|
| 159 |
+
if (__Pyx_ImportFunction_3_1_4(module, "setElementClassLookupFunction", (void (**)(void))&__pyx_api_f_4lxml_5etree_setElementClassLookupFunction, "void (_element_class_lookup_function, PyObject *)") < 0) goto bad;
|
| 160 |
+
if (__Pyx_ImportFunction_3_1_4(module, "lookupDefaultElementClass", (void (**)(void))&__pyx_api_f_4lxml_5etree_lookupDefaultElementClass, "PyObject *(PyObject *, PyObject *, xmlNode *)") < 0) goto bad;
|
| 161 |
+
if (__Pyx_ImportFunction_3_1_4(module, "lookupNamespaceElementClass", (void (**)(void))&__pyx_api_f_4lxml_5etree_lookupNamespaceElementClass, "PyObject *(PyObject *, PyObject *, xmlNode *)") < 0) goto bad;
|
| 162 |
+
if (__Pyx_ImportFunction_3_1_4(module, "callLookupFallback", (void (**)(void))&__pyx_api_f_4lxml_5etree_callLookupFallback, "PyObject *(struct LxmlFallbackElementClassLookup *, struct LxmlDocument *, xmlNode *)") < 0) goto bad;
|
| 163 |
+
if (__Pyx_ImportFunction_3_1_4(module, "tagMatches", (void (**)(void))&__pyx_api_f_4lxml_5etree_tagMatches, "int (xmlNode *, const xmlChar *, const xmlChar *)") < 0) goto bad;
|
| 164 |
+
if (__Pyx_ImportFunction_3_1_4(module, "documentOrRaise", (void (**)(void))&__pyx_api_f_4lxml_5etree_documentOrRaise, "struct LxmlDocument *(PyObject *)") < 0) goto bad;
|
| 165 |
+
if (__Pyx_ImportFunction_3_1_4(module, "rootNodeOrRaise", (void (**)(void))&__pyx_api_f_4lxml_5etree_rootNodeOrRaise, "struct LxmlElement *(PyObject *)") < 0) goto bad;
|
| 166 |
+
if (__Pyx_ImportFunction_3_1_4(module, "hasText", (void (**)(void))&__pyx_api_f_4lxml_5etree_hasText, "int (xmlNode *)") < 0) goto bad;
|
| 167 |
+
if (__Pyx_ImportFunction_3_1_4(module, "hasTail", (void (**)(void))&__pyx_api_f_4lxml_5etree_hasTail, "int (xmlNode *)") < 0) goto bad;
|
| 168 |
+
if (__Pyx_ImportFunction_3_1_4(module, "textOf", (void (**)(void))&__pyx_api_f_4lxml_5etree_textOf, "PyObject *(xmlNode *)") < 0) goto bad;
|
| 169 |
+
if (__Pyx_ImportFunction_3_1_4(module, "tailOf", (void (**)(void))&__pyx_api_f_4lxml_5etree_tailOf, "PyObject *(xmlNode *)") < 0) goto bad;
|
| 170 |
+
if (__Pyx_ImportFunction_3_1_4(module, "setNodeText", (void (**)(void))&__pyx_api_f_4lxml_5etree_setNodeText, "int (xmlNode *, PyObject *)") < 0) goto bad;
|
| 171 |
+
if (__Pyx_ImportFunction_3_1_4(module, "setTailText", (void (**)(void))&__pyx_api_f_4lxml_5etree_setTailText, "int (xmlNode *, PyObject *)") < 0) goto bad;
|
| 172 |
+
if (__Pyx_ImportFunction_3_1_4(module, "attributeValue", (void (**)(void))&__pyx_api_f_4lxml_5etree_attributeValue, "PyObject *(xmlNode *, xmlAttr *)") < 0) goto bad;
|
| 173 |
+
if (__Pyx_ImportFunction_3_1_4(module, "attributeValueFromNsName", (void (**)(void))&__pyx_api_f_4lxml_5etree_attributeValueFromNsName, "PyObject *(xmlNode *, const xmlChar *, const xmlChar *)") < 0) goto bad;
|
| 174 |
+
if (__Pyx_ImportFunction_3_1_4(module, "getAttributeValue", (void (**)(void))&__pyx_api_f_4lxml_5etree_getAttributeValue, "PyObject *(struct LxmlElement *, PyObject *, PyObject *)") < 0) goto bad;
|
| 175 |
+
if (__Pyx_ImportFunction_3_1_4(module, "iterattributes", (void (**)(void))&__pyx_api_f_4lxml_5etree_iterattributes, "PyObject *(struct LxmlElement *, int)") < 0) goto bad;
|
| 176 |
+
if (__Pyx_ImportFunction_3_1_4(module, "collectAttributes", (void (**)(void))&__pyx_api_f_4lxml_5etree_collectAttributes, "PyObject *(xmlNode *, int)") < 0) goto bad;
|
| 177 |
+
if (__Pyx_ImportFunction_3_1_4(module, "setAttributeValue", (void (**)(void))&__pyx_api_f_4lxml_5etree_setAttributeValue, "int (struct LxmlElement *, PyObject *, PyObject *)") < 0) goto bad;
|
| 178 |
+
if (__Pyx_ImportFunction_3_1_4(module, "delAttribute", (void (**)(void))&__pyx_api_f_4lxml_5etree_delAttribute, "int (struct LxmlElement *, PyObject *)") < 0) goto bad;
|
| 179 |
+
if (__Pyx_ImportFunction_3_1_4(module, "delAttributeFromNsName", (void (**)(void))&__pyx_api_f_4lxml_5etree_delAttributeFromNsName, "int (xmlNode *, const xmlChar *, const xmlChar *)") < 0) goto bad;
|
| 180 |
+
if (__Pyx_ImportFunction_3_1_4(module, "hasChild", (void (**)(void))&__pyx_api_f_4lxml_5etree_hasChild, "int (xmlNode *)") < 0) goto bad;
|
| 181 |
+
if (__Pyx_ImportFunction_3_1_4(module, "findChild", (void (**)(void))&__pyx_api_f_4lxml_5etree_findChild, "xmlNode *(xmlNode *, Py_ssize_t)") < 0) goto bad;
|
| 182 |
+
if (__Pyx_ImportFunction_3_1_4(module, "findChildForwards", (void (**)(void))&__pyx_api_f_4lxml_5etree_findChildForwards, "xmlNode *(xmlNode *, Py_ssize_t)") < 0) goto bad;
|
| 183 |
+
if (__Pyx_ImportFunction_3_1_4(module, "findChildBackwards", (void (**)(void))&__pyx_api_f_4lxml_5etree_findChildBackwards, "xmlNode *(xmlNode *, Py_ssize_t)") < 0) goto bad;
|
| 184 |
+
if (__Pyx_ImportFunction_3_1_4(module, "nextElement", (void (**)(void))&__pyx_api_f_4lxml_5etree_nextElement, "xmlNode *(xmlNode *)") < 0) goto bad;
|
| 185 |
+
if (__Pyx_ImportFunction_3_1_4(module, "previousElement", (void (**)(void))&__pyx_api_f_4lxml_5etree_previousElement, "xmlNode *(xmlNode *)") < 0) goto bad;
|
| 186 |
+
if (__Pyx_ImportFunction_3_1_4(module, "appendChild", (void (**)(void))&__pyx_api_f_4lxml_5etree_appendChild, "void (struct LxmlElement *, struct LxmlElement *)") < 0) goto bad;
|
| 187 |
+
if (__Pyx_ImportFunction_3_1_4(module, "appendChildToElement", (void (**)(void))&__pyx_api_f_4lxml_5etree_appendChildToElement, "int (struct LxmlElement *, struct LxmlElement *)") < 0) goto bad;
|
| 188 |
+
if (__Pyx_ImportFunction_3_1_4(module, "pyunicode", (void (**)(void))&__pyx_api_f_4lxml_5etree_pyunicode, "PyObject *(const xmlChar *)") < 0) goto bad;
|
| 189 |
+
if (__Pyx_ImportFunction_3_1_4(module, "utf8", (void (**)(void))&__pyx_api_f_4lxml_5etree_utf8, "PyObject *(PyObject *)") < 0) goto bad;
|
| 190 |
+
if (__Pyx_ImportFunction_3_1_4(module, "getNsTag", (void (**)(void))&__pyx_api_f_4lxml_5etree_getNsTag, "PyObject *(PyObject *)") < 0) goto bad;
|
| 191 |
+
if (__Pyx_ImportFunction_3_1_4(module, "getNsTagWithEmptyNs", (void (**)(void))&__pyx_api_f_4lxml_5etree_getNsTagWithEmptyNs, "PyObject *(PyObject *)") < 0) goto bad;
|
| 192 |
+
if (__Pyx_ImportFunction_3_1_4(module, "namespacedName", (void (**)(void))&__pyx_api_f_4lxml_5etree_namespacedName, "PyObject *(xmlNode *)") < 0) goto bad;
|
| 193 |
+
if (__Pyx_ImportFunction_3_1_4(module, "namespacedNameFromNsName", (void (**)(void))&__pyx_api_f_4lxml_5etree_namespacedNameFromNsName, "PyObject *(const xmlChar *, const xmlChar *)") < 0) goto bad;
|
| 194 |
+
if (__Pyx_ImportFunction_3_1_4(module, "iteratorStoreNext", (void (**)(void))&__pyx_api_f_4lxml_5etree_iteratorStoreNext, "void (struct LxmlElementIterator *, struct LxmlElement *)") < 0) goto bad;
|
| 195 |
+
if (__Pyx_ImportFunction_3_1_4(module, "initTagMatch", (void (**)(void))&__pyx_api_f_4lxml_5etree_initTagMatch, "void (struct LxmlElementTagMatcher *, PyObject *)") < 0) goto bad;
|
| 196 |
+
if (__Pyx_ImportFunction_3_1_4(module, "findOrBuildNodeNsPrefix", (void (**)(void))&__pyx_api_f_4lxml_5etree_findOrBuildNodeNsPrefix, "xmlNs *(struct LxmlDocument *, xmlNode *, const xmlChar *, const xmlChar *)") < 0) goto bad;
|
| 197 |
+
Py_DECREF(module); module = 0;
|
| 198 |
+
return 0;
|
| 199 |
+
bad:
|
| 200 |
+
Py_XDECREF(module);
|
| 201 |
+
return -1;
|
| 202 |
+
}
|
| 203 |
+
|
| 204 |
+
#endif /* !__PYX_HAVE_API__lxml__etree */
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/extensions.pxi
ADDED
|
@@ -0,0 +1,830 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# support for extension functions in XPath and XSLT
|
| 2 |
+
|
| 3 |
+
cdef class XPathError(LxmlError):
|
| 4 |
+
"""Base class of all XPath errors.
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
cdef class XPathEvalError(XPathError):
|
| 8 |
+
"""Error during XPath evaluation.
|
| 9 |
+
"""
|
| 10 |
+
|
| 11 |
+
cdef class XPathFunctionError(XPathEvalError):
|
| 12 |
+
"""Internal error looking up an XPath extension function.
|
| 13 |
+
"""
|
| 14 |
+
|
| 15 |
+
cdef class XPathResultError(XPathEvalError):
|
| 16 |
+
"""Error handling an XPath result.
|
| 17 |
+
"""
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
# forward declarations
|
| 21 |
+
|
| 22 |
+
ctypedef int (*_register_function)(void* ctxt, name_utf, ns_uri_utf)
|
| 23 |
+
cdef class _ExsltRegExp
|
| 24 |
+
|
| 25 |
+
################################################################################
|
| 26 |
+
# Base class for XSLT and XPath evaluation contexts: functions, namespaces, ...
|
| 27 |
+
|
| 28 |
+
@cython.internal
|
| 29 |
+
cdef class _BaseContext:
|
| 30 |
+
cdef xpath.xmlXPathContext* _xpathCtxt
|
| 31 |
+
cdef _Document _doc
|
| 32 |
+
cdef dict _extensions
|
| 33 |
+
cdef list _namespaces
|
| 34 |
+
cdef list _global_namespaces
|
| 35 |
+
cdef dict _utf_refs
|
| 36 |
+
cdef dict _function_cache
|
| 37 |
+
cdef dict _eval_context_dict
|
| 38 |
+
cdef bint _build_smart_strings
|
| 39 |
+
# for exception handling and temporary reference keeping:
|
| 40 |
+
cdef _TempStore _temp_refs
|
| 41 |
+
cdef set _temp_documents
|
| 42 |
+
cdef _ExceptionContext _exc
|
| 43 |
+
cdef _ErrorLog _error_log
|
| 44 |
+
|
| 45 |
+
def __init__(self, namespaces, extensions, error_log, enable_regexp,
|
| 46 |
+
build_smart_strings):
|
| 47 |
+
cdef _ExsltRegExp _regexp
|
| 48 |
+
cdef dict new_extensions
|
| 49 |
+
cdef list ns
|
| 50 |
+
self._utf_refs = {}
|
| 51 |
+
self._global_namespaces = []
|
| 52 |
+
self._function_cache = {}
|
| 53 |
+
self._eval_context_dict = None
|
| 54 |
+
self._error_log = error_log
|
| 55 |
+
|
| 56 |
+
if extensions is not None:
|
| 57 |
+
# convert extensions to UTF-8
|
| 58 |
+
if isinstance(extensions, dict):
|
| 59 |
+
extensions = (extensions,)
|
| 60 |
+
# format: [ {(ns, name):function} ] -> {(ns_utf, name_utf):function}
|
| 61 |
+
new_extensions = {}
|
| 62 |
+
for extension in extensions:
|
| 63 |
+
for (ns_uri, name), function in extension.items():
|
| 64 |
+
if name is None:
|
| 65 |
+
raise ValueError, "extensions must have non empty names"
|
| 66 |
+
ns_utf = self._to_utf(ns_uri)
|
| 67 |
+
name_utf = self._to_utf(name)
|
| 68 |
+
new_extensions[(ns_utf, name_utf)] = function
|
| 69 |
+
extensions = new_extensions or None
|
| 70 |
+
|
| 71 |
+
if namespaces is not None:
|
| 72 |
+
if isinstance(namespaces, dict):
|
| 73 |
+
namespaces = namespaces.items()
|
| 74 |
+
if namespaces:
|
| 75 |
+
ns = []
|
| 76 |
+
for prefix, ns_uri in namespaces:
|
| 77 |
+
if prefix is None or not prefix:
|
| 78 |
+
raise TypeError, \
|
| 79 |
+
"empty namespace prefix is not supported in XPath"
|
| 80 |
+
if ns_uri is None or not ns_uri:
|
| 81 |
+
raise TypeError, \
|
| 82 |
+
"setting default namespace is not supported in XPath"
|
| 83 |
+
prefix_utf = self._to_utf(prefix)
|
| 84 |
+
ns_uri_utf = self._to_utf(ns_uri)
|
| 85 |
+
ns.append( (prefix_utf, ns_uri_utf) )
|
| 86 |
+
namespaces = ns
|
| 87 |
+
else:
|
| 88 |
+
namespaces = None
|
| 89 |
+
|
| 90 |
+
self._doc = None
|
| 91 |
+
self._exc = _ExceptionContext()
|
| 92 |
+
self._extensions = extensions
|
| 93 |
+
self._namespaces = namespaces
|
| 94 |
+
self._temp_refs = _TempStore()
|
| 95 |
+
self._temp_documents = set()
|
| 96 |
+
self._build_smart_strings = build_smart_strings
|
| 97 |
+
|
| 98 |
+
if enable_regexp:
|
| 99 |
+
_regexp = _ExsltRegExp()
|
| 100 |
+
_regexp._register_in_context(self)
|
| 101 |
+
|
| 102 |
+
cdef _BaseContext _copy(self):
|
| 103 |
+
cdef _BaseContext context
|
| 104 |
+
if self._namespaces is not None:
|
| 105 |
+
namespaces = self._namespaces[:]
|
| 106 |
+
else:
|
| 107 |
+
namespaces = None
|
| 108 |
+
context = self.__class__(namespaces, None, self._error_log, False,
|
| 109 |
+
self._build_smart_strings)
|
| 110 |
+
if self._extensions is not None:
|
| 111 |
+
context._extensions = self._extensions.copy()
|
| 112 |
+
return context
|
| 113 |
+
|
| 114 |
+
cdef bytes _to_utf(self, s):
|
| 115 |
+
"Convert to UTF-8 and keep a reference to the encoded string"
|
| 116 |
+
cdef python.PyObject* dict_result
|
| 117 |
+
if s is None:
|
| 118 |
+
return None
|
| 119 |
+
dict_result = python.PyDict_GetItem(self._utf_refs, s)
|
| 120 |
+
if dict_result is not NULL:
|
| 121 |
+
return <bytes>dict_result
|
| 122 |
+
utf = _utf8(s)
|
| 123 |
+
self._utf_refs[s] = utf
|
| 124 |
+
if python.IS_PYPY:
|
| 125 |
+
# use C level refs, PyPy refs are not enough!
|
| 126 |
+
python.Py_INCREF(utf)
|
| 127 |
+
return utf
|
| 128 |
+
|
| 129 |
+
cdef void _set_xpath_context(self, xpath.xmlXPathContext* xpathCtxt) noexcept:
|
| 130 |
+
self._xpathCtxt = xpathCtxt
|
| 131 |
+
xpathCtxt.userData = <void*>self
|
| 132 |
+
# Need a cast here because older libxml2 releases do not use 'const' in the functype.
|
| 133 |
+
xpathCtxt.error = <xmlerror.xmlStructuredErrorFunc> _receiveXPathError
|
| 134 |
+
|
| 135 |
+
@cython.final
|
| 136 |
+
cdef _register_context(self, _Document doc):
|
| 137 |
+
self._doc = doc
|
| 138 |
+
self._exc.clear()
|
| 139 |
+
|
| 140 |
+
@cython.final
|
| 141 |
+
cdef _cleanup_context(self):
|
| 142 |
+
#xpath.xmlXPathRegisteredNsCleanup(self._xpathCtxt)
|
| 143 |
+
#self.unregisterGlobalNamespaces()
|
| 144 |
+
if python.IS_PYPY:
|
| 145 |
+
# clean up double refs in PyPy (see "_to_utf()" method)
|
| 146 |
+
for ref in self._utf_refs.itervalues():
|
| 147 |
+
python.Py_DECREF(ref)
|
| 148 |
+
self._utf_refs.clear()
|
| 149 |
+
self._eval_context_dict = None
|
| 150 |
+
self._doc = None
|
| 151 |
+
|
| 152 |
+
@cython.final
|
| 153 |
+
cdef _release_context(self):
|
| 154 |
+
if self._xpathCtxt is not NULL:
|
| 155 |
+
self._xpathCtxt.userData = NULL
|
| 156 |
+
self._xpathCtxt = NULL
|
| 157 |
+
|
| 158 |
+
# namespaces (internal UTF-8 methods with leading '_')
|
| 159 |
+
|
| 160 |
+
cdef addNamespace(self, prefix, ns_uri):
|
| 161 |
+
cdef list namespaces
|
| 162 |
+
if prefix is None:
|
| 163 |
+
raise TypeError, "empty prefix is not supported in XPath"
|
| 164 |
+
prefix_utf = self._to_utf(prefix)
|
| 165 |
+
ns_uri_utf = self._to_utf(ns_uri)
|
| 166 |
+
new_item = (prefix_utf, ns_uri_utf)
|
| 167 |
+
if self._namespaces is None:
|
| 168 |
+
self._namespaces = [new_item]
|
| 169 |
+
else:
|
| 170 |
+
namespaces = []
|
| 171 |
+
for item in self._namespaces:
|
| 172 |
+
if item[0] == prefix_utf:
|
| 173 |
+
item = new_item
|
| 174 |
+
new_item = None
|
| 175 |
+
namespaces.append(item)
|
| 176 |
+
if new_item is not None:
|
| 177 |
+
namespaces.append(new_item)
|
| 178 |
+
self._namespaces = namespaces
|
| 179 |
+
if self._xpathCtxt is not NULL:
|
| 180 |
+
xpath.xmlXPathRegisterNs(
|
| 181 |
+
self._xpathCtxt, _xcstr(prefix_utf), _xcstr(ns_uri_utf))
|
| 182 |
+
|
| 183 |
+
cdef registerNamespace(self, prefix, ns_uri):
|
| 184 |
+
if prefix is None:
|
| 185 |
+
raise TypeError, "empty prefix is not supported in XPath"
|
| 186 |
+
prefix_utf = self._to_utf(prefix)
|
| 187 |
+
ns_uri_utf = self._to_utf(ns_uri)
|
| 188 |
+
self._global_namespaces.append(prefix_utf)
|
| 189 |
+
xpath.xmlXPathRegisterNs(self._xpathCtxt,
|
| 190 |
+
_xcstr(prefix_utf), _xcstr(ns_uri_utf))
|
| 191 |
+
|
| 192 |
+
cdef registerLocalNamespaces(self):
|
| 193 |
+
if self._namespaces is None:
|
| 194 |
+
return
|
| 195 |
+
for prefix_utf, ns_uri_utf in self._namespaces:
|
| 196 |
+
xpath.xmlXPathRegisterNs(
|
| 197 |
+
self._xpathCtxt, _xcstr(prefix_utf), _xcstr(ns_uri_utf))
|
| 198 |
+
|
| 199 |
+
cdef registerGlobalNamespaces(self):
|
| 200 |
+
cdef list ns_prefixes = _find_all_extension_prefixes()
|
| 201 |
+
if python.PyList_GET_SIZE(ns_prefixes) > 0:
|
| 202 |
+
for prefix_utf, ns_uri_utf in ns_prefixes:
|
| 203 |
+
self._global_namespaces.append(prefix_utf)
|
| 204 |
+
xpath.xmlXPathRegisterNs(
|
| 205 |
+
self._xpathCtxt, _xcstr(prefix_utf), _xcstr(ns_uri_utf))
|
| 206 |
+
|
| 207 |
+
cdef unregisterGlobalNamespaces(self):
|
| 208 |
+
if python.PyList_GET_SIZE(self._global_namespaces) > 0:
|
| 209 |
+
for prefix_utf in self._global_namespaces:
|
| 210 |
+
xpath.xmlXPathRegisterNs(self._xpathCtxt,
|
| 211 |
+
_xcstr(prefix_utf), NULL)
|
| 212 |
+
del self._global_namespaces[:]
|
| 213 |
+
|
| 214 |
+
cdef void _unregisterNamespace(self, prefix_utf) noexcept:
|
| 215 |
+
xpath.xmlXPathRegisterNs(self._xpathCtxt,
|
| 216 |
+
_xcstr(prefix_utf), NULL)
|
| 217 |
+
|
| 218 |
+
# extension functions
|
| 219 |
+
|
| 220 |
+
cdef int _addLocalExtensionFunction(self, ns_utf, name_utf, function) except -1:
|
| 221 |
+
if self._extensions is None:
|
| 222 |
+
self._extensions = {}
|
| 223 |
+
self._extensions[(ns_utf, name_utf)] = function
|
| 224 |
+
return 0
|
| 225 |
+
|
| 226 |
+
cdef registerGlobalFunctions(self, void* ctxt,
|
| 227 |
+
_register_function reg_func):
|
| 228 |
+
cdef python.PyObject* dict_result
|
| 229 |
+
cdef dict d
|
| 230 |
+
for ns_utf, ns_functions in __FUNCTION_NAMESPACE_REGISTRIES.iteritems():
|
| 231 |
+
dict_result = python.PyDict_GetItem(
|
| 232 |
+
self._function_cache, ns_utf)
|
| 233 |
+
if dict_result is not NULL:
|
| 234 |
+
d = <dict>dict_result
|
| 235 |
+
else:
|
| 236 |
+
d = {}
|
| 237 |
+
self._function_cache[ns_utf] = d
|
| 238 |
+
for name_utf, function in ns_functions.iteritems():
|
| 239 |
+
d[name_utf] = function
|
| 240 |
+
reg_func(ctxt, name_utf, ns_utf)
|
| 241 |
+
|
| 242 |
+
cdef registerLocalFunctions(self, void* ctxt,
|
| 243 |
+
_register_function reg_func):
|
| 244 |
+
cdef python.PyObject* dict_result
|
| 245 |
+
cdef dict d
|
| 246 |
+
if self._extensions is None:
|
| 247 |
+
return # done
|
| 248 |
+
last_ns = None
|
| 249 |
+
d = None
|
| 250 |
+
for (ns_utf, name_utf), function in self._extensions.iteritems():
|
| 251 |
+
if ns_utf is not last_ns or d is None:
|
| 252 |
+
last_ns = ns_utf
|
| 253 |
+
dict_result = python.PyDict_GetItem(
|
| 254 |
+
self._function_cache, ns_utf)
|
| 255 |
+
if dict_result is not NULL:
|
| 256 |
+
d = <dict>dict_result
|
| 257 |
+
else:
|
| 258 |
+
d = {}
|
| 259 |
+
self._function_cache[ns_utf] = d
|
| 260 |
+
d[name_utf] = function
|
| 261 |
+
reg_func(ctxt, name_utf, ns_utf)
|
| 262 |
+
|
| 263 |
+
cdef unregisterAllFunctions(self, void* ctxt,
|
| 264 |
+
_register_function unreg_func):
|
| 265 |
+
for ns_utf, functions in self._function_cache.iteritems():
|
| 266 |
+
for name_utf in functions:
|
| 267 |
+
unreg_func(ctxt, name_utf, ns_utf)
|
| 268 |
+
|
| 269 |
+
cdef unregisterGlobalFunctions(self, void* ctxt,
|
| 270 |
+
_register_function unreg_func):
|
| 271 |
+
for ns_utf, functions in self._function_cache.items():
|
| 272 |
+
for name_utf in functions:
|
| 273 |
+
if self._extensions is None or \
|
| 274 |
+
(ns_utf, name_utf) not in self._extensions:
|
| 275 |
+
unreg_func(ctxt, name_utf, ns_utf)
|
| 276 |
+
|
| 277 |
+
@cython.final
|
| 278 |
+
cdef _find_cached_function(self, const_xmlChar* c_ns_uri, const_xmlChar* c_name):
|
| 279 |
+
"""Lookup an extension function in the cache and return it.
|
| 280 |
+
|
| 281 |
+
Parameters: c_ns_uri may be NULL, c_name must not be NULL
|
| 282 |
+
"""
|
| 283 |
+
cdef python.PyObject* c_dict
|
| 284 |
+
cdef python.PyObject* dict_result
|
| 285 |
+
c_dict = python.PyDict_GetItem(
|
| 286 |
+
self._function_cache, None if c_ns_uri is NULL else c_ns_uri)
|
| 287 |
+
if c_dict is not NULL:
|
| 288 |
+
dict_result = python.PyDict_GetItem(
|
| 289 |
+
<object>c_dict, <unsigned char*>c_name)
|
| 290 |
+
if dict_result is not NULL:
|
| 291 |
+
return <object>dict_result
|
| 292 |
+
return None
|
| 293 |
+
|
| 294 |
+
# Python access to the XPath context for extension functions
|
| 295 |
+
|
| 296 |
+
@property
|
| 297 |
+
def context_node(self):
|
| 298 |
+
cdef xmlNode* c_node
|
| 299 |
+
if self._xpathCtxt is NULL:
|
| 300 |
+
raise XPathError, \
|
| 301 |
+
"XPath context is only usable during the evaluation"
|
| 302 |
+
c_node = self._xpathCtxt.node
|
| 303 |
+
if c_node is NULL:
|
| 304 |
+
raise XPathError, "no context node"
|
| 305 |
+
if c_node.doc != self._xpathCtxt.doc:
|
| 306 |
+
raise XPathError, \
|
| 307 |
+
"document-external context nodes are not supported"
|
| 308 |
+
if self._doc is None:
|
| 309 |
+
raise XPathError, "document context is missing"
|
| 310 |
+
return _elementFactory(self._doc, c_node)
|
| 311 |
+
|
| 312 |
+
@property
|
| 313 |
+
def eval_context(self):
|
| 314 |
+
if self._eval_context_dict is None:
|
| 315 |
+
self._eval_context_dict = {}
|
| 316 |
+
return self._eval_context_dict
|
| 317 |
+
|
| 318 |
+
# Python reference keeping during XPath function evaluation
|
| 319 |
+
|
| 320 |
+
@cython.final
|
| 321 |
+
cdef _release_temp_refs(self):
|
| 322 |
+
"Free temporarily referenced objects from this context."
|
| 323 |
+
self._temp_refs.clear()
|
| 324 |
+
self._temp_documents.clear()
|
| 325 |
+
|
| 326 |
+
@cython.final
|
| 327 |
+
cdef _hold(self, obj):
|
| 328 |
+
"""A way to temporarily hold references to nodes in the evaluator.
|
| 329 |
+
|
| 330 |
+
This is needed because otherwise nodes created in XPath extension
|
| 331 |
+
functions would be reference counted too soon, during the XPath
|
| 332 |
+
evaluation. This is most important in the case of exceptions.
|
| 333 |
+
"""
|
| 334 |
+
cdef _Element element
|
| 335 |
+
if isinstance(obj, _Element):
|
| 336 |
+
self._temp_refs.add(obj)
|
| 337 |
+
self._temp_documents.add((<_Element>obj)._doc)
|
| 338 |
+
return
|
| 339 |
+
elif _isString(obj) or not python.PySequence_Check(obj):
|
| 340 |
+
return
|
| 341 |
+
for o in obj:
|
| 342 |
+
if isinstance(o, _Element):
|
| 343 |
+
#print "Holding element:", <int>element._c_node
|
| 344 |
+
self._temp_refs.add(o)
|
| 345 |
+
#print "Holding document:", <int>element._doc._c_doc
|
| 346 |
+
self._temp_documents.add((<_Element>o)._doc)
|
| 347 |
+
|
| 348 |
+
@cython.final
|
| 349 |
+
cdef _Document _findDocumentForNode(self, xmlNode* c_node):
|
| 350 |
+
"""If an XPath expression returns an element from a different
|
| 351 |
+
document than the current context document, we call this to
|
| 352 |
+
see if it was possibly created by an extension and is a known
|
| 353 |
+
document instance.
|
| 354 |
+
"""
|
| 355 |
+
cdef _Document doc
|
| 356 |
+
for doc in self._temp_documents:
|
| 357 |
+
if doc is not None and doc._c_doc is c_node.doc:
|
| 358 |
+
return doc
|
| 359 |
+
return None
|
| 360 |
+
|
| 361 |
+
|
| 362 |
+
# libxml2 keeps these error messages in a static array in its code
|
| 363 |
+
# and doesn't give us access to them ...
|
| 364 |
+
|
| 365 |
+
cdef tuple LIBXML2_XPATH_ERROR_MESSAGES = (
|
| 366 |
+
b"Ok",
|
| 367 |
+
b"Number encoding",
|
| 368 |
+
b"Unfinished literal",
|
| 369 |
+
b"Start of literal",
|
| 370 |
+
b"Expected $ for variable reference",
|
| 371 |
+
b"Undefined variable",
|
| 372 |
+
b"Invalid predicate",
|
| 373 |
+
b"Invalid expression",
|
| 374 |
+
b"Missing closing curly brace",
|
| 375 |
+
b"Unregistered function",
|
| 376 |
+
b"Invalid operand",
|
| 377 |
+
b"Invalid type",
|
| 378 |
+
b"Invalid number of arguments",
|
| 379 |
+
b"Invalid context size",
|
| 380 |
+
b"Invalid context position",
|
| 381 |
+
b"Memory allocation error",
|
| 382 |
+
b"Syntax error",
|
| 383 |
+
b"Resource error",
|
| 384 |
+
b"Sub resource error",
|
| 385 |
+
b"Undefined namespace prefix",
|
| 386 |
+
b"Encoding error",
|
| 387 |
+
b"Char out of XML range",
|
| 388 |
+
b"Invalid or incomplete context",
|
| 389 |
+
b"Stack usage error",
|
| 390 |
+
b"Forbidden variable\n",
|
| 391 |
+
b"?? Unknown error ??\n",
|
| 392 |
+
)
|
| 393 |
+
|
| 394 |
+
cdef void _forwardXPathError(void* c_ctxt, const xmlerror.xmlError* c_error) noexcept with gil:
|
| 395 |
+
cdef xmlerror.xmlError error
|
| 396 |
+
cdef int xpath_code
|
| 397 |
+
if c_error.message is not NULL:
|
| 398 |
+
error.message = c_error.message
|
| 399 |
+
else:
|
| 400 |
+
xpath_code = c_error.code - xmlerror.XML_XPATH_EXPRESSION_OK
|
| 401 |
+
if 0 <= xpath_code < len(LIBXML2_XPATH_ERROR_MESSAGES):
|
| 402 |
+
error.message = _cstr(LIBXML2_XPATH_ERROR_MESSAGES[xpath_code])
|
| 403 |
+
else:
|
| 404 |
+
error.message = b"unknown error"
|
| 405 |
+
error.domain = c_error.domain
|
| 406 |
+
error.code = c_error.code
|
| 407 |
+
error.level = c_error.level
|
| 408 |
+
error.line = c_error.line
|
| 409 |
+
error.int2 = c_error.int1 # column
|
| 410 |
+
error.file = c_error.file
|
| 411 |
+
error.node = NULL
|
| 412 |
+
|
| 413 |
+
(<_BaseContext>c_ctxt)._error_log._receive(&error)
|
| 414 |
+
|
| 415 |
+
cdef void _receiveXPathError(void* c_context, const xmlerror.xmlError* error) noexcept nogil:
|
| 416 |
+
if not __DEBUG:
|
| 417 |
+
return
|
| 418 |
+
if c_context is NULL:
|
| 419 |
+
_forwardError(NULL, error)
|
| 420 |
+
else:
|
| 421 |
+
_forwardXPathError(c_context, error)
|
| 422 |
+
|
| 423 |
+
|
| 424 |
+
def Extension(module, function_mapping=None, *, ns=None):
|
| 425 |
+
"""Extension(module, function_mapping=None, ns=None)
|
| 426 |
+
|
| 427 |
+
Build a dictionary of extension functions from the functions
|
| 428 |
+
defined in a module or the methods of an object.
|
| 429 |
+
|
| 430 |
+
As second argument, you can pass an additional mapping of
|
| 431 |
+
attribute names to XPath function names, or a list of function
|
| 432 |
+
names that should be taken.
|
| 433 |
+
|
| 434 |
+
The ``ns`` keyword argument accepts a namespace URI for the XPath
|
| 435 |
+
functions.
|
| 436 |
+
"""
|
| 437 |
+
cdef dict functions = {}
|
| 438 |
+
if isinstance(function_mapping, dict):
|
| 439 |
+
for function_name, xpath_name in function_mapping.items():
|
| 440 |
+
functions[(ns, xpath_name)] = getattr(module, function_name)
|
| 441 |
+
else:
|
| 442 |
+
if function_mapping is None:
|
| 443 |
+
function_mapping = [ name for name in dir(module)
|
| 444 |
+
if not name.startswith('_') ]
|
| 445 |
+
for function_name in function_mapping:
|
| 446 |
+
functions[(ns, function_name)] = getattr(module, function_name)
|
| 447 |
+
return functions
|
| 448 |
+
|
| 449 |
+
################################################################################
|
| 450 |
+
# EXSLT regexp implementation
|
| 451 |
+
|
| 452 |
+
@cython.final
|
| 453 |
+
@cython.internal
|
| 454 |
+
cdef class _ExsltRegExp:
|
| 455 |
+
cdef dict _compile_map
|
| 456 |
+
def __cinit__(self):
|
| 457 |
+
self._compile_map = {}
|
| 458 |
+
|
| 459 |
+
cdef _make_string(self, value):
|
| 460 |
+
if _isString(value):
|
| 461 |
+
return value
|
| 462 |
+
elif isinstance(value, list):
|
| 463 |
+
# node set: take recursive text concatenation of first element
|
| 464 |
+
if python.PyList_GET_SIZE(value) == 0:
|
| 465 |
+
return ''
|
| 466 |
+
firstnode = value[0]
|
| 467 |
+
if _isString(firstnode):
|
| 468 |
+
return firstnode
|
| 469 |
+
elif isinstance(firstnode, _Element):
|
| 470 |
+
c_text = tree.xmlNodeGetContent((<_Element>firstnode)._c_node)
|
| 471 |
+
if c_text is NULL:
|
| 472 |
+
raise MemoryError()
|
| 473 |
+
try:
|
| 474 |
+
return funicode(c_text)
|
| 475 |
+
finally:
|
| 476 |
+
tree.xmlFree(c_text)
|
| 477 |
+
else:
|
| 478 |
+
return unicode(firstnode)
|
| 479 |
+
else:
|
| 480 |
+
return unicode(value)
|
| 481 |
+
|
| 482 |
+
cdef _compile(self, rexp, ignore_case):
|
| 483 |
+
cdef python.PyObject* c_result
|
| 484 |
+
rexp = self._make_string(rexp)
|
| 485 |
+
key = (rexp, ignore_case)
|
| 486 |
+
c_result = python.PyDict_GetItem(self._compile_map, key)
|
| 487 |
+
if c_result is not NULL:
|
| 488 |
+
return <object>c_result
|
| 489 |
+
py_flags = re.UNICODE
|
| 490 |
+
if ignore_case:
|
| 491 |
+
py_flags = py_flags | re.IGNORECASE
|
| 492 |
+
rexp_compiled = re.compile(rexp, py_flags)
|
| 493 |
+
self._compile_map[key] = rexp_compiled
|
| 494 |
+
return rexp_compiled
|
| 495 |
+
|
| 496 |
+
def test(self, ctxt, s, rexp, flags=''):
|
| 497 |
+
flags = self._make_string(flags)
|
| 498 |
+
s = self._make_string(s)
|
| 499 |
+
rexpc = self._compile(rexp, 'i' in flags)
|
| 500 |
+
if rexpc.search(s) is None:
|
| 501 |
+
return False
|
| 502 |
+
else:
|
| 503 |
+
return True
|
| 504 |
+
|
| 505 |
+
def match(self, ctxt, s, rexp, flags=''):
|
| 506 |
+
cdef list result_list
|
| 507 |
+
flags = self._make_string(flags)
|
| 508 |
+
s = self._make_string(s)
|
| 509 |
+
rexpc = self._compile(rexp, 'i' in flags)
|
| 510 |
+
if 'g' in flags:
|
| 511 |
+
results = rexpc.findall(s)
|
| 512 |
+
if not results:
|
| 513 |
+
return ()
|
| 514 |
+
else:
|
| 515 |
+
result = rexpc.search(s)
|
| 516 |
+
if not result:
|
| 517 |
+
return ()
|
| 518 |
+
results = [ result.group() ]
|
| 519 |
+
results.extend( result.groups('') )
|
| 520 |
+
result_list = []
|
| 521 |
+
root = Element('matches')
|
| 522 |
+
for s_match in results:
|
| 523 |
+
if python.PyTuple_CheckExact(s_match):
|
| 524 |
+
s_match = ''.join(s_match)
|
| 525 |
+
elem = SubElement(root, 'match')
|
| 526 |
+
elem.text = s_match
|
| 527 |
+
result_list.append(elem)
|
| 528 |
+
return result_list
|
| 529 |
+
|
| 530 |
+
def replace(self, ctxt, s, rexp, flags, replacement):
|
| 531 |
+
replacement = self._make_string(replacement)
|
| 532 |
+
flags = self._make_string(flags)
|
| 533 |
+
s = self._make_string(s)
|
| 534 |
+
rexpc = self._compile(rexp, 'i' in flags)
|
| 535 |
+
count: object = 0 if 'g' in flags else 1
|
| 536 |
+
return rexpc.sub(replacement, s, count)
|
| 537 |
+
|
| 538 |
+
cdef _register_in_context(self, _BaseContext context):
|
| 539 |
+
ns = b"http://exslt.org/regular-expressions"
|
| 540 |
+
context._addLocalExtensionFunction(ns, b"test", self.test)
|
| 541 |
+
context._addLocalExtensionFunction(ns, b"match", self.match)
|
| 542 |
+
context._addLocalExtensionFunction(ns, b"replace", self.replace)
|
| 543 |
+
|
| 544 |
+
|
| 545 |
+
################################################################################
|
| 546 |
+
# helper functions
|
| 547 |
+
|
| 548 |
+
cdef xpath.xmlXPathObject* _wrapXPathObject(object obj, _Document doc,
|
| 549 |
+
_BaseContext context) except NULL:
|
| 550 |
+
cdef xpath.xmlNodeSet* resultSet
|
| 551 |
+
cdef _Element fake_node = None
|
| 552 |
+
cdef xmlNode* c_node
|
| 553 |
+
|
| 554 |
+
if isinstance(obj, unicode):
|
| 555 |
+
obj = _utf8(obj)
|
| 556 |
+
if isinstance(obj, bytes):
|
| 557 |
+
# libxml2 copies the string value
|
| 558 |
+
return xpath.xmlXPathNewCString(_cstr(obj))
|
| 559 |
+
if isinstance(obj, bool):
|
| 560 |
+
return xpath.xmlXPathNewBoolean(obj)
|
| 561 |
+
if python.PyNumber_Check(obj):
|
| 562 |
+
return xpath.xmlXPathNewFloat(obj)
|
| 563 |
+
if obj is None:
|
| 564 |
+
resultSet = xpath.xmlXPathNodeSetCreate(NULL)
|
| 565 |
+
elif isinstance(obj, _Element):
|
| 566 |
+
resultSet = xpath.xmlXPathNodeSetCreate((<_Element>obj)._c_node)
|
| 567 |
+
elif python.PySequence_Check(obj):
|
| 568 |
+
resultSet = xpath.xmlXPathNodeSetCreate(NULL)
|
| 569 |
+
try:
|
| 570 |
+
for value in obj:
|
| 571 |
+
if isinstance(value, _Element):
|
| 572 |
+
if context is not None:
|
| 573 |
+
context._hold(value)
|
| 574 |
+
xpath.xmlXPathNodeSetAdd(resultSet, (<_Element>value)._c_node)
|
| 575 |
+
else:
|
| 576 |
+
if context is None or doc is None:
|
| 577 |
+
raise XPathResultError, \
|
| 578 |
+
f"Non-Element values not supported at this point - got {value!r}"
|
| 579 |
+
# support strings by appending text nodes to an Element
|
| 580 |
+
if isinstance(value, unicode):
|
| 581 |
+
value = _utf8(value)
|
| 582 |
+
if isinstance(value, bytes):
|
| 583 |
+
if fake_node is None:
|
| 584 |
+
fake_node = _makeElement("text-root", NULL, doc, None,
|
| 585 |
+
None, None, None, None, None)
|
| 586 |
+
context._hold(fake_node)
|
| 587 |
+
else:
|
| 588 |
+
# append a comment node to keep the text nodes separate
|
| 589 |
+
c_node = tree.xmlNewDocComment(doc._c_doc, <unsigned char*>"")
|
| 590 |
+
if c_node is NULL:
|
| 591 |
+
raise MemoryError()
|
| 592 |
+
tree.xmlAddChild(fake_node._c_node, c_node)
|
| 593 |
+
context._hold(value)
|
| 594 |
+
c_node = tree.xmlNewDocText(doc._c_doc, _xcstr(value))
|
| 595 |
+
if c_node is NULL:
|
| 596 |
+
raise MemoryError()
|
| 597 |
+
tree.xmlAddChild(fake_node._c_node, c_node)
|
| 598 |
+
xpath.xmlXPathNodeSetAdd(resultSet, c_node)
|
| 599 |
+
else:
|
| 600 |
+
raise XPathResultError, \
|
| 601 |
+
f"This is not a supported node-set result: {value!r}"
|
| 602 |
+
except:
|
| 603 |
+
xpath.xmlXPathFreeNodeSet(resultSet)
|
| 604 |
+
raise
|
| 605 |
+
else:
|
| 606 |
+
raise XPathResultError, f"Unknown return type: {python._fqtypename(obj).decode('utf8')}"
|
| 607 |
+
return xpath.xmlXPathWrapNodeSet(resultSet)
|
| 608 |
+
|
| 609 |
+
cdef object _unwrapXPathObject(xpath.xmlXPathObject* xpathObj,
|
| 610 |
+
_Document doc, _BaseContext context):
|
| 611 |
+
if xpathObj.type == xpath.XPATH_UNDEFINED:
|
| 612 |
+
raise XPathResultError, "Undefined xpath result"
|
| 613 |
+
elif xpathObj.type == xpath.XPATH_NODESET:
|
| 614 |
+
return _createNodeSetResult(xpathObj, doc, context)
|
| 615 |
+
elif xpathObj.type == xpath.XPATH_BOOLEAN:
|
| 616 |
+
return xpathObj.boolval
|
| 617 |
+
elif xpathObj.type == xpath.XPATH_NUMBER:
|
| 618 |
+
return xpathObj.floatval
|
| 619 |
+
elif xpathObj.type == xpath.XPATH_STRING:
|
| 620 |
+
stringval = funicode(xpathObj.stringval)
|
| 621 |
+
if context._build_smart_strings:
|
| 622 |
+
stringval = _elementStringResultFactory(
|
| 623 |
+
stringval, None, None, False)
|
| 624 |
+
return stringval
|
| 625 |
+
elif xpathObj.type == xpath.XPATH_POINT:
|
| 626 |
+
raise NotImplementedError, "XPATH_POINT"
|
| 627 |
+
elif xpathObj.type == xpath.XPATH_RANGE:
|
| 628 |
+
raise NotImplementedError, "XPATH_RANGE"
|
| 629 |
+
elif xpathObj.type == xpath.XPATH_LOCATIONSET:
|
| 630 |
+
raise NotImplementedError, "XPATH_LOCATIONSET"
|
| 631 |
+
elif xpathObj.type == xpath.XPATH_USERS:
|
| 632 |
+
raise NotImplementedError, "XPATH_USERS"
|
| 633 |
+
elif xpathObj.type == xpath.XPATH_XSLT_TREE:
|
| 634 |
+
return _createNodeSetResult(xpathObj, doc, context)
|
| 635 |
+
else:
|
| 636 |
+
raise XPathResultError, f"Unknown xpath result {xpathObj.type}"
|
| 637 |
+
|
| 638 |
+
cdef object _createNodeSetResult(xpath.xmlXPathObject* xpathObj, _Document doc,
|
| 639 |
+
_BaseContext context):
|
| 640 |
+
cdef xmlNode* c_node
|
| 641 |
+
cdef int i
|
| 642 |
+
cdef list result
|
| 643 |
+
result = []
|
| 644 |
+
if xpathObj.nodesetval is NULL:
|
| 645 |
+
return result
|
| 646 |
+
for i in range(xpathObj.nodesetval.nodeNr):
|
| 647 |
+
c_node = xpathObj.nodesetval.nodeTab[i]
|
| 648 |
+
_unpackNodeSetEntry(result, c_node, doc, context,
|
| 649 |
+
xpathObj.type == xpath.XPATH_XSLT_TREE)
|
| 650 |
+
return result
|
| 651 |
+
|
| 652 |
+
cdef _unpackNodeSetEntry(list results, xmlNode* c_node, _Document doc,
|
| 653 |
+
_BaseContext context, bint is_fragment):
|
| 654 |
+
cdef xmlNode* c_child
|
| 655 |
+
if _isElement(c_node):
|
| 656 |
+
if c_node.doc != doc._c_doc and c_node.doc._private is NULL:
|
| 657 |
+
# XXX: works, but maybe not always the right thing to do?
|
| 658 |
+
# XPath: only runs when extensions create or copy trees
|
| 659 |
+
# -> we store Python refs to these, so that is OK
|
| 660 |
+
# XSLT: can it leak when merging trees from multiple sources?
|
| 661 |
+
c_node = tree.xmlDocCopyNode(c_node, doc._c_doc, 1)
|
| 662 |
+
# FIXME: call _instantiateElementFromXPath() instead?
|
| 663 |
+
results.append(
|
| 664 |
+
_fakeDocElementFactory(doc, c_node))
|
| 665 |
+
elif c_node.type == tree.XML_TEXT_NODE or \
|
| 666 |
+
c_node.type == tree.XML_CDATA_SECTION_NODE or \
|
| 667 |
+
c_node.type == tree.XML_ATTRIBUTE_NODE:
|
| 668 |
+
results.append(
|
| 669 |
+
_buildElementStringResult(doc, c_node, context))
|
| 670 |
+
elif c_node.type == tree.XML_NAMESPACE_DECL:
|
| 671 |
+
results.append( (funicodeOrNone((<xmlNs*>c_node).prefix),
|
| 672 |
+
funicodeOrNone((<xmlNs*>c_node).href)) )
|
| 673 |
+
elif c_node.type == tree.XML_DOCUMENT_NODE or \
|
| 674 |
+
c_node.type == tree.XML_HTML_DOCUMENT_NODE:
|
| 675 |
+
# ignored for everything but result tree fragments
|
| 676 |
+
if is_fragment:
|
| 677 |
+
c_child = c_node.children
|
| 678 |
+
while c_child is not NULL:
|
| 679 |
+
_unpackNodeSetEntry(results, c_child, doc, context, 0)
|
| 680 |
+
c_child = c_child.next
|
| 681 |
+
elif c_node.type == tree.XML_XINCLUDE_START or \
|
| 682 |
+
c_node.type == tree.XML_XINCLUDE_END:
|
| 683 |
+
pass
|
| 684 |
+
else:
|
| 685 |
+
raise NotImplementedError, \
|
| 686 |
+
f"Not yet implemented result node type: {c_node.type}"
|
| 687 |
+
|
| 688 |
+
cdef void _freeXPathObject(xpath.xmlXPathObject* xpathObj) noexcept:
|
| 689 |
+
"""Free the XPath object, but *never* free the *content* of node sets.
|
| 690 |
+
Python dealloc will do that for us.
|
| 691 |
+
"""
|
| 692 |
+
if xpathObj.nodesetval is not NULL:
|
| 693 |
+
xpath.xmlXPathFreeNodeSet(xpathObj.nodesetval)
|
| 694 |
+
xpathObj.nodesetval = NULL
|
| 695 |
+
xpath.xmlXPathFreeObject(xpathObj)
|
| 696 |
+
|
| 697 |
+
cdef _Element _instantiateElementFromXPath(xmlNode* c_node, _Document doc,
|
| 698 |
+
_BaseContext context):
|
| 699 |
+
# NOTE: this may copy the element - only call this when it can't leak
|
| 700 |
+
if c_node.doc != doc._c_doc and c_node.doc._private is NULL:
|
| 701 |
+
# not from the context document and not from a fake document
|
| 702 |
+
# either => may still be from a known document, e.g. one
|
| 703 |
+
# created by an extension function
|
| 704 |
+
node_doc = context._findDocumentForNode(c_node)
|
| 705 |
+
if node_doc is None:
|
| 706 |
+
# not from a known document at all! => can only make a
|
| 707 |
+
# safety copy here
|
| 708 |
+
c_node = tree.xmlDocCopyNode(c_node, doc._c_doc, 1)
|
| 709 |
+
else:
|
| 710 |
+
doc = node_doc
|
| 711 |
+
return _fakeDocElementFactory(doc, c_node)
|
| 712 |
+
|
| 713 |
+
################################################################################
|
| 714 |
+
# special str/unicode subclasses
|
| 715 |
+
|
| 716 |
+
@cython.final
|
| 717 |
+
cdef class _ElementUnicodeResult(unicode):
|
| 718 |
+
cdef _Element _parent
|
| 719 |
+
cdef readonly object attrname
|
| 720 |
+
cdef readonly bint is_tail
|
| 721 |
+
|
| 722 |
+
def getparent(self):
|
| 723 |
+
return self._parent
|
| 724 |
+
|
| 725 |
+
@property
|
| 726 |
+
def is_text(self):
|
| 727 |
+
return self._parent is not None and not (self.is_tail or self.attrname is not None)
|
| 728 |
+
|
| 729 |
+
@property
|
| 730 |
+
def is_attribute(self):
|
| 731 |
+
return self.attrname is not None
|
| 732 |
+
|
| 733 |
+
cdef object _elementStringResultFactory(string_value, _Element parent,
|
| 734 |
+
attrname, bint is_tail):
|
| 735 |
+
result = _ElementUnicodeResult(string_value)
|
| 736 |
+
result._parent = parent
|
| 737 |
+
result.is_tail = is_tail
|
| 738 |
+
result.attrname = attrname
|
| 739 |
+
return result
|
| 740 |
+
|
| 741 |
+
cdef object _buildElementStringResult(_Document doc, xmlNode* c_node,
|
| 742 |
+
_BaseContext context):
|
| 743 |
+
cdef _Element parent = None
|
| 744 |
+
cdef object attrname = None
|
| 745 |
+
cdef xmlNode* c_element
|
| 746 |
+
cdef bint is_tail
|
| 747 |
+
|
| 748 |
+
if c_node.type == tree.XML_ATTRIBUTE_NODE:
|
| 749 |
+
attrname = _namespacedName(c_node)
|
| 750 |
+
is_tail = 0
|
| 751 |
+
s = tree.xmlNodeGetContent(c_node)
|
| 752 |
+
try:
|
| 753 |
+
value = funicode(s)
|
| 754 |
+
finally:
|
| 755 |
+
tree.xmlFree(s)
|
| 756 |
+
c_element = NULL
|
| 757 |
+
else:
|
| 758 |
+
#assert c_node.type == tree.XML_TEXT_NODE or c_node.type == tree.XML_CDATA_SECTION_NODE, "invalid node type"
|
| 759 |
+
# may be tail text or normal text
|
| 760 |
+
value = funicode(c_node.content)
|
| 761 |
+
c_element = _previousElement(c_node)
|
| 762 |
+
is_tail = c_element is not NULL
|
| 763 |
+
|
| 764 |
+
if not context._build_smart_strings:
|
| 765 |
+
return value
|
| 766 |
+
|
| 767 |
+
if c_element is NULL:
|
| 768 |
+
# non-tail text or attribute text
|
| 769 |
+
c_element = c_node.parent
|
| 770 |
+
while c_element is not NULL and not _isElement(c_element):
|
| 771 |
+
c_element = c_element.parent
|
| 772 |
+
|
| 773 |
+
if c_element is not NULL:
|
| 774 |
+
parent = _instantiateElementFromXPath(c_element, doc, context)
|
| 775 |
+
|
| 776 |
+
return _elementStringResultFactory(
|
| 777 |
+
value, parent, attrname, is_tail)
|
| 778 |
+
|
| 779 |
+
################################################################################
|
| 780 |
+
# callbacks for XPath/XSLT extension functions
|
| 781 |
+
|
| 782 |
+
cdef void _extension_function_call(_BaseContext context, function,
|
| 783 |
+
xpath.xmlXPathParserContext* ctxt, int nargs) noexcept:
|
| 784 |
+
cdef _Document doc
|
| 785 |
+
cdef xpath.xmlXPathObject* obj
|
| 786 |
+
cdef list args
|
| 787 |
+
cdef int i
|
| 788 |
+
doc = context._doc
|
| 789 |
+
try:
|
| 790 |
+
args = []
|
| 791 |
+
for i in range(nargs):
|
| 792 |
+
obj = xpath.valuePop(ctxt)
|
| 793 |
+
o = _unwrapXPathObject(obj, doc, context)
|
| 794 |
+
_freeXPathObject(obj)
|
| 795 |
+
args.append(o)
|
| 796 |
+
args.reverse()
|
| 797 |
+
|
| 798 |
+
res = function(context, *args)
|
| 799 |
+
# wrap result for XPath consumption
|
| 800 |
+
obj = _wrapXPathObject(res, doc, context)
|
| 801 |
+
# prevent Python from deallocating elements handed to libxml2
|
| 802 |
+
context._hold(res)
|
| 803 |
+
xpath.valuePush(ctxt, obj)
|
| 804 |
+
except:
|
| 805 |
+
xpath.xmlXPathErr(ctxt, xpath.XPATH_EXPR_ERROR)
|
| 806 |
+
context._exc._store_raised()
|
| 807 |
+
finally:
|
| 808 |
+
return # swallow any further exceptions
|
| 809 |
+
|
| 810 |
+
# lookup the function by name and call it
|
| 811 |
+
|
| 812 |
+
cdef void _xpath_function_call(xpath.xmlXPathParserContext* ctxt,
|
| 813 |
+
int nargs) noexcept with gil:
|
| 814 |
+
cdef _BaseContext context
|
| 815 |
+
cdef xpath.xmlXPathContext* rctxt = ctxt.context
|
| 816 |
+
context = <_BaseContext> rctxt.userData
|
| 817 |
+
try:
|
| 818 |
+
function = context._find_cached_function(rctxt.functionURI, rctxt.function)
|
| 819 |
+
if function is not None:
|
| 820 |
+
_extension_function_call(context, function, ctxt, nargs)
|
| 821 |
+
else:
|
| 822 |
+
xpath.xmlXPathErr(ctxt, xpath.XPATH_UNKNOWN_FUNC_ERROR)
|
| 823 |
+
context._exc._store_exception(XPathFunctionError(
|
| 824 |
+
f"XPath function '{_namespacedNameFromNsName(rctxt.functionURI, rctxt.function)}' not found"))
|
| 825 |
+
except:
|
| 826 |
+
# may not be the right error, but we need to tell libxml2 *something*
|
| 827 |
+
xpath.xmlXPathErr(ctxt, xpath.XPATH_UNKNOWN_FUNC_ERROR)
|
| 828 |
+
context._exc._store_raised()
|
| 829 |
+
finally:
|
| 830 |
+
return # swallow any further exceptions
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/lxml.etree.h
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* Generated by Cython 3.1.4 */
|
| 2 |
+
|
| 3 |
+
#ifndef __PYX_HAVE__lxml__etree
|
| 4 |
+
#define __PYX_HAVE__lxml__etree
|
| 5 |
+
|
| 6 |
+
#include "Python.h"
|
| 7 |
+
struct LxmlDocument;
|
| 8 |
+
struct LxmlElement;
|
| 9 |
+
struct LxmlElementTree;
|
| 10 |
+
struct LxmlElementTagMatcher;
|
| 11 |
+
struct LxmlElementIterator;
|
| 12 |
+
struct LxmlElementBase;
|
| 13 |
+
struct LxmlElementClassLookup;
|
| 14 |
+
struct LxmlFallbackElementClassLookup;
|
| 15 |
+
|
| 16 |
+
/* "lxml/etree.pyx":451
|
| 17 |
+
*
|
| 18 |
+
* # type of a function that steps from node to node
|
| 19 |
+
* ctypedef public xmlNode* (*_node_to_node_function)(xmlNode*) # <<<<<<<<<<<<<<
|
| 20 |
+
*
|
| 21 |
+
*
|
| 22 |
+
*/
|
| 23 |
+
typedef xmlNode *(*_node_to_node_function)(xmlNode *);
|
| 24 |
+
|
| 25 |
+
/* "lxml/etree.pyx":465
|
| 26 |
+
* # Public Python API
|
| 27 |
+
*
|
| 28 |
+
* @cython.final # <<<<<<<<<<<<<<
|
| 29 |
+
* @cython.freelist(8)
|
| 30 |
+
* cdef public class _Document [ type LxmlDocumentType, object LxmlDocument ]:
|
| 31 |
+
*/
|
| 32 |
+
struct LxmlDocument {
|
| 33 |
+
PyObject_HEAD
|
| 34 |
+
struct __pyx_vtabstruct_4lxml_5etree__Document *__pyx_vtab;
|
| 35 |
+
int _ns_counter;
|
| 36 |
+
PyObject *_prefix_tail;
|
| 37 |
+
xmlDoc *_c_doc;
|
| 38 |
+
struct __pyx_obj_4lxml_5etree__BaseParser *_parser;
|
| 39 |
+
};
|
| 40 |
+
|
| 41 |
+
/* "lxml/etree.pyx":817
|
| 42 |
+
*
|
| 43 |
+
*
|
| 44 |
+
* @cython.no_gc_clear # <<<<<<<<<<<<<<
|
| 45 |
+
* cdef public class _Element [ type LxmlElementType, object LxmlElement ]:
|
| 46 |
+
* """Element class.
|
| 47 |
+
*/
|
| 48 |
+
struct LxmlElement {
|
| 49 |
+
PyObject_HEAD
|
| 50 |
+
struct LxmlDocument *_doc;
|
| 51 |
+
xmlNode *_c_node;
|
| 52 |
+
PyObject *_tag;
|
| 53 |
+
};
|
| 54 |
+
|
| 55 |
+
/* "lxml/etree.pyx":1991
|
| 56 |
+
*
|
| 57 |
+
*
|
| 58 |
+
* cdef public class _ElementTree [ type LxmlElementTreeType, # <<<<<<<<<<<<<<
|
| 59 |
+
* object LxmlElementTree ]:
|
| 60 |
+
* cdef _Document _doc
|
| 61 |
+
*/
|
| 62 |
+
struct LxmlElementTree {
|
| 63 |
+
PyObject_HEAD
|
| 64 |
+
struct __pyx_vtabstruct_4lxml_5etree__ElementTree *__pyx_vtab;
|
| 65 |
+
struct LxmlDocument *_doc;
|
| 66 |
+
struct LxmlElement *_context_node;
|
| 67 |
+
};
|
| 68 |
+
|
| 69 |
+
/* "lxml/etree.pyx":2765
|
| 70 |
+
*
|
| 71 |
+
*
|
| 72 |
+
* cdef public class _ElementTagMatcher [ object LxmlElementTagMatcher, # <<<<<<<<<<<<<<
|
| 73 |
+
* type LxmlElementTagMatcherType ]:
|
| 74 |
+
* """
|
| 75 |
+
*/
|
| 76 |
+
struct LxmlElementTagMatcher {
|
| 77 |
+
PyObject_HEAD
|
| 78 |
+
struct __pyx_vtabstruct_4lxml_5etree__ElementTagMatcher *__pyx_vtab;
|
| 79 |
+
PyObject *_pystrings;
|
| 80 |
+
int _node_type;
|
| 81 |
+
char *_href;
|
| 82 |
+
char *_name;
|
| 83 |
+
};
|
| 84 |
+
|
| 85 |
+
/* "lxml/etree.pyx":2796
|
| 86 |
+
* self._name = NULL
|
| 87 |
+
*
|
| 88 |
+
* cdef public class _ElementIterator(_ElementTagMatcher) [ # <<<<<<<<<<<<<<
|
| 89 |
+
* object LxmlElementIterator, type LxmlElementIteratorType ]:
|
| 90 |
+
* """
|
| 91 |
+
*/
|
| 92 |
+
struct LxmlElementIterator {
|
| 93 |
+
struct LxmlElementTagMatcher __pyx_base;
|
| 94 |
+
struct LxmlElement *_node;
|
| 95 |
+
_node_to_node_function _next_element;
|
| 96 |
+
};
|
| 97 |
+
|
| 98 |
+
/* "src/lxml/classlookup.pxi":6
|
| 99 |
+
* # Custom Element classes
|
| 100 |
+
*
|
| 101 |
+
* cdef public class ElementBase(_Element) [ type LxmlElementBaseType, # <<<<<<<<<<<<<<
|
| 102 |
+
* object LxmlElementBase ]:
|
| 103 |
+
* """ElementBase(*children, attrib=None, nsmap=None, **_extra)
|
| 104 |
+
*/
|
| 105 |
+
struct LxmlElementBase {
|
| 106 |
+
struct LxmlElement __pyx_base;
|
| 107 |
+
};
|
| 108 |
+
|
| 109 |
+
/* "src/lxml/classlookup.pxi":210
|
| 110 |
+
* # Element class lookup
|
| 111 |
+
*
|
| 112 |
+
* ctypedef public object (*_element_class_lookup_function)(object, _Document, xmlNode*) # <<<<<<<<<<<<<<
|
| 113 |
+
*
|
| 114 |
+
* # class to store element class lookup functions
|
| 115 |
+
*/
|
| 116 |
+
typedef PyObject *(*_element_class_lookup_function)(PyObject *, struct LxmlDocument *, xmlNode *);
|
| 117 |
+
|
| 118 |
+
/* "src/lxml/classlookup.pxi":213
|
| 119 |
+
*
|
| 120 |
+
* # class to store element class lookup functions
|
| 121 |
+
* cdef public class ElementClassLookup [ type LxmlElementClassLookupType, # <<<<<<<<<<<<<<
|
| 122 |
+
* object LxmlElementClassLookup ]:
|
| 123 |
+
* """ElementClassLookup(self)
|
| 124 |
+
*/
|
| 125 |
+
struct LxmlElementClassLookup {
|
| 126 |
+
PyObject_HEAD
|
| 127 |
+
_element_class_lookup_function _lookup_function;
|
| 128 |
+
};
|
| 129 |
+
|
| 130 |
+
/* "src/lxml/classlookup.pxi":221
|
| 131 |
+
*
|
| 132 |
+
*
|
| 133 |
+
* cdef public class FallbackElementClassLookup(ElementClassLookup) \ # <<<<<<<<<<<<<<
|
| 134 |
+
* [ type LxmlFallbackElementClassLookupType,
|
| 135 |
+
* object LxmlFallbackElementClassLookup ]:
|
| 136 |
+
*/
|
| 137 |
+
struct LxmlFallbackElementClassLookup {
|
| 138 |
+
struct LxmlElementClassLookup __pyx_base;
|
| 139 |
+
struct __pyx_vtabstruct_4lxml_5etree_FallbackElementClassLookup *__pyx_vtab;
|
| 140 |
+
struct LxmlElementClassLookup *fallback;
|
| 141 |
+
_element_class_lookup_function _fallback_function;
|
| 142 |
+
};
|
| 143 |
+
|
| 144 |
+
#ifndef __PYX_HAVE_API__lxml__etree
|
| 145 |
+
|
| 146 |
+
#ifdef CYTHON_EXTERN_C
|
| 147 |
+
#undef __PYX_EXTERN_C
|
| 148 |
+
#define __PYX_EXTERN_C CYTHON_EXTERN_C
|
| 149 |
+
#elif defined(__PYX_EXTERN_C)
|
| 150 |
+
#ifdef _MSC_VER
|
| 151 |
+
#pragma message ("Please do not define the '__PYX_EXTERN_C' macro externally. Use 'CYTHON_EXTERN_C' instead.")
|
| 152 |
+
#else
|
| 153 |
+
#warning Please do not define the '__PYX_EXTERN_C' macro externally. Use 'CYTHON_EXTERN_C' instead.
|
| 154 |
+
#endif
|
| 155 |
+
#else
|
| 156 |
+
#ifdef __cplusplus
|
| 157 |
+
#define __PYX_EXTERN_C extern "C"
|
| 158 |
+
#else
|
| 159 |
+
#define __PYX_EXTERN_C extern
|
| 160 |
+
#endif
|
| 161 |
+
#endif
|
| 162 |
+
|
| 163 |
+
#ifndef DL_IMPORT
|
| 164 |
+
#define DL_IMPORT(_T) _T
|
| 165 |
+
#endif
|
| 166 |
+
|
| 167 |
+
__PYX_EXTERN_C DL_IMPORT(PyTypeObject) LxmlDocumentType;
|
| 168 |
+
__PYX_EXTERN_C DL_IMPORT(PyTypeObject) LxmlElementType;
|
| 169 |
+
__PYX_EXTERN_C DL_IMPORT(PyTypeObject) LxmlElementTreeType;
|
| 170 |
+
__PYX_EXTERN_C DL_IMPORT(PyTypeObject) LxmlElementTagMatcherType;
|
| 171 |
+
__PYX_EXTERN_C DL_IMPORT(PyTypeObject) LxmlElementIteratorType;
|
| 172 |
+
__PYX_EXTERN_C DL_IMPORT(PyTypeObject) LxmlElementBaseType;
|
| 173 |
+
__PYX_EXTERN_C DL_IMPORT(PyTypeObject) LxmlElementClassLookupType;
|
| 174 |
+
__PYX_EXTERN_C DL_IMPORT(PyTypeObject) LxmlFallbackElementClassLookupType;
|
| 175 |
+
|
| 176 |
+
__PYX_EXTERN_C struct LxmlElement *deepcopyNodeToDocument(struct LxmlDocument *, xmlNode *);
|
| 177 |
+
__PYX_EXTERN_C struct LxmlElementTree *elementTreeFactory(struct LxmlElement *);
|
| 178 |
+
__PYX_EXTERN_C struct LxmlElementTree *newElementTree(struct LxmlElement *, PyObject *);
|
| 179 |
+
__PYX_EXTERN_C struct LxmlElementTree *adoptExternalDocument(xmlDoc *, PyObject *, int);
|
| 180 |
+
__PYX_EXTERN_C struct LxmlElement *elementFactory(struct LxmlDocument *, xmlNode *);
|
| 181 |
+
__PYX_EXTERN_C struct LxmlElement *makeElement(PyObject *, struct LxmlDocument *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *);
|
| 182 |
+
__PYX_EXTERN_C struct LxmlElement *makeSubElement(struct LxmlElement *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *);
|
| 183 |
+
__PYX_EXTERN_C void setElementClassLookupFunction(_element_class_lookup_function, PyObject *);
|
| 184 |
+
__PYX_EXTERN_C PyObject *lookupDefaultElementClass(PyObject *, PyObject *, xmlNode *);
|
| 185 |
+
__PYX_EXTERN_C PyObject *lookupNamespaceElementClass(PyObject *, PyObject *, xmlNode *);
|
| 186 |
+
__PYX_EXTERN_C PyObject *callLookupFallback(struct LxmlFallbackElementClassLookup *, struct LxmlDocument *, xmlNode *);
|
| 187 |
+
__PYX_EXTERN_C int tagMatches(xmlNode *, const xmlChar *, const xmlChar *);
|
| 188 |
+
__PYX_EXTERN_C struct LxmlDocument *documentOrRaise(PyObject *);
|
| 189 |
+
__PYX_EXTERN_C struct LxmlElement *rootNodeOrRaise(PyObject *);
|
| 190 |
+
__PYX_EXTERN_C int hasText(xmlNode *);
|
| 191 |
+
__PYX_EXTERN_C int hasTail(xmlNode *);
|
| 192 |
+
__PYX_EXTERN_C PyObject *textOf(xmlNode *);
|
| 193 |
+
__PYX_EXTERN_C PyObject *tailOf(xmlNode *);
|
| 194 |
+
__PYX_EXTERN_C int setNodeText(xmlNode *, PyObject *);
|
| 195 |
+
__PYX_EXTERN_C int setTailText(xmlNode *, PyObject *);
|
| 196 |
+
__PYX_EXTERN_C PyObject *attributeValue(xmlNode *, xmlAttr *);
|
| 197 |
+
__PYX_EXTERN_C PyObject *attributeValueFromNsName(xmlNode *, const xmlChar *, const xmlChar *);
|
| 198 |
+
__PYX_EXTERN_C PyObject *getAttributeValue(struct LxmlElement *, PyObject *, PyObject *);
|
| 199 |
+
__PYX_EXTERN_C PyObject *iterattributes(struct LxmlElement *, int);
|
| 200 |
+
__PYX_EXTERN_C PyObject *collectAttributes(xmlNode *, int);
|
| 201 |
+
__PYX_EXTERN_C int setAttributeValue(struct LxmlElement *, PyObject *, PyObject *);
|
| 202 |
+
__PYX_EXTERN_C int delAttribute(struct LxmlElement *, PyObject *);
|
| 203 |
+
__PYX_EXTERN_C int delAttributeFromNsName(xmlNode *, const xmlChar *, const xmlChar *);
|
| 204 |
+
__PYX_EXTERN_C int hasChild(xmlNode *);
|
| 205 |
+
__PYX_EXTERN_C xmlNode *findChild(xmlNode *, Py_ssize_t);
|
| 206 |
+
__PYX_EXTERN_C xmlNode *findChildForwards(xmlNode *, Py_ssize_t);
|
| 207 |
+
__PYX_EXTERN_C xmlNode *findChildBackwards(xmlNode *, Py_ssize_t);
|
| 208 |
+
__PYX_EXTERN_C xmlNode *nextElement(xmlNode *);
|
| 209 |
+
__PYX_EXTERN_C xmlNode *previousElement(xmlNode *);
|
| 210 |
+
__PYX_EXTERN_C void appendChild(struct LxmlElement *, struct LxmlElement *);
|
| 211 |
+
__PYX_EXTERN_C int appendChildToElement(struct LxmlElement *, struct LxmlElement *);
|
| 212 |
+
__PYX_EXTERN_C PyObject *pyunicode(const xmlChar *);
|
| 213 |
+
__PYX_EXTERN_C PyObject *utf8(PyObject *);
|
| 214 |
+
__PYX_EXTERN_C PyObject *getNsTag(PyObject *);
|
| 215 |
+
__PYX_EXTERN_C PyObject *getNsTagWithEmptyNs(PyObject *);
|
| 216 |
+
__PYX_EXTERN_C PyObject *namespacedName(xmlNode *);
|
| 217 |
+
__PYX_EXTERN_C PyObject *namespacedNameFromNsName(const xmlChar *, const xmlChar *);
|
| 218 |
+
__PYX_EXTERN_C void iteratorStoreNext(struct LxmlElementIterator *, struct LxmlElement *);
|
| 219 |
+
__PYX_EXTERN_C void initTagMatch(struct LxmlElementTagMatcher *, PyObject *);
|
| 220 |
+
__PYX_EXTERN_C xmlNs *findOrBuildNodeNsPrefix(struct LxmlDocument *, xmlNode *, const xmlChar *, const xmlChar *);
|
| 221 |
+
|
| 222 |
+
#endif /* !__PYX_HAVE_API__lxml__etree */
|
| 223 |
+
|
| 224 |
+
/* WARNING: the interface of the module init function changed in CPython 3.5. */
|
| 225 |
+
/* It now returns a PyModuleDef instance instead of a PyModule instance. */
|
| 226 |
+
|
| 227 |
+
/* WARNING: Use PyImport_AppendInittab("etree", PyInit_etree) instead of calling PyInit_etree directly from Python 3.5 */
|
| 228 |
+
PyMODINIT_FUNC PyInit_etree(void);
|
| 229 |
+
|
| 230 |
+
#if PY_VERSION_HEX >= 0x03050000 && (defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER) || (defined(__cplusplus) && __cplusplus >= 201402L))
|
| 231 |
+
#if defined(__cplusplus) && __cplusplus >= 201402L
|
| 232 |
+
[[deprecated("Use PyImport_AppendInittab(\"etree\", PyInit_etree) instead of calling PyInit_etree directly.")]] inline
|
| 233 |
+
#elif defined(__GNUC__) || defined(__clang__)
|
| 234 |
+
__attribute__ ((__deprecated__("Use PyImport_AppendInittab(\"etree\", PyInit_etree) instead of calling PyInit_etree directly."), __unused__)) __inline__
|
| 235 |
+
#elif defined(_MSC_VER)
|
| 236 |
+
__declspec(deprecated("Use PyImport_AppendInittab(\"etree\", PyInit_etree) instead of calling PyInit_etree directly.")) __inline
|
| 237 |
+
#endif
|
| 238 |
+
static PyObject* __PYX_WARN_IF_PyInit_etree_INIT_CALLED(PyObject* res) {
|
| 239 |
+
return res;
|
| 240 |
+
}
|
| 241 |
+
#define PyInit_etree() __PYX_WARN_IF_PyInit_etree_INIT_CALLED(PyInit_etree())
|
| 242 |
+
#endif
|
| 243 |
+
|
| 244 |
+
#endif /* !__PYX_HAVE__lxml__etree */
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/objectpath.pxi
ADDED
|
@@ -0,0 +1,332 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
################################################################################
|
| 2 |
+
# ObjectPath
|
| 3 |
+
|
| 4 |
+
ctypedef struct _ObjectPath:
|
| 5 |
+
const_xmlChar* href
|
| 6 |
+
const_xmlChar* name
|
| 7 |
+
Py_ssize_t index
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
cdef object _NO_DEFAULT = object()
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
cdef class ObjectPath:
|
| 14 |
+
"""ObjectPath(path)
|
| 15 |
+
Immutable object that represents a compiled object path.
|
| 16 |
+
|
| 17 |
+
Example for a path: 'root.child[1].{other}child[25]'
|
| 18 |
+
"""
|
| 19 |
+
cdef readonly object find
|
| 20 |
+
cdef list _path
|
| 21 |
+
cdef object _path_str
|
| 22 |
+
cdef _ObjectPath* _c_path
|
| 23 |
+
cdef Py_ssize_t _path_len
|
| 24 |
+
def __init__(self, path):
|
| 25 |
+
if python._isString(path):
|
| 26 |
+
self._path = _parse_object_path_string(path)
|
| 27 |
+
self._path_str = path
|
| 28 |
+
else:
|
| 29 |
+
self._path = _parse_object_path_list(path)
|
| 30 |
+
self._path_str = '.'.join(path)
|
| 31 |
+
self._path_len = len(self._path)
|
| 32 |
+
self._c_path = _build_object_path_segments(self._path)
|
| 33 |
+
self.find = self.__call__
|
| 34 |
+
|
| 35 |
+
def __dealloc__(self):
|
| 36 |
+
if self._c_path is not NULL:
|
| 37 |
+
python.lxml_free(self._c_path)
|
| 38 |
+
|
| 39 |
+
def __str__(self):
|
| 40 |
+
return self._path_str
|
| 41 |
+
|
| 42 |
+
def __call__(self, _Element root not None, *_default):
|
| 43 |
+
"""Follow the attribute path in the object structure and return the
|
| 44 |
+
target attribute value.
|
| 45 |
+
|
| 46 |
+
If it it not found, either returns a default value (if one was passed
|
| 47 |
+
as second argument) or raises AttributeError.
|
| 48 |
+
"""
|
| 49 |
+
if _default:
|
| 50 |
+
if len(_default) > 1:
|
| 51 |
+
raise TypeError, "invalid number of arguments: needs one or two"
|
| 52 |
+
default = _default[0]
|
| 53 |
+
else:
|
| 54 |
+
default = _NO_DEFAULT
|
| 55 |
+
return _find_object_path(root, self._c_path, self._path_len, default)
|
| 56 |
+
|
| 57 |
+
def hasattr(self, _Element root not None):
|
| 58 |
+
"hasattr(self, root)"
|
| 59 |
+
try:
|
| 60 |
+
_find_object_path(root, self._c_path, self._path_len, _NO_DEFAULT)
|
| 61 |
+
except AttributeError:
|
| 62 |
+
return False
|
| 63 |
+
return True
|
| 64 |
+
|
| 65 |
+
def setattr(self, _Element root not None, value):
|
| 66 |
+
"""setattr(self, root, value)
|
| 67 |
+
|
| 68 |
+
Set the value of the target element in a subtree.
|
| 69 |
+
|
| 70 |
+
If any of the children on the path does not exist, it is created.
|
| 71 |
+
"""
|
| 72 |
+
_create_object_path(root, self._c_path, self._path_len, 1, value)
|
| 73 |
+
|
| 74 |
+
def addattr(self, _Element root not None, value):
|
| 75 |
+
"""addattr(self, root, value)
|
| 76 |
+
|
| 77 |
+
Append a value to the target element in a subtree.
|
| 78 |
+
|
| 79 |
+
If any of the children on the path does not exist, it is created.
|
| 80 |
+
"""
|
| 81 |
+
_create_object_path(root, self._c_path, self._path_len, 0, value)
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
cdef object __MATCH_PATH_SEGMENT = re.compile(
|
| 85 |
+
r"(\.?)\s*(?:\{([^}]*)\})?\s*([^.{}\[\]\s]+)\s*(?:\[\s*([-0-9]+)\s*\])?",
|
| 86 |
+
re.U).match
|
| 87 |
+
|
| 88 |
+
cdef tuple _RELATIVE_PATH_SEGMENT = (None, None, 0)
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
cdef list _parse_object_path_string(_path):
|
| 92 |
+
"""Parse object path string into a (ns, name, index) list.
|
| 93 |
+
"""
|
| 94 |
+
cdef bint has_dot
|
| 95 |
+
cdef unicode path
|
| 96 |
+
new_path = []
|
| 97 |
+
if isinstance(_path, bytes):
|
| 98 |
+
path = (<bytes>_path).decode('ascii')
|
| 99 |
+
elif type(_path) is not unicode:
|
| 100 |
+
path = unicode(_path)
|
| 101 |
+
else:
|
| 102 |
+
path = _path
|
| 103 |
+
path = path.strip()
|
| 104 |
+
if path == '.':
|
| 105 |
+
return [_RELATIVE_PATH_SEGMENT]
|
| 106 |
+
path_pos = 0
|
| 107 |
+
while path:
|
| 108 |
+
match = __MATCH_PATH_SEGMENT(path, path_pos)
|
| 109 |
+
if match is None:
|
| 110 |
+
break
|
| 111 |
+
|
| 112 |
+
dot, ns, name, index = match.groups()
|
| 113 |
+
index = int(index) if index else 0
|
| 114 |
+
has_dot = dot == '.'
|
| 115 |
+
if not new_path:
|
| 116 |
+
if has_dot:
|
| 117 |
+
# path '.child' => ignore root
|
| 118 |
+
new_path.append(_RELATIVE_PATH_SEGMENT)
|
| 119 |
+
elif index:
|
| 120 |
+
raise ValueError, "index not allowed on root node"
|
| 121 |
+
elif not has_dot:
|
| 122 |
+
raise ValueError, "invalid path"
|
| 123 |
+
if ns is not None:
|
| 124 |
+
ns = python.PyUnicode_AsUTF8String(ns)
|
| 125 |
+
name = python.PyUnicode_AsUTF8String(name)
|
| 126 |
+
new_path.append( (ns, name, index) )
|
| 127 |
+
|
| 128 |
+
path_pos = match.end()
|
| 129 |
+
if not new_path or len(path) > path_pos:
|
| 130 |
+
raise ValueError, "invalid path"
|
| 131 |
+
return new_path
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
cdef list _parse_object_path_list(path):
|
| 135 |
+
"""Parse object path sequence into a (ns, name, index) list.
|
| 136 |
+
"""
|
| 137 |
+
new_path = []
|
| 138 |
+
for item in path:
|
| 139 |
+
item = item.strip()
|
| 140 |
+
if not new_path and item == '':
|
| 141 |
+
# path '.child' => ignore root
|
| 142 |
+
ns = name = None
|
| 143 |
+
index = 0
|
| 144 |
+
else:
|
| 145 |
+
ns, name = cetree.getNsTag(item)
|
| 146 |
+
c_name = _xcstr(name)
|
| 147 |
+
index_pos = tree.xmlStrchr(c_name, c'[')
|
| 148 |
+
if index_pos is NULL:
|
| 149 |
+
index = 0
|
| 150 |
+
else:
|
| 151 |
+
index_end = tree.xmlStrchr(index_pos + 1, c']')
|
| 152 |
+
if index_end is NULL:
|
| 153 |
+
raise ValueError, "index must be enclosed in []"
|
| 154 |
+
index = int(index_pos[1:index_end - index_pos])
|
| 155 |
+
if not new_path and index != 0:
|
| 156 |
+
raise ValueError, "index not allowed on root node"
|
| 157 |
+
name = <bytes>c_name[:index_pos - c_name]
|
| 158 |
+
new_path.append( (ns, name, index) )
|
| 159 |
+
if not new_path:
|
| 160 |
+
raise ValueError, "invalid path"
|
| 161 |
+
return new_path
|
| 162 |
+
|
| 163 |
+
|
| 164 |
+
cdef _ObjectPath* _build_object_path_segments(list path_list) except NULL:
|
| 165 |
+
cdef _ObjectPath* c_path
|
| 166 |
+
cdef _ObjectPath* c_path_segments
|
| 167 |
+
c_path_segments = <_ObjectPath*>python.lxml_malloc(len(path_list), sizeof(_ObjectPath))
|
| 168 |
+
if c_path_segments is NULL:
|
| 169 |
+
raise MemoryError()
|
| 170 |
+
c_path = c_path_segments
|
| 171 |
+
for href, name, index in path_list:
|
| 172 |
+
c_path[0].href = _xcstr(href) if href is not None else NULL
|
| 173 |
+
c_path[0].name = _xcstr(name) if name is not None else NULL
|
| 174 |
+
c_path[0].index = index
|
| 175 |
+
c_path += 1
|
| 176 |
+
return c_path_segments
|
| 177 |
+
|
| 178 |
+
|
| 179 |
+
cdef _find_object_path(_Element root, _ObjectPath* c_path, Py_ssize_t c_path_len, default_value):
|
| 180 |
+
"""Follow the path to find the target element.
|
| 181 |
+
"""
|
| 182 |
+
cdef tree.xmlNode* c_node
|
| 183 |
+
cdef Py_ssize_t c_index
|
| 184 |
+
c_node = root._c_node
|
| 185 |
+
c_name = c_path[0].name
|
| 186 |
+
c_href = c_path[0].href
|
| 187 |
+
if c_href is NULL or c_href[0] == c'\0':
|
| 188 |
+
c_href = tree._getNs(c_node)
|
| 189 |
+
if not cetree.tagMatches(c_node, c_href, c_name):
|
| 190 |
+
if default_value is not _NO_DEFAULT:
|
| 191 |
+
return default_value
|
| 192 |
+
else:
|
| 193 |
+
raise ValueError(
|
| 194 |
+
f"root element does not match: need {cetree.namespacedNameFromNsName(c_href, c_name)}, got {root.tag}")
|
| 195 |
+
|
| 196 |
+
while c_node is not NULL:
|
| 197 |
+
c_path_len -= 1
|
| 198 |
+
if c_path_len <= 0:
|
| 199 |
+
break
|
| 200 |
+
|
| 201 |
+
c_path += 1
|
| 202 |
+
if c_path[0].href is not NULL:
|
| 203 |
+
c_href = c_path[0].href # otherwise: keep parent namespace
|
| 204 |
+
c_name = tree.xmlDictExists(c_node.doc.dict, c_path[0].name, -1)
|
| 205 |
+
if c_name is NULL:
|
| 206 |
+
c_name = c_path[0].name
|
| 207 |
+
c_node = NULL
|
| 208 |
+
break
|
| 209 |
+
c_index = c_path[0].index
|
| 210 |
+
c_node = c_node.last if c_index < 0 else c_node.children
|
| 211 |
+
c_node = _findFollowingSibling(c_node, c_href, c_name, c_index)
|
| 212 |
+
|
| 213 |
+
if c_node is not NULL:
|
| 214 |
+
return cetree.elementFactory(root._doc, c_node)
|
| 215 |
+
elif default_value is not _NO_DEFAULT:
|
| 216 |
+
return default_value
|
| 217 |
+
else:
|
| 218 |
+
tag = cetree.namespacedNameFromNsName(c_href, c_name)
|
| 219 |
+
raise AttributeError, f"no such child: {tag}"
|
| 220 |
+
|
| 221 |
+
|
| 222 |
+
cdef _create_object_path(_Element root, _ObjectPath* c_path,
|
| 223 |
+
Py_ssize_t c_path_len, int replace, value):
|
| 224 |
+
"""Follow the path to find the target element, build the missing children
|
| 225 |
+
as needed and set the target element to 'value'. If replace is true, an
|
| 226 |
+
existing value is replaced, otherwise the new value is added.
|
| 227 |
+
"""
|
| 228 |
+
cdef _Element child
|
| 229 |
+
cdef tree.xmlNode* c_node
|
| 230 |
+
cdef tree.xmlNode* c_child
|
| 231 |
+
cdef Py_ssize_t c_index
|
| 232 |
+
if c_path_len == 1:
|
| 233 |
+
raise TypeError, "cannot update root node"
|
| 234 |
+
|
| 235 |
+
c_node = root._c_node
|
| 236 |
+
c_name = c_path[0].name
|
| 237 |
+
c_href = c_path[0].href
|
| 238 |
+
if c_href is NULL or c_href[0] == c'\0':
|
| 239 |
+
c_href = tree._getNs(c_node)
|
| 240 |
+
if not cetree.tagMatches(c_node, c_href, c_name):
|
| 241 |
+
raise ValueError(
|
| 242 |
+
f"root element does not match: need {cetree.namespacedNameFromNsName(c_href, c_name)}, got {root.tag}")
|
| 243 |
+
|
| 244 |
+
while c_path_len > 1:
|
| 245 |
+
c_path_len -= 1
|
| 246 |
+
c_path += 1
|
| 247 |
+
if c_path[0].href is not NULL:
|
| 248 |
+
c_href = c_path[0].href # otherwise: keep parent namespace
|
| 249 |
+
c_index = c_path[0].index
|
| 250 |
+
c_name = tree.xmlDictExists(c_node.doc.dict, c_path[0].name, -1)
|
| 251 |
+
if c_name is NULL:
|
| 252 |
+
c_name = c_path[0].name
|
| 253 |
+
c_child = NULL
|
| 254 |
+
else:
|
| 255 |
+
c_child = c_node.last if c_index < 0 else c_node.children
|
| 256 |
+
c_child = _findFollowingSibling(c_child, c_href, c_name, c_index)
|
| 257 |
+
|
| 258 |
+
if c_child is not NULL:
|
| 259 |
+
c_node = c_child
|
| 260 |
+
elif c_index != 0:
|
| 261 |
+
raise TypeError, "creating indexed path attributes is not supported"
|
| 262 |
+
elif c_path_len == 1:
|
| 263 |
+
_appendValue(cetree.elementFactory(root._doc, c_node),
|
| 264 |
+
cetree.namespacedNameFromNsName(c_href, c_name),
|
| 265 |
+
value)
|
| 266 |
+
return
|
| 267 |
+
else:
|
| 268 |
+
child = cetree.makeSubElement(
|
| 269 |
+
cetree.elementFactory(root._doc, c_node),
|
| 270 |
+
cetree.namespacedNameFromNsName(c_href, c_name),
|
| 271 |
+
None, None, None, None)
|
| 272 |
+
c_node = child._c_node
|
| 273 |
+
|
| 274 |
+
# if we get here, the entire path was already there
|
| 275 |
+
if replace:
|
| 276 |
+
element = cetree.elementFactory(root._doc, c_node)
|
| 277 |
+
_replaceElement(element, value)
|
| 278 |
+
else:
|
| 279 |
+
_appendValue(cetree.elementFactory(root._doc, c_node.parent),
|
| 280 |
+
cetree.namespacedName(c_node), value)
|
| 281 |
+
|
| 282 |
+
|
| 283 |
+
cdef list _build_descendant_paths(tree.xmlNode* c_node, prefix_string):
|
| 284 |
+
"""Returns a list of all descendant paths.
|
| 285 |
+
"""
|
| 286 |
+
cdef list path, path_list
|
| 287 |
+
tag = cetree.namespacedName(c_node)
|
| 288 |
+
if prefix_string:
|
| 289 |
+
if prefix_string[-1] != '.':
|
| 290 |
+
prefix_string += '.'
|
| 291 |
+
prefix_string = prefix_string + tag
|
| 292 |
+
else:
|
| 293 |
+
prefix_string = tag
|
| 294 |
+
path = [prefix_string]
|
| 295 |
+
path_list = []
|
| 296 |
+
_recursive_build_descendant_paths(c_node, path, path_list)
|
| 297 |
+
return path_list
|
| 298 |
+
|
| 299 |
+
|
| 300 |
+
cdef int _recursive_build_descendant_paths(tree.xmlNode* c_node,
|
| 301 |
+
list path, list path_list) except -1:
|
| 302 |
+
"""Fills the list 'path_list' with all descendant paths, initial prefix
|
| 303 |
+
being in the list 'path'.
|
| 304 |
+
"""
|
| 305 |
+
cdef tree.xmlNode* c_child
|
| 306 |
+
tags = {}
|
| 307 |
+
path_list.append('.'.join(path))
|
| 308 |
+
c_href = tree._getNs(c_node)
|
| 309 |
+
c_child = c_node.children
|
| 310 |
+
while c_child is not NULL:
|
| 311 |
+
while c_child.type != tree.XML_ELEMENT_NODE:
|
| 312 |
+
c_child = c_child.next
|
| 313 |
+
if c_child is NULL:
|
| 314 |
+
return 0
|
| 315 |
+
if c_href is tree._getNs(c_child):
|
| 316 |
+
tag = pyunicode(c_child.name)
|
| 317 |
+
elif c_href is not NULL and tree._getNs(c_child) is NULL:
|
| 318 |
+
# special case: parent has namespace, child does not
|
| 319 |
+
tag = '{}' + pyunicode(c_child.name)
|
| 320 |
+
else:
|
| 321 |
+
tag = cetree.namespacedName(c_child)
|
| 322 |
+
count = tags.get(tag)
|
| 323 |
+
if count is None:
|
| 324 |
+
tags[tag] = 1
|
| 325 |
+
else:
|
| 326 |
+
tags[tag] = count + 1
|
| 327 |
+
tag += f'[{count}]'
|
| 328 |
+
path.append(tag)
|
| 329 |
+
_recursive_build_descendant_paths(c_child, path, path_list)
|
| 330 |
+
del path[-1]
|
| 331 |
+
c_child = c_child.next
|
| 332 |
+
return 0
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/proxy.pxi
ADDED
|
@@ -0,0 +1,622 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Proxy functions and low level node allocation stuff
|
| 2 |
+
|
| 3 |
+
# Proxies represent elements, their reference is stored in the C
|
| 4 |
+
# structure of the respective node to avoid multiple instantiation of
|
| 5 |
+
# the Python class.
|
| 6 |
+
|
| 7 |
+
@cython.linetrace(False)
|
| 8 |
+
@cython.profile(False)
|
| 9 |
+
cdef inline _Element getProxy(xmlNode* c_node):
|
| 10 |
+
"""Get a proxy for a given node.
|
| 11 |
+
"""
|
| 12 |
+
#print "getProxy for:", <int>c_node
|
| 13 |
+
if c_node is not NULL and c_node._private is not NULL:
|
| 14 |
+
return <_Element>c_node._private
|
| 15 |
+
else:
|
| 16 |
+
return None
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
@cython.linetrace(False)
|
| 20 |
+
@cython.profile(False)
|
| 21 |
+
cdef inline bint hasProxy(xmlNode* c_node):
|
| 22 |
+
if c_node._private is NULL:
|
| 23 |
+
return False
|
| 24 |
+
return True
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
@cython.linetrace(False)
|
| 28 |
+
@cython.profile(False)
|
| 29 |
+
cdef inline int _registerProxy(_Element proxy, _Document doc,
|
| 30 |
+
xmlNode* c_node) except -1:
|
| 31 |
+
"""Register a proxy and type for the node it's proxying for.
|
| 32 |
+
"""
|
| 33 |
+
#print "registering for:", <int>proxy._c_node
|
| 34 |
+
assert not hasProxy(c_node), "double registering proxy!"
|
| 35 |
+
proxy._doc = doc
|
| 36 |
+
proxy._c_node = c_node
|
| 37 |
+
c_node._private = <void*>proxy
|
| 38 |
+
return 0
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
@cython.linetrace(False)
|
| 42 |
+
@cython.profile(False)
|
| 43 |
+
cdef inline int _unregisterProxy(_Element proxy) except -1:
|
| 44 |
+
"""Unregister a proxy for the node it's proxying for.
|
| 45 |
+
"""
|
| 46 |
+
cdef xmlNode* c_node = proxy._c_node
|
| 47 |
+
assert c_node._private is <void*>proxy, "Tried to unregister unknown proxy"
|
| 48 |
+
c_node._private = NULL
|
| 49 |
+
return 0
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
################################################################################
|
| 53 |
+
# temporarily make a node the root node of its document
|
| 54 |
+
|
| 55 |
+
cdef xmlDoc* _fakeRootDoc(xmlDoc* c_base_doc, xmlNode* c_node) except NULL:
|
| 56 |
+
return _plainFakeRootDoc(c_base_doc, c_node, 1)
|
| 57 |
+
|
| 58 |
+
cdef xmlDoc* _plainFakeRootDoc(xmlDoc* c_base_doc, xmlNode* c_node,
|
| 59 |
+
bint with_siblings) except NULL:
|
| 60 |
+
# build a temporary document that has the given node as root node
|
| 61 |
+
# note that copy and original must not be modified during its lifetime!!
|
| 62 |
+
# always call _destroyFakeDoc() after use!
|
| 63 |
+
cdef xmlNode* c_child
|
| 64 |
+
cdef xmlNode* c_root
|
| 65 |
+
cdef xmlNode* c_new_root
|
| 66 |
+
cdef xmlDoc* c_doc
|
| 67 |
+
if with_siblings or (c_node.prev is NULL and c_node.next is NULL):
|
| 68 |
+
c_root = tree.xmlDocGetRootElement(c_base_doc)
|
| 69 |
+
if c_root is c_node:
|
| 70 |
+
# already the root node, no siblings
|
| 71 |
+
return c_base_doc
|
| 72 |
+
|
| 73 |
+
c_doc = _copyDoc(c_base_doc, 0) # non recursive!
|
| 74 |
+
c_new_root = tree.xmlDocCopyNode(c_node, c_doc, 2) # non recursive!
|
| 75 |
+
tree.xmlDocSetRootElement(c_doc, c_new_root)
|
| 76 |
+
_copyParentNamespaces(c_node, c_new_root)
|
| 77 |
+
|
| 78 |
+
c_new_root.children = c_node.children
|
| 79 |
+
c_new_root.last = c_node.last
|
| 80 |
+
c_new_root.next = c_new_root.prev = NULL
|
| 81 |
+
|
| 82 |
+
# store original node
|
| 83 |
+
c_doc._private = c_node
|
| 84 |
+
|
| 85 |
+
# divert parent pointers of children
|
| 86 |
+
c_child = c_new_root.children
|
| 87 |
+
while c_child is not NULL:
|
| 88 |
+
c_child.parent = c_new_root
|
| 89 |
+
c_child = c_child.next
|
| 90 |
+
|
| 91 |
+
c_doc.children = c_new_root
|
| 92 |
+
return c_doc
|
| 93 |
+
|
| 94 |
+
cdef void _destroyFakeDoc(xmlDoc* c_base_doc, xmlDoc* c_doc) noexcept:
|
| 95 |
+
# delete a temporary document
|
| 96 |
+
cdef xmlNode* c_child
|
| 97 |
+
cdef xmlNode* c_parent
|
| 98 |
+
cdef xmlNode* c_root
|
| 99 |
+
if c_doc is c_base_doc:
|
| 100 |
+
return
|
| 101 |
+
c_root = tree.xmlDocGetRootElement(c_doc)
|
| 102 |
+
|
| 103 |
+
# restore parent pointers of children
|
| 104 |
+
c_parent = <xmlNode*>c_doc._private
|
| 105 |
+
c_child = c_root.children
|
| 106 |
+
while c_child is not NULL:
|
| 107 |
+
c_child.parent = c_parent
|
| 108 |
+
c_child = c_child.next
|
| 109 |
+
|
| 110 |
+
# prevent recursive removal of children
|
| 111 |
+
c_root.children = c_root.last = NULL
|
| 112 |
+
tree.xmlFreeDoc(c_doc)
|
| 113 |
+
|
| 114 |
+
cdef _Element _fakeDocElementFactory(_Document doc, xmlNode* c_element):
|
| 115 |
+
"""Special element factory for cases where we need to create a fake
|
| 116 |
+
root document, but still need to instantiate arbitrary nodes from
|
| 117 |
+
it. If we instantiate the fake root node, things will turn bad
|
| 118 |
+
when it's destroyed.
|
| 119 |
+
|
| 120 |
+
Instead, if we are asked to instantiate the fake root node, we
|
| 121 |
+
instantiate the original node instead.
|
| 122 |
+
"""
|
| 123 |
+
if c_element.doc is not doc._c_doc:
|
| 124 |
+
if c_element.doc._private is not NULL:
|
| 125 |
+
if c_element is c_element.doc.children:
|
| 126 |
+
c_element = <xmlNode*>c_element.doc._private
|
| 127 |
+
#assert c_element.type == tree.XML_ELEMENT_NODE
|
| 128 |
+
return _elementFactory(doc, c_element)
|
| 129 |
+
|
| 130 |
+
################################################################################
|
| 131 |
+
# support for freeing tree elements when proxy objects are destroyed
|
| 132 |
+
|
| 133 |
+
cdef int attemptDeallocation(xmlNode* c_node) noexcept:
|
| 134 |
+
"""Attempt deallocation of c_node (or higher up in tree).
|
| 135 |
+
"""
|
| 136 |
+
cdef xmlNode* c_top
|
| 137 |
+
# could be we actually aren't referring to the tree at all
|
| 138 |
+
if c_node is NULL:
|
| 139 |
+
#print "not freeing, node is NULL"
|
| 140 |
+
return 0
|
| 141 |
+
c_top = getDeallocationTop(c_node)
|
| 142 |
+
if c_top is not NULL:
|
| 143 |
+
#print "freeing:", c_top.name
|
| 144 |
+
_removeText(c_top.next) # tail
|
| 145 |
+
tree.xmlFreeNode(c_top)
|
| 146 |
+
return 1
|
| 147 |
+
return 0
|
| 148 |
+
|
| 149 |
+
cdef xmlNode* getDeallocationTop(xmlNode* c_node) noexcept:
|
| 150 |
+
"""Return the top of the tree that can be deallocated, or NULL.
|
| 151 |
+
"""
|
| 152 |
+
cdef xmlNode* c_next
|
| 153 |
+
#print "trying to do deallocating:", c_node.type
|
| 154 |
+
if hasProxy(c_node):
|
| 155 |
+
#print "Not freeing: proxies still exist"
|
| 156 |
+
return NULL
|
| 157 |
+
while c_node.parent is not NULL:
|
| 158 |
+
c_node = c_node.parent
|
| 159 |
+
#print "checking:", c_current.type
|
| 160 |
+
if c_node.type == tree.XML_DOCUMENT_NODE or \
|
| 161 |
+
c_node.type == tree.XML_HTML_DOCUMENT_NODE:
|
| 162 |
+
#print "not freeing: still in doc"
|
| 163 |
+
return NULL
|
| 164 |
+
# if we're still attached to the document, don't deallocate
|
| 165 |
+
if hasProxy(c_node):
|
| 166 |
+
#print "Not freeing: proxies still exist"
|
| 167 |
+
return NULL
|
| 168 |
+
# see whether we have children to deallocate
|
| 169 |
+
if not canDeallocateChildNodes(c_node):
|
| 170 |
+
return NULL
|
| 171 |
+
# see whether we have siblings to deallocate
|
| 172 |
+
c_next = c_node.prev
|
| 173 |
+
while c_next:
|
| 174 |
+
if _isElement(c_next):
|
| 175 |
+
if hasProxy(c_next) or not canDeallocateChildNodes(c_next):
|
| 176 |
+
return NULL
|
| 177 |
+
c_next = c_next.prev
|
| 178 |
+
c_next = c_node.next
|
| 179 |
+
while c_next:
|
| 180 |
+
if _isElement(c_next):
|
| 181 |
+
if hasProxy(c_next) or not canDeallocateChildNodes(c_next):
|
| 182 |
+
return NULL
|
| 183 |
+
c_next = c_next.next
|
| 184 |
+
return c_node
|
| 185 |
+
|
| 186 |
+
cdef int canDeallocateChildNodes(xmlNode* c_parent) noexcept:
|
| 187 |
+
cdef xmlNode* c_node
|
| 188 |
+
c_node = c_parent.children
|
| 189 |
+
tree.BEGIN_FOR_EACH_ELEMENT_FROM(c_parent, c_node, 1)
|
| 190 |
+
if hasProxy(c_node):
|
| 191 |
+
return 0
|
| 192 |
+
tree.END_FOR_EACH_ELEMENT_FROM(c_node)
|
| 193 |
+
return 1
|
| 194 |
+
|
| 195 |
+
################################################################################
|
| 196 |
+
# fix _Document references and namespaces when a node changes documents
|
| 197 |
+
|
| 198 |
+
cdef void _copyParentNamespaces(xmlNode* c_from_node, xmlNode* c_to_node) noexcept nogil:
|
| 199 |
+
"""Copy the namespaces of all ancestors of c_from_node to c_to_node.
|
| 200 |
+
"""
|
| 201 |
+
cdef xmlNode* c_parent
|
| 202 |
+
cdef xmlNs* c_ns
|
| 203 |
+
cdef xmlNs* c_new_ns
|
| 204 |
+
cdef int prefix_known
|
| 205 |
+
c_parent = c_from_node.parent
|
| 206 |
+
while c_parent and (tree._isElementOrXInclude(c_parent) or
|
| 207 |
+
c_parent.type == tree.XML_DOCUMENT_NODE):
|
| 208 |
+
c_new_ns = c_parent.nsDef
|
| 209 |
+
while c_new_ns:
|
| 210 |
+
# libxml2 will check if the prefix is already defined
|
| 211 |
+
tree.xmlNewNs(c_to_node, c_new_ns.href, c_new_ns.prefix)
|
| 212 |
+
c_new_ns = c_new_ns.next
|
| 213 |
+
c_parent = c_parent.parent
|
| 214 |
+
|
| 215 |
+
|
| 216 |
+
ctypedef struct _ns_update_map:
|
| 217 |
+
xmlNs* old
|
| 218 |
+
xmlNs* new
|
| 219 |
+
|
| 220 |
+
|
| 221 |
+
ctypedef struct _nscache:
|
| 222 |
+
_ns_update_map* ns_map
|
| 223 |
+
size_t size
|
| 224 |
+
size_t last
|
| 225 |
+
|
| 226 |
+
|
| 227 |
+
cdef int _growNsCache(_nscache* c_ns_cache) except -1:
|
| 228 |
+
cdef _ns_update_map* ns_map_ptr
|
| 229 |
+
if c_ns_cache.size == 0:
|
| 230 |
+
c_ns_cache.size = 20
|
| 231 |
+
else:
|
| 232 |
+
c_ns_cache.size *= 2
|
| 233 |
+
ns_map_ptr = <_ns_update_map*> python.lxml_realloc(
|
| 234 |
+
c_ns_cache.ns_map, c_ns_cache.size, sizeof(_ns_update_map))
|
| 235 |
+
if not ns_map_ptr:
|
| 236 |
+
python.lxml_free(c_ns_cache.ns_map)
|
| 237 |
+
c_ns_cache.ns_map = NULL
|
| 238 |
+
raise MemoryError()
|
| 239 |
+
c_ns_cache.ns_map = ns_map_ptr
|
| 240 |
+
return 0
|
| 241 |
+
|
| 242 |
+
|
| 243 |
+
cdef inline int _appendToNsCache(_nscache* c_ns_cache,
|
| 244 |
+
xmlNs* c_old_ns, xmlNs* c_new_ns) except -1:
|
| 245 |
+
if c_ns_cache.last >= c_ns_cache.size:
|
| 246 |
+
_growNsCache(c_ns_cache)
|
| 247 |
+
c_ns_cache.ns_map[c_ns_cache.last] = _ns_update_map(old=c_old_ns, new=c_new_ns)
|
| 248 |
+
c_ns_cache.last += 1
|
| 249 |
+
|
| 250 |
+
|
| 251 |
+
cdef int _stripRedundantNamespaceDeclarations(xmlNode* c_element, _nscache* c_ns_cache,
|
| 252 |
+
xmlNs** c_del_ns_list) except -1:
|
| 253 |
+
"""Removes namespace declarations from an element that are already
|
| 254 |
+
defined in its parents. Does not free the xmlNs's, just prepends
|
| 255 |
+
them to the c_del_ns_list.
|
| 256 |
+
"""
|
| 257 |
+
cdef xmlNs* c_ns
|
| 258 |
+
cdef xmlNs* c_ns_next
|
| 259 |
+
cdef xmlNs** c_nsdef
|
| 260 |
+
# use a xmlNs** to handle assignments to "c_element.nsDef" correctly
|
| 261 |
+
c_nsdef = &c_element.nsDef
|
| 262 |
+
while c_nsdef[0] is not NULL:
|
| 263 |
+
c_ns = tree.xmlSearchNsByHref(
|
| 264 |
+
c_element.doc, c_element.parent, c_nsdef[0].href)
|
| 265 |
+
if c_ns is NULL:
|
| 266 |
+
# new namespace href => keep and cache the ns declaration
|
| 267 |
+
_appendToNsCache(c_ns_cache, c_nsdef[0], c_nsdef[0])
|
| 268 |
+
c_nsdef = &c_nsdef[0].next
|
| 269 |
+
else:
|
| 270 |
+
# known namespace href => cache mapping and strip old ns
|
| 271 |
+
_appendToNsCache(c_ns_cache, c_nsdef[0], c_ns)
|
| 272 |
+
# cut out c_nsdef.next and prepend it to garbage chain
|
| 273 |
+
c_ns_next = c_nsdef[0].next
|
| 274 |
+
c_nsdef[0].next = c_del_ns_list[0]
|
| 275 |
+
c_del_ns_list[0] = c_nsdef[0]
|
| 276 |
+
c_nsdef[0] = c_ns_next
|
| 277 |
+
return 0
|
| 278 |
+
|
| 279 |
+
|
| 280 |
+
cdef void _cleanUpFromNamespaceAdaptation(xmlNode* c_start_node,
|
| 281 |
+
_nscache* c_ns_cache, xmlNs* c_del_ns_list) noexcept:
|
| 282 |
+
# Try to recover from exceptions with really bad timing. We were in the middle
|
| 283 |
+
# of ripping out xmlNS-es and likely ran out of memory. Try to fix up the tree
|
| 284 |
+
# by re-adding the original xmlNs declarations (which might still be used in some
|
| 285 |
+
# places).
|
| 286 |
+
if c_ns_cache.ns_map:
|
| 287 |
+
python.lxml_free(c_ns_cache.ns_map)
|
| 288 |
+
if c_del_ns_list:
|
| 289 |
+
if not c_start_node.nsDef:
|
| 290 |
+
c_start_node.nsDef = c_del_ns_list
|
| 291 |
+
else:
|
| 292 |
+
c_ns = c_start_node.nsDef
|
| 293 |
+
while c_ns.next:
|
| 294 |
+
c_ns = c_ns.next
|
| 295 |
+
c_ns.next = c_del_ns_list
|
| 296 |
+
|
| 297 |
+
|
| 298 |
+
cdef int moveNodeToDocument(_Document doc, xmlDoc* c_source_doc,
|
| 299 |
+
xmlNode* c_element) except -1:
|
| 300 |
+
"""Fix the xmlNs pointers of a node and its subtree that were moved.
|
| 301 |
+
|
| 302 |
+
Originally copied from libxml2's xmlReconciliateNs(). Expects
|
| 303 |
+
libxml2 doc pointers of node to be correct already, but fixes
|
| 304 |
+
_Document references.
|
| 305 |
+
|
| 306 |
+
For each node in the subtree, we do this:
|
| 307 |
+
|
| 308 |
+
1) Remove redundant declarations of namespace that are already
|
| 309 |
+
defined in its parents.
|
| 310 |
+
|
| 311 |
+
2) Replace namespaces that are *not* defined on the node or its
|
| 312 |
+
parents by the equivalent namespace declarations that *are*
|
| 313 |
+
defined on the node or its parents (possibly using a different
|
| 314 |
+
prefix). If a namespace is unknown, declare a new one on the
|
| 315 |
+
node.
|
| 316 |
+
|
| 317 |
+
3) Reassign the names of tags and attribute from the dict of the
|
| 318 |
+
target document *iff* it is different from the dict used in the
|
| 319 |
+
source subtree.
|
| 320 |
+
|
| 321 |
+
4) Set the Document reference to the new Document (if different).
|
| 322 |
+
This is done on backtracking to keep the original Document
|
| 323 |
+
alive as long as possible, until all its elements are updated.
|
| 324 |
+
|
| 325 |
+
Note that the namespace declarations are removed from the tree in
|
| 326 |
+
step 1), but freed only after the complete subtree was traversed
|
| 327 |
+
and all occurrences were replaced by tree-internal pointers.
|
| 328 |
+
"""
|
| 329 |
+
cdef xmlNode* c_start_node
|
| 330 |
+
cdef xmlNode* c_node
|
| 331 |
+
cdef xmlDoc* c_doc = doc._c_doc
|
| 332 |
+
cdef tree.xmlAttr* c_attr
|
| 333 |
+
cdef char* c_name
|
| 334 |
+
cdef _nscache c_ns_cache = [NULL, 0, 0]
|
| 335 |
+
cdef xmlNs* c_del_ns_list = NULL
|
| 336 |
+
cdef proxy_count = 0
|
| 337 |
+
|
| 338 |
+
if not tree._isElementOrXInclude(c_element):
|
| 339 |
+
return 0
|
| 340 |
+
|
| 341 |
+
c_start_node = c_element
|
| 342 |
+
|
| 343 |
+
tree.BEGIN_FOR_EACH_FROM(c_element, c_element, 1)
|
| 344 |
+
if tree._isElementOrXInclude(c_element):
|
| 345 |
+
if hasProxy(c_element):
|
| 346 |
+
proxy_count += 1
|
| 347 |
+
|
| 348 |
+
# 1) cut out namespaces defined here that are already known by
|
| 349 |
+
# the ancestors
|
| 350 |
+
if c_element.nsDef is not NULL:
|
| 351 |
+
try:
|
| 352 |
+
_stripRedundantNamespaceDeclarations(c_element, &c_ns_cache, &c_del_ns_list)
|
| 353 |
+
except:
|
| 354 |
+
_cleanUpFromNamespaceAdaptation(c_start_node, &c_ns_cache, c_del_ns_list)
|
| 355 |
+
raise
|
| 356 |
+
|
| 357 |
+
# 2) make sure the namespaces of an element and its attributes
|
| 358 |
+
# are declared in this document (i.e. on the node or its parents)
|
| 359 |
+
if c_element.ns is not NULL:
|
| 360 |
+
_fixCNs(doc, c_start_node, c_element, &c_ns_cache, c_del_ns_list)
|
| 361 |
+
|
| 362 |
+
c_node = <xmlNode*>c_element.properties
|
| 363 |
+
while c_node is not NULL:
|
| 364 |
+
if c_node.ns is not NULL:
|
| 365 |
+
_fixCNs(doc, c_start_node, c_node, &c_ns_cache, c_del_ns_list)
|
| 366 |
+
c_node = c_node.next
|
| 367 |
+
|
| 368 |
+
tree.END_FOR_EACH_FROM(c_element)
|
| 369 |
+
|
| 370 |
+
# free now unused namespace declarations
|
| 371 |
+
if c_del_ns_list is not NULL:
|
| 372 |
+
tree.xmlFreeNsList(c_del_ns_list)
|
| 373 |
+
|
| 374 |
+
# cleanup
|
| 375 |
+
if c_ns_cache.ns_map is not NULL:
|
| 376 |
+
python.lxml_free(c_ns_cache.ns_map)
|
| 377 |
+
|
| 378 |
+
# 3) fix the names in the tree if we moved it from a different thread
|
| 379 |
+
if doc._c_doc.dict is not c_source_doc.dict:
|
| 380 |
+
fixThreadDictNames(c_start_node, c_source_doc.dict, doc._c_doc.dict)
|
| 381 |
+
|
| 382 |
+
# 4) fix _Document references
|
| 383 |
+
# (and potentially deallocate the source document)
|
| 384 |
+
if proxy_count > 0:
|
| 385 |
+
if proxy_count == 1 and c_start_node._private is not NULL:
|
| 386 |
+
proxy = getProxy(c_start_node)
|
| 387 |
+
if proxy is not None:
|
| 388 |
+
if proxy._doc is not doc:
|
| 389 |
+
proxy._doc = doc
|
| 390 |
+
else:
|
| 391 |
+
fixElementDocument(c_start_node, doc, proxy_count)
|
| 392 |
+
else:
|
| 393 |
+
fixElementDocument(c_start_node, doc, proxy_count)
|
| 394 |
+
|
| 395 |
+
return 0
|
| 396 |
+
|
| 397 |
+
|
| 398 |
+
cdef void _setTreeDoc(xmlNode* c_node, xmlDoc* c_doc) noexcept:
|
| 399 |
+
"""Adaptation of 'xmlSetTreeDoc()' that deep-fixes the document links iteratively.
|
| 400 |
+
It avoids https://gitlab.gnome.org/GNOME/libxml2/issues/42
|
| 401 |
+
"""
|
| 402 |
+
tree.BEGIN_FOR_EACH_FROM(c_node, c_node, 1)
|
| 403 |
+
if c_node.type == tree.XML_ELEMENT_NODE:
|
| 404 |
+
c_attr = <tree.xmlAttr*>c_node.properties
|
| 405 |
+
while c_attr:
|
| 406 |
+
if c_attr.atype == tree.XML_ATTRIBUTE_ID:
|
| 407 |
+
tree.xmlRemoveID(c_node.doc, c_attr)
|
| 408 |
+
c_attr.doc = c_doc
|
| 409 |
+
_fixDocChildren(c_attr.children, c_doc)
|
| 410 |
+
c_attr = c_attr.next
|
| 411 |
+
# Set doc link for all nodes, not only elements.
|
| 412 |
+
c_node.doc = c_doc
|
| 413 |
+
tree.END_FOR_EACH_FROM(c_node)
|
| 414 |
+
|
| 415 |
+
|
| 416 |
+
cdef inline void _fixDocChildren(xmlNode* c_child, xmlDoc* c_doc) noexcept:
|
| 417 |
+
while c_child:
|
| 418 |
+
c_child.doc = c_doc
|
| 419 |
+
if c_child.children:
|
| 420 |
+
_fixDocChildren(c_child.children, c_doc)
|
| 421 |
+
c_child = c_child.next
|
| 422 |
+
|
| 423 |
+
|
| 424 |
+
cdef int _fixCNs(_Document doc, xmlNode* c_start_node, xmlNode* c_node,
|
| 425 |
+
_nscache* c_ns_cache, xmlNs* c_del_ns_list) except -1:
|
| 426 |
+
cdef xmlNs* c_ns = NULL
|
| 427 |
+
cdef bint is_prefixed_attr = (c_node.type == tree.XML_ATTRIBUTE_NODE and c_node.ns.prefix)
|
| 428 |
+
|
| 429 |
+
for ns_map in c_ns_cache.ns_map[:c_ns_cache.last]:
|
| 430 |
+
if c_node.ns is ns_map.old:
|
| 431 |
+
if is_prefixed_attr and not ns_map.new.prefix:
|
| 432 |
+
# avoid dropping prefix from attributes
|
| 433 |
+
continue
|
| 434 |
+
c_ns = ns_map.new
|
| 435 |
+
break
|
| 436 |
+
|
| 437 |
+
if c_ns:
|
| 438 |
+
c_node.ns = c_ns
|
| 439 |
+
else:
|
| 440 |
+
# not in cache or not acceptable
|
| 441 |
+
# => find a replacement from this document
|
| 442 |
+
try:
|
| 443 |
+
c_ns = doc._findOrBuildNodeNs(
|
| 444 |
+
c_start_node, c_node.ns.href, c_node.ns.prefix,
|
| 445 |
+
c_node.type == tree.XML_ATTRIBUTE_NODE)
|
| 446 |
+
c_node.ns = c_ns
|
| 447 |
+
_appendToNsCache(c_ns_cache, c_node.ns, c_ns)
|
| 448 |
+
except:
|
| 449 |
+
_cleanUpFromNamespaceAdaptation(c_start_node, c_ns_cache, c_del_ns_list)
|
| 450 |
+
raise
|
| 451 |
+
return 0
|
| 452 |
+
|
| 453 |
+
|
| 454 |
+
cdef int fixElementDocument(xmlNode* c_element, _Document doc,
|
| 455 |
+
size_t proxy_count) except -1:
|
| 456 |
+
cdef xmlNode* c_node = c_element
|
| 457 |
+
cdef _Element proxy = None # init-to-None required due to fake-loop below
|
| 458 |
+
tree.BEGIN_FOR_EACH_FROM(c_element, c_node, 1)
|
| 459 |
+
if c_node._private is not NULL:
|
| 460 |
+
proxy = getProxy(c_node)
|
| 461 |
+
if proxy is not None:
|
| 462 |
+
if proxy._doc is not doc:
|
| 463 |
+
proxy._doc = doc
|
| 464 |
+
proxy_count -= 1
|
| 465 |
+
if proxy_count == 0:
|
| 466 |
+
return 0
|
| 467 |
+
tree.END_FOR_EACH_FROM(c_node)
|
| 468 |
+
|
| 469 |
+
|
| 470 |
+
cdef void fixThreadDictNames(xmlNode* c_element,
|
| 471 |
+
tree.xmlDict* c_src_dict,
|
| 472 |
+
tree.xmlDict* c_dict) noexcept nogil:
|
| 473 |
+
# re-assign the names of tags and attributes
|
| 474 |
+
#
|
| 475 |
+
# this should only be called when the element is based on a
|
| 476 |
+
# different libxml2 tag name dictionary
|
| 477 |
+
if c_element.type == tree.XML_DOCUMENT_NODE or \
|
| 478 |
+
c_element.type == tree.XML_HTML_DOCUMENT_NODE:
|
| 479 |
+
# may define "xml" namespace
|
| 480 |
+
fixThreadDictNsForNode(c_element, c_src_dict, c_dict)
|
| 481 |
+
if c_element.doc.extSubset:
|
| 482 |
+
fixThreadDictNamesForDtd(c_element.doc.extSubset, c_src_dict, c_dict)
|
| 483 |
+
if c_element.doc.intSubset:
|
| 484 |
+
fixThreadDictNamesForDtd(c_element.doc.intSubset, c_src_dict, c_dict)
|
| 485 |
+
c_element = c_element.children
|
| 486 |
+
while c_element is not NULL:
|
| 487 |
+
fixThreadDictNamesForNode(c_element, c_src_dict, c_dict)
|
| 488 |
+
c_element = c_element.next
|
| 489 |
+
elif tree._isElementOrXInclude(c_element):
|
| 490 |
+
fixThreadDictNamesForNode(c_element, c_src_dict, c_dict)
|
| 491 |
+
|
| 492 |
+
|
| 493 |
+
cdef inline void _fixThreadDictPtr(const_xmlChar** c_ptr,
|
| 494 |
+
tree.xmlDict* c_src_dict,
|
| 495 |
+
tree.xmlDict* c_dict) noexcept nogil:
|
| 496 |
+
c_str = c_ptr[0]
|
| 497 |
+
if c_str and c_src_dict and tree.xmlDictOwns(c_src_dict, c_str):
|
| 498 |
+
# return value can be NULL on memory error, but we don't handle that here
|
| 499 |
+
c_str = tree.xmlDictLookup(c_dict, c_str, -1)
|
| 500 |
+
if c_str:
|
| 501 |
+
c_ptr[0] = c_str
|
| 502 |
+
|
| 503 |
+
|
| 504 |
+
cdef void fixThreadDictNamesForNode(xmlNode* c_element,
|
| 505 |
+
tree.xmlDict* c_src_dict,
|
| 506 |
+
tree.xmlDict* c_dict) noexcept nogil:
|
| 507 |
+
cdef xmlNode* c_node = c_element
|
| 508 |
+
tree.BEGIN_FOR_EACH_FROM(c_element, c_node, 1)
|
| 509 |
+
if c_node.type in (tree.XML_ELEMENT_NODE, tree.XML_XINCLUDE_START):
|
| 510 |
+
fixThreadDictNamesForAttributes(
|
| 511 |
+
c_node.properties, c_src_dict, c_dict)
|
| 512 |
+
fixThreadDictNsForNode(c_node, c_src_dict, c_dict)
|
| 513 |
+
_fixThreadDictPtr(&c_node.name, c_src_dict, c_dict)
|
| 514 |
+
elif c_node.type == tree.XML_TEXT_NODE:
|
| 515 |
+
# libxml2's SAX2 parser interns some indentation space
|
| 516 |
+
fixThreadDictContentForNode(c_node, c_src_dict, c_dict)
|
| 517 |
+
elif c_node.type == tree.XML_COMMENT_NODE:
|
| 518 |
+
pass # don't touch c_node.name
|
| 519 |
+
else:
|
| 520 |
+
_fixThreadDictPtr(&c_node.name, c_src_dict, c_dict)
|
| 521 |
+
tree.END_FOR_EACH_FROM(c_node)
|
| 522 |
+
|
| 523 |
+
|
| 524 |
+
cdef inline void fixThreadDictNamesForAttributes(tree.xmlAttr* c_attr,
|
| 525 |
+
tree.xmlDict* c_src_dict,
|
| 526 |
+
tree.xmlDict* c_dict) noexcept nogil:
|
| 527 |
+
cdef xmlNode* c_child
|
| 528 |
+
cdef xmlNode* c_node = <xmlNode*>c_attr
|
| 529 |
+
while c_node is not NULL:
|
| 530 |
+
if c_node.type not in (tree.XML_TEXT_NODE, tree.XML_COMMENT_NODE):
|
| 531 |
+
_fixThreadDictPtr(&c_node.name, c_src_dict, c_dict)
|
| 532 |
+
# libxml2 keeps some (!) attribute values in the dict
|
| 533 |
+
c_child = c_node.children
|
| 534 |
+
while c_child is not NULL:
|
| 535 |
+
fixThreadDictContentForNode(c_child, c_src_dict, c_dict)
|
| 536 |
+
c_child = c_child.next
|
| 537 |
+
c_node = c_node.next
|
| 538 |
+
|
| 539 |
+
|
| 540 |
+
cdef inline void fixThreadDictContentForNode(xmlNode* c_node,
|
| 541 |
+
tree.xmlDict* c_src_dict,
|
| 542 |
+
tree.xmlDict* c_dict) noexcept nogil:
|
| 543 |
+
if c_node.content is not NULL and \
|
| 544 |
+
c_node.content is not <xmlChar*>&c_node.properties:
|
| 545 |
+
if tree.xmlDictOwns(c_src_dict, c_node.content):
|
| 546 |
+
# result can be NULL on memory error, but we don't handle that here
|
| 547 |
+
c_node.content = <xmlChar*>tree.xmlDictLookup(c_dict, c_node.content, -1)
|
| 548 |
+
|
| 549 |
+
|
| 550 |
+
cdef inline void fixThreadDictNsForNode(xmlNode* c_node,
|
| 551 |
+
tree.xmlDict* c_src_dict,
|
| 552 |
+
tree.xmlDict* c_dict) noexcept nogil:
|
| 553 |
+
cdef xmlNs* c_ns = c_node.nsDef
|
| 554 |
+
while c_ns is not NULL:
|
| 555 |
+
_fixThreadDictPtr(&c_ns.href, c_src_dict, c_dict)
|
| 556 |
+
_fixThreadDictPtr(&c_ns.prefix, c_src_dict, c_dict)
|
| 557 |
+
c_ns = c_ns.next
|
| 558 |
+
|
| 559 |
+
|
| 560 |
+
cdef void fixThreadDictNamesForDtd(tree.xmlDtd* c_dtd,
|
| 561 |
+
tree.xmlDict* c_src_dict,
|
| 562 |
+
tree.xmlDict* c_dict) noexcept nogil:
|
| 563 |
+
cdef xmlNode* c_node
|
| 564 |
+
cdef tree.xmlElement* c_element
|
| 565 |
+
cdef tree.xmlAttribute* c_attribute
|
| 566 |
+
cdef tree.xmlEntity* c_entity
|
| 567 |
+
|
| 568 |
+
c_node = c_dtd.children
|
| 569 |
+
while c_node:
|
| 570 |
+
if c_node.type == tree.XML_ELEMENT_DECL:
|
| 571 |
+
c_element = <tree.xmlElement*>c_node
|
| 572 |
+
if c_element.content:
|
| 573 |
+
_fixThreadDictPtr(&c_element.content.name, c_src_dict, c_dict)
|
| 574 |
+
_fixThreadDictPtr(&c_element.content.prefix, c_src_dict, c_dict)
|
| 575 |
+
c_attribute = c_element.attributes
|
| 576 |
+
while c_attribute:
|
| 577 |
+
if tree.LIBXML_VERSION < 21500:
|
| 578 |
+
# libxml2 2.15 no longer stores default values in the dict.
|
| 579 |
+
# See https://gitlab.gnome.org/GNOME/libxml2/-/commit/24628f25
|
| 580 |
+
_fixThreadDictPtr(<const_xmlChar**>&c_attribute.defaultValue, c_src_dict, c_dict)
|
| 581 |
+
_fixThreadDictPtr(&c_attribute.name, c_src_dict, c_dict)
|
| 582 |
+
_fixThreadDictPtr(&c_attribute.prefix, c_src_dict, c_dict)
|
| 583 |
+
_fixThreadDictPtr(&c_attribute.elem, c_src_dict, c_dict)
|
| 584 |
+
c_attribute = c_attribute.nexth
|
| 585 |
+
elif c_node.type == tree.XML_ENTITY_DECL:
|
| 586 |
+
c_entity = <tree.xmlEntity*>c_node
|
| 587 |
+
_fixThreadDictPtr(&c_entity.name, c_src_dict, c_dict)
|
| 588 |
+
_fixThreadDictPtr(&c_entity.ExternalID, c_src_dict, c_dict)
|
| 589 |
+
_fixThreadDictPtr(&c_entity.SystemID, c_src_dict, c_dict)
|
| 590 |
+
_fixThreadDictPtr(<const_xmlChar**>&c_entity.content, c_src_dict, c_dict)
|
| 591 |
+
c_node = c_node.next
|
| 592 |
+
|
| 593 |
+
|
| 594 |
+
################################################################################
|
| 595 |
+
# adopt an xmlDoc from an external libxml2 document source
|
| 596 |
+
|
| 597 |
+
cdef _Document _adoptForeignDoc(xmlDoc* c_doc, _BaseParser parser=None, bint is_owned=True):
|
| 598 |
+
"""Convert and wrap an externally produced xmlDoc for use in lxml.
|
| 599 |
+
Assures that all '_private' pointers are NULL to prevent accidental
|
| 600 |
+
dereference into lxml proxy objects.
|
| 601 |
+
"""
|
| 602 |
+
if c_doc is NULL:
|
| 603 |
+
raise ValueError("Illegal document provided: NULL")
|
| 604 |
+
if c_doc.type not in (tree.XML_DOCUMENT_NODE, tree.XML_HTML_DOCUMENT_NODE):
|
| 605 |
+
doc_type = c_doc.type
|
| 606 |
+
if is_owned:
|
| 607 |
+
tree.xmlFreeDoc(c_doc)
|
| 608 |
+
raise ValueError(f"Illegal document provided: expected XML or HTML, found {doc_type}")
|
| 609 |
+
|
| 610 |
+
cdef xmlNode* c_node = <xmlNode*>c_doc
|
| 611 |
+
|
| 612 |
+
if is_owned:
|
| 613 |
+
tree.BEGIN_FOR_EACH_FROM(<xmlNode*>c_doc, c_node, 1)
|
| 614 |
+
c_node._private = NULL
|
| 615 |
+
tree.END_FOR_EACH_FROM(c_node)
|
| 616 |
+
else:
|
| 617 |
+
# create a fresh copy that lxml owns
|
| 618 |
+
c_doc = tree.xmlCopyDoc(c_doc, 1)
|
| 619 |
+
if c_doc is NULL:
|
| 620 |
+
raise MemoryError()
|
| 621 |
+
|
| 622 |
+
return _documentFactory(c_doc, parser)
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/public-api.pxi
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Public C API for lxml.etree
|
| 2 |
+
|
| 3 |
+
cdef public api _Element deepcopyNodeToDocument(_Document doc, xmlNode* c_root):
|
| 4 |
+
"Recursively copy the element into the document. doc is not modified."
|
| 5 |
+
cdef xmlNode* c_node
|
| 6 |
+
c_node = _copyNodeToDoc(c_root, doc._c_doc)
|
| 7 |
+
return _elementFactory(doc, c_node)
|
| 8 |
+
|
| 9 |
+
cdef public api _ElementTree elementTreeFactory(_Element context_node):
|
| 10 |
+
_assertValidNode(context_node)
|
| 11 |
+
return newElementTree(context_node, _ElementTree)
|
| 12 |
+
|
| 13 |
+
cdef public api _ElementTree newElementTree(_Element context_node,
|
| 14 |
+
object subclass):
|
| 15 |
+
if <void*>context_node is NULL or context_node is None:
|
| 16 |
+
raise TypeError
|
| 17 |
+
_assertValidNode(context_node)
|
| 18 |
+
return _newElementTree(context_node._doc, context_node, subclass)
|
| 19 |
+
|
| 20 |
+
cdef public api _ElementTree adoptExternalDocument(xmlDoc* c_doc, parser, bint is_owned):
|
| 21 |
+
if c_doc is NULL:
|
| 22 |
+
raise TypeError
|
| 23 |
+
doc = _adoptForeignDoc(c_doc, parser, is_owned)
|
| 24 |
+
return _elementTreeFactory(doc, None)
|
| 25 |
+
|
| 26 |
+
cdef public api _Element elementFactory(_Document doc, xmlNode* c_node):
|
| 27 |
+
if c_node is NULL or doc is None:
|
| 28 |
+
raise TypeError
|
| 29 |
+
return _elementFactory(doc, c_node)
|
| 30 |
+
|
| 31 |
+
cdef public api _Element makeElement(tag, _Document doc, parser,
|
| 32 |
+
text, tail, attrib, nsmap):
|
| 33 |
+
return _makeElement(tag, NULL, doc, parser, text, tail, attrib, nsmap, None)
|
| 34 |
+
|
| 35 |
+
cdef public api _Element makeSubElement(_Element parent, tag, text, tail,
|
| 36 |
+
attrib, nsmap):
|
| 37 |
+
_assertValidNode(parent)
|
| 38 |
+
return _makeSubElement(parent, tag, text, tail, attrib, nsmap, None)
|
| 39 |
+
|
| 40 |
+
cdef public api void setElementClassLookupFunction(
|
| 41 |
+
_element_class_lookup_function function, state):
|
| 42 |
+
_setElementClassLookupFunction(function, state)
|
| 43 |
+
|
| 44 |
+
cdef public api object lookupDefaultElementClass(state, doc, xmlNode* c_node):
|
| 45 |
+
return _lookupDefaultElementClass(state, doc, c_node)
|
| 46 |
+
|
| 47 |
+
cdef public api object lookupNamespaceElementClass(state, doc, xmlNode* c_node):
|
| 48 |
+
return _find_nselement_class(state, doc, c_node)
|
| 49 |
+
|
| 50 |
+
cdef public api object callLookupFallback(FallbackElementClassLookup lookup,
|
| 51 |
+
_Document doc, xmlNode* c_node):
|
| 52 |
+
return _callLookupFallback(lookup, doc, c_node)
|
| 53 |
+
|
| 54 |
+
cdef public api int tagMatches(xmlNode* c_node, const_xmlChar* c_href, const_xmlChar* c_name):
|
| 55 |
+
if c_node is NULL:
|
| 56 |
+
return -1
|
| 57 |
+
return _tagMatches(c_node, c_href, c_name)
|
| 58 |
+
|
| 59 |
+
cdef public api _Document documentOrRaise(object input):
|
| 60 |
+
return _documentOrRaise(input)
|
| 61 |
+
|
| 62 |
+
cdef public api _Element rootNodeOrRaise(object input):
|
| 63 |
+
return _rootNodeOrRaise(input)
|
| 64 |
+
|
| 65 |
+
cdef public api bint hasText(xmlNode* c_node):
|
| 66 |
+
return _hasText(c_node)
|
| 67 |
+
|
| 68 |
+
cdef public api bint hasTail(xmlNode* c_node):
|
| 69 |
+
return _hasTail(c_node)
|
| 70 |
+
|
| 71 |
+
cdef public api unicode textOf(xmlNode* c_node):
|
| 72 |
+
if c_node is NULL:
|
| 73 |
+
return None
|
| 74 |
+
return _collectText(c_node.children)
|
| 75 |
+
|
| 76 |
+
cdef public api unicode tailOf(xmlNode* c_node):
|
| 77 |
+
if c_node is NULL:
|
| 78 |
+
return None
|
| 79 |
+
return _collectText(c_node.next)
|
| 80 |
+
|
| 81 |
+
cdef public api int setNodeText(xmlNode* c_node, text) except -1:
|
| 82 |
+
if c_node is NULL:
|
| 83 |
+
raise ValueError
|
| 84 |
+
return _setNodeText(c_node, text)
|
| 85 |
+
|
| 86 |
+
cdef public api int setTailText(xmlNode* c_node, text) except -1:
|
| 87 |
+
if c_node is NULL:
|
| 88 |
+
raise ValueError
|
| 89 |
+
return _setTailText(c_node, text)
|
| 90 |
+
|
| 91 |
+
cdef public api unicode attributeValue(xmlNode* c_element, xmlAttr* c_attrib_node):
|
| 92 |
+
return _attributeValue(c_element, c_attrib_node)
|
| 93 |
+
|
| 94 |
+
cdef public api unicode attributeValueFromNsName(xmlNode* c_element,
|
| 95 |
+
const_xmlChar* ns, const_xmlChar* name):
|
| 96 |
+
return _attributeValueFromNsName(c_element, ns, name)
|
| 97 |
+
|
| 98 |
+
cdef public api object getAttributeValue(_Element element, key, default):
|
| 99 |
+
_assertValidNode(element)
|
| 100 |
+
return _getAttributeValue(element, key, default)
|
| 101 |
+
|
| 102 |
+
cdef public api object iterattributes(_Element element, int keysvalues):
|
| 103 |
+
_assertValidNode(element)
|
| 104 |
+
return _attributeIteratorFactory(element, keysvalues)
|
| 105 |
+
|
| 106 |
+
cdef public api list collectAttributes(xmlNode* c_element, int keysvalues):
|
| 107 |
+
return _collectAttributes(c_element, keysvalues)
|
| 108 |
+
|
| 109 |
+
cdef public api int setAttributeValue(_Element element, key, value) except -1:
|
| 110 |
+
_assertValidNode(element)
|
| 111 |
+
return _setAttributeValue(element, key, value)
|
| 112 |
+
|
| 113 |
+
cdef public api int delAttribute(_Element element, key) except -1:
|
| 114 |
+
_assertValidNode(element)
|
| 115 |
+
return _delAttribute(element, key)
|
| 116 |
+
|
| 117 |
+
cdef public api int delAttributeFromNsName(tree.xmlNode* c_element,
|
| 118 |
+
const_xmlChar* c_href, const_xmlChar* c_name):
|
| 119 |
+
return _delAttributeFromNsName(c_element, c_href, c_name)
|
| 120 |
+
|
| 121 |
+
cdef public api bint hasChild(xmlNode* c_node):
|
| 122 |
+
return _hasChild(c_node)
|
| 123 |
+
|
| 124 |
+
cdef public api xmlNode* findChild(xmlNode* c_node, Py_ssize_t index):
|
| 125 |
+
return _findChild(c_node, index)
|
| 126 |
+
|
| 127 |
+
cdef public api xmlNode* findChildForwards(xmlNode* c_node, Py_ssize_t index):
|
| 128 |
+
return _findChildForwards(c_node, index)
|
| 129 |
+
|
| 130 |
+
cdef public api xmlNode* findChildBackwards(xmlNode* c_node, Py_ssize_t index):
|
| 131 |
+
return _findChildBackwards(c_node, index)
|
| 132 |
+
|
| 133 |
+
cdef public api xmlNode* nextElement(xmlNode* c_node):
|
| 134 |
+
return _nextElement(c_node)
|
| 135 |
+
|
| 136 |
+
cdef public api xmlNode* previousElement(xmlNode* c_node):
|
| 137 |
+
return _previousElement(c_node)
|
| 138 |
+
|
| 139 |
+
cdef public api void appendChild(_Element parent, _Element child):
|
| 140 |
+
# deprecated, use appendChildToElement() instead!
|
| 141 |
+
_appendChild(parent, child)
|
| 142 |
+
|
| 143 |
+
cdef public api int appendChildToElement(_Element parent, _Element child) except -1:
|
| 144 |
+
return _appendChild(parent, child)
|
| 145 |
+
|
| 146 |
+
cdef public api unicode pyunicode(const_xmlChar* s):
|
| 147 |
+
if s is NULL:
|
| 148 |
+
raise TypeError
|
| 149 |
+
return funicode(s)
|
| 150 |
+
|
| 151 |
+
cdef public api bytes utf8(object s):
|
| 152 |
+
return _utf8(s)
|
| 153 |
+
|
| 154 |
+
cdef public api tuple getNsTag(object tag):
|
| 155 |
+
return _getNsTag(tag)
|
| 156 |
+
|
| 157 |
+
cdef public api tuple getNsTagWithEmptyNs(object tag):
|
| 158 |
+
return _getNsTagWithEmptyNs(tag)
|
| 159 |
+
|
| 160 |
+
cdef public api unicode namespacedName(xmlNode* c_node):
|
| 161 |
+
return _namespacedName(c_node)
|
| 162 |
+
|
| 163 |
+
cdef public api unicode namespacedNameFromNsName(const_xmlChar* href, const_xmlChar* name):
|
| 164 |
+
return _namespacedNameFromNsName(href, name)
|
| 165 |
+
|
| 166 |
+
cdef public api void iteratorStoreNext(_ElementIterator iterator, _Element node):
|
| 167 |
+
# deprecated!
|
| 168 |
+
iterator._storeNext(node)
|
| 169 |
+
|
| 170 |
+
cdef public api void initTagMatch(_ElementTagMatcher matcher, tag):
|
| 171 |
+
# deprecated!
|
| 172 |
+
matcher._initTagMatch(tag)
|
| 173 |
+
|
| 174 |
+
cdef public api tree.xmlNs* findOrBuildNodeNsPrefix(
|
| 175 |
+
_Document doc, xmlNode* c_node, const_xmlChar* href, const_xmlChar* prefix) except NULL:
|
| 176 |
+
if doc is None:
|
| 177 |
+
raise TypeError
|
| 178 |
+
return doc._findOrBuildNodeNs(c_node, href, prefix, 0)
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/pyclasslookup.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# dummy module for backwards compatibility
|
| 2 |
+
|
| 3 |
+
from lxml.etree import PythonElementClassLookup
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/relaxng.pxi
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# support for RelaxNG validation
|
| 2 |
+
from lxml.includes cimport relaxng
|
| 3 |
+
|
| 4 |
+
cdef object _rnc2rng
|
| 5 |
+
try:
|
| 6 |
+
import rnc2rng as _rnc2rng
|
| 7 |
+
except ImportError:
|
| 8 |
+
_rnc2rng = None
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
cdef int _require_rnc2rng() except -1:
|
| 12 |
+
if _rnc2rng is None:
|
| 13 |
+
raise RelaxNGParseError(
|
| 14 |
+
'compact syntax not supported (please install rnc2rng)')
|
| 15 |
+
return 0
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
cdef class RelaxNGError(LxmlError):
|
| 19 |
+
"""Base class for RelaxNG errors.
|
| 20 |
+
"""
|
| 21 |
+
|
| 22 |
+
cdef class RelaxNGParseError(RelaxNGError):
|
| 23 |
+
"""Error while parsing an XML document as RelaxNG.
|
| 24 |
+
"""
|
| 25 |
+
|
| 26 |
+
cdef class RelaxNGValidateError(RelaxNGError):
|
| 27 |
+
"""Error while validating an XML document with a RelaxNG schema.
|
| 28 |
+
"""
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
################################################################################
|
| 32 |
+
# RelaxNG
|
| 33 |
+
|
| 34 |
+
cdef class RelaxNG(_Validator):
|
| 35 |
+
"""RelaxNG(self, etree=None, file=None)
|
| 36 |
+
Turn a document into a Relax NG validator.
|
| 37 |
+
|
| 38 |
+
Either pass a schema as Element or ElementTree, or pass a file or
|
| 39 |
+
filename through the ``file`` keyword argument.
|
| 40 |
+
"""
|
| 41 |
+
cdef relaxng.xmlRelaxNG* _c_schema
|
| 42 |
+
def __cinit__(self):
|
| 43 |
+
self._c_schema = NULL
|
| 44 |
+
|
| 45 |
+
def __init__(self, etree=None, *, file=None):
|
| 46 |
+
cdef _Document doc
|
| 47 |
+
cdef _Element root_node
|
| 48 |
+
cdef xmlDoc* fake_c_doc = NULL
|
| 49 |
+
cdef relaxng.xmlRelaxNGParserCtxt* parser_ctxt = NULL
|
| 50 |
+
_Validator.__init__(self)
|
| 51 |
+
if etree is not None:
|
| 52 |
+
doc = _documentOrRaise(etree)
|
| 53 |
+
root_node = _rootNodeOrRaise(etree)
|
| 54 |
+
fake_c_doc = _fakeRootDoc(doc._c_doc, root_node._c_node)
|
| 55 |
+
parser_ctxt = relaxng.xmlRelaxNGNewDocParserCtxt(fake_c_doc)
|
| 56 |
+
elif file is not None:
|
| 57 |
+
if _isString(file):
|
| 58 |
+
if file[-4:].lower() == '.rnc':
|
| 59 |
+
_require_rnc2rng()
|
| 60 |
+
rng_data_utf8 = _utf8(_rnc2rng.dumps(_rnc2rng.load(file)))
|
| 61 |
+
doc = _parseMemoryDocument(rng_data_utf8, parser=None, url=file)
|
| 62 |
+
parser_ctxt = relaxng.xmlRelaxNGNewDocParserCtxt(doc._c_doc)
|
| 63 |
+
else:
|
| 64 |
+
doc = None
|
| 65 |
+
filename = _encodeFilename(file)
|
| 66 |
+
with self._error_log:
|
| 67 |
+
orig_loader = _register_document_loader()
|
| 68 |
+
parser_ctxt = relaxng.xmlRelaxNGNewParserCtxt(_cstr(filename))
|
| 69 |
+
_reset_document_loader(orig_loader)
|
| 70 |
+
elif (_getFilenameForFile(file) or '')[-4:].lower() == '.rnc':
|
| 71 |
+
_require_rnc2rng()
|
| 72 |
+
rng_data_utf8 = _utf8(_rnc2rng.dumps(_rnc2rng.load(file)))
|
| 73 |
+
doc = _parseMemoryDocument(
|
| 74 |
+
rng_data_utf8, parser=None, url=_getFilenameForFile(file))
|
| 75 |
+
parser_ctxt = relaxng.xmlRelaxNGNewDocParserCtxt(doc._c_doc)
|
| 76 |
+
else:
|
| 77 |
+
doc = _parseDocument(file, parser=None, base_url=None)
|
| 78 |
+
parser_ctxt = relaxng.xmlRelaxNGNewDocParserCtxt(doc._c_doc)
|
| 79 |
+
else:
|
| 80 |
+
raise RelaxNGParseError, "No tree or file given"
|
| 81 |
+
|
| 82 |
+
if parser_ctxt is NULL:
|
| 83 |
+
if fake_c_doc is not NULL:
|
| 84 |
+
_destroyFakeDoc(doc._c_doc, fake_c_doc)
|
| 85 |
+
raise RelaxNGParseError(
|
| 86 |
+
self._error_log._buildExceptionMessage(
|
| 87 |
+
"Document is not parsable as Relax NG"),
|
| 88 |
+
self._error_log)
|
| 89 |
+
|
| 90 |
+
# Need a cast here because older libxml2 releases do not use 'const' in the functype.
|
| 91 |
+
relaxng.xmlRelaxNGSetParserStructuredErrors(
|
| 92 |
+
parser_ctxt, <xmlerror.xmlStructuredErrorFunc> _receiveError, <void*>self._error_log)
|
| 93 |
+
_connectGenericErrorLog(self._error_log, xmlerror.XML_FROM_RELAXNGP)
|
| 94 |
+
self._c_schema = relaxng.xmlRelaxNGParse(parser_ctxt)
|
| 95 |
+
_connectGenericErrorLog(None)
|
| 96 |
+
|
| 97 |
+
relaxng.xmlRelaxNGFreeParserCtxt(parser_ctxt)
|
| 98 |
+
if self._c_schema is NULL:
|
| 99 |
+
if fake_c_doc is not NULL:
|
| 100 |
+
_destroyFakeDoc(doc._c_doc, fake_c_doc)
|
| 101 |
+
raise RelaxNGParseError(
|
| 102 |
+
self._error_log._buildExceptionMessage(
|
| 103 |
+
"Document is not valid Relax NG"),
|
| 104 |
+
self._error_log)
|
| 105 |
+
if fake_c_doc is not NULL:
|
| 106 |
+
_destroyFakeDoc(doc._c_doc, fake_c_doc)
|
| 107 |
+
|
| 108 |
+
def __dealloc__(self):
|
| 109 |
+
relaxng.xmlRelaxNGFree(self._c_schema)
|
| 110 |
+
|
| 111 |
+
def __call__(self, etree):
|
| 112 |
+
"""__call__(self, etree)
|
| 113 |
+
|
| 114 |
+
Validate doc using Relax NG.
|
| 115 |
+
|
| 116 |
+
Returns true if document is valid, false if not."""
|
| 117 |
+
cdef _Document doc
|
| 118 |
+
cdef _Element root_node
|
| 119 |
+
cdef xmlDoc* c_doc
|
| 120 |
+
cdef relaxng.xmlRelaxNGValidCtxt* valid_ctxt
|
| 121 |
+
cdef int ret
|
| 122 |
+
|
| 123 |
+
assert self._c_schema is not NULL, "RelaxNG instance not initialised"
|
| 124 |
+
doc = _documentOrRaise(etree)
|
| 125 |
+
root_node = _rootNodeOrRaise(etree)
|
| 126 |
+
|
| 127 |
+
valid_ctxt = relaxng.xmlRelaxNGNewValidCtxt(self._c_schema)
|
| 128 |
+
if valid_ctxt is NULL:
|
| 129 |
+
raise MemoryError()
|
| 130 |
+
|
| 131 |
+
try:
|
| 132 |
+
self._error_log.clear()
|
| 133 |
+
# Need a cast here because older libxml2 releases do not use 'const' in the functype.
|
| 134 |
+
relaxng.xmlRelaxNGSetValidStructuredErrors(
|
| 135 |
+
valid_ctxt, <xmlerror.xmlStructuredErrorFunc> _receiveError, <void*>self._error_log)
|
| 136 |
+
_connectGenericErrorLog(self._error_log, xmlerror.XML_FROM_RELAXNGV)
|
| 137 |
+
c_doc = _fakeRootDoc(doc._c_doc, root_node._c_node)
|
| 138 |
+
with nogil:
|
| 139 |
+
ret = relaxng.xmlRelaxNGValidateDoc(valid_ctxt, c_doc)
|
| 140 |
+
_destroyFakeDoc(doc._c_doc, c_doc)
|
| 141 |
+
finally:
|
| 142 |
+
_connectGenericErrorLog(None)
|
| 143 |
+
relaxng.xmlRelaxNGFreeValidCtxt(valid_ctxt)
|
| 144 |
+
|
| 145 |
+
if ret == -1:
|
| 146 |
+
raise RelaxNGValidateError(
|
| 147 |
+
"Internal error in Relax NG validation",
|
| 148 |
+
self._error_log)
|
| 149 |
+
if ret == 0:
|
| 150 |
+
return True
|
| 151 |
+
else:
|
| 152 |
+
return False
|
| 153 |
+
|
| 154 |
+
@classmethod
|
| 155 |
+
def from_rnc_string(cls, src, base_url=None):
|
| 156 |
+
"""Parse a RelaxNG schema in compact syntax from a text string
|
| 157 |
+
|
| 158 |
+
Requires the rnc2rng package to be installed.
|
| 159 |
+
|
| 160 |
+
Passing the source URL or file path of the source as 'base_url'
|
| 161 |
+
will enable resolving resource references relative to the source.
|
| 162 |
+
"""
|
| 163 |
+
_require_rnc2rng()
|
| 164 |
+
rng_str = utf8(_rnc2rng.dumps(_rnc2rng.loads(src)))
|
| 165 |
+
return cls(_parseMemoryDocument(rng_str, parser=None, url=base_url))
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/sax.py
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
SAX-based adapter to copy trees from/to the Python standard library.
|
| 3 |
+
|
| 4 |
+
Use the `ElementTreeContentHandler` class to build an ElementTree from
|
| 5 |
+
SAX events.
|
| 6 |
+
|
| 7 |
+
Use the `ElementTreeProducer` class or the `saxify()` function to fire
|
| 8 |
+
the SAX events of an ElementTree against a SAX ContentHandler.
|
| 9 |
+
|
| 10 |
+
See https://lxml.de/sax.html
|
| 11 |
+
"""
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
from xml.sax.handler import ContentHandler
|
| 15 |
+
from lxml import etree
|
| 16 |
+
from lxml.etree import ElementTree, SubElement
|
| 17 |
+
from lxml.etree import Comment, ProcessingInstruction
|
| 18 |
+
|
| 19 |
+
try:
|
| 20 |
+
from types import GenericAlias as _GenericAlias
|
| 21 |
+
except ImportError:
|
| 22 |
+
# Python 3.8 - we only need this as return value from "__class_getitem__"
|
| 23 |
+
def _GenericAlias(cls, item):
|
| 24 |
+
return f"{cls.__name__}[{item.__name__}]"
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
class SaxError(etree.LxmlError):
|
| 28 |
+
"""General SAX error.
|
| 29 |
+
"""
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def _getNsTag(tag):
|
| 33 |
+
if tag[0] == '{' and '}' in tag:
|
| 34 |
+
return tuple(tag[1:].split('}', 1))
|
| 35 |
+
else:
|
| 36 |
+
return None, tag
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
class ElementTreeContentHandler(ContentHandler):
|
| 40 |
+
"""Build an lxml ElementTree from SAX events.
|
| 41 |
+
"""
|
| 42 |
+
def __init__(self, makeelement=None):
|
| 43 |
+
ContentHandler.__init__(self)
|
| 44 |
+
self._root = None
|
| 45 |
+
self._root_siblings = []
|
| 46 |
+
self._element_stack = []
|
| 47 |
+
self._default_ns = None
|
| 48 |
+
self._ns_mapping = { None : [None] }
|
| 49 |
+
self._new_mappings = {}
|
| 50 |
+
if makeelement is None:
|
| 51 |
+
makeelement = etree.Element
|
| 52 |
+
self._makeelement = makeelement
|
| 53 |
+
|
| 54 |
+
def _get_etree(self):
|
| 55 |
+
"Contains the generated ElementTree after parsing is finished."
|
| 56 |
+
return ElementTree(self._root)
|
| 57 |
+
|
| 58 |
+
etree = property(_get_etree, doc=_get_etree.__doc__)
|
| 59 |
+
|
| 60 |
+
def setDocumentLocator(self, locator):
|
| 61 |
+
pass
|
| 62 |
+
|
| 63 |
+
def startDocument(self):
|
| 64 |
+
pass
|
| 65 |
+
|
| 66 |
+
def endDocument(self):
|
| 67 |
+
pass
|
| 68 |
+
|
| 69 |
+
def startPrefixMapping(self, prefix, uri):
|
| 70 |
+
self._new_mappings[prefix] = uri
|
| 71 |
+
try:
|
| 72 |
+
self._ns_mapping[prefix].append(uri)
|
| 73 |
+
except KeyError:
|
| 74 |
+
self._ns_mapping[prefix] = [uri]
|
| 75 |
+
if prefix is None:
|
| 76 |
+
self._default_ns = uri
|
| 77 |
+
|
| 78 |
+
def endPrefixMapping(self, prefix):
|
| 79 |
+
ns_uri_list = self._ns_mapping[prefix]
|
| 80 |
+
ns_uri_list.pop()
|
| 81 |
+
if prefix is None:
|
| 82 |
+
self._default_ns = ns_uri_list[-1]
|
| 83 |
+
|
| 84 |
+
def _buildTag(self, ns_name_tuple):
|
| 85 |
+
ns_uri, local_name = ns_name_tuple
|
| 86 |
+
if ns_uri:
|
| 87 |
+
el_tag = "{%s}%s" % ns_name_tuple
|
| 88 |
+
elif self._default_ns:
|
| 89 |
+
el_tag = "{%s}%s" % (self._default_ns, local_name)
|
| 90 |
+
else:
|
| 91 |
+
el_tag = local_name
|
| 92 |
+
return el_tag
|
| 93 |
+
|
| 94 |
+
def startElementNS(self, ns_name, qname, attributes=None):
|
| 95 |
+
el_name = self._buildTag(ns_name)
|
| 96 |
+
if attributes:
|
| 97 |
+
attrs = {}
|
| 98 |
+
try:
|
| 99 |
+
iter_attributes = attributes.iteritems()
|
| 100 |
+
except AttributeError:
|
| 101 |
+
iter_attributes = attributes.items()
|
| 102 |
+
|
| 103 |
+
for name_tuple, value in iter_attributes:
|
| 104 |
+
if name_tuple[0]:
|
| 105 |
+
attr_name = "{%s}%s" % name_tuple
|
| 106 |
+
else:
|
| 107 |
+
attr_name = name_tuple[1]
|
| 108 |
+
attrs[attr_name] = value
|
| 109 |
+
else:
|
| 110 |
+
attrs = None
|
| 111 |
+
|
| 112 |
+
element_stack = self._element_stack
|
| 113 |
+
if self._root is None:
|
| 114 |
+
element = self._root = \
|
| 115 |
+
self._makeelement(el_name, attrs, self._new_mappings)
|
| 116 |
+
if self._root_siblings and hasattr(element, 'addprevious'):
|
| 117 |
+
for sibling in self._root_siblings:
|
| 118 |
+
element.addprevious(sibling)
|
| 119 |
+
del self._root_siblings[:]
|
| 120 |
+
else:
|
| 121 |
+
element = SubElement(element_stack[-1], el_name,
|
| 122 |
+
attrs, self._new_mappings)
|
| 123 |
+
element_stack.append(element)
|
| 124 |
+
|
| 125 |
+
self._new_mappings.clear()
|
| 126 |
+
|
| 127 |
+
def processingInstruction(self, target, data):
|
| 128 |
+
pi = ProcessingInstruction(target, data)
|
| 129 |
+
if self._root is None:
|
| 130 |
+
self._root_siblings.append(pi)
|
| 131 |
+
else:
|
| 132 |
+
self._element_stack[-1].append(pi)
|
| 133 |
+
|
| 134 |
+
def endElementNS(self, ns_name, qname):
|
| 135 |
+
element = self._element_stack.pop()
|
| 136 |
+
el_tag = self._buildTag(ns_name)
|
| 137 |
+
if el_tag != element.tag:
|
| 138 |
+
raise SaxError("Unexpected element closed: " + el_tag)
|
| 139 |
+
|
| 140 |
+
def startElement(self, name, attributes=None):
|
| 141 |
+
if attributes:
|
| 142 |
+
attributes = {(None, k): v for k, v in attributes.items()}
|
| 143 |
+
self.startElementNS((None, name), name, attributes)
|
| 144 |
+
|
| 145 |
+
def endElement(self, name):
|
| 146 |
+
self.endElementNS((None, name), name)
|
| 147 |
+
|
| 148 |
+
def characters(self, data):
|
| 149 |
+
last_element = self._element_stack[-1]
|
| 150 |
+
try:
|
| 151 |
+
# if there already is a child element, we must append to its tail
|
| 152 |
+
last_element = last_element[-1]
|
| 153 |
+
except IndexError:
|
| 154 |
+
# otherwise: append to the text
|
| 155 |
+
last_element.text = (last_element.text or '') + data
|
| 156 |
+
else:
|
| 157 |
+
last_element.tail = (last_element.tail or '') + data
|
| 158 |
+
|
| 159 |
+
ignorableWhitespace = characters
|
| 160 |
+
|
| 161 |
+
# Allow subscripting sax.ElementTreeContentHandler in type annotions (PEP 560)
|
| 162 |
+
def __class_getitem__(cls, item):
|
| 163 |
+
return _GenericAlias(cls, item)
|
| 164 |
+
|
| 165 |
+
|
| 166 |
+
class ElementTreeProducer:
|
| 167 |
+
"""Produces SAX events for an element and children.
|
| 168 |
+
"""
|
| 169 |
+
def __init__(self, element_or_tree, content_handler):
|
| 170 |
+
try:
|
| 171 |
+
element = element_or_tree.getroot()
|
| 172 |
+
except AttributeError:
|
| 173 |
+
element = element_or_tree
|
| 174 |
+
self._element = element
|
| 175 |
+
self._content_handler = content_handler
|
| 176 |
+
from xml.sax.xmlreader import AttributesNSImpl as attr_class
|
| 177 |
+
self._attr_class = attr_class
|
| 178 |
+
self._empty_attributes = attr_class({}, {})
|
| 179 |
+
|
| 180 |
+
def saxify(self):
|
| 181 |
+
self._content_handler.startDocument()
|
| 182 |
+
|
| 183 |
+
element = self._element
|
| 184 |
+
if hasattr(element, 'getprevious'):
|
| 185 |
+
siblings = []
|
| 186 |
+
sibling = element.getprevious()
|
| 187 |
+
while getattr(sibling, 'tag', None) is ProcessingInstruction:
|
| 188 |
+
siblings.append(sibling)
|
| 189 |
+
sibling = sibling.getprevious()
|
| 190 |
+
for sibling in siblings[::-1]:
|
| 191 |
+
self._recursive_saxify(sibling, {})
|
| 192 |
+
|
| 193 |
+
self._recursive_saxify(element, {})
|
| 194 |
+
|
| 195 |
+
if hasattr(element, 'getnext'):
|
| 196 |
+
sibling = element.getnext()
|
| 197 |
+
while getattr(sibling, 'tag', None) is ProcessingInstruction:
|
| 198 |
+
self._recursive_saxify(sibling, {})
|
| 199 |
+
sibling = sibling.getnext()
|
| 200 |
+
|
| 201 |
+
self._content_handler.endDocument()
|
| 202 |
+
|
| 203 |
+
def _recursive_saxify(self, element, parent_nsmap):
|
| 204 |
+
content_handler = self._content_handler
|
| 205 |
+
tag = element.tag
|
| 206 |
+
if tag is Comment or tag is ProcessingInstruction:
|
| 207 |
+
if tag is ProcessingInstruction:
|
| 208 |
+
content_handler.processingInstruction(
|
| 209 |
+
element.target, element.text)
|
| 210 |
+
tail = element.tail
|
| 211 |
+
if tail:
|
| 212 |
+
content_handler.characters(tail)
|
| 213 |
+
return
|
| 214 |
+
|
| 215 |
+
element_nsmap = element.nsmap
|
| 216 |
+
new_prefixes = []
|
| 217 |
+
if element_nsmap != parent_nsmap:
|
| 218 |
+
# There have been updates to the namespace
|
| 219 |
+
for prefix, ns_uri in element_nsmap.items():
|
| 220 |
+
if parent_nsmap.get(prefix) != ns_uri:
|
| 221 |
+
new_prefixes.append( (prefix, ns_uri) )
|
| 222 |
+
|
| 223 |
+
attribs = element.items()
|
| 224 |
+
if attribs:
|
| 225 |
+
attr_values = {}
|
| 226 |
+
attr_qnames = {}
|
| 227 |
+
for attr_ns_name, value in attribs:
|
| 228 |
+
attr_ns_tuple = _getNsTag(attr_ns_name)
|
| 229 |
+
attr_values[attr_ns_tuple] = value
|
| 230 |
+
attr_qnames[attr_ns_tuple] = self._build_qname(
|
| 231 |
+
attr_ns_tuple[0], attr_ns_tuple[1], element_nsmap,
|
| 232 |
+
preferred_prefix=None, is_attribute=True)
|
| 233 |
+
sax_attributes = self._attr_class(attr_values, attr_qnames)
|
| 234 |
+
else:
|
| 235 |
+
sax_attributes = self._empty_attributes
|
| 236 |
+
|
| 237 |
+
ns_uri, local_name = _getNsTag(tag)
|
| 238 |
+
qname = self._build_qname(
|
| 239 |
+
ns_uri, local_name, element_nsmap, element.prefix, is_attribute=False)
|
| 240 |
+
|
| 241 |
+
for prefix, uri in new_prefixes:
|
| 242 |
+
content_handler.startPrefixMapping(prefix, uri)
|
| 243 |
+
content_handler.startElementNS(
|
| 244 |
+
(ns_uri, local_name), qname, sax_attributes)
|
| 245 |
+
text = element.text
|
| 246 |
+
if text:
|
| 247 |
+
content_handler.characters(text)
|
| 248 |
+
for child in element:
|
| 249 |
+
self._recursive_saxify(child, element_nsmap)
|
| 250 |
+
content_handler.endElementNS((ns_uri, local_name), qname)
|
| 251 |
+
for prefix, uri in new_prefixes:
|
| 252 |
+
content_handler.endPrefixMapping(prefix)
|
| 253 |
+
tail = element.tail
|
| 254 |
+
if tail:
|
| 255 |
+
content_handler.characters(tail)
|
| 256 |
+
|
| 257 |
+
def _build_qname(self, ns_uri, local_name, nsmap, preferred_prefix, is_attribute):
|
| 258 |
+
if ns_uri is None:
|
| 259 |
+
return local_name
|
| 260 |
+
|
| 261 |
+
if not is_attribute and nsmap.get(preferred_prefix) == ns_uri:
|
| 262 |
+
prefix = preferred_prefix
|
| 263 |
+
else:
|
| 264 |
+
# Pick the first matching prefix, in alphabetical order.
|
| 265 |
+
candidates = [
|
| 266 |
+
pfx for (pfx, uri) in nsmap.items()
|
| 267 |
+
if pfx is not None and uri == ns_uri
|
| 268 |
+
]
|
| 269 |
+
prefix = (
|
| 270 |
+
candidates[0] if len(candidates) == 1
|
| 271 |
+
else min(candidates) if candidates
|
| 272 |
+
else None
|
| 273 |
+
)
|
| 274 |
+
|
| 275 |
+
if prefix is None:
|
| 276 |
+
# Default namespace
|
| 277 |
+
return local_name
|
| 278 |
+
return prefix + ':' + local_name
|
| 279 |
+
|
| 280 |
+
|
| 281 |
+
def saxify(element_or_tree, content_handler):
|
| 282 |
+
"""One-shot helper to generate SAX events from an XML tree and fire
|
| 283 |
+
them against a SAX ContentHandler.
|
| 284 |
+
"""
|
| 285 |
+
return ElementTreeProducer(element_or_tree, content_handler).saxify()
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/schematron.pxi
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# support for Schematron validation
|
| 2 |
+
from lxml.includes cimport schematron
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
cdef class SchematronError(LxmlError):
|
| 6 |
+
"""Base class of all Schematron errors.
|
| 7 |
+
"""
|
| 8 |
+
|
| 9 |
+
cdef class SchematronParseError(SchematronError):
|
| 10 |
+
"""Error while parsing an XML document as Schematron schema.
|
| 11 |
+
"""
|
| 12 |
+
|
| 13 |
+
cdef class SchematronValidateError(SchematronError):
|
| 14 |
+
"""Error while validating an XML document with a Schematron schema.
|
| 15 |
+
"""
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
################################################################################
|
| 19 |
+
# Schematron
|
| 20 |
+
|
| 21 |
+
cdef class Schematron(_Validator):
|
| 22 |
+
"""Schematron(self, etree=None, file=None)
|
| 23 |
+
A Schematron validator.
|
| 24 |
+
|
| 25 |
+
Pass a root Element or an ElementTree to turn it into a validator.
|
| 26 |
+
Alternatively, pass a filename as keyword argument 'file' to parse from
|
| 27 |
+
the file system.
|
| 28 |
+
|
| 29 |
+
Schematron is a less well known, but very powerful schema language. The main
|
| 30 |
+
idea is to use the capabilities of XPath to put restrictions on the structure
|
| 31 |
+
and the content of XML documents. Here is a simple example::
|
| 32 |
+
|
| 33 |
+
>>> schematron = Schematron(XML('''
|
| 34 |
+
... <schema xmlns="http://www.ascc.net/xml/schematron" >
|
| 35 |
+
... <pattern name="id is the only permitted attribute name">
|
| 36 |
+
... <rule context="*">
|
| 37 |
+
... <report test="@*[not(name()='id')]">Attribute
|
| 38 |
+
... <name path="@*[not(name()='id')]"/> is forbidden<name/>
|
| 39 |
+
... </report>
|
| 40 |
+
... </rule>
|
| 41 |
+
... </pattern>
|
| 42 |
+
... </schema>
|
| 43 |
+
... '''))
|
| 44 |
+
|
| 45 |
+
>>> xml = XML('''
|
| 46 |
+
... <AAA name="aaa">
|
| 47 |
+
... <BBB id="bbb"/>
|
| 48 |
+
... <CCC color="ccc"/>
|
| 49 |
+
... </AAA>
|
| 50 |
+
... ''')
|
| 51 |
+
|
| 52 |
+
>>> schematron.validate(xml)
|
| 53 |
+
0
|
| 54 |
+
|
| 55 |
+
>>> xml = XML('''
|
| 56 |
+
... <AAA id="aaa">
|
| 57 |
+
... <BBB id="bbb"/>
|
| 58 |
+
... <CCC/>
|
| 59 |
+
... </AAA>
|
| 60 |
+
... ''')
|
| 61 |
+
|
| 62 |
+
>>> schematron.validate(xml)
|
| 63 |
+
1
|
| 64 |
+
|
| 65 |
+
Schematron was added to libxml2 in version 2.6.21. Before version 2.6.32,
|
| 66 |
+
however, Schematron lacked support for error reporting other than to stderr.
|
| 67 |
+
This version is therefore required to retrieve validation warnings and
|
| 68 |
+
errors in lxml.
|
| 69 |
+
"""
|
| 70 |
+
cdef schematron.xmlSchematron* _c_schema
|
| 71 |
+
cdef xmlDoc* _c_schema_doc
|
| 72 |
+
|
| 73 |
+
def __init__(self, etree=None, *, file=None):
|
| 74 |
+
cdef _Document doc
|
| 75 |
+
cdef _Element root_node
|
| 76 |
+
cdef xmlNode* c_node
|
| 77 |
+
cdef char* c_href
|
| 78 |
+
cdef schematron.xmlSchematronParserCtxt* parser_ctxt = NULL
|
| 79 |
+
_Validator.__init__(self)
|
| 80 |
+
if not config.ENABLE_SCHEMATRON:
|
| 81 |
+
raise SchematronError, \
|
| 82 |
+
"lxml.etree was compiled without Schematron support."
|
| 83 |
+
|
| 84 |
+
import warnings
|
| 85 |
+
warnings.warn(
|
| 86 |
+
"The (non-ISO) Schematron feature is deprecated and will be removed from libxml2 and lxml. "
|
| 87 |
+
"Use 'lxml.isoschematron' instead.",
|
| 88 |
+
DeprecationWarning,
|
| 89 |
+
)
|
| 90 |
+
|
| 91 |
+
if etree is not None:
|
| 92 |
+
doc = _documentOrRaise(etree)
|
| 93 |
+
root_node = _rootNodeOrRaise(etree)
|
| 94 |
+
self._c_schema_doc = _copyDocRoot(doc._c_doc, root_node._c_node)
|
| 95 |
+
parser_ctxt = schematron.xmlSchematronNewDocParserCtxt(self._c_schema_doc)
|
| 96 |
+
elif file is not None:
|
| 97 |
+
filename = _getFilenameForFile(file)
|
| 98 |
+
if filename is None:
|
| 99 |
+
# XXX assume a string object
|
| 100 |
+
filename = file
|
| 101 |
+
filename = _encodeFilename(filename)
|
| 102 |
+
with self._error_log:
|
| 103 |
+
orig_loader = _register_document_loader()
|
| 104 |
+
parser_ctxt = schematron.xmlSchematronNewParserCtxt(_cstr(filename))
|
| 105 |
+
_reset_document_loader(orig_loader)
|
| 106 |
+
else:
|
| 107 |
+
raise SchematronParseError, "No tree or file given"
|
| 108 |
+
|
| 109 |
+
if parser_ctxt is NULL:
|
| 110 |
+
if self._c_schema_doc is not NULL:
|
| 111 |
+
tree.xmlFreeDoc(self._c_schema_doc)
|
| 112 |
+
self._c_schema_doc = NULL
|
| 113 |
+
raise MemoryError()
|
| 114 |
+
|
| 115 |
+
try:
|
| 116 |
+
with self._error_log:
|
| 117 |
+
orig_loader = _register_document_loader()
|
| 118 |
+
self._c_schema = schematron.xmlSchematronParse(parser_ctxt)
|
| 119 |
+
_reset_document_loader(orig_loader)
|
| 120 |
+
finally:
|
| 121 |
+
schematron.xmlSchematronFreeParserCtxt(parser_ctxt)
|
| 122 |
+
|
| 123 |
+
if self._c_schema is NULL:
|
| 124 |
+
raise SchematronParseError(
|
| 125 |
+
"Document is not a valid Schematron schema",
|
| 126 |
+
self._error_log)
|
| 127 |
+
|
| 128 |
+
def __dealloc__(self):
|
| 129 |
+
schematron.xmlSchematronFree(self._c_schema)
|
| 130 |
+
if self._c_schema_doc is not NULL:
|
| 131 |
+
tree.xmlFreeDoc(self._c_schema_doc)
|
| 132 |
+
|
| 133 |
+
def __call__(self, etree):
|
| 134 |
+
"""__call__(self, etree)
|
| 135 |
+
|
| 136 |
+
Validate doc using Schematron.
|
| 137 |
+
|
| 138 |
+
Returns true if document is valid, false if not."""
|
| 139 |
+
cdef _Document doc
|
| 140 |
+
cdef _Element root_node
|
| 141 |
+
cdef xmlDoc* c_doc
|
| 142 |
+
cdef schematron.xmlSchematronValidCtxt* valid_ctxt
|
| 143 |
+
cdef int ret
|
| 144 |
+
|
| 145 |
+
assert self._c_schema is not NULL, "Schematron instance not initialised"
|
| 146 |
+
doc = _documentOrRaise(etree)
|
| 147 |
+
root_node = _rootNodeOrRaise(etree)
|
| 148 |
+
|
| 149 |
+
valid_ctxt = schematron.xmlSchematronNewValidCtxt(
|
| 150 |
+
self._c_schema, schematron.XML_SCHEMATRON_OUT_ERROR)
|
| 151 |
+
if valid_ctxt is NULL:
|
| 152 |
+
raise MemoryError()
|
| 153 |
+
|
| 154 |
+
try:
|
| 155 |
+
self._error_log.clear()
|
| 156 |
+
# Need a cast here because older libxml2 releases do not use 'const' in the functype.
|
| 157 |
+
schematron.xmlSchematronSetValidStructuredErrors(
|
| 158 |
+
valid_ctxt, <xmlerror.xmlStructuredErrorFunc> _receiveError, <void*>self._error_log)
|
| 159 |
+
c_doc = _fakeRootDoc(doc._c_doc, root_node._c_node)
|
| 160 |
+
with nogil:
|
| 161 |
+
ret = schematron.xmlSchematronValidateDoc(valid_ctxt, c_doc)
|
| 162 |
+
_destroyFakeDoc(doc._c_doc, c_doc)
|
| 163 |
+
finally:
|
| 164 |
+
schematron.xmlSchematronFreeValidCtxt(valid_ctxt)
|
| 165 |
+
|
| 166 |
+
if ret == -1:
|
| 167 |
+
raise SchematronValidateError(
|
| 168 |
+
"Internal error in Schematron validation",
|
| 169 |
+
self._error_log)
|
| 170 |
+
if ret == 0:
|
| 171 |
+
return True
|
| 172 |
+
else:
|
| 173 |
+
return False
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/serializer.pxi
ADDED
|
@@ -0,0 +1,1849 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# XML serialization and output functions
|
| 2 |
+
|
| 3 |
+
cdef object GzipFile
|
| 4 |
+
from gzip import GzipFile
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
cdef class SerialisationError(LxmlError):
|
| 8 |
+
"""A libxml2 error that occurred during serialisation.
|
| 9 |
+
"""
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
cdef enum _OutputMethods:
|
| 13 |
+
OUTPUT_METHOD_XML
|
| 14 |
+
OUTPUT_METHOD_HTML
|
| 15 |
+
OUTPUT_METHOD_TEXT
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
cdef int _findOutputMethod(method) except -1:
|
| 19 |
+
if method is None:
|
| 20 |
+
return OUTPUT_METHOD_XML
|
| 21 |
+
method = method.lower()
|
| 22 |
+
if method == "xml":
|
| 23 |
+
return OUTPUT_METHOD_XML
|
| 24 |
+
if method == "html":
|
| 25 |
+
return OUTPUT_METHOD_HTML
|
| 26 |
+
if method == "text":
|
| 27 |
+
return OUTPUT_METHOD_TEXT
|
| 28 |
+
raise ValueError(f"unknown output method {method!r}")
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
cdef _textToString(xmlNode* c_node, encoding, bint with_tail):
|
| 32 |
+
cdef bint needs_conversion
|
| 33 |
+
cdef const_xmlChar* c_text
|
| 34 |
+
cdef xmlNode* c_text_node
|
| 35 |
+
cdef tree.xmlBuffer* c_buffer
|
| 36 |
+
cdef int error_result
|
| 37 |
+
|
| 38 |
+
c_buffer = tree.xmlBufferCreate()
|
| 39 |
+
if c_buffer is NULL:
|
| 40 |
+
raise MemoryError()
|
| 41 |
+
|
| 42 |
+
with nogil:
|
| 43 |
+
error_result = tree.xmlNodeBufGetContent(c_buffer, c_node)
|
| 44 |
+
if with_tail:
|
| 45 |
+
c_text_node = _textNodeOrSkip(c_node.next)
|
| 46 |
+
while c_text_node is not NULL:
|
| 47 |
+
tree.xmlBufferWriteChar(c_buffer, <const_char*>c_text_node.content)
|
| 48 |
+
c_text_node = _textNodeOrSkip(c_text_node.next)
|
| 49 |
+
c_text = tree.xmlBufferContent(c_buffer)
|
| 50 |
+
|
| 51 |
+
if error_result < 0 or c_text is NULL:
|
| 52 |
+
tree.xmlBufferFree(c_buffer)
|
| 53 |
+
raise SerialisationError, "Error during serialisation (out of memory?)"
|
| 54 |
+
|
| 55 |
+
try:
|
| 56 |
+
needs_conversion = 0
|
| 57 |
+
if encoding is unicode:
|
| 58 |
+
needs_conversion = 1
|
| 59 |
+
elif encoding is not None:
|
| 60 |
+
# Python prefers lower case encoding names
|
| 61 |
+
encoding = encoding.lower()
|
| 62 |
+
if encoding not in ('utf8', 'utf-8'):
|
| 63 |
+
if encoding == 'ascii':
|
| 64 |
+
if isutf8l(c_text, tree.xmlBufferLength(c_buffer)):
|
| 65 |
+
# will raise a decode error below
|
| 66 |
+
needs_conversion = 1
|
| 67 |
+
else:
|
| 68 |
+
needs_conversion = 1
|
| 69 |
+
|
| 70 |
+
if needs_conversion:
|
| 71 |
+
text = (<const_char*>c_text)[:tree.xmlBufferLength(c_buffer)].decode('utf8')
|
| 72 |
+
if encoding is not unicode:
|
| 73 |
+
encoding = _utf8(encoding)
|
| 74 |
+
text = python.PyUnicode_AsEncodedString(
|
| 75 |
+
text, encoding, 'strict')
|
| 76 |
+
else:
|
| 77 |
+
text = (<unsigned char*>c_text)[:tree.xmlBufferLength(c_buffer)]
|
| 78 |
+
finally:
|
| 79 |
+
tree.xmlBufferFree(c_buffer)
|
| 80 |
+
return text
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
cdef _tostring(_Element element, encoding, doctype, method,
|
| 84 |
+
bint write_xml_declaration, bint write_complete_document,
|
| 85 |
+
bint pretty_print, bint with_tail, int standalone):
|
| 86 |
+
"""Serialize an element to an encoded string representation of its XML
|
| 87 |
+
tree.
|
| 88 |
+
"""
|
| 89 |
+
cdef tree.xmlOutputBuffer* c_buffer
|
| 90 |
+
cdef tree.xmlBuf* c_result_buffer
|
| 91 |
+
cdef tree.xmlCharEncodingHandler* enchandler
|
| 92 |
+
cdef const_char* c_enc
|
| 93 |
+
cdef const_xmlChar* c_version
|
| 94 |
+
cdef const_xmlChar* c_doctype
|
| 95 |
+
cdef int c_method
|
| 96 |
+
cdef int error_result
|
| 97 |
+
if element is None:
|
| 98 |
+
return None
|
| 99 |
+
_assertValidNode(element)
|
| 100 |
+
c_method = _findOutputMethod(method)
|
| 101 |
+
if c_method == OUTPUT_METHOD_TEXT:
|
| 102 |
+
return _textToString(element._c_node, encoding, with_tail)
|
| 103 |
+
if encoding is None or encoding is unicode:
|
| 104 |
+
c_enc = NULL
|
| 105 |
+
else:
|
| 106 |
+
encoding = _utf8(encoding)
|
| 107 |
+
c_enc = _cstr(encoding)
|
| 108 |
+
if doctype is None:
|
| 109 |
+
c_doctype = NULL
|
| 110 |
+
else:
|
| 111 |
+
doctype = _utf8(doctype)
|
| 112 |
+
c_doctype = _xcstr(doctype)
|
| 113 |
+
# it is necessary to *and* find the encoding handler *and* use
|
| 114 |
+
# encoding during output
|
| 115 |
+
enchandler = tree.xmlFindCharEncodingHandler(c_enc)
|
| 116 |
+
if enchandler is NULL and c_enc is not NULL:
|
| 117 |
+
if encoding is not None:
|
| 118 |
+
encoding = encoding.decode('UTF-8')
|
| 119 |
+
raise LookupError, f"unknown encoding: '{encoding}'"
|
| 120 |
+
c_buffer = tree.xmlAllocOutputBuffer(enchandler)
|
| 121 |
+
if c_buffer is NULL:
|
| 122 |
+
tree.xmlCharEncCloseFunc(enchandler)
|
| 123 |
+
raise MemoryError()
|
| 124 |
+
|
| 125 |
+
with nogil:
|
| 126 |
+
_writeNodeToBuffer(c_buffer, element._c_node, c_enc, c_doctype, c_method,
|
| 127 |
+
write_xml_declaration, write_complete_document,
|
| 128 |
+
pretty_print, with_tail, standalone)
|
| 129 |
+
tree.xmlOutputBufferFlush(c_buffer)
|
| 130 |
+
if c_buffer.conv is not NULL:
|
| 131 |
+
c_result_buffer = c_buffer.conv
|
| 132 |
+
else:
|
| 133 |
+
c_result_buffer = c_buffer.buffer
|
| 134 |
+
|
| 135 |
+
error_result = c_buffer.error
|
| 136 |
+
if error_result != xmlerror.XML_ERR_OK:
|
| 137 |
+
tree.xmlOutputBufferClose(c_buffer)
|
| 138 |
+
_raiseSerialisationError(error_result)
|
| 139 |
+
|
| 140 |
+
try:
|
| 141 |
+
if encoding is unicode:
|
| 142 |
+
result = (<unsigned char*>tree.xmlBufContent(
|
| 143 |
+
c_result_buffer))[:tree.xmlBufUse(c_result_buffer)].decode('UTF-8')
|
| 144 |
+
else:
|
| 145 |
+
result = <bytes>(<unsigned char*>tree.xmlBufContent(
|
| 146 |
+
c_result_buffer))[:tree.xmlBufUse(c_result_buffer)]
|
| 147 |
+
finally:
|
| 148 |
+
error_result = tree.xmlOutputBufferClose(c_buffer)
|
| 149 |
+
if error_result == -1:
|
| 150 |
+
_raiseSerialisationError(error_result)
|
| 151 |
+
return result
|
| 152 |
+
|
| 153 |
+
cdef bytes _tostringC14N(element_or_tree, bint exclusive, bint with_comments, inclusive_ns_prefixes):
|
| 154 |
+
cdef xmlDoc* c_doc
|
| 155 |
+
cdef xmlChar* c_buffer = NULL
|
| 156 |
+
cdef int byte_count = -1
|
| 157 |
+
cdef bytes result
|
| 158 |
+
cdef _Document doc
|
| 159 |
+
cdef _Element element
|
| 160 |
+
cdef xmlChar **c_inclusive_ns_prefixes
|
| 161 |
+
|
| 162 |
+
if isinstance(element_or_tree, _Element):
|
| 163 |
+
_assertValidNode(<_Element>element_or_tree)
|
| 164 |
+
doc = (<_Element>element_or_tree)._doc
|
| 165 |
+
c_doc = _plainFakeRootDoc(doc._c_doc, (<_Element>element_or_tree)._c_node, 0)
|
| 166 |
+
else:
|
| 167 |
+
doc = _documentOrRaise(element_or_tree)
|
| 168 |
+
_assertValidDoc(doc)
|
| 169 |
+
c_doc = doc._c_doc
|
| 170 |
+
|
| 171 |
+
c_inclusive_ns_prefixes = _convert_ns_prefixes(c_doc.dict, inclusive_ns_prefixes) if inclusive_ns_prefixes else NULL
|
| 172 |
+
try:
|
| 173 |
+
with nogil:
|
| 174 |
+
byte_count = c14n.xmlC14NDocDumpMemory(
|
| 175 |
+
c_doc, NULL, exclusive, c_inclusive_ns_prefixes, with_comments, &c_buffer)
|
| 176 |
+
|
| 177 |
+
finally:
|
| 178 |
+
_destroyFakeDoc(doc._c_doc, c_doc)
|
| 179 |
+
if c_inclusive_ns_prefixes is not NULL:
|
| 180 |
+
python.lxml_free(c_inclusive_ns_prefixes)
|
| 181 |
+
|
| 182 |
+
if byte_count < 0 or c_buffer is NULL:
|
| 183 |
+
if c_buffer is not NULL:
|
| 184 |
+
tree.xmlFree(c_buffer)
|
| 185 |
+
raise C14NError, "C14N failed"
|
| 186 |
+
try:
|
| 187 |
+
result = c_buffer[:byte_count]
|
| 188 |
+
finally:
|
| 189 |
+
tree.xmlFree(c_buffer)
|
| 190 |
+
return result
|
| 191 |
+
|
| 192 |
+
cdef _raiseSerialisationError(int error_result):
|
| 193 |
+
if error_result == xmlerror.XML_ERR_NO_MEMORY:
|
| 194 |
+
raise MemoryError()
|
| 195 |
+
message = ErrorTypes._getName(error_result)
|
| 196 |
+
if message is None:
|
| 197 |
+
message = f"unknown error {error_result}"
|
| 198 |
+
raise SerialisationError, message
|
| 199 |
+
|
| 200 |
+
############################################################
|
| 201 |
+
# low-level serialisation functions
|
| 202 |
+
|
| 203 |
+
cdef void _writeDoctype(tree.xmlOutputBuffer* c_buffer,
|
| 204 |
+
const_xmlChar* c_doctype) noexcept nogil:
|
| 205 |
+
tree.xmlOutputBufferWrite(c_buffer, tree.xmlStrlen(c_doctype),
|
| 206 |
+
<const_char*>c_doctype)
|
| 207 |
+
tree.xmlOutputBufferWriteString(c_buffer, "\n")
|
| 208 |
+
|
| 209 |
+
cdef void _writeNodeToBuffer(tree.xmlOutputBuffer* c_buffer,
|
| 210 |
+
xmlNode* c_node, const_char* encoding, const_xmlChar* c_doctype,
|
| 211 |
+
int c_method, bint write_xml_declaration,
|
| 212 |
+
bint write_complete_document,
|
| 213 |
+
bint pretty_print, bint with_tail,
|
| 214 |
+
int standalone) noexcept nogil:
|
| 215 |
+
cdef xmlNode* c_nsdecl_node
|
| 216 |
+
cdef xmlDoc* c_doc = c_node.doc
|
| 217 |
+
if write_xml_declaration and c_method == OUTPUT_METHOD_XML:
|
| 218 |
+
_writeDeclarationToBuffer(c_buffer, c_doc.version, encoding, standalone)
|
| 219 |
+
|
| 220 |
+
# comments/processing instructions before doctype declaration
|
| 221 |
+
if write_complete_document and not c_buffer.error and c_doc.intSubset:
|
| 222 |
+
_writePrevSiblings(c_buffer, <xmlNode*>c_doc.intSubset, encoding, pretty_print)
|
| 223 |
+
|
| 224 |
+
if c_doctype:
|
| 225 |
+
_writeDoctype(c_buffer, c_doctype)
|
| 226 |
+
# write internal DTD subset, preceding PIs/comments, etc.
|
| 227 |
+
if write_complete_document and not c_buffer.error:
|
| 228 |
+
if c_doctype is NULL:
|
| 229 |
+
_writeDtdToBuffer(c_buffer, c_doc, c_node.name, c_method, encoding)
|
| 230 |
+
_writePrevSiblings(c_buffer, c_node, encoding, pretty_print)
|
| 231 |
+
|
| 232 |
+
c_nsdecl_node = c_node
|
| 233 |
+
if not c_node.parent or c_node.parent.type != tree.XML_DOCUMENT_NODE:
|
| 234 |
+
# copy the node and add namespaces from parents
|
| 235 |
+
# this is required to make libxml write them
|
| 236 |
+
c_nsdecl_node = tree.xmlCopyNode(c_node, 2)
|
| 237 |
+
if not c_nsdecl_node:
|
| 238 |
+
c_buffer.error = xmlerror.XML_ERR_NO_MEMORY
|
| 239 |
+
return
|
| 240 |
+
_copyParentNamespaces(c_node, c_nsdecl_node)
|
| 241 |
+
|
| 242 |
+
c_nsdecl_node.parent = c_node.parent
|
| 243 |
+
c_nsdecl_node.children = c_node.children
|
| 244 |
+
c_nsdecl_node.last = c_node.last
|
| 245 |
+
|
| 246 |
+
# write node
|
| 247 |
+
if c_method == OUTPUT_METHOD_HTML:
|
| 248 |
+
tree.htmlNodeDumpFormatOutput(
|
| 249 |
+
c_buffer, c_doc, c_nsdecl_node, encoding, pretty_print)
|
| 250 |
+
else:
|
| 251 |
+
tree.xmlNodeDumpOutput(
|
| 252 |
+
c_buffer, c_doc, c_nsdecl_node, 0, pretty_print, encoding)
|
| 253 |
+
|
| 254 |
+
if c_nsdecl_node is not c_node:
|
| 255 |
+
# clean up
|
| 256 |
+
c_nsdecl_node.children = c_nsdecl_node.last = NULL
|
| 257 |
+
tree.xmlFreeNode(c_nsdecl_node)
|
| 258 |
+
|
| 259 |
+
if c_buffer.error:
|
| 260 |
+
return
|
| 261 |
+
|
| 262 |
+
# write tail, trailing comments, etc.
|
| 263 |
+
if with_tail:
|
| 264 |
+
_writeTail(c_buffer, c_node, encoding, c_method, pretty_print)
|
| 265 |
+
if write_complete_document:
|
| 266 |
+
_writeNextSiblings(c_buffer, c_node, encoding, pretty_print)
|
| 267 |
+
if pretty_print:
|
| 268 |
+
tree.xmlOutputBufferWrite(c_buffer, 1, "\n")
|
| 269 |
+
|
| 270 |
+
cdef void _writeDeclarationToBuffer(tree.xmlOutputBuffer* c_buffer,
|
| 271 |
+
const_xmlChar* version, const_char* encoding,
|
| 272 |
+
int standalone) noexcept nogil:
|
| 273 |
+
if version is NULL:
|
| 274 |
+
version = <unsigned char*>"1.0"
|
| 275 |
+
tree.xmlOutputBufferWrite(c_buffer, 15, "<?xml version='")
|
| 276 |
+
tree.xmlOutputBufferWriteString(c_buffer, <const_char*>version)
|
| 277 |
+
tree.xmlOutputBufferWrite(c_buffer, 12, "' encoding='")
|
| 278 |
+
tree.xmlOutputBufferWriteString(c_buffer, encoding)
|
| 279 |
+
if standalone == 0:
|
| 280 |
+
tree.xmlOutputBufferWrite(c_buffer, 20, "' standalone='no'?>\n")
|
| 281 |
+
elif standalone == 1:
|
| 282 |
+
tree.xmlOutputBufferWrite(c_buffer, 21, "' standalone='yes'?>\n")
|
| 283 |
+
else:
|
| 284 |
+
tree.xmlOutputBufferWrite(c_buffer, 4, "'?>\n")
|
| 285 |
+
|
| 286 |
+
cdef void _writeDtdToBuffer(tree.xmlOutputBuffer* c_buffer,
|
| 287 |
+
xmlDoc* c_doc, const_xmlChar* c_root_name,
|
| 288 |
+
int c_method, const_char* encoding) noexcept nogil:
|
| 289 |
+
cdef tree.xmlDtd* c_dtd
|
| 290 |
+
cdef xmlNode* c_node
|
| 291 |
+
cdef char* quotechar
|
| 292 |
+
c_dtd = c_doc.intSubset
|
| 293 |
+
if not c_dtd or not c_dtd.name:
|
| 294 |
+
return
|
| 295 |
+
|
| 296 |
+
# Name in document type declaration must match the root element tag.
|
| 297 |
+
# For XML, case sensitive match, for HTML insensitive.
|
| 298 |
+
if c_method == OUTPUT_METHOD_HTML:
|
| 299 |
+
if tree.xmlStrcasecmp(c_root_name, c_dtd.name) != 0:
|
| 300 |
+
return
|
| 301 |
+
else:
|
| 302 |
+
if tree.xmlStrcmp(c_root_name, c_dtd.name) != 0:
|
| 303 |
+
return
|
| 304 |
+
|
| 305 |
+
tree.xmlOutputBufferWrite(c_buffer, 10, "<!DOCTYPE ")
|
| 306 |
+
tree.xmlOutputBufferWriteString(c_buffer, <const_char*>c_dtd.name)
|
| 307 |
+
|
| 308 |
+
cdef const_xmlChar* public_id = c_dtd.ExternalID
|
| 309 |
+
cdef const_xmlChar* sys_url = c_dtd.SystemID
|
| 310 |
+
if public_id and public_id[0] == b'\0':
|
| 311 |
+
public_id = NULL
|
| 312 |
+
if sys_url and sys_url[0] == b'\0':
|
| 313 |
+
sys_url = NULL
|
| 314 |
+
|
| 315 |
+
if public_id:
|
| 316 |
+
tree.xmlOutputBufferWrite(c_buffer, 9, ' PUBLIC "')
|
| 317 |
+
tree.xmlOutputBufferWriteString(c_buffer, <const_char*>public_id)
|
| 318 |
+
if sys_url:
|
| 319 |
+
tree.xmlOutputBufferWrite(c_buffer, 2, '" ')
|
| 320 |
+
else:
|
| 321 |
+
tree.xmlOutputBufferWrite(c_buffer, 1, '"')
|
| 322 |
+
elif sys_url:
|
| 323 |
+
tree.xmlOutputBufferWrite(c_buffer, 8, ' SYSTEM ')
|
| 324 |
+
|
| 325 |
+
if sys_url:
|
| 326 |
+
if tree.xmlStrchr(sys_url, b'"'):
|
| 327 |
+
quotechar = '\''
|
| 328 |
+
else:
|
| 329 |
+
quotechar = '"'
|
| 330 |
+
tree.xmlOutputBufferWrite(c_buffer, 1, quotechar)
|
| 331 |
+
tree.xmlOutputBufferWriteString(c_buffer, <const_char*>sys_url)
|
| 332 |
+
tree.xmlOutputBufferWrite(c_buffer, 1, quotechar)
|
| 333 |
+
|
| 334 |
+
if (not c_dtd.entities and not c_dtd.elements and
|
| 335 |
+
not c_dtd.attributes and not c_dtd.notations and
|
| 336 |
+
not c_dtd.pentities):
|
| 337 |
+
tree.xmlOutputBufferWrite(c_buffer, 2, '>\n')
|
| 338 |
+
return
|
| 339 |
+
|
| 340 |
+
tree.xmlOutputBufferWrite(c_buffer, 3, ' [\n')
|
| 341 |
+
if c_dtd.notations and not c_buffer.error:
|
| 342 |
+
c_buf = tree.xmlBufferCreate()
|
| 343 |
+
if not c_buf:
|
| 344 |
+
c_buffer.error = xmlerror.XML_ERR_NO_MEMORY
|
| 345 |
+
return
|
| 346 |
+
tree.xmlDumpNotationTable(c_buf, <tree.xmlNotationTable*>c_dtd.notations)
|
| 347 |
+
tree.xmlOutputBufferWrite(
|
| 348 |
+
c_buffer, tree.xmlBufferLength(c_buf),
|
| 349 |
+
<const_char*>tree.xmlBufferContent(c_buf))
|
| 350 |
+
tree.xmlBufferFree(c_buf)
|
| 351 |
+
c_node = c_dtd.children
|
| 352 |
+
while c_node and not c_buffer.error:
|
| 353 |
+
tree.xmlNodeDumpOutput(c_buffer, c_node.doc, c_node, 0, 0, encoding)
|
| 354 |
+
c_node = c_node.next
|
| 355 |
+
tree.xmlOutputBufferWrite(c_buffer, 3, "]>\n")
|
| 356 |
+
|
| 357 |
+
cdef void _writeTail(tree.xmlOutputBuffer* c_buffer, xmlNode* c_node,
|
| 358 |
+
const_char* encoding, int c_method, bint pretty_print) noexcept nogil:
|
| 359 |
+
"Write the element tail."
|
| 360 |
+
c_node = c_node.next
|
| 361 |
+
while c_node and not c_buffer.error and c_node.type in (
|
| 362 |
+
tree.XML_TEXT_NODE, tree.XML_CDATA_SECTION_NODE):
|
| 363 |
+
if c_method == OUTPUT_METHOD_HTML:
|
| 364 |
+
tree.htmlNodeDumpFormatOutput(
|
| 365 |
+
c_buffer, c_node.doc, c_node, encoding, pretty_print)
|
| 366 |
+
else:
|
| 367 |
+
tree.xmlNodeDumpOutput(
|
| 368 |
+
c_buffer, c_node.doc, c_node, 0, pretty_print, encoding)
|
| 369 |
+
c_node = c_node.next
|
| 370 |
+
|
| 371 |
+
cdef void _writePrevSiblings(tree.xmlOutputBuffer* c_buffer, xmlNode* c_node,
|
| 372 |
+
const_char* encoding, bint pretty_print) noexcept nogil:
|
| 373 |
+
cdef xmlNode* c_sibling
|
| 374 |
+
if c_node.parent and _isElement(c_node.parent):
|
| 375 |
+
return
|
| 376 |
+
# we are at a root node, so add PI and comment siblings
|
| 377 |
+
c_sibling = c_node
|
| 378 |
+
while c_sibling.prev and \
|
| 379 |
+
(c_sibling.prev.type == tree.XML_PI_NODE or
|
| 380 |
+
c_sibling.prev.type == tree.XML_COMMENT_NODE):
|
| 381 |
+
c_sibling = c_sibling.prev
|
| 382 |
+
while c_sibling is not c_node and not c_buffer.error:
|
| 383 |
+
tree.xmlNodeDumpOutput(c_buffer, c_node.doc, c_sibling, 0,
|
| 384 |
+
pretty_print, encoding)
|
| 385 |
+
if pretty_print:
|
| 386 |
+
tree.xmlOutputBufferWriteString(c_buffer, "\n")
|
| 387 |
+
c_sibling = c_sibling.next
|
| 388 |
+
|
| 389 |
+
cdef void _writeNextSiblings(tree.xmlOutputBuffer* c_buffer, xmlNode* c_node,
|
| 390 |
+
const_char* encoding, bint pretty_print) noexcept nogil:
|
| 391 |
+
cdef xmlNode* c_sibling
|
| 392 |
+
if c_node.parent and _isElement(c_node.parent):
|
| 393 |
+
return
|
| 394 |
+
# we are at a root node, so add PI and comment siblings
|
| 395 |
+
c_sibling = c_node.next
|
| 396 |
+
while not c_buffer.error and c_sibling and \
|
| 397 |
+
(c_sibling.type == tree.XML_PI_NODE or
|
| 398 |
+
c_sibling.type == tree.XML_COMMENT_NODE):
|
| 399 |
+
if pretty_print:
|
| 400 |
+
tree.xmlOutputBufferWriteString(c_buffer, "\n")
|
| 401 |
+
tree.xmlNodeDumpOutput(c_buffer, c_node.doc, c_sibling, 0,
|
| 402 |
+
pretty_print, encoding)
|
| 403 |
+
c_sibling = c_sibling.next
|
| 404 |
+
|
| 405 |
+
|
| 406 |
+
# copied and adapted from libxml2 (xmlBufAttrSerializeTxtContent())
|
| 407 |
+
cdef _write_attr_string(tree.xmlOutputBuffer* buf, const char *string):
|
| 408 |
+
cdef const char *base
|
| 409 |
+
cdef const char *cur
|
| 410 |
+
|
| 411 |
+
if string == NULL:
|
| 412 |
+
return
|
| 413 |
+
|
| 414 |
+
base = cur = <const char*>string
|
| 415 |
+
while cur[0] != 0:
|
| 416 |
+
if cur[0] == b'\n':
|
| 417 |
+
if base != cur:
|
| 418 |
+
tree.xmlOutputBufferWrite(buf, cur - base, base)
|
| 419 |
+
|
| 420 |
+
tree.xmlOutputBufferWrite(buf, 5, " ")
|
| 421 |
+
cur += 1
|
| 422 |
+
base = cur
|
| 423 |
+
|
| 424 |
+
elif cur[0] == b'\r':
|
| 425 |
+
if base != cur:
|
| 426 |
+
tree.xmlOutputBufferWrite(buf, cur - base, base)
|
| 427 |
+
|
| 428 |
+
tree.xmlOutputBufferWrite(buf, 5, " ")
|
| 429 |
+
cur += 1
|
| 430 |
+
base = cur
|
| 431 |
+
|
| 432 |
+
elif cur[0] == b'\t':
|
| 433 |
+
if base != cur:
|
| 434 |
+
tree.xmlOutputBufferWrite(buf, cur - base, base)
|
| 435 |
+
|
| 436 |
+
tree.xmlOutputBufferWrite(buf, 4, "	")
|
| 437 |
+
cur += 1
|
| 438 |
+
base = cur
|
| 439 |
+
|
| 440 |
+
elif cur[0] == b'"':
|
| 441 |
+
if base != cur:
|
| 442 |
+
tree.xmlOutputBufferWrite(buf, cur - base, base)
|
| 443 |
+
|
| 444 |
+
tree.xmlOutputBufferWrite(buf, 6, """)
|
| 445 |
+
cur += 1
|
| 446 |
+
base = cur
|
| 447 |
+
|
| 448 |
+
elif cur[0] == b'<':
|
| 449 |
+
if base != cur:
|
| 450 |
+
tree.xmlOutputBufferWrite(buf, cur - base, base)
|
| 451 |
+
|
| 452 |
+
tree.xmlOutputBufferWrite(buf, 4, "<")
|
| 453 |
+
cur += 1
|
| 454 |
+
base = cur
|
| 455 |
+
|
| 456 |
+
elif cur[0] == b'>':
|
| 457 |
+
if base != cur:
|
| 458 |
+
tree.xmlOutputBufferWrite(buf, cur - base, base)
|
| 459 |
+
|
| 460 |
+
tree.xmlOutputBufferWrite(buf, 4, ">")
|
| 461 |
+
cur += 1
|
| 462 |
+
base = cur
|
| 463 |
+
elif cur[0] == b'&':
|
| 464 |
+
if base != cur:
|
| 465 |
+
tree.xmlOutputBufferWrite(buf, cur - base, base)
|
| 466 |
+
|
| 467 |
+
tree.xmlOutputBufferWrite(buf, 5, "&")
|
| 468 |
+
cur += 1
|
| 469 |
+
base = cur
|
| 470 |
+
|
| 471 |
+
else:
|
| 472 |
+
# Leave further encoding and escaping to the buffer encoder.
|
| 473 |
+
cur += 1
|
| 474 |
+
|
| 475 |
+
if base != cur:
|
| 476 |
+
tree.xmlOutputBufferWrite(buf, cur - base, base)
|
| 477 |
+
|
| 478 |
+
|
| 479 |
+
cdef void _write_cdata_section(tree.xmlOutputBuffer* buf, const char* c_data, const char* c_end):
|
| 480 |
+
tree.xmlOutputBufferWrite(buf, 9, "<![CDATA[")
|
| 481 |
+
while c_end - c_data > limits.INT_MAX:
|
| 482 |
+
tree.xmlOutputBufferWrite(buf, limits.INT_MAX, c_data)
|
| 483 |
+
c_data += limits.INT_MAX
|
| 484 |
+
tree.xmlOutputBufferWrite(buf, c_end - c_data, c_data)
|
| 485 |
+
tree.xmlOutputBufferWrite(buf, 3, "]]>")
|
| 486 |
+
|
| 487 |
+
|
| 488 |
+
cdef _write_cdata_string(tree.xmlOutputBuffer* buf, bytes bstring):
|
| 489 |
+
cdef const char* c_data = bstring
|
| 490 |
+
cdef const char* c_end = c_data + len(bstring)
|
| 491 |
+
cdef const char* c_pos = c_data
|
| 492 |
+
cdef bint nothing_written = True
|
| 493 |
+
|
| 494 |
+
while True:
|
| 495 |
+
c_pos = <const char*> cstring_h.memchr(c_pos, b']', c_end - c_pos)
|
| 496 |
+
if not c_pos:
|
| 497 |
+
break
|
| 498 |
+
c_pos += 1
|
| 499 |
+
next_char = c_pos[0]
|
| 500 |
+
c_pos += 1
|
| 501 |
+
if next_char != b']':
|
| 502 |
+
continue
|
| 503 |
+
# Found ']]', c_pos points to next character.
|
| 504 |
+
while c_pos[0] == b']':
|
| 505 |
+
c_pos += 1
|
| 506 |
+
if c_pos[0] != b'>':
|
| 507 |
+
if c_pos == c_end:
|
| 508 |
+
break
|
| 509 |
+
# c_pos[0] is neither ']' nor '>', continue with next character.
|
| 510 |
+
c_pos += 1
|
| 511 |
+
continue
|
| 512 |
+
|
| 513 |
+
# Write section up to ']]' and start next block at trailing '>'.
|
| 514 |
+
_write_cdata_section(buf, c_data, c_pos)
|
| 515 |
+
nothing_written = False
|
| 516 |
+
c_data = c_pos
|
| 517 |
+
c_pos += 1
|
| 518 |
+
|
| 519 |
+
if nothing_written or c_data < c_end:
|
| 520 |
+
_write_cdata_section(buf, c_data, c_end)
|
| 521 |
+
|
| 522 |
+
|
| 523 |
+
############################################################
|
| 524 |
+
# output to file-like objects
|
| 525 |
+
|
| 526 |
+
cdef object io_open
|
| 527 |
+
from io import open as io_open
|
| 528 |
+
|
| 529 |
+
cdef object gzip
|
| 530 |
+
import gzip
|
| 531 |
+
|
| 532 |
+
cdef object getwriter
|
| 533 |
+
from codecs import getwriter
|
| 534 |
+
cdef object utf8_writer = getwriter('utf8')
|
| 535 |
+
|
| 536 |
+
cdef object contextmanager
|
| 537 |
+
from contextlib import contextmanager
|
| 538 |
+
|
| 539 |
+
cdef object _open_utf8_file
|
| 540 |
+
|
| 541 |
+
@contextmanager
|
| 542 |
+
def _open_utf8_file(file, compression=0):
|
| 543 |
+
file = _getFSPathOrObject(file)
|
| 544 |
+
if _isString(file):
|
| 545 |
+
if compression:
|
| 546 |
+
with gzip.GzipFile(file, mode='wb', compresslevel=compression) as zf:
|
| 547 |
+
yield utf8_writer(zf)
|
| 548 |
+
else:
|
| 549 |
+
with io_open(file, 'w', encoding='utf8') as f:
|
| 550 |
+
yield f
|
| 551 |
+
else:
|
| 552 |
+
if compression:
|
| 553 |
+
with gzip.GzipFile(fileobj=file, mode='wb', compresslevel=compression) as zf:
|
| 554 |
+
yield utf8_writer(zf)
|
| 555 |
+
else:
|
| 556 |
+
yield utf8_writer(file)
|
| 557 |
+
|
| 558 |
+
|
| 559 |
+
@cython.final
|
| 560 |
+
@cython.internal
|
| 561 |
+
cdef class _FilelikeWriter:
|
| 562 |
+
cdef object _filelike
|
| 563 |
+
cdef object _close_filelike
|
| 564 |
+
cdef _ExceptionContext _exc_context
|
| 565 |
+
cdef _ErrorLog error_log
|
| 566 |
+
|
| 567 |
+
def __cinit__(self, filelike, exc_context=None, compression=None, close=False):
|
| 568 |
+
if compression is not None and compression > 0:
|
| 569 |
+
filelike = GzipFile(
|
| 570 |
+
fileobj=filelike, mode='wb', compresslevel=compression)
|
| 571 |
+
self._close_filelike = filelike.close
|
| 572 |
+
elif close:
|
| 573 |
+
self._close_filelike = filelike.close
|
| 574 |
+
self._filelike = filelike
|
| 575 |
+
if exc_context is None:
|
| 576 |
+
self._exc_context = _ExceptionContext()
|
| 577 |
+
else:
|
| 578 |
+
self._exc_context = exc_context
|
| 579 |
+
self.error_log = _ErrorLog()
|
| 580 |
+
|
| 581 |
+
cdef tree.xmlOutputBuffer* _createOutputBuffer(
|
| 582 |
+
self, tree.xmlCharEncodingHandler* enchandler) except NULL:
|
| 583 |
+
cdef tree.xmlOutputBuffer* c_buffer
|
| 584 |
+
c_buffer = tree.xmlOutputBufferCreateIO(
|
| 585 |
+
<tree.xmlOutputWriteCallback>_writeFilelikeWriter, _closeFilelikeWriter,
|
| 586 |
+
<python.PyObject*>self, enchandler)
|
| 587 |
+
if c_buffer is NULL:
|
| 588 |
+
raise IOError, "Could not create I/O writer context."
|
| 589 |
+
return c_buffer
|
| 590 |
+
|
| 591 |
+
cdef int write(self, char* c_buffer, int size) noexcept:
|
| 592 |
+
try:
|
| 593 |
+
if self._filelike is None:
|
| 594 |
+
raise IOError, "File is already closed"
|
| 595 |
+
py_buffer = <bytes>c_buffer[:size]
|
| 596 |
+
self._filelike.write(py_buffer)
|
| 597 |
+
except:
|
| 598 |
+
size = -1
|
| 599 |
+
self._exc_context._store_raised()
|
| 600 |
+
finally:
|
| 601 |
+
return size # and swallow any further exceptions
|
| 602 |
+
|
| 603 |
+
cdef int close(self) noexcept:
|
| 604 |
+
retval = 0
|
| 605 |
+
try:
|
| 606 |
+
if self._close_filelike is not None:
|
| 607 |
+
self._close_filelike()
|
| 608 |
+
# we should not close the file here as we didn't open it
|
| 609 |
+
self._filelike = None
|
| 610 |
+
except:
|
| 611 |
+
retval = -1
|
| 612 |
+
self._exc_context._store_raised()
|
| 613 |
+
finally:
|
| 614 |
+
return retval # and swallow any further exceptions
|
| 615 |
+
|
| 616 |
+
cdef int _writeFilelikeWriter(void* ctxt, char* c_buffer, int length) noexcept:
|
| 617 |
+
return (<_FilelikeWriter>ctxt).write(c_buffer, length)
|
| 618 |
+
|
| 619 |
+
cdef int _closeFilelikeWriter(void* ctxt) noexcept:
|
| 620 |
+
return (<_FilelikeWriter>ctxt).close()
|
| 621 |
+
|
| 622 |
+
cdef _tofilelike(f, _Element element, encoding, doctype, method,
|
| 623 |
+
bint write_xml_declaration, bint write_doctype,
|
| 624 |
+
bint pretty_print, bint with_tail, int standalone,
|
| 625 |
+
int compression):
|
| 626 |
+
cdef _FilelikeWriter writer = None
|
| 627 |
+
cdef tree.xmlOutputBuffer* c_buffer
|
| 628 |
+
cdef tree.xmlCharEncodingHandler* enchandler
|
| 629 |
+
cdef const_char* c_enc
|
| 630 |
+
cdef const_xmlChar* c_doctype
|
| 631 |
+
cdef int error_result
|
| 632 |
+
|
| 633 |
+
c_method = _findOutputMethod(method)
|
| 634 |
+
if c_method == OUTPUT_METHOD_TEXT:
|
| 635 |
+
data = _textToString(element._c_node, encoding, with_tail)
|
| 636 |
+
if compression:
|
| 637 |
+
bytes_out = BytesIO()
|
| 638 |
+
with GzipFile(fileobj=bytes_out, mode='wb', compresslevel=compression) as gzip_file:
|
| 639 |
+
gzip_file.write(data)
|
| 640 |
+
data = bytes_out.getvalue()
|
| 641 |
+
f = _getFSPathOrObject(f)
|
| 642 |
+
if _isString(f):
|
| 643 |
+
filename8 = _encodeFilename(f)
|
| 644 |
+
with open(filename8, 'wb') as f:
|
| 645 |
+
f.write(data)
|
| 646 |
+
else:
|
| 647 |
+
f.write(data)
|
| 648 |
+
return
|
| 649 |
+
|
| 650 |
+
if encoding is None:
|
| 651 |
+
c_enc = NULL
|
| 652 |
+
else:
|
| 653 |
+
encoding = _utf8(encoding)
|
| 654 |
+
c_enc = _cstr(encoding)
|
| 655 |
+
if doctype is None:
|
| 656 |
+
c_doctype = NULL
|
| 657 |
+
else:
|
| 658 |
+
doctype = _utf8(doctype)
|
| 659 |
+
c_doctype = _xcstr(doctype)
|
| 660 |
+
|
| 661 |
+
writer = _create_output_buffer(f, c_enc, compression, &c_buffer, close=False)
|
| 662 |
+
if writer is None:
|
| 663 |
+
with nogil:
|
| 664 |
+
error_result = _serialise_node(
|
| 665 |
+
c_buffer, c_doctype, c_enc, element._c_node, c_method,
|
| 666 |
+
write_xml_declaration, write_doctype, pretty_print, with_tail, standalone)
|
| 667 |
+
else:
|
| 668 |
+
error_result = _serialise_node(
|
| 669 |
+
c_buffer, c_doctype, c_enc, element._c_node, c_method,
|
| 670 |
+
write_xml_declaration, write_doctype, pretty_print, with_tail, standalone)
|
| 671 |
+
|
| 672 |
+
if writer is not None:
|
| 673 |
+
writer._exc_context._raise_if_stored()
|
| 674 |
+
if error_result != xmlerror.XML_ERR_OK:
|
| 675 |
+
_raiseSerialisationError(error_result)
|
| 676 |
+
|
| 677 |
+
|
| 678 |
+
cdef int _serialise_node(tree.xmlOutputBuffer* c_buffer, const_xmlChar* c_doctype,
|
| 679 |
+
const_char* c_enc, xmlNode* c_node, int c_method,
|
| 680 |
+
bint write_xml_declaration, bint write_doctype, bint pretty_print,
|
| 681 |
+
bint with_tail, int standalone) noexcept nogil:
|
| 682 |
+
_writeNodeToBuffer(
|
| 683 |
+
c_buffer, c_node, c_enc, c_doctype, c_method,
|
| 684 |
+
write_xml_declaration, write_doctype, pretty_print, with_tail, standalone)
|
| 685 |
+
error_result = c_buffer.error
|
| 686 |
+
if error_result == xmlerror.XML_ERR_OK:
|
| 687 |
+
error_result = tree.xmlOutputBufferClose(c_buffer)
|
| 688 |
+
if error_result != -1:
|
| 689 |
+
error_result = xmlerror.XML_ERR_OK
|
| 690 |
+
else:
|
| 691 |
+
tree.xmlOutputBufferClose(c_buffer)
|
| 692 |
+
return error_result
|
| 693 |
+
|
| 694 |
+
|
| 695 |
+
cdef _FilelikeWriter _create_output_buffer(
|
| 696 |
+
f, const_char* c_enc, int c_compression,
|
| 697 |
+
tree.xmlOutputBuffer** c_buffer_ret, bint close):
|
| 698 |
+
cdef tree.xmlOutputBuffer* c_buffer
|
| 699 |
+
cdef _FilelikeWriter writer
|
| 700 |
+
cdef bytes filename8
|
| 701 |
+
enchandler = tree.xmlFindCharEncodingHandler(c_enc)
|
| 702 |
+
if enchandler is NULL:
|
| 703 |
+
raise LookupError(
|
| 704 |
+
f"unknown encoding: '{c_enc.decode('UTF-8') if c_enc is not NULL else u''}'")
|
| 705 |
+
try:
|
| 706 |
+
f = _getFSPathOrObject(f)
|
| 707 |
+
|
| 708 |
+
if c_compression and not HAS_ZLIB_COMPRESSION and _isString(f):
|
| 709 |
+
# Let "_FilelikeWriter" fall back to Python's GzipFile.
|
| 710 |
+
f = open(f, mode="wb")
|
| 711 |
+
close = True
|
| 712 |
+
|
| 713 |
+
if _isString(f):
|
| 714 |
+
filename8 = _encodeFilename(f)
|
| 715 |
+
if b'%' in filename8 and (
|
| 716 |
+
# Exclude absolute Windows paths and file:// URLs.
|
| 717 |
+
_isFilePath(<const xmlChar*>filename8) not in (NO_FILE_PATH, ABS_WIN_FILE_PATH)
|
| 718 |
+
or filename8[:7].lower() == b'file://'):
|
| 719 |
+
# A file path (not a URL) containing the '%' URL escape character.
|
| 720 |
+
# libxml2 uses URL-unescaping on these, so escape the path before passing it in.
|
| 721 |
+
filename8 = filename8.replace(b'%', b'%25')
|
| 722 |
+
c_buffer = tree.xmlOutputBufferCreateFilename(
|
| 723 |
+
_cstr(filename8), enchandler, c_compression)
|
| 724 |
+
if c_buffer is NULL:
|
| 725 |
+
python.PyErr_SetFromErrno(IOError) # raises IOError
|
| 726 |
+
writer = None
|
| 727 |
+
elif hasattr(f, 'write'):
|
| 728 |
+
writer = _FilelikeWriter(f, compression=c_compression, close=close)
|
| 729 |
+
c_buffer = writer._createOutputBuffer(enchandler)
|
| 730 |
+
else:
|
| 731 |
+
raise TypeError(
|
| 732 |
+
f"File or filename expected, got '{python._fqtypename(f).decode('UTF-8')}'")
|
| 733 |
+
except:
|
| 734 |
+
tree.xmlCharEncCloseFunc(enchandler)
|
| 735 |
+
raise
|
| 736 |
+
c_buffer_ret[0] = c_buffer
|
| 737 |
+
return writer
|
| 738 |
+
|
| 739 |
+
cdef xmlChar **_convert_ns_prefixes(tree.xmlDict* c_dict, ns_prefixes) except NULL:
|
| 740 |
+
cdef size_t i, num_ns_prefixes = len(ns_prefixes)
|
| 741 |
+
# Need to allocate one extra memory block to handle last NULL entry
|
| 742 |
+
c_ns_prefixes = <xmlChar **>python.lxml_malloc(num_ns_prefixes + 1, sizeof(xmlChar*))
|
| 743 |
+
if not c_ns_prefixes:
|
| 744 |
+
raise MemoryError()
|
| 745 |
+
i = 0
|
| 746 |
+
try:
|
| 747 |
+
for prefix in ns_prefixes:
|
| 748 |
+
prefix_utf = _utf8(prefix)
|
| 749 |
+
c_prefix_len = len(prefix_utf)
|
| 750 |
+
if c_prefix_len > limits.INT_MAX:
|
| 751 |
+
raise ValueError("Prefix too long")
|
| 752 |
+
c_prefix = tree.xmlDictExists(c_dict, _xcstr(prefix_utf), <int> c_prefix_len)
|
| 753 |
+
if c_prefix:
|
| 754 |
+
# unknown prefixes do not need to get serialised
|
| 755 |
+
c_ns_prefixes[i] = <xmlChar*>c_prefix
|
| 756 |
+
i += 1
|
| 757 |
+
except:
|
| 758 |
+
python.lxml_free(c_ns_prefixes)
|
| 759 |
+
raise
|
| 760 |
+
|
| 761 |
+
c_ns_prefixes[i] = NULL # append end marker
|
| 762 |
+
return c_ns_prefixes
|
| 763 |
+
|
| 764 |
+
cdef _tofilelikeC14N(f, _Element element, bint exclusive, bint with_comments,
|
| 765 |
+
int compression, inclusive_ns_prefixes):
|
| 766 |
+
cdef _FilelikeWriter writer = None
|
| 767 |
+
cdef tree.xmlOutputBuffer* c_buffer
|
| 768 |
+
cdef xmlChar **c_inclusive_ns_prefixes = NULL
|
| 769 |
+
cdef char* c_filename
|
| 770 |
+
cdef xmlDoc* c_base_doc
|
| 771 |
+
cdef xmlDoc* c_doc
|
| 772 |
+
cdef int bytes_count, error = 0
|
| 773 |
+
|
| 774 |
+
c_base_doc = element._c_node.doc
|
| 775 |
+
c_doc = _fakeRootDoc(c_base_doc, element._c_node)
|
| 776 |
+
try:
|
| 777 |
+
c_inclusive_ns_prefixes = (
|
| 778 |
+
_convert_ns_prefixes(c_doc.dict, inclusive_ns_prefixes)
|
| 779 |
+
if inclusive_ns_prefixes else NULL)
|
| 780 |
+
|
| 781 |
+
f = _getFSPathOrObject(f)
|
| 782 |
+
|
| 783 |
+
close = False
|
| 784 |
+
if compression and not HAS_ZLIB_COMPRESSION and _isString(f):
|
| 785 |
+
# Let "_FilelikeWriter" fall back to Python's GzipFile.
|
| 786 |
+
f = open(f, mode="wb")
|
| 787 |
+
close = True
|
| 788 |
+
|
| 789 |
+
if _isString(f):
|
| 790 |
+
filename8 = _encodeFilename(f)
|
| 791 |
+
c_filename = _cstr(filename8)
|
| 792 |
+
with nogil:
|
| 793 |
+
error = c14n.xmlC14NDocSave(
|
| 794 |
+
c_doc, NULL, exclusive, c_inclusive_ns_prefixes,
|
| 795 |
+
with_comments, c_filename, compression)
|
| 796 |
+
elif hasattr(f, 'write'):
|
| 797 |
+
writer = _FilelikeWriter(f, compression=compression, close=close)
|
| 798 |
+
c_buffer = writer._createOutputBuffer(NULL)
|
| 799 |
+
try:
|
| 800 |
+
with writer.error_log:
|
| 801 |
+
bytes_count = c14n.xmlC14NDocSaveTo(
|
| 802 |
+
c_doc, NULL, exclusive, c_inclusive_ns_prefixes,
|
| 803 |
+
with_comments, c_buffer)
|
| 804 |
+
finally:
|
| 805 |
+
error = tree.xmlOutputBufferClose(c_buffer)
|
| 806 |
+
if bytes_count < 0:
|
| 807 |
+
error = bytes_count
|
| 808 |
+
elif error != -1:
|
| 809 |
+
error = xmlerror.XML_ERR_OK
|
| 810 |
+
else:
|
| 811 |
+
raise TypeError(f"File or filename expected, got '{python._fqtypename(f).decode('UTF-8')}'")
|
| 812 |
+
finally:
|
| 813 |
+
_destroyFakeDoc(c_base_doc, c_doc)
|
| 814 |
+
if c_inclusive_ns_prefixes is not NULL:
|
| 815 |
+
python.lxml_free(c_inclusive_ns_prefixes)
|
| 816 |
+
|
| 817 |
+
if writer is not None:
|
| 818 |
+
writer._exc_context._raise_if_stored()
|
| 819 |
+
|
| 820 |
+
if error < 0:
|
| 821 |
+
message = "C14N failed"
|
| 822 |
+
if writer is not None:
|
| 823 |
+
errors = writer.error_log
|
| 824 |
+
if len(errors):
|
| 825 |
+
message = errors[0].message
|
| 826 |
+
raise C14NError(message)
|
| 827 |
+
|
| 828 |
+
|
| 829 |
+
# C14N 2.0
|
| 830 |
+
|
| 831 |
+
def canonicalize(xml_data=None, *, out=None, from_file=None, **options):
|
| 832 |
+
"""Convert XML to its C14N 2.0 serialised form.
|
| 833 |
+
|
| 834 |
+
If *out* is provided, it must be a file or file-like object that receives
|
| 835 |
+
the serialised canonical XML output (text, not bytes) through its ``.write()``
|
| 836 |
+
method. To write to a file, open it in text mode with encoding "utf-8".
|
| 837 |
+
If *out* is not provided, this function returns the output as text string.
|
| 838 |
+
|
| 839 |
+
Either *xml_data* (an XML string, tree or Element) or *file*
|
| 840 |
+
(a file path or file-like object) must be provided as input.
|
| 841 |
+
|
| 842 |
+
The configuration options are the same as for the ``C14NWriterTarget``.
|
| 843 |
+
"""
|
| 844 |
+
if xml_data is None and from_file is None:
|
| 845 |
+
raise ValueError("Either 'xml_data' or 'from_file' must be provided as input")
|
| 846 |
+
|
| 847 |
+
sio = None
|
| 848 |
+
if out is None:
|
| 849 |
+
sio = out = StringIO()
|
| 850 |
+
|
| 851 |
+
target = C14NWriterTarget(out.write, **options)
|
| 852 |
+
|
| 853 |
+
if xml_data is not None and not isinstance(xml_data, basestring):
|
| 854 |
+
_tree_to_target(xml_data, target)
|
| 855 |
+
return sio.getvalue() if sio is not None else None
|
| 856 |
+
|
| 857 |
+
cdef _FeedParser parser = XMLParser(
|
| 858 |
+
target=target,
|
| 859 |
+
attribute_defaults=True,
|
| 860 |
+
collect_ids=False,
|
| 861 |
+
)
|
| 862 |
+
|
| 863 |
+
if xml_data is not None:
|
| 864 |
+
parser.feed(xml_data)
|
| 865 |
+
parser.close()
|
| 866 |
+
elif from_file is not None:
|
| 867 |
+
try:
|
| 868 |
+
_parseDocument(from_file, parser, base_url=None)
|
| 869 |
+
except _TargetParserResult:
|
| 870 |
+
pass
|
| 871 |
+
|
| 872 |
+
return sio.getvalue() if sio is not None else None
|
| 873 |
+
|
| 874 |
+
|
| 875 |
+
cdef _tree_to_target(element, target):
|
| 876 |
+
for event, elem in iterwalk(element, events=('start', 'end', 'start-ns', 'comment', 'pi')):
|
| 877 |
+
text = None
|
| 878 |
+
if event == 'start':
|
| 879 |
+
target.start(elem.tag, elem.attrib)
|
| 880 |
+
text = elem.text
|
| 881 |
+
elif event == 'end':
|
| 882 |
+
target.end(elem.tag)
|
| 883 |
+
text = elem.tail
|
| 884 |
+
elif event == 'start-ns':
|
| 885 |
+
target.start_ns(*elem)
|
| 886 |
+
continue
|
| 887 |
+
elif event == 'comment':
|
| 888 |
+
target.comment(elem.text)
|
| 889 |
+
text = elem.tail
|
| 890 |
+
elif event == 'pi':
|
| 891 |
+
target.pi(elem.target, elem.text)
|
| 892 |
+
text = elem.tail
|
| 893 |
+
if text:
|
| 894 |
+
target.data(text)
|
| 895 |
+
return target.close()
|
| 896 |
+
|
| 897 |
+
|
| 898 |
+
cdef object _looks_like_prefix_name = re.compile(r'^\w+:\w+$', re.UNICODE).match
|
| 899 |
+
|
| 900 |
+
|
| 901 |
+
cdef class C14NWriterTarget:
|
| 902 |
+
"""
|
| 903 |
+
Canonicalization writer target for the XMLParser.
|
| 904 |
+
|
| 905 |
+
Serialises parse events to XML C14N 2.0.
|
| 906 |
+
|
| 907 |
+
Configuration options:
|
| 908 |
+
|
| 909 |
+
- *with_comments*: set to true to include comments
|
| 910 |
+
- *strip_text*: set to true to strip whitespace before and after text content
|
| 911 |
+
- *rewrite_prefixes*: set to true to replace namespace prefixes by "n{number}"
|
| 912 |
+
- *qname_aware_tags*: a set of qname aware tag names in which prefixes
|
| 913 |
+
should be replaced in text content
|
| 914 |
+
- *qname_aware_attrs*: a set of qname aware attribute names in which prefixes
|
| 915 |
+
should be replaced in text content
|
| 916 |
+
- *exclude_attrs*: a set of attribute names that should not be serialised
|
| 917 |
+
- *exclude_tags*: a set of tag names that should not be serialised
|
| 918 |
+
"""
|
| 919 |
+
cdef object _write
|
| 920 |
+
cdef list _data
|
| 921 |
+
cdef set _qname_aware_tags
|
| 922 |
+
cdef object _find_qname_aware_attrs
|
| 923 |
+
cdef list _declared_ns_stack
|
| 924 |
+
cdef list _ns_stack
|
| 925 |
+
cdef dict _prefix_map
|
| 926 |
+
cdef list _preserve_space
|
| 927 |
+
cdef tuple _pending_start
|
| 928 |
+
cdef set _exclude_tags
|
| 929 |
+
cdef set _exclude_attrs
|
| 930 |
+
cdef Py_ssize_t _ignored_depth
|
| 931 |
+
cdef bint _with_comments
|
| 932 |
+
cdef bint _strip_text
|
| 933 |
+
cdef bint _rewrite_prefixes
|
| 934 |
+
cdef bint _root_seen
|
| 935 |
+
cdef bint _root_done
|
| 936 |
+
|
| 937 |
+
def __init__(self, write, *,
|
| 938 |
+
with_comments=False, strip_text=False, rewrite_prefixes=False,
|
| 939 |
+
qname_aware_tags=None, qname_aware_attrs=None,
|
| 940 |
+
exclude_attrs=None, exclude_tags=None):
|
| 941 |
+
self._write = write
|
| 942 |
+
self._data = []
|
| 943 |
+
self._with_comments = with_comments
|
| 944 |
+
self._strip_text = strip_text
|
| 945 |
+
self._exclude_attrs = set(exclude_attrs) if exclude_attrs else None
|
| 946 |
+
self._exclude_tags = set(exclude_tags) if exclude_tags else None
|
| 947 |
+
|
| 948 |
+
self._rewrite_prefixes = rewrite_prefixes
|
| 949 |
+
if qname_aware_tags:
|
| 950 |
+
self._qname_aware_tags = set(qname_aware_tags)
|
| 951 |
+
else:
|
| 952 |
+
self._qname_aware_tags = None
|
| 953 |
+
if qname_aware_attrs:
|
| 954 |
+
self._find_qname_aware_attrs = set(qname_aware_attrs).intersection
|
| 955 |
+
else:
|
| 956 |
+
self._find_qname_aware_attrs = None
|
| 957 |
+
|
| 958 |
+
# Stack with globally and newly declared namespaces as (uri, prefix) pairs.
|
| 959 |
+
self._declared_ns_stack = [[
|
| 960 |
+
("http://www.w3.org/XML/1998/namespace", "xml"),
|
| 961 |
+
]]
|
| 962 |
+
# Stack with user declared namespace prefixes as (uri, prefix) pairs.
|
| 963 |
+
self._ns_stack = []
|
| 964 |
+
if not rewrite_prefixes:
|
| 965 |
+
self._ns_stack.append(_DEFAULT_NAMESPACE_PREFIXES_ITEMS)
|
| 966 |
+
self._ns_stack.append([])
|
| 967 |
+
self._prefix_map = {}
|
| 968 |
+
self._preserve_space = [False]
|
| 969 |
+
self._pending_start = None
|
| 970 |
+
self._ignored_depth = 0
|
| 971 |
+
self._root_seen = False
|
| 972 |
+
self._root_done = False
|
| 973 |
+
|
| 974 |
+
def _iter_namespaces(self, ns_stack):
|
| 975 |
+
for namespaces in reversed(ns_stack):
|
| 976 |
+
if namespaces: # almost no element declares new namespaces
|
| 977 |
+
yield from namespaces
|
| 978 |
+
|
| 979 |
+
cdef _resolve_prefix_name(self, prefixed_name):
|
| 980 |
+
prefix, name = prefixed_name.split(':', 1)
|
| 981 |
+
for uri, p in self._iter_namespaces(self._ns_stack):
|
| 982 |
+
if p == prefix:
|
| 983 |
+
return f'{{{uri}}}{name}'
|
| 984 |
+
raise ValueError(f'Prefix {prefix} of QName "{prefixed_name}" is not declared in scope')
|
| 985 |
+
|
| 986 |
+
cdef _qname(self, qname, uri=None):
|
| 987 |
+
if uri is None:
|
| 988 |
+
uri, tag = qname[1:].rsplit('}', 1) if qname[:1] == '{' else ('', qname)
|
| 989 |
+
else:
|
| 990 |
+
tag = qname
|
| 991 |
+
|
| 992 |
+
prefixes_seen = set()
|
| 993 |
+
for u, prefix in self._iter_namespaces(self._declared_ns_stack):
|
| 994 |
+
if u == uri and prefix not in prefixes_seen:
|
| 995 |
+
return f'{prefix}:{tag}' if prefix else tag, tag, uri
|
| 996 |
+
prefixes_seen.add(prefix)
|
| 997 |
+
|
| 998 |
+
# Not declared yet => add new declaration.
|
| 999 |
+
if self._rewrite_prefixes:
|
| 1000 |
+
if uri in self._prefix_map:
|
| 1001 |
+
prefix = self._prefix_map[uri]
|
| 1002 |
+
else:
|
| 1003 |
+
prefix = self._prefix_map[uri] = f'n{len(self._prefix_map)}'
|
| 1004 |
+
self._declared_ns_stack[-1].append((uri, prefix))
|
| 1005 |
+
return f'{prefix}:{tag}', tag, uri
|
| 1006 |
+
|
| 1007 |
+
if not uri and '' not in prefixes_seen:
|
| 1008 |
+
# No default namespace declared => no prefix needed.
|
| 1009 |
+
return tag, tag, uri
|
| 1010 |
+
|
| 1011 |
+
for u, prefix in self._iter_namespaces(self._ns_stack):
|
| 1012 |
+
if u == uri:
|
| 1013 |
+
self._declared_ns_stack[-1].append((uri, prefix))
|
| 1014 |
+
return f'{prefix}:{tag}' if prefix else tag, tag, uri
|
| 1015 |
+
|
| 1016 |
+
if not uri:
|
| 1017 |
+
# As soon as a default namespace is defined,
|
| 1018 |
+
# anything that has no namespace (and thus, no prefix) goes there.
|
| 1019 |
+
return tag, tag, uri
|
| 1020 |
+
|
| 1021 |
+
raise ValueError(f'Namespace "{uri}" of name "{tag}" is not declared in scope')
|
| 1022 |
+
|
| 1023 |
+
def data(self, data):
|
| 1024 |
+
if not self._ignored_depth:
|
| 1025 |
+
self._data.append(data)
|
| 1026 |
+
|
| 1027 |
+
cdef _flush(self):
|
| 1028 |
+
cdef unicode data = ''.join(self._data)
|
| 1029 |
+
del self._data[:]
|
| 1030 |
+
if self._strip_text and not self._preserve_space[-1]:
|
| 1031 |
+
data = data.strip()
|
| 1032 |
+
if self._pending_start is not None:
|
| 1033 |
+
(tag, attrs, new_namespaces), self._pending_start = self._pending_start, None
|
| 1034 |
+
qname_text = data if ':' in data and _looks_like_prefix_name(data) else None
|
| 1035 |
+
self._start(tag, attrs, new_namespaces, qname_text)
|
| 1036 |
+
if qname_text is not None:
|
| 1037 |
+
return
|
| 1038 |
+
if data and self._root_seen:
|
| 1039 |
+
self._write(_escape_cdata_c14n(data))
|
| 1040 |
+
|
| 1041 |
+
def start_ns(self, prefix, uri):
|
| 1042 |
+
if self._ignored_depth:
|
| 1043 |
+
return
|
| 1044 |
+
# we may have to resolve qnames in text content
|
| 1045 |
+
if self._data:
|
| 1046 |
+
self._flush()
|
| 1047 |
+
self._ns_stack[-1].append((uri, prefix))
|
| 1048 |
+
|
| 1049 |
+
def start(self, tag, attrs):
|
| 1050 |
+
if self._exclude_tags is not None and (
|
| 1051 |
+
self._ignored_depth or tag in self._exclude_tags):
|
| 1052 |
+
self._ignored_depth += 1
|
| 1053 |
+
return
|
| 1054 |
+
if self._data:
|
| 1055 |
+
self._flush()
|
| 1056 |
+
|
| 1057 |
+
new_namespaces = []
|
| 1058 |
+
self._declared_ns_stack.append(new_namespaces)
|
| 1059 |
+
|
| 1060 |
+
if self._qname_aware_tags is not None and tag in self._qname_aware_tags:
|
| 1061 |
+
# Need to parse text first to see if it requires a prefix declaration.
|
| 1062 |
+
self._pending_start = (tag, attrs, new_namespaces)
|
| 1063 |
+
return
|
| 1064 |
+
self._start(tag, attrs, new_namespaces)
|
| 1065 |
+
|
| 1066 |
+
cdef _start(self, tag, attrs, new_namespaces, qname_text=None):
|
| 1067 |
+
if self._exclude_attrs is not None and attrs:
|
| 1068 |
+
attrs = {k: v for k, v in attrs.items() if k not in self._exclude_attrs}
|
| 1069 |
+
|
| 1070 |
+
qnames = {tag, *attrs}
|
| 1071 |
+
resolved_names = {}
|
| 1072 |
+
|
| 1073 |
+
# Resolve prefixes in attribute and tag text.
|
| 1074 |
+
if qname_text is not None:
|
| 1075 |
+
qname = resolved_names[qname_text] = self._resolve_prefix_name(qname_text)
|
| 1076 |
+
qnames.add(qname)
|
| 1077 |
+
if self._find_qname_aware_attrs is not None and attrs:
|
| 1078 |
+
qattrs = self._find_qname_aware_attrs(attrs)
|
| 1079 |
+
if qattrs:
|
| 1080 |
+
for attr_name in qattrs:
|
| 1081 |
+
value = attrs[attr_name]
|
| 1082 |
+
if _looks_like_prefix_name(value):
|
| 1083 |
+
qname = resolved_names[value] = self._resolve_prefix_name(value)
|
| 1084 |
+
qnames.add(qname)
|
| 1085 |
+
else:
|
| 1086 |
+
qattrs = None
|
| 1087 |
+
else:
|
| 1088 |
+
qattrs = None
|
| 1089 |
+
|
| 1090 |
+
# Assign prefixes in lexicographical order of used URIs.
|
| 1091 |
+
parsed_qnames = {n: self._qname(n) for n in sorted(
|
| 1092 |
+
qnames, key=lambda n: n.split('}', 1))}
|
| 1093 |
+
|
| 1094 |
+
# Write namespace declarations in prefix order ...
|
| 1095 |
+
if new_namespaces:
|
| 1096 |
+
attr_list = [
|
| 1097 |
+
('xmlns:' + prefix if prefix else 'xmlns', uri)
|
| 1098 |
+
for uri, prefix in new_namespaces
|
| 1099 |
+
]
|
| 1100 |
+
attr_list.sort()
|
| 1101 |
+
else:
|
| 1102 |
+
# almost always empty
|
| 1103 |
+
attr_list = []
|
| 1104 |
+
|
| 1105 |
+
# ... followed by attributes in URI+name order
|
| 1106 |
+
if attrs:
|
| 1107 |
+
for k, v in sorted(attrs.items()):
|
| 1108 |
+
if qattrs is not None and k in qattrs and v in resolved_names:
|
| 1109 |
+
v = parsed_qnames[resolved_names[v]][0]
|
| 1110 |
+
attr_qname, attr_name, uri = parsed_qnames[k]
|
| 1111 |
+
# No prefix for attributes in default ('') namespace.
|
| 1112 |
+
attr_list.append((attr_qname if uri else attr_name, v))
|
| 1113 |
+
|
| 1114 |
+
# Honour xml:space attributes.
|
| 1115 |
+
space_behaviour = attrs.get('{http://www.w3.org/XML/1998/namespace}space')
|
| 1116 |
+
self._preserve_space.append(
|
| 1117 |
+
space_behaviour == 'preserve' if space_behaviour
|
| 1118 |
+
else self._preserve_space[-1])
|
| 1119 |
+
|
| 1120 |
+
# Write the tag.
|
| 1121 |
+
write = self._write
|
| 1122 |
+
write('<' + parsed_qnames[tag][0])
|
| 1123 |
+
if attr_list:
|
| 1124 |
+
write(''.join([f' {k}="{_escape_attrib_c14n(v)}"' for k, v in attr_list]))
|
| 1125 |
+
write('>')
|
| 1126 |
+
|
| 1127 |
+
# Write the resolved qname text content.
|
| 1128 |
+
if qname_text is not None:
|
| 1129 |
+
write(_escape_cdata_c14n(parsed_qnames[resolved_names[qname_text]][0]))
|
| 1130 |
+
|
| 1131 |
+
self._root_seen = True
|
| 1132 |
+
self._ns_stack.append([])
|
| 1133 |
+
|
| 1134 |
+
def end(self, tag):
|
| 1135 |
+
if self._ignored_depth:
|
| 1136 |
+
self._ignored_depth -= 1
|
| 1137 |
+
return
|
| 1138 |
+
if self._data:
|
| 1139 |
+
self._flush()
|
| 1140 |
+
self._write(f'</{self._qname(tag)[0]}>')
|
| 1141 |
+
self._preserve_space.pop()
|
| 1142 |
+
self._root_done = len(self._preserve_space) == 1
|
| 1143 |
+
self._declared_ns_stack.pop()
|
| 1144 |
+
self._ns_stack.pop()
|
| 1145 |
+
|
| 1146 |
+
def comment(self, text):
|
| 1147 |
+
if not self._with_comments:
|
| 1148 |
+
return
|
| 1149 |
+
if self._ignored_depth:
|
| 1150 |
+
return
|
| 1151 |
+
if self._root_done:
|
| 1152 |
+
self._write('\n')
|
| 1153 |
+
elif self._root_seen and self._data:
|
| 1154 |
+
self._flush()
|
| 1155 |
+
self._write(f'<!--{_escape_cdata_c14n(text)}-->')
|
| 1156 |
+
if not self._root_seen:
|
| 1157 |
+
self._write('\n')
|
| 1158 |
+
|
| 1159 |
+
def pi(self, target, data):
|
| 1160 |
+
if self._ignored_depth:
|
| 1161 |
+
return
|
| 1162 |
+
if self._root_done:
|
| 1163 |
+
self._write('\n')
|
| 1164 |
+
elif self._root_seen and self._data:
|
| 1165 |
+
self._flush()
|
| 1166 |
+
self._write(
|
| 1167 |
+
f'<?{target} {_escape_cdata_c14n(data)}?>' if data else f'<?{target}?>')
|
| 1168 |
+
if not self._root_seen:
|
| 1169 |
+
self._write('\n')
|
| 1170 |
+
|
| 1171 |
+
def close(self):
|
| 1172 |
+
return None
|
| 1173 |
+
|
| 1174 |
+
|
| 1175 |
+
cdef _raise_serialization_error(text):
|
| 1176 |
+
raise TypeError("cannot serialize %r (type %s)" % (text, type(text).__name__))
|
| 1177 |
+
|
| 1178 |
+
|
| 1179 |
+
cdef unicode _escape_cdata_c14n(stext):
|
| 1180 |
+
# escape character data
|
| 1181 |
+
cdef unicode text
|
| 1182 |
+
cdef Py_UCS4 ch
|
| 1183 |
+
cdef Py_ssize_t start = 0, pos = 0
|
| 1184 |
+
cdef list substrings = None
|
| 1185 |
+
try:
|
| 1186 |
+
text = unicode(stext)
|
| 1187 |
+
except (TypeError, AttributeError):
|
| 1188 |
+
return _raise_serialization_error(stext)
|
| 1189 |
+
|
| 1190 |
+
for pos, ch in enumerate(text):
|
| 1191 |
+
if ch == '&':
|
| 1192 |
+
escape = '&'
|
| 1193 |
+
elif ch == '<':
|
| 1194 |
+
escape = '<'
|
| 1195 |
+
elif ch == '>':
|
| 1196 |
+
escape = '>'
|
| 1197 |
+
elif ch == '\r':
|
| 1198 |
+
escape = '
'
|
| 1199 |
+
else:
|
| 1200 |
+
continue
|
| 1201 |
+
|
| 1202 |
+
if substrings is None:
|
| 1203 |
+
substrings = []
|
| 1204 |
+
if pos > start:
|
| 1205 |
+
substrings.append(text[start:pos])
|
| 1206 |
+
substrings.append(escape)
|
| 1207 |
+
start = pos + 1
|
| 1208 |
+
|
| 1209 |
+
if substrings is None:
|
| 1210 |
+
return text
|
| 1211 |
+
if pos >= start:
|
| 1212 |
+
substrings.append(text[start:pos+1])
|
| 1213 |
+
return ''.join(substrings)
|
| 1214 |
+
|
| 1215 |
+
|
| 1216 |
+
cdef unicode _escape_attrib_c14n(stext):
|
| 1217 |
+
# escape attribute value
|
| 1218 |
+
cdef unicode text
|
| 1219 |
+
cdef Py_UCS4 ch
|
| 1220 |
+
cdef Py_ssize_t start = 0, pos = 0
|
| 1221 |
+
cdef list substrings = None
|
| 1222 |
+
try:
|
| 1223 |
+
text = unicode(stext)
|
| 1224 |
+
except (TypeError, AttributeError):
|
| 1225 |
+
return _raise_serialization_error(stext)
|
| 1226 |
+
|
| 1227 |
+
for pos, ch in enumerate(text):
|
| 1228 |
+
if ch == '&':
|
| 1229 |
+
escape = '&'
|
| 1230 |
+
elif ch == '<':
|
| 1231 |
+
escape = '<'
|
| 1232 |
+
elif ch == '"':
|
| 1233 |
+
escape = '"'
|
| 1234 |
+
elif ch == '\t':
|
| 1235 |
+
escape = '	'
|
| 1236 |
+
elif ch == '\n':
|
| 1237 |
+
escape = '
'
|
| 1238 |
+
elif ch == '\r':
|
| 1239 |
+
escape = '
'
|
| 1240 |
+
else:
|
| 1241 |
+
continue
|
| 1242 |
+
|
| 1243 |
+
if substrings is None:
|
| 1244 |
+
substrings = []
|
| 1245 |
+
if pos > start:
|
| 1246 |
+
substrings.append(text[start:pos])
|
| 1247 |
+
substrings.append(escape)
|
| 1248 |
+
start = pos + 1
|
| 1249 |
+
|
| 1250 |
+
if substrings is None:
|
| 1251 |
+
return text
|
| 1252 |
+
if pos >= start:
|
| 1253 |
+
substrings.append(text[start:pos+1])
|
| 1254 |
+
return ''.join(substrings)
|
| 1255 |
+
|
| 1256 |
+
|
| 1257 |
+
# incremental serialisation
|
| 1258 |
+
|
| 1259 |
+
cdef class xmlfile:
|
| 1260 |
+
"""xmlfile(self, output_file, encoding=None, compression=None, close=False, buffered=True)
|
| 1261 |
+
|
| 1262 |
+
A simple mechanism for incremental XML serialisation.
|
| 1263 |
+
|
| 1264 |
+
Usage example::
|
| 1265 |
+
|
| 1266 |
+
with xmlfile("somefile.xml", encoding='utf-8') as xf:
|
| 1267 |
+
xf.write_declaration(standalone=True)
|
| 1268 |
+
xf.write_doctype('<!DOCTYPE root SYSTEM "some.dtd">')
|
| 1269 |
+
|
| 1270 |
+
# generate an element (the root element)
|
| 1271 |
+
with xf.element('root'):
|
| 1272 |
+
# write a complete Element into the open root element
|
| 1273 |
+
xf.write(etree.Element('test'))
|
| 1274 |
+
|
| 1275 |
+
# generate and write more Elements, e.g. through iterparse
|
| 1276 |
+
for element in generate_some_elements():
|
| 1277 |
+
# serialise generated elements into the XML file
|
| 1278 |
+
xf.write(element)
|
| 1279 |
+
|
| 1280 |
+
# or write multiple Elements or strings at once
|
| 1281 |
+
xf.write(etree.Element('start'), "text", etree.Element('end'))
|
| 1282 |
+
|
| 1283 |
+
If 'output_file' is a file(-like) object, passing ``close=True`` will
|
| 1284 |
+
close it when exiting the context manager. By default, it is left
|
| 1285 |
+
to the owner to do that. When a file path is used, lxml will take care
|
| 1286 |
+
of opening and closing the file itself. Also, when a compression level
|
| 1287 |
+
is set, lxml will deliberately close the file to make sure all data gets
|
| 1288 |
+
compressed and written.
|
| 1289 |
+
|
| 1290 |
+
Setting ``buffered=False`` will flush the output after each operation,
|
| 1291 |
+
such as opening or closing an ``xf.element()`` block or calling
|
| 1292 |
+
``xf.write()``. Alternatively, calling ``xf.flush()`` can be used to
|
| 1293 |
+
explicitly flush any pending output when buffering is enabled.
|
| 1294 |
+
"""
|
| 1295 |
+
cdef object output_file
|
| 1296 |
+
cdef bytes encoding
|
| 1297 |
+
cdef _IncrementalFileWriter writer
|
| 1298 |
+
cdef _AsyncIncrementalFileWriter async_writer
|
| 1299 |
+
cdef int compresslevel
|
| 1300 |
+
cdef bint close
|
| 1301 |
+
cdef bint buffered
|
| 1302 |
+
cdef int method
|
| 1303 |
+
|
| 1304 |
+
def __init__(self, output_file not None, encoding=None, compression=None,
|
| 1305 |
+
close=False, buffered=True):
|
| 1306 |
+
self.output_file = output_file
|
| 1307 |
+
self.encoding = _utf8orNone(encoding)
|
| 1308 |
+
self.compresslevel = compression or 0
|
| 1309 |
+
self.close = close
|
| 1310 |
+
self.buffered = buffered
|
| 1311 |
+
self.method = OUTPUT_METHOD_XML
|
| 1312 |
+
|
| 1313 |
+
def __enter__(self):
|
| 1314 |
+
assert self.output_file is not None
|
| 1315 |
+
self.writer = _IncrementalFileWriter(
|
| 1316 |
+
self.output_file, self.encoding, self.compresslevel,
|
| 1317 |
+
self.close, self.buffered, self.method)
|
| 1318 |
+
return self.writer
|
| 1319 |
+
|
| 1320 |
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
| 1321 |
+
if self.writer is not None:
|
| 1322 |
+
old_writer, self.writer = self.writer, None
|
| 1323 |
+
raise_on_error = exc_type is None
|
| 1324 |
+
old_writer._close(raise_on_error)
|
| 1325 |
+
if self.close:
|
| 1326 |
+
self.output_file = None
|
| 1327 |
+
|
| 1328 |
+
async def __aenter__(self):
|
| 1329 |
+
assert self.output_file is not None
|
| 1330 |
+
if isinstance(self.output_file, basestring):
|
| 1331 |
+
raise TypeError("Cannot asynchronously write to a plain file")
|
| 1332 |
+
if not hasattr(self.output_file, 'write'):
|
| 1333 |
+
raise TypeError("Output file needs an async .write() method")
|
| 1334 |
+
self.async_writer = _AsyncIncrementalFileWriter(
|
| 1335 |
+
self.output_file, self.encoding, self.compresslevel,
|
| 1336 |
+
self.close, self.buffered, self.method)
|
| 1337 |
+
return self.async_writer
|
| 1338 |
+
|
| 1339 |
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
| 1340 |
+
if self.async_writer is not None:
|
| 1341 |
+
old_writer, self.async_writer = self.async_writer, None
|
| 1342 |
+
raise_on_error = exc_type is None
|
| 1343 |
+
await old_writer._close(raise_on_error)
|
| 1344 |
+
if self.close:
|
| 1345 |
+
self.output_file = None
|
| 1346 |
+
|
| 1347 |
+
|
| 1348 |
+
cdef class htmlfile(xmlfile):
|
| 1349 |
+
"""htmlfile(self, output_file, encoding=None, compression=None, close=False, buffered=True)
|
| 1350 |
+
|
| 1351 |
+
A simple mechanism for incremental HTML serialisation. Works the same as
|
| 1352 |
+
xmlfile.
|
| 1353 |
+
"""
|
| 1354 |
+
def __init__(self, *args, **kwargs):
|
| 1355 |
+
super().__init__(*args, **kwargs)
|
| 1356 |
+
self.method = OUTPUT_METHOD_HTML
|
| 1357 |
+
|
| 1358 |
+
|
| 1359 |
+
cdef enum _IncrementalFileWriterStatus:
|
| 1360 |
+
WRITER_STARTING = 0
|
| 1361 |
+
WRITER_DECL_WRITTEN = 1
|
| 1362 |
+
WRITER_DTD_WRITTEN = 2
|
| 1363 |
+
WRITER_IN_ELEMENT = 3
|
| 1364 |
+
WRITER_FINISHED = 4
|
| 1365 |
+
|
| 1366 |
+
|
| 1367 |
+
@cython.final
|
| 1368 |
+
@cython.internal
|
| 1369 |
+
cdef class _IncrementalFileWriter:
|
| 1370 |
+
cdef tree.xmlOutputBuffer* _c_out
|
| 1371 |
+
cdef bytes _encoding
|
| 1372 |
+
cdef const_char* _c_encoding
|
| 1373 |
+
cdef _FilelikeWriter _target
|
| 1374 |
+
cdef list _element_stack
|
| 1375 |
+
cdef int _status
|
| 1376 |
+
cdef int _method
|
| 1377 |
+
cdef bint _buffered
|
| 1378 |
+
|
| 1379 |
+
def __cinit__(self, outfile, bytes encoding, int compresslevel, bint close,
|
| 1380 |
+
bint buffered, int method):
|
| 1381 |
+
self._status = WRITER_STARTING
|
| 1382 |
+
self._element_stack = []
|
| 1383 |
+
if encoding is None:
|
| 1384 |
+
# We always need a document encoding to make the attribute serialisation
|
| 1385 |
+
# of libxml2 identical to ours.
|
| 1386 |
+
encoding = b'ASCII'
|
| 1387 |
+
self._encoding = encoding
|
| 1388 |
+
self._c_encoding = _cstr(encoding)
|
| 1389 |
+
self._buffered = buffered
|
| 1390 |
+
self._target = _create_output_buffer(
|
| 1391 |
+
outfile, self._c_encoding, compresslevel, &self._c_out, close)
|
| 1392 |
+
self._method = method
|
| 1393 |
+
|
| 1394 |
+
def __dealloc__(self):
|
| 1395 |
+
if self._c_out is not NULL:
|
| 1396 |
+
tree.xmlOutputBufferClose(self._c_out)
|
| 1397 |
+
|
| 1398 |
+
def write_declaration(self, version=None, standalone=None, doctype=None):
|
| 1399 |
+
"""write_declaration(self, version=None, standalone=None, doctype=None)
|
| 1400 |
+
|
| 1401 |
+
Write an XML declaration and (optionally) a doctype into the file.
|
| 1402 |
+
"""
|
| 1403 |
+
assert self._c_out is not NULL
|
| 1404 |
+
cdef const_xmlChar* c_version
|
| 1405 |
+
cdef int c_standalone
|
| 1406 |
+
if self._method != OUTPUT_METHOD_XML:
|
| 1407 |
+
raise LxmlSyntaxError("only XML documents have declarations")
|
| 1408 |
+
if self._status >= WRITER_DECL_WRITTEN:
|
| 1409 |
+
raise LxmlSyntaxError("XML declaration already written")
|
| 1410 |
+
version = _utf8orNone(version)
|
| 1411 |
+
c_version = _xcstr(version) if version is not None else NULL
|
| 1412 |
+
doctype = _utf8orNone(doctype)
|
| 1413 |
+
if standalone is None:
|
| 1414 |
+
c_standalone = -1
|
| 1415 |
+
else:
|
| 1416 |
+
c_standalone = 1 if standalone else 0
|
| 1417 |
+
_writeDeclarationToBuffer(self._c_out, c_version, self._c_encoding, c_standalone)
|
| 1418 |
+
if doctype is not None:
|
| 1419 |
+
_writeDoctype(self._c_out, _xcstr(doctype))
|
| 1420 |
+
self._status = WRITER_DTD_WRITTEN
|
| 1421 |
+
else:
|
| 1422 |
+
self._status = WRITER_DECL_WRITTEN
|
| 1423 |
+
if not self._buffered:
|
| 1424 |
+
tree.xmlOutputBufferFlush(self._c_out)
|
| 1425 |
+
self._handle_error(self._c_out.error)
|
| 1426 |
+
|
| 1427 |
+
def write_doctype(self, doctype):
|
| 1428 |
+
"""write_doctype(self, doctype)
|
| 1429 |
+
|
| 1430 |
+
Writes the given doctype declaration verbatimly into the file.
|
| 1431 |
+
"""
|
| 1432 |
+
assert self._c_out is not NULL
|
| 1433 |
+
if doctype is None:
|
| 1434 |
+
return
|
| 1435 |
+
if self._status >= WRITER_DTD_WRITTEN:
|
| 1436 |
+
raise LxmlSyntaxError("DOCTYPE already written or cannot write it here")
|
| 1437 |
+
doctype = _utf8(doctype)
|
| 1438 |
+
_writeDoctype(self._c_out, _xcstr(doctype))
|
| 1439 |
+
self._status = WRITER_DTD_WRITTEN
|
| 1440 |
+
if not self._buffered:
|
| 1441 |
+
tree.xmlOutputBufferFlush(self._c_out)
|
| 1442 |
+
self._handle_error(self._c_out.error)
|
| 1443 |
+
|
| 1444 |
+
def method(self, method):
|
| 1445 |
+
"""method(self, method)
|
| 1446 |
+
|
| 1447 |
+
Returns a context manager that overrides and restores the output method.
|
| 1448 |
+
method is one of (None, 'xml', 'html') where None means 'xml'.
|
| 1449 |
+
"""
|
| 1450 |
+
assert self._c_out is not NULL
|
| 1451 |
+
c_method = self._method if method is None else _findOutputMethod(method)
|
| 1452 |
+
return _MethodChanger(self, c_method)
|
| 1453 |
+
|
| 1454 |
+
def element(self, tag, attrib=None, nsmap=None, method=None, **_extra):
|
| 1455 |
+
"""element(self, tag, attrib=None, nsmap=None, method, **_extra)
|
| 1456 |
+
|
| 1457 |
+
Returns a context manager that writes an opening and closing tag.
|
| 1458 |
+
method is one of (None, 'xml', 'html') where None means 'xml'.
|
| 1459 |
+
"""
|
| 1460 |
+
assert self._c_out is not NULL
|
| 1461 |
+
attributes = []
|
| 1462 |
+
if attrib is not None:
|
| 1463 |
+
for name, value in _iter_attrib(attrib):
|
| 1464 |
+
if name not in _extra:
|
| 1465 |
+
ns, name = _getNsTag(name)
|
| 1466 |
+
attributes.append((ns, name, _utf8(value)))
|
| 1467 |
+
if _extra:
|
| 1468 |
+
for name, value in _extra.iteritems():
|
| 1469 |
+
ns, name = _getNsTag(name)
|
| 1470 |
+
attributes.append((ns, name, _utf8(value)))
|
| 1471 |
+
reversed_nsmap = {}
|
| 1472 |
+
if nsmap:
|
| 1473 |
+
for prefix, ns in nsmap.items():
|
| 1474 |
+
if prefix is not None:
|
| 1475 |
+
prefix = _utf8(prefix)
|
| 1476 |
+
_prefixValidOrRaise(prefix)
|
| 1477 |
+
reversed_nsmap[_utf8(ns)] = prefix
|
| 1478 |
+
ns, name = _getNsTag(tag)
|
| 1479 |
+
|
| 1480 |
+
c_method = self._method if method is None else _findOutputMethod(method)
|
| 1481 |
+
|
| 1482 |
+
return _FileWriterElement(self, (ns, name, attributes, reversed_nsmap), c_method)
|
| 1483 |
+
|
| 1484 |
+
cdef _write_qname(self, bytes name, bytes prefix):
|
| 1485 |
+
if prefix: # empty bytes for no prefix (not None to allow sorting)
|
| 1486 |
+
tree.xmlOutputBufferWrite(self._c_out, len(prefix), _cstr(prefix))
|
| 1487 |
+
tree.xmlOutputBufferWrite(self._c_out, 1, ':')
|
| 1488 |
+
tree.xmlOutputBufferWrite(self._c_out, len(name), _cstr(name))
|
| 1489 |
+
|
| 1490 |
+
cdef _write_start_element(self, element_config):
|
| 1491 |
+
if self._status > WRITER_IN_ELEMENT:
|
| 1492 |
+
raise LxmlSyntaxError("cannot append trailing element to complete XML document")
|
| 1493 |
+
ns, name, attributes, nsmap = element_config
|
| 1494 |
+
flat_namespace_map, new_namespaces = self._collect_namespaces(nsmap)
|
| 1495 |
+
prefix = self._find_prefix(ns, flat_namespace_map, new_namespaces)
|
| 1496 |
+
tree.xmlOutputBufferWrite(self._c_out, 1, '<')
|
| 1497 |
+
self._write_qname(name, prefix)
|
| 1498 |
+
|
| 1499 |
+
self._write_attributes_and_namespaces(
|
| 1500 |
+
attributes, flat_namespace_map, new_namespaces)
|
| 1501 |
+
|
| 1502 |
+
tree.xmlOutputBufferWrite(self._c_out, 1, '>')
|
| 1503 |
+
if not self._buffered:
|
| 1504 |
+
tree.xmlOutputBufferFlush(self._c_out)
|
| 1505 |
+
self._handle_error(self._c_out.error)
|
| 1506 |
+
|
| 1507 |
+
self._element_stack.append((ns, name, prefix, flat_namespace_map))
|
| 1508 |
+
self._status = WRITER_IN_ELEMENT
|
| 1509 |
+
|
| 1510 |
+
cdef _write_attributes_and_namespaces(self, list attributes,
|
| 1511 |
+
dict flat_namespace_map,
|
| 1512 |
+
list new_namespaces):
|
| 1513 |
+
if attributes:
|
| 1514 |
+
# _find_prefix() may append to new_namespaces => build them first
|
| 1515 |
+
attributes = [
|
| 1516 |
+
(self._find_prefix(ns, flat_namespace_map, new_namespaces), name, value)
|
| 1517 |
+
for ns, name, value in attributes ]
|
| 1518 |
+
if new_namespaces:
|
| 1519 |
+
new_namespaces.sort()
|
| 1520 |
+
self._write_attributes_list(new_namespaces)
|
| 1521 |
+
if attributes:
|
| 1522 |
+
self._write_attributes_list(attributes)
|
| 1523 |
+
|
| 1524 |
+
cdef _write_attributes_list(self, list attributes):
|
| 1525 |
+
for prefix, name, value in attributes:
|
| 1526 |
+
tree.xmlOutputBufferWrite(self._c_out, 1, ' ')
|
| 1527 |
+
self._write_qname(name, prefix)
|
| 1528 |
+
tree.xmlOutputBufferWrite(self._c_out, 2, '="')
|
| 1529 |
+
_write_attr_string(self._c_out, _cstr(value))
|
| 1530 |
+
|
| 1531 |
+
tree.xmlOutputBufferWrite(self._c_out, 1, '"')
|
| 1532 |
+
|
| 1533 |
+
cdef _write_end_element(self, element_config):
|
| 1534 |
+
if self._status != WRITER_IN_ELEMENT:
|
| 1535 |
+
raise LxmlSyntaxError("not in an element")
|
| 1536 |
+
if not self._element_stack or self._element_stack[-1][:2] != element_config[:2]:
|
| 1537 |
+
raise LxmlSyntaxError("inconsistent exit action in context manager")
|
| 1538 |
+
|
| 1539 |
+
# If previous write operations failed, the context manager exit might still call us.
|
| 1540 |
+
# That is ok, but we stop writing closing tags and handling errors in that case.
|
| 1541 |
+
# For all non-I/O errors, we continue writing closing tags if we can.
|
| 1542 |
+
ok_to_write = self._c_out.error == xmlerror.XML_ERR_OK
|
| 1543 |
+
|
| 1544 |
+
name, prefix = self._element_stack.pop()[1:3]
|
| 1545 |
+
if ok_to_write:
|
| 1546 |
+
tree.xmlOutputBufferWrite(self._c_out, 2, '</')
|
| 1547 |
+
self._write_qname(name, prefix)
|
| 1548 |
+
tree.xmlOutputBufferWrite(self._c_out, 1, '>')
|
| 1549 |
+
|
| 1550 |
+
if not self._element_stack:
|
| 1551 |
+
self._status = WRITER_FINISHED
|
| 1552 |
+
if ok_to_write:
|
| 1553 |
+
if not self._buffered:
|
| 1554 |
+
tree.xmlOutputBufferFlush(self._c_out)
|
| 1555 |
+
self._handle_error(self._c_out.error)
|
| 1556 |
+
|
| 1557 |
+
cdef _find_prefix(self, bytes href, dict flat_namespaces_map, list new_namespaces):
|
| 1558 |
+
if href is None:
|
| 1559 |
+
return None
|
| 1560 |
+
if href in flat_namespaces_map:
|
| 1561 |
+
return flat_namespaces_map[href]
|
| 1562 |
+
# need to create a new prefix
|
| 1563 |
+
prefixes = flat_namespaces_map.values()
|
| 1564 |
+
i = 0
|
| 1565 |
+
while True:
|
| 1566 |
+
prefix = _utf8('ns%d' % i)
|
| 1567 |
+
if prefix not in prefixes:
|
| 1568 |
+
new_namespaces.append((b'xmlns', prefix, href))
|
| 1569 |
+
flat_namespaces_map[href] = prefix
|
| 1570 |
+
return prefix
|
| 1571 |
+
i += 1
|
| 1572 |
+
|
| 1573 |
+
cdef _collect_namespaces(self, dict nsmap):
|
| 1574 |
+
new_namespaces = []
|
| 1575 |
+
flat_namespaces_map = {}
|
| 1576 |
+
for ns, prefix in nsmap.iteritems():
|
| 1577 |
+
flat_namespaces_map[ns] = prefix
|
| 1578 |
+
if prefix is None:
|
| 1579 |
+
# use empty bytes rather than None to allow sorting
|
| 1580 |
+
new_namespaces.append((b'', b'xmlns', ns))
|
| 1581 |
+
else:
|
| 1582 |
+
new_namespaces.append((b'xmlns', prefix, ns))
|
| 1583 |
+
# merge in flat namespace map of parent
|
| 1584 |
+
if self._element_stack:
|
| 1585 |
+
for ns, prefix in (<dict>self._element_stack[-1][-1]).iteritems():
|
| 1586 |
+
if flat_namespaces_map.get(ns) is None:
|
| 1587 |
+
# unknown or empty prefix => prefer a 'real' prefix
|
| 1588 |
+
flat_namespaces_map[ns] = prefix
|
| 1589 |
+
return flat_namespaces_map, new_namespaces
|
| 1590 |
+
|
| 1591 |
+
def write(self, *args, bint with_tail=True, bint pretty_print=False, method=None):
|
| 1592 |
+
"""write(self, *args, with_tail=True, pretty_print=False, method=None)
|
| 1593 |
+
|
| 1594 |
+
Write subtrees or strings into the file.
|
| 1595 |
+
|
| 1596 |
+
If method is not None, it should be one of ('html', 'xml', 'text')
|
| 1597 |
+
to temporarily override the output method.
|
| 1598 |
+
"""
|
| 1599 |
+
assert self._c_out is not NULL
|
| 1600 |
+
c_method = self._method if method is None else _findOutputMethod(method)
|
| 1601 |
+
|
| 1602 |
+
for content in args:
|
| 1603 |
+
if _isString(content):
|
| 1604 |
+
if self._status != WRITER_IN_ELEMENT:
|
| 1605 |
+
if self._status > WRITER_IN_ELEMENT or content.strip():
|
| 1606 |
+
raise LxmlSyntaxError("not in an element")
|
| 1607 |
+
bstring = _utf8(content)
|
| 1608 |
+
if not bstring:
|
| 1609 |
+
continue
|
| 1610 |
+
|
| 1611 |
+
ns, name, _, _ = self._element_stack[-1]
|
| 1612 |
+
if (c_method == OUTPUT_METHOD_HTML and
|
| 1613 |
+
ns in (None, b'http://www.w3.org/1999/xhtml') and
|
| 1614 |
+
name in (b'script', b'style')):
|
| 1615 |
+
tree.xmlOutputBufferWrite(self._c_out, len(bstring), _cstr(bstring))
|
| 1616 |
+
|
| 1617 |
+
else:
|
| 1618 |
+
tree.xmlOutputBufferWriteEscape(self._c_out, _xcstr(bstring), NULL)
|
| 1619 |
+
|
| 1620 |
+
elif isinstance(content, CDATA):
|
| 1621 |
+
if self._status > WRITER_IN_ELEMENT:
|
| 1622 |
+
raise LxmlSyntaxError("not in an element")
|
| 1623 |
+
_write_cdata_string(self._c_out, (<CDATA>content)._utf8_data)
|
| 1624 |
+
|
| 1625 |
+
elif iselement(content):
|
| 1626 |
+
if self._status > WRITER_IN_ELEMENT:
|
| 1627 |
+
raise LxmlSyntaxError("cannot append trailing element to complete XML document")
|
| 1628 |
+
_writeNodeToBuffer(self._c_out, (<_Element>content)._c_node,
|
| 1629 |
+
self._c_encoding, NULL, c_method,
|
| 1630 |
+
False, False, pretty_print, with_tail, False)
|
| 1631 |
+
if (<_Element>content)._c_node.type == tree.XML_ELEMENT_NODE:
|
| 1632 |
+
if not self._element_stack:
|
| 1633 |
+
self._status = WRITER_FINISHED
|
| 1634 |
+
|
| 1635 |
+
elif content is not None:
|
| 1636 |
+
raise TypeError(
|
| 1637 |
+
f"got invalid input value of type {type(content)}, expected string, CDATA or Element")
|
| 1638 |
+
|
| 1639 |
+
self._handle_error(self._c_out.error)
|
| 1640 |
+
|
| 1641 |
+
if not self._buffered:
|
| 1642 |
+
tree.xmlOutputBufferFlush(self._c_out)
|
| 1643 |
+
self._handle_error(self._c_out.error)
|
| 1644 |
+
|
| 1645 |
+
def flush(self):
|
| 1646 |
+
"""flush(self)
|
| 1647 |
+
|
| 1648 |
+
Write any pending content of the current output buffer to the stream.
|
| 1649 |
+
"""
|
| 1650 |
+
assert self._c_out is not NULL
|
| 1651 |
+
tree.xmlOutputBufferFlush(self._c_out)
|
| 1652 |
+
self._handle_error(self._c_out.error)
|
| 1653 |
+
|
| 1654 |
+
cdef _close(self, bint raise_on_error):
|
| 1655 |
+
if raise_on_error:
|
| 1656 |
+
if self._status < WRITER_IN_ELEMENT:
|
| 1657 |
+
raise LxmlSyntaxError("no content written")
|
| 1658 |
+
if self._element_stack:
|
| 1659 |
+
raise LxmlSyntaxError("pending open tags on close")
|
| 1660 |
+
error_result = self._c_out.error
|
| 1661 |
+
if error_result == xmlerror.XML_ERR_OK:
|
| 1662 |
+
error_result = tree.xmlOutputBufferClose(self._c_out)
|
| 1663 |
+
if error_result != -1:
|
| 1664 |
+
error_result = xmlerror.XML_ERR_OK
|
| 1665 |
+
else:
|
| 1666 |
+
tree.xmlOutputBufferClose(self._c_out)
|
| 1667 |
+
self._status = WRITER_FINISHED
|
| 1668 |
+
self._c_out = NULL
|
| 1669 |
+
del self._element_stack[:]
|
| 1670 |
+
if raise_on_error:
|
| 1671 |
+
self._handle_error(error_result)
|
| 1672 |
+
|
| 1673 |
+
cdef _handle_error(self, int error_result):
|
| 1674 |
+
if error_result != xmlerror.XML_ERR_OK:
|
| 1675 |
+
if self._target is not None:
|
| 1676 |
+
self._target._exc_context._raise_if_stored()
|
| 1677 |
+
_raiseSerialisationError(error_result)
|
| 1678 |
+
|
| 1679 |
+
|
| 1680 |
+
@cython.final
|
| 1681 |
+
@cython.internal
|
| 1682 |
+
cdef class _AsyncDataWriter:
|
| 1683 |
+
cdef list _data
|
| 1684 |
+
def __cinit__(self):
|
| 1685 |
+
self._data = []
|
| 1686 |
+
|
| 1687 |
+
cdef bytes collect(self):
|
| 1688 |
+
data = b''.join(self._data)
|
| 1689 |
+
del self._data[:]
|
| 1690 |
+
return data
|
| 1691 |
+
|
| 1692 |
+
def write(self, data):
|
| 1693 |
+
self._data.append(data)
|
| 1694 |
+
|
| 1695 |
+
def close(self):
|
| 1696 |
+
pass
|
| 1697 |
+
|
| 1698 |
+
|
| 1699 |
+
@cython.final
|
| 1700 |
+
@cython.internal
|
| 1701 |
+
cdef class _AsyncIncrementalFileWriter:
|
| 1702 |
+
cdef _IncrementalFileWriter _writer
|
| 1703 |
+
cdef _AsyncDataWriter _buffer
|
| 1704 |
+
cdef object _async_outfile
|
| 1705 |
+
cdef int _flush_after_writes
|
| 1706 |
+
cdef bint _should_close
|
| 1707 |
+
cdef bint _buffered
|
| 1708 |
+
|
| 1709 |
+
def __cinit__(self, async_outfile, bytes encoding, int compresslevel, bint close,
|
| 1710 |
+
bint buffered, int method):
|
| 1711 |
+
self._flush_after_writes = 20
|
| 1712 |
+
self._async_outfile = async_outfile
|
| 1713 |
+
self._should_close = close
|
| 1714 |
+
self._buffered = buffered
|
| 1715 |
+
self._buffer = _AsyncDataWriter()
|
| 1716 |
+
self._writer = _IncrementalFileWriter(
|
| 1717 |
+
self._buffer, encoding, compresslevel, close=True, buffered=False, method=method)
|
| 1718 |
+
|
| 1719 |
+
cdef bytes _flush(self):
|
| 1720 |
+
if not self._buffered or len(self._buffer._data) > self._flush_after_writes:
|
| 1721 |
+
return self._buffer.collect()
|
| 1722 |
+
return None
|
| 1723 |
+
|
| 1724 |
+
async def flush(self):
|
| 1725 |
+
self._writer.flush()
|
| 1726 |
+
data = self._buffer.collect()
|
| 1727 |
+
if data:
|
| 1728 |
+
await self._async_outfile.write(data)
|
| 1729 |
+
|
| 1730 |
+
async def write_declaration(self, version=None, standalone=None, doctype=None):
|
| 1731 |
+
self._writer.write_declaration(version, standalone, doctype)
|
| 1732 |
+
data = self._flush()
|
| 1733 |
+
if data:
|
| 1734 |
+
await self._async_outfile.write(data)
|
| 1735 |
+
|
| 1736 |
+
async def write_doctype(self, doctype):
|
| 1737 |
+
self._writer.write_doctype(doctype)
|
| 1738 |
+
data = self._flush()
|
| 1739 |
+
if data:
|
| 1740 |
+
await self._async_outfile.write(data)
|
| 1741 |
+
|
| 1742 |
+
async def write(self, *args, with_tail=True, pretty_print=False, method=None):
|
| 1743 |
+
self._writer.write(*args, with_tail=with_tail, pretty_print=pretty_print, method=method)
|
| 1744 |
+
data = self._flush()
|
| 1745 |
+
if data:
|
| 1746 |
+
await self._async_outfile.write(data)
|
| 1747 |
+
|
| 1748 |
+
def method(self, method):
|
| 1749 |
+
return self._writer.method(method)
|
| 1750 |
+
|
| 1751 |
+
def element(self, tag, attrib=None, nsmap=None, method=None, **_extra):
|
| 1752 |
+
element_writer = self._writer.element(tag, attrib, nsmap, method, **_extra)
|
| 1753 |
+
return _AsyncFileWriterElement(element_writer, self)
|
| 1754 |
+
|
| 1755 |
+
async def _close(self, bint raise_on_error):
|
| 1756 |
+
self._writer._close(raise_on_error)
|
| 1757 |
+
data = self._buffer.collect()
|
| 1758 |
+
if data:
|
| 1759 |
+
await self._async_outfile.write(data)
|
| 1760 |
+
if self._should_close:
|
| 1761 |
+
await self._async_outfile.close()
|
| 1762 |
+
|
| 1763 |
+
|
| 1764 |
+
@cython.final
|
| 1765 |
+
@cython.internal
|
| 1766 |
+
cdef class _AsyncFileWriterElement:
|
| 1767 |
+
cdef _FileWriterElement _element_writer
|
| 1768 |
+
cdef _AsyncIncrementalFileWriter _writer
|
| 1769 |
+
|
| 1770 |
+
def __cinit__(self, _FileWriterElement element_writer not None,
|
| 1771 |
+
_AsyncIncrementalFileWriter writer not None):
|
| 1772 |
+
self._element_writer = element_writer
|
| 1773 |
+
self._writer = writer
|
| 1774 |
+
|
| 1775 |
+
async def __aenter__(self):
|
| 1776 |
+
self._element_writer.__enter__()
|
| 1777 |
+
data = self._writer._flush()
|
| 1778 |
+
if data:
|
| 1779 |
+
await self._writer._async_outfile.write(data)
|
| 1780 |
+
|
| 1781 |
+
async def __aexit__(self, *args):
|
| 1782 |
+
self._element_writer.__exit__(*args)
|
| 1783 |
+
data = self._writer._flush()
|
| 1784 |
+
if data:
|
| 1785 |
+
await self._writer._async_outfile.write(data)
|
| 1786 |
+
|
| 1787 |
+
|
| 1788 |
+
@cython.final
|
| 1789 |
+
@cython.internal
|
| 1790 |
+
@cython.freelist(8)
|
| 1791 |
+
cdef class _FileWriterElement:
|
| 1792 |
+
cdef _IncrementalFileWriter _writer
|
| 1793 |
+
cdef object _element
|
| 1794 |
+
cdef int _new_method
|
| 1795 |
+
cdef int _old_method
|
| 1796 |
+
|
| 1797 |
+
def __cinit__(self, _IncrementalFileWriter writer not None, element_config, int method):
|
| 1798 |
+
self._writer = writer
|
| 1799 |
+
self._element = element_config
|
| 1800 |
+
self._new_method = method
|
| 1801 |
+
self._old_method = writer._method
|
| 1802 |
+
|
| 1803 |
+
def __enter__(self):
|
| 1804 |
+
self._writer._method = self._new_method
|
| 1805 |
+
self._writer._write_start_element(self._element)
|
| 1806 |
+
|
| 1807 |
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
| 1808 |
+
self._writer._write_end_element(self._element)
|
| 1809 |
+
self._writer._method = self._old_method
|
| 1810 |
+
|
| 1811 |
+
|
| 1812 |
+
@cython.final
|
| 1813 |
+
@cython.internal
|
| 1814 |
+
@cython.freelist(8)
|
| 1815 |
+
cdef class _MethodChanger:
|
| 1816 |
+
cdef _IncrementalFileWriter _writer
|
| 1817 |
+
cdef int _new_method
|
| 1818 |
+
cdef int _old_method
|
| 1819 |
+
cdef bint _entered
|
| 1820 |
+
cdef bint _exited
|
| 1821 |
+
|
| 1822 |
+
def __cinit__(self, _IncrementalFileWriter writer not None, int method):
|
| 1823 |
+
self._writer = writer
|
| 1824 |
+
self._new_method = method
|
| 1825 |
+
self._old_method = writer._method
|
| 1826 |
+
self._entered = False
|
| 1827 |
+
self._exited = False
|
| 1828 |
+
|
| 1829 |
+
def __enter__(self):
|
| 1830 |
+
if self._entered:
|
| 1831 |
+
raise LxmlSyntaxError("Inconsistent enter action in context manager")
|
| 1832 |
+
self._writer._method = self._new_method
|
| 1833 |
+
self._entered = True
|
| 1834 |
+
|
| 1835 |
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
| 1836 |
+
if self._exited:
|
| 1837 |
+
raise LxmlSyntaxError("Inconsistent exit action in context manager")
|
| 1838 |
+
if self._writer._method != self._new_method:
|
| 1839 |
+
raise LxmlSyntaxError("Method changed outside of context manager")
|
| 1840 |
+
self._writer._method = self._old_method
|
| 1841 |
+
self._exited = True
|
| 1842 |
+
|
| 1843 |
+
async def __aenter__(self):
|
| 1844 |
+
# for your async convenience
|
| 1845 |
+
return self.__enter__()
|
| 1846 |
+
|
| 1847 |
+
async def __aexit__(self, *args):
|
| 1848 |
+
# for your async convenience
|
| 1849 |
+
return self.__exit__(*args)
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/xinclude.pxi
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# XInclude processing
|
| 2 |
+
|
| 3 |
+
from lxml.includes cimport xinclude
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
cdef class XIncludeError(LxmlError):
|
| 7 |
+
"""Error during XInclude processing.
|
| 8 |
+
"""
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
cdef class XInclude:
|
| 12 |
+
"""XInclude(self)
|
| 13 |
+
XInclude processor.
|
| 14 |
+
|
| 15 |
+
Create an instance and call it on an Element to run XInclude
|
| 16 |
+
processing.
|
| 17 |
+
"""
|
| 18 |
+
cdef _ErrorLog _error_log
|
| 19 |
+
def __init__(self):
|
| 20 |
+
self._error_log = _ErrorLog()
|
| 21 |
+
|
| 22 |
+
@property
|
| 23 |
+
def error_log(self):
|
| 24 |
+
assert self._error_log is not None, "XInclude instance not initialised"
|
| 25 |
+
return self._error_log.copy()
|
| 26 |
+
|
| 27 |
+
def __call__(self, _Element node not None):
|
| 28 |
+
"__call__(self, node)"
|
| 29 |
+
# We cannot pass the XML_PARSE_NOXINCNODE option as this would free
|
| 30 |
+
# the XInclude nodes - there may still be Python references to them!
|
| 31 |
+
# Therefore, we allow XInclude nodes to be converted to
|
| 32 |
+
# XML_XINCLUDE_START nodes. XML_XINCLUDE_END nodes are added as
|
| 33 |
+
# siblings. Tree traversal will simply ignore them as they are not
|
| 34 |
+
# typed as elements. The included fragment is added between the two,
|
| 35 |
+
# i.e. as a sibling, which does not conflict with traversal.
|
| 36 |
+
cdef int result
|
| 37 |
+
_assertValidNode(node)
|
| 38 |
+
assert self._error_log is not None, "XInclude processor not initialised"
|
| 39 |
+
if node._doc._parser is not None:
|
| 40 |
+
parse_options = node._doc._parser._parse_options
|
| 41 |
+
context = node._doc._parser._getParserContext()
|
| 42 |
+
c_context = <void*>context
|
| 43 |
+
else:
|
| 44 |
+
parse_options = 0
|
| 45 |
+
context = None
|
| 46 |
+
c_context = NULL
|
| 47 |
+
|
| 48 |
+
self._error_log.connect()
|
| 49 |
+
if tree.LIBXML_VERSION < 20704 or not c_context:
|
| 50 |
+
__GLOBAL_PARSER_CONTEXT.pushImpliedContext(context)
|
| 51 |
+
with nogil:
|
| 52 |
+
orig_loader = _register_document_loader()
|
| 53 |
+
if c_context:
|
| 54 |
+
result = xinclude.xmlXIncludeProcessTreeFlagsData(
|
| 55 |
+
node._c_node, parse_options, c_context)
|
| 56 |
+
else:
|
| 57 |
+
result = xinclude.xmlXIncludeProcessTree(node._c_node)
|
| 58 |
+
_reset_document_loader(orig_loader)
|
| 59 |
+
if tree.LIBXML_VERSION < 20704 or not c_context:
|
| 60 |
+
__GLOBAL_PARSER_CONTEXT.popImpliedContext()
|
| 61 |
+
self._error_log.disconnect()
|
| 62 |
+
|
| 63 |
+
if result == -1:
|
| 64 |
+
raise XIncludeError(
|
| 65 |
+
self._error_log._buildExceptionMessage(
|
| 66 |
+
"XInclude processing failed"),
|
| 67 |
+
self._error_log)
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/xmlerror.pxi
ADDED
|
@@ -0,0 +1,1662 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# DEBUG and error logging
|
| 2 |
+
|
| 3 |
+
from lxml.includes cimport xmlerror
|
| 4 |
+
from lxml cimport cvarargs
|
| 5 |
+
|
| 6 |
+
DEF GLOBAL_ERROR_LOG = "_GlobalErrorLog"
|
| 7 |
+
DEF XSLT_ERROR_LOG = "_XSLTErrorLog"
|
| 8 |
+
|
| 9 |
+
# module level API functions
|
| 10 |
+
|
| 11 |
+
def clear_error_log():
|
| 12 |
+
"""clear_error_log()
|
| 13 |
+
|
| 14 |
+
Clear the global error log. Note that this log is already bound to a
|
| 15 |
+
fixed size.
|
| 16 |
+
|
| 17 |
+
Note: since lxml 2.2, the global error log is local to a thread
|
| 18 |
+
and this function will only clear the global error log of the
|
| 19 |
+
current thread.
|
| 20 |
+
"""
|
| 21 |
+
_getThreadErrorLog(GLOBAL_ERROR_LOG).clear()
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
# setup for global log:
|
| 25 |
+
|
| 26 |
+
cdef void _initThreadLogging() noexcept:
|
| 27 |
+
# Disable generic error lines from libxml2.
|
| 28 |
+
_connectGenericErrorLog(None)
|
| 29 |
+
|
| 30 |
+
# Divert XSLT error messages to the global XSLT error log instead of stderr.
|
| 31 |
+
xslt.xsltSetGenericErrorFunc(NULL, <xmlerror.xmlGenericErrorFunc>_receiveXSLTError)
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
# Logging classes
|
| 35 |
+
|
| 36 |
+
@cython.final
|
| 37 |
+
@cython.freelist(16)
|
| 38 |
+
cdef class _LogEntry:
|
| 39 |
+
"""A log message entry from an error log.
|
| 40 |
+
|
| 41 |
+
Attributes:
|
| 42 |
+
|
| 43 |
+
- message: the message text
|
| 44 |
+
- domain: the domain ID (see lxml.etree.ErrorDomains)
|
| 45 |
+
- type: the message type ID (see lxml.etree.ErrorTypes)
|
| 46 |
+
- level: the log level ID (see lxml.etree.ErrorLevels)
|
| 47 |
+
- line: the line at which the message originated (if applicable)
|
| 48 |
+
- column: the character column at which the message originated (if applicable)
|
| 49 |
+
- filename: the name of the file in which the message originated (if applicable)
|
| 50 |
+
- path: the location in which the error was found (if available)
|
| 51 |
+
"""
|
| 52 |
+
cdef readonly int domain
|
| 53 |
+
cdef readonly int type
|
| 54 |
+
cdef readonly int level
|
| 55 |
+
cdef readonly long line
|
| 56 |
+
cdef readonly int column
|
| 57 |
+
cdef basestring _message
|
| 58 |
+
cdef basestring _filename
|
| 59 |
+
cdef char* _c_message
|
| 60 |
+
cdef xmlChar* _c_filename
|
| 61 |
+
cdef xmlChar* _c_path
|
| 62 |
+
|
| 63 |
+
def __dealloc__(self):
|
| 64 |
+
tree.xmlFree(self._c_message)
|
| 65 |
+
tree.xmlFree(self._c_filename)
|
| 66 |
+
tree.xmlFree(self._c_path)
|
| 67 |
+
|
| 68 |
+
@cython.final
|
| 69 |
+
cdef int _setError(self, const xmlerror.xmlError* error) except -1:
|
| 70 |
+
self.domain = error.domain
|
| 71 |
+
self.type = error.code
|
| 72 |
+
self.level = <int>error.level
|
| 73 |
+
self.line = <long>error.line
|
| 74 |
+
self.column = error.int2
|
| 75 |
+
self._c_message = NULL
|
| 76 |
+
self._c_filename = NULL
|
| 77 |
+
self._c_path = NULL
|
| 78 |
+
if (error.message is NULL or
|
| 79 |
+
error.message[0] == b'\0' or
|
| 80 |
+
error.message[0] == b'\n' and error.message[1] == b'\0'):
|
| 81 |
+
self._message = "unknown error"
|
| 82 |
+
else:
|
| 83 |
+
self._message = None
|
| 84 |
+
self._c_message = <char*> tree.xmlStrdup(
|
| 85 |
+
<const_xmlChar*> error.message)
|
| 86 |
+
if not self._c_message:
|
| 87 |
+
raise MemoryError()
|
| 88 |
+
if error.file is NULL:
|
| 89 |
+
self._filename = '<string>'
|
| 90 |
+
else:
|
| 91 |
+
self._filename = None
|
| 92 |
+
self._c_filename = tree.xmlStrdup(<const_xmlChar*> error.file)
|
| 93 |
+
if not self._c_filename:
|
| 94 |
+
raise MemoryError()
|
| 95 |
+
if error.node is not NULL:
|
| 96 |
+
self._c_path = tree.xmlGetNodePath(<xmlNode*> error.node)
|
| 97 |
+
c_line = tree.xmlGetLineNo(<xmlNode*> error.node)
|
| 98 |
+
if c_line > limits.INT_MAX:
|
| 99 |
+
self.line = c_line
|
| 100 |
+
|
| 101 |
+
@cython.final
|
| 102 |
+
cdef _setGeneric(self, int domain, int type, int level, long line,
|
| 103 |
+
message, filename):
|
| 104 |
+
self.domain = domain
|
| 105 |
+
self.type = type
|
| 106 |
+
self.level = level
|
| 107 |
+
self.line = line
|
| 108 |
+
self.column = 0
|
| 109 |
+
self._message = message
|
| 110 |
+
self._filename = filename
|
| 111 |
+
self._c_path = NULL
|
| 112 |
+
|
| 113 |
+
def __repr__(self):
|
| 114 |
+
return "%s:%d:%d:%s:%s:%s: %s" % (
|
| 115 |
+
self.filename, self.line, self.column, self.level_name,
|
| 116 |
+
self.domain_name, self.type_name, self.message)
|
| 117 |
+
|
| 118 |
+
@property
|
| 119 |
+
def domain_name(self):
|
| 120 |
+
"""The name of the error domain. See lxml.etree.ErrorDomains
|
| 121 |
+
"""
|
| 122 |
+
return ErrorDomains._getName(self.domain, "unknown")
|
| 123 |
+
|
| 124 |
+
@property
|
| 125 |
+
def type_name(self):
|
| 126 |
+
"""The name of the error type. See lxml.etree.ErrorTypes
|
| 127 |
+
"""
|
| 128 |
+
if self.domain == ErrorDomains.RELAXNGV:
|
| 129 |
+
getName = RelaxNGErrorTypes._getName
|
| 130 |
+
else:
|
| 131 |
+
getName = ErrorTypes._getName
|
| 132 |
+
return getName(self.type, "unknown")
|
| 133 |
+
|
| 134 |
+
@property
|
| 135 |
+
def level_name(self):
|
| 136 |
+
"""The name of the error level. See lxml.etree.ErrorLevels
|
| 137 |
+
"""
|
| 138 |
+
return ErrorLevels._getName(self.level, "unknown")
|
| 139 |
+
|
| 140 |
+
@property
|
| 141 |
+
def message(self):
|
| 142 |
+
"""The log message string.
|
| 143 |
+
"""
|
| 144 |
+
cdef size_t size
|
| 145 |
+
if self._message is not None:
|
| 146 |
+
return self._message
|
| 147 |
+
if self._c_message is NULL:
|
| 148 |
+
return None
|
| 149 |
+
size = cstring_h.strlen(self._c_message)
|
| 150 |
+
if size > 0 and self._c_message[size-1] == b'\n':
|
| 151 |
+
size -= 1 # strip EOL
|
| 152 |
+
# cannot use funicode() here because the message may contain
|
| 153 |
+
# byte encoded file paths etc.
|
| 154 |
+
try:
|
| 155 |
+
self._message = self._c_message[:size].decode('utf8')
|
| 156 |
+
except UnicodeDecodeError:
|
| 157 |
+
try:
|
| 158 |
+
self._message = self._c_message[:size].decode(
|
| 159 |
+
'ascii', 'backslashreplace')
|
| 160 |
+
except UnicodeDecodeError:
|
| 161 |
+
self._message = '<undecodable error message>'
|
| 162 |
+
if self._c_message:
|
| 163 |
+
# clean up early
|
| 164 |
+
tree.xmlFree(self._c_message)
|
| 165 |
+
self._c_message = NULL
|
| 166 |
+
return self._message
|
| 167 |
+
|
| 168 |
+
@property
|
| 169 |
+
def filename(self):
|
| 170 |
+
"""The file path where the report originated, if any.
|
| 171 |
+
"""
|
| 172 |
+
if self._filename is None:
|
| 173 |
+
if self._c_filename is not NULL:
|
| 174 |
+
self._filename = _decodeFilename(self._c_filename)
|
| 175 |
+
# clean up early
|
| 176 |
+
tree.xmlFree(self._c_filename)
|
| 177 |
+
self._c_filename = NULL
|
| 178 |
+
return self._filename
|
| 179 |
+
|
| 180 |
+
@property
|
| 181 |
+
def path(self):
|
| 182 |
+
"""The XPath for the node where the error was detected.
|
| 183 |
+
"""
|
| 184 |
+
return funicode(self._c_path) if self._c_path is not NULL else None
|
| 185 |
+
|
| 186 |
+
|
| 187 |
+
cdef class _BaseErrorLog:
|
| 188 |
+
cdef _LogEntry _first_error
|
| 189 |
+
cdef readonly object last_error
|
| 190 |
+
def __init__(self, first_error, last_error):
|
| 191 |
+
self._first_error = first_error
|
| 192 |
+
self.last_error = last_error
|
| 193 |
+
|
| 194 |
+
cpdef copy(self):
|
| 195 |
+
return _BaseErrorLog(self._first_error, self.last_error)
|
| 196 |
+
|
| 197 |
+
def __repr__(self):
|
| 198 |
+
return ''
|
| 199 |
+
|
| 200 |
+
cpdef receive(self, _LogEntry entry):
|
| 201 |
+
pass
|
| 202 |
+
|
| 203 |
+
@cython.final
|
| 204 |
+
cdef int _receive(self, const xmlerror.xmlError* error) except -1:
|
| 205 |
+
cdef bint is_error
|
| 206 |
+
cdef _LogEntry entry
|
| 207 |
+
cdef _BaseErrorLog global_log
|
| 208 |
+
entry = _LogEntry.__new__(_LogEntry)
|
| 209 |
+
entry._setError(error)
|
| 210 |
+
is_error = error.level == xmlerror.XML_ERR_ERROR or \
|
| 211 |
+
error.level == xmlerror.XML_ERR_FATAL
|
| 212 |
+
global_log = _getThreadErrorLog(GLOBAL_ERROR_LOG)
|
| 213 |
+
if global_log is not self:
|
| 214 |
+
global_log.receive(entry)
|
| 215 |
+
if is_error:
|
| 216 |
+
global_log.last_error = entry
|
| 217 |
+
self.receive(entry)
|
| 218 |
+
if is_error:
|
| 219 |
+
self.last_error = entry
|
| 220 |
+
|
| 221 |
+
@cython.final
|
| 222 |
+
cdef int _receiveGeneric(self, int domain, int type, int level, long line,
|
| 223 |
+
message, filename) except -1:
|
| 224 |
+
cdef bint is_error
|
| 225 |
+
cdef _LogEntry entry
|
| 226 |
+
cdef _BaseErrorLog global_log
|
| 227 |
+
entry = _LogEntry.__new__(_LogEntry)
|
| 228 |
+
entry._setGeneric(domain, type, level, line, message, filename)
|
| 229 |
+
is_error = level == xmlerror.XML_ERR_ERROR or \
|
| 230 |
+
level == xmlerror.XML_ERR_FATAL
|
| 231 |
+
global_log = _getThreadErrorLog(GLOBAL_ERROR_LOG)
|
| 232 |
+
if global_log is not self:
|
| 233 |
+
global_log.receive(entry)
|
| 234 |
+
if is_error:
|
| 235 |
+
global_log.last_error = entry
|
| 236 |
+
self.receive(entry)
|
| 237 |
+
if is_error:
|
| 238 |
+
self.last_error = entry
|
| 239 |
+
|
| 240 |
+
@cython.final
|
| 241 |
+
cdef _buildParseException(self, exctype, default_message):
|
| 242 |
+
code = xmlerror.XML_ERR_INTERNAL_ERROR
|
| 243 |
+
if self._first_error is None:
|
| 244 |
+
return exctype(default_message, code, 0, 0)
|
| 245 |
+
message = self._first_error.message
|
| 246 |
+
if message:
|
| 247 |
+
code = self._first_error.type
|
| 248 |
+
else:
|
| 249 |
+
message = default_message
|
| 250 |
+
line = self._first_error.line
|
| 251 |
+
column = self._first_error.column
|
| 252 |
+
filename = self._first_error.filename
|
| 253 |
+
if line > 0:
|
| 254 |
+
if column > 0:
|
| 255 |
+
message = f"{message}, line {line}, column {column}"
|
| 256 |
+
else:
|
| 257 |
+
message = f"{message}, line {line}"
|
| 258 |
+
return exctype(message, code, line, column, filename)
|
| 259 |
+
|
| 260 |
+
@cython.final
|
| 261 |
+
cdef _buildExceptionMessage(self, default_message):
|
| 262 |
+
if self._first_error is None:
|
| 263 |
+
return default_message
|
| 264 |
+
if self._first_error.message:
|
| 265 |
+
message = self._first_error.message
|
| 266 |
+
elif default_message is None:
|
| 267 |
+
return None
|
| 268 |
+
else:
|
| 269 |
+
message = default_message
|
| 270 |
+
if self._first_error.line > 0:
|
| 271 |
+
if self._first_error.column > 0:
|
| 272 |
+
message = f"{message}, line {self._first_error.line}, column {self._first_error.column}"
|
| 273 |
+
else:
|
| 274 |
+
message = f"{message}, line {self._first_error.line}"
|
| 275 |
+
return message
|
| 276 |
+
|
| 277 |
+
cdef class _ListErrorLog(_BaseErrorLog):
|
| 278 |
+
"Immutable base version of a list based error log."
|
| 279 |
+
cdef list _entries
|
| 280 |
+
cdef int _offset
|
| 281 |
+
def __init__(self, entries, first_error, last_error):
|
| 282 |
+
if entries:
|
| 283 |
+
if first_error is None:
|
| 284 |
+
first_error = entries[0]
|
| 285 |
+
if last_error is None:
|
| 286 |
+
last_error = entries[-1]
|
| 287 |
+
_BaseErrorLog.__init__(self, first_error, last_error)
|
| 288 |
+
self._entries = entries
|
| 289 |
+
|
| 290 |
+
cpdef copy(self):
|
| 291 |
+
"""Creates a shallow copy of this error log. Reuses the list of
|
| 292 |
+
entries.
|
| 293 |
+
"""
|
| 294 |
+
cdef _ListErrorLog log = _ListErrorLog(
|
| 295 |
+
self._entries, self._first_error, self.last_error)
|
| 296 |
+
log._offset = self._offset
|
| 297 |
+
return log
|
| 298 |
+
|
| 299 |
+
def __iter__(self):
|
| 300 |
+
entries = self._entries
|
| 301 |
+
if self._offset:
|
| 302 |
+
entries = islice(entries, self._offset)
|
| 303 |
+
return iter(entries)
|
| 304 |
+
|
| 305 |
+
def __repr__(self):
|
| 306 |
+
return '\n'.join([repr(entry) for entry in self])
|
| 307 |
+
|
| 308 |
+
def __getitem__(self, index):
|
| 309 |
+
if self._offset:
|
| 310 |
+
index += self._offset
|
| 311 |
+
return self._entries[index]
|
| 312 |
+
|
| 313 |
+
def __len__(self):
|
| 314 |
+
return len(self._entries) - self._offset
|
| 315 |
+
|
| 316 |
+
def __contains__(self, error_type):
|
| 317 |
+
cdef Py_ssize_t i
|
| 318 |
+
for i, entry in enumerate(self._entries):
|
| 319 |
+
if i < self._offset:
|
| 320 |
+
continue
|
| 321 |
+
if entry.type == error_type:
|
| 322 |
+
return True
|
| 323 |
+
return False
|
| 324 |
+
|
| 325 |
+
def __bool__(self):
|
| 326 |
+
return len(self._entries) > self._offset
|
| 327 |
+
|
| 328 |
+
def filter_domains(self, domains):
|
| 329 |
+
"""Filter the errors by the given domains and return a new error log
|
| 330 |
+
containing the matches.
|
| 331 |
+
"""
|
| 332 |
+
cdef _LogEntry entry
|
| 333 |
+
if isinstance(domains, int):
|
| 334 |
+
domains = (domains,)
|
| 335 |
+
filtered = [entry for entry in self if entry.domain in domains]
|
| 336 |
+
return _ListErrorLog(filtered, None, None)
|
| 337 |
+
|
| 338 |
+
def filter_types(self, types):
|
| 339 |
+
"""filter_types(self, types)
|
| 340 |
+
|
| 341 |
+
Filter the errors by the given types and return a new error
|
| 342 |
+
log containing the matches.
|
| 343 |
+
"""
|
| 344 |
+
cdef _LogEntry entry
|
| 345 |
+
if isinstance(types, int):
|
| 346 |
+
types = (types,)
|
| 347 |
+
filtered = [entry for entry in self if entry.type in types]
|
| 348 |
+
return _ListErrorLog(filtered, None, None)
|
| 349 |
+
|
| 350 |
+
def filter_levels(self, levels):
|
| 351 |
+
"""filter_levels(self, levels)
|
| 352 |
+
|
| 353 |
+
Filter the errors by the given error levels and return a new
|
| 354 |
+
error log containing the matches.
|
| 355 |
+
"""
|
| 356 |
+
cdef _LogEntry entry
|
| 357 |
+
if isinstance(levels, int):
|
| 358 |
+
levels = (levels,)
|
| 359 |
+
filtered = [entry for entry in self if entry.level in levels]
|
| 360 |
+
return _ListErrorLog(filtered, None, None)
|
| 361 |
+
|
| 362 |
+
def filter_from_level(self, level):
|
| 363 |
+
"""filter_from_level(self, level)
|
| 364 |
+
|
| 365 |
+
Return a log with all messages of the requested level of worse.
|
| 366 |
+
"""
|
| 367 |
+
cdef _LogEntry entry
|
| 368 |
+
filtered = [entry for entry in self if entry.level >= level]
|
| 369 |
+
return _ListErrorLog(filtered, None, None)
|
| 370 |
+
|
| 371 |
+
def filter_from_fatals(self):
|
| 372 |
+
"""filter_from_fatals(self)
|
| 373 |
+
|
| 374 |
+
Convenience method to get all fatal error messages.
|
| 375 |
+
"""
|
| 376 |
+
return self.filter_from_level(ErrorLevels.FATAL)
|
| 377 |
+
|
| 378 |
+
def filter_from_errors(self):
|
| 379 |
+
"""filter_from_errors(self)
|
| 380 |
+
|
| 381 |
+
Convenience method to get all error messages or worse.
|
| 382 |
+
"""
|
| 383 |
+
return self.filter_from_level(ErrorLevels.ERROR)
|
| 384 |
+
|
| 385 |
+
def filter_from_warnings(self):
|
| 386 |
+
"""filter_from_warnings(self)
|
| 387 |
+
|
| 388 |
+
Convenience method to get all warnings or worse.
|
| 389 |
+
"""
|
| 390 |
+
return self.filter_from_level(ErrorLevels.WARNING)
|
| 391 |
+
|
| 392 |
+
|
| 393 |
+
@cython.final
|
| 394 |
+
@cython.internal
|
| 395 |
+
cdef class _ErrorLogContext:
|
| 396 |
+
"""
|
| 397 |
+
Error log context for the 'with' statement.
|
| 398 |
+
Stores a reference to the current callbacks to allow for
|
| 399 |
+
recursively stacked log contexts.
|
| 400 |
+
"""
|
| 401 |
+
cdef xmlerror.xmlStructuredErrorFunc old_error_func
|
| 402 |
+
cdef void* old_error_context
|
| 403 |
+
cdef xmlerror.xmlGenericErrorFunc old_xslt_error_func
|
| 404 |
+
cdef void* old_xslt_error_context
|
| 405 |
+
cdef _BaseErrorLog old_xslt_error_log
|
| 406 |
+
|
| 407 |
+
cdef int push_error_log(self, _BaseErrorLog log) except -1:
|
| 408 |
+
self.old_error_func = xmlerror.xmlStructuredError
|
| 409 |
+
self.old_error_context = xmlerror.xmlStructuredErrorContext
|
| 410 |
+
xmlerror.xmlSetStructuredErrorFunc(
|
| 411 |
+
<void*>log, <xmlerror.xmlStructuredErrorFunc>_receiveError)
|
| 412 |
+
|
| 413 |
+
# xslt.xsltSetGenericErrorFunc() is not thread-local => keep error log in TLS
|
| 414 |
+
self.old_xslt_error_func = xslt.xsltGenericError
|
| 415 |
+
self.old_xslt_error_context = xslt.xsltGenericErrorContext
|
| 416 |
+
self.old_xslt_error_log = _getThreadErrorLog(XSLT_ERROR_LOG)
|
| 417 |
+
_setThreadErrorLog(XSLT_ERROR_LOG, log)
|
| 418 |
+
xslt.xsltSetGenericErrorFunc(
|
| 419 |
+
NULL, <xmlerror.xmlGenericErrorFunc>_receiveXSLTError)
|
| 420 |
+
return 0
|
| 421 |
+
|
| 422 |
+
cdef int pop_error_log(self) except -1:
|
| 423 |
+
xmlerror.xmlSetStructuredErrorFunc(
|
| 424 |
+
self.old_error_context, self.old_error_func)
|
| 425 |
+
xslt.xsltSetGenericErrorFunc(
|
| 426 |
+
self.old_xslt_error_context, self.old_xslt_error_func)
|
| 427 |
+
_setThreadErrorLog(XSLT_ERROR_LOG, self.old_xslt_error_log)
|
| 428 |
+
self.old_xslt_error_log= None
|
| 429 |
+
return 0
|
| 430 |
+
|
| 431 |
+
|
| 432 |
+
cdef class _ErrorLog(_ListErrorLog):
|
| 433 |
+
cdef list _logContexts
|
| 434 |
+
def __cinit__(self):
|
| 435 |
+
self._logContexts = []
|
| 436 |
+
|
| 437 |
+
def __init__(self):
|
| 438 |
+
_ListErrorLog.__init__(self, [], None, None)
|
| 439 |
+
|
| 440 |
+
@cython.final
|
| 441 |
+
cdef int __enter__(self) except -1:
|
| 442 |
+
return self.connect()
|
| 443 |
+
|
| 444 |
+
def __exit__(self, *args):
|
| 445 |
+
# TODO: make this a cdef function when Cython supports it
|
| 446 |
+
self.disconnect()
|
| 447 |
+
|
| 448 |
+
@cython.final
|
| 449 |
+
cdef int connect(self) except -1:
|
| 450 |
+
self._first_error = None
|
| 451 |
+
del self._entries[:]
|
| 452 |
+
|
| 453 |
+
cdef _ErrorLogContext context = _ErrorLogContext.__new__(_ErrorLogContext)
|
| 454 |
+
context.push_error_log(self)
|
| 455 |
+
self._logContexts.append(context)
|
| 456 |
+
return 0
|
| 457 |
+
|
| 458 |
+
@cython.final
|
| 459 |
+
cdef int disconnect(self) except -1:
|
| 460 |
+
cdef _ErrorLogContext context = self._logContexts.pop()
|
| 461 |
+
context.pop_error_log()
|
| 462 |
+
return 0
|
| 463 |
+
|
| 464 |
+
cpdef clear(self):
|
| 465 |
+
self._first_error = None
|
| 466 |
+
self.last_error = None
|
| 467 |
+
self._offset = 0
|
| 468 |
+
del self._entries[:]
|
| 469 |
+
|
| 470 |
+
cpdef copy(self):
|
| 471 |
+
"""Creates a shallow copy of this error log and the list of entries.
|
| 472 |
+
"""
|
| 473 |
+
return _ListErrorLog(
|
| 474 |
+
self._entries[self._offset:],
|
| 475 |
+
self._first_error, self.last_error)
|
| 476 |
+
|
| 477 |
+
def __iter__(self):
|
| 478 |
+
return iter(self._entries[self._offset:])
|
| 479 |
+
|
| 480 |
+
cpdef receive(self, _LogEntry entry):
|
| 481 |
+
if self._first_error is None and entry.level >= xmlerror.XML_ERR_ERROR:
|
| 482 |
+
self._first_error = entry
|
| 483 |
+
self._entries.append(entry)
|
| 484 |
+
|
| 485 |
+
cdef class _DomainErrorLog(_ErrorLog):
|
| 486 |
+
def __init__(self, domains):
|
| 487 |
+
_ErrorLog.__init__(self)
|
| 488 |
+
self._accepted_domains = tuple(domains)
|
| 489 |
+
|
| 490 |
+
cpdef receive(self, _LogEntry entry):
|
| 491 |
+
if entry.domain in self._accepted_domains:
|
| 492 |
+
_ErrorLog.receive(self, entry)
|
| 493 |
+
|
| 494 |
+
cdef class _RotatingErrorLog(_ErrorLog):
|
| 495 |
+
cdef int _max_len
|
| 496 |
+
def __init__(self, max_len):
|
| 497 |
+
_ErrorLog.__init__(self)
|
| 498 |
+
self._max_len = max_len
|
| 499 |
+
|
| 500 |
+
cpdef receive(self, _LogEntry entry):
|
| 501 |
+
if self._first_error is None and entry.level >= xmlerror.XML_ERR_ERROR:
|
| 502 |
+
self._first_error = entry
|
| 503 |
+
self._entries.append(entry)
|
| 504 |
+
|
| 505 |
+
if len(self._entries) > self._max_len:
|
| 506 |
+
self._offset += 1
|
| 507 |
+
if self._offset > self._max_len // 3:
|
| 508 |
+
offset = self._offset
|
| 509 |
+
self._offset = 0
|
| 510 |
+
del self._entries[:offset]
|
| 511 |
+
|
| 512 |
+
cdef class PyErrorLog(_BaseErrorLog):
|
| 513 |
+
"""PyErrorLog(self, logger_name=None, logger=None)
|
| 514 |
+
A global error log that connects to the Python stdlib logging package.
|
| 515 |
+
|
| 516 |
+
The constructor accepts an optional logger name or a readily
|
| 517 |
+
instantiated logger instance.
|
| 518 |
+
|
| 519 |
+
If you want to change the mapping between libxml2's ErrorLevels and Python
|
| 520 |
+
logging levels, you can modify the level_map dictionary from a subclass.
|
| 521 |
+
|
| 522 |
+
The default mapping is::
|
| 523 |
+
|
| 524 |
+
ErrorLevels.WARNING = logging.WARNING
|
| 525 |
+
ErrorLevels.ERROR = logging.ERROR
|
| 526 |
+
ErrorLevels.FATAL = logging.CRITICAL
|
| 527 |
+
|
| 528 |
+
You can also override the method ``receive()`` that takes a LogEntry
|
| 529 |
+
object and calls ``self.log(log_entry, format_string, arg1, arg2, ...)``
|
| 530 |
+
with appropriate data.
|
| 531 |
+
"""
|
| 532 |
+
cdef readonly dict level_map
|
| 533 |
+
cdef object _map_level
|
| 534 |
+
cdef object _log
|
| 535 |
+
def __init__(self, logger_name=None, logger=None):
|
| 536 |
+
_BaseErrorLog.__init__(self, None, None)
|
| 537 |
+
import logging
|
| 538 |
+
self.level_map = {
|
| 539 |
+
ErrorLevels.WARNING : logging.WARNING,
|
| 540 |
+
ErrorLevels.ERROR : logging.ERROR,
|
| 541 |
+
ErrorLevels.FATAL : logging.CRITICAL
|
| 542 |
+
}
|
| 543 |
+
self._map_level = self.level_map.get
|
| 544 |
+
if logger is None:
|
| 545 |
+
if logger_name:
|
| 546 |
+
logger = logging.getLogger(logger_name)
|
| 547 |
+
else:
|
| 548 |
+
logger = logging.getLogger()
|
| 549 |
+
self._log = logger.log
|
| 550 |
+
|
| 551 |
+
cpdef copy(self):
|
| 552 |
+
"""Dummy method that returns an empty error log.
|
| 553 |
+
"""
|
| 554 |
+
return _ListErrorLog([], None, None)
|
| 555 |
+
|
| 556 |
+
def log(self, log_entry, message, *args):
|
| 557 |
+
"""log(self, log_entry, message, *args)
|
| 558 |
+
|
| 559 |
+
Called by the .receive() method to log a _LogEntry instance to
|
| 560 |
+
the Python logging system. This handles the error level
|
| 561 |
+
mapping.
|
| 562 |
+
|
| 563 |
+
In the default implementation, the ``message`` argument
|
| 564 |
+
receives a complete log line, and there are no further
|
| 565 |
+
``args``. To change the message format, it is best to
|
| 566 |
+
override the .receive() method instead of this one.
|
| 567 |
+
"""
|
| 568 |
+
self._log(
|
| 569 |
+
self._map_level(log_entry.level, 0),
|
| 570 |
+
message, *args
|
| 571 |
+
)
|
| 572 |
+
|
| 573 |
+
cpdef receive(self, _LogEntry log_entry):
|
| 574 |
+
"""receive(self, log_entry)
|
| 575 |
+
|
| 576 |
+
Receive a _LogEntry instance from the logging system. Calls
|
| 577 |
+
the .log() method with appropriate parameters::
|
| 578 |
+
|
| 579 |
+
self.log(log_entry, repr(log_entry))
|
| 580 |
+
|
| 581 |
+
You can override this method to provide your own log output
|
| 582 |
+
format.
|
| 583 |
+
"""
|
| 584 |
+
self.log(log_entry, repr(log_entry))
|
| 585 |
+
|
| 586 |
+
# thread-local, global list log to collect error output messages from
|
| 587 |
+
# libxml2/libxslt
|
| 588 |
+
|
| 589 |
+
cdef _BaseErrorLog __GLOBAL_ERROR_LOG = _RotatingErrorLog(__MAX_LOG_SIZE)
|
| 590 |
+
|
| 591 |
+
|
| 592 |
+
cdef _BaseErrorLog _getThreadErrorLog(name):
|
| 593 |
+
"""Retrieve the current error log with name 'name' of this thread."""
|
| 594 |
+
cdef python.PyObject* thread_dict
|
| 595 |
+
thread_dict = python.PyThreadState_GetDict()
|
| 596 |
+
if thread_dict is NULL:
|
| 597 |
+
return __GLOBAL_ERROR_LOG
|
| 598 |
+
try:
|
| 599 |
+
return (<object>thread_dict)[name]
|
| 600 |
+
except KeyError:
|
| 601 |
+
log = (<object>thread_dict)[name] = \
|
| 602 |
+
_RotatingErrorLog(__MAX_LOG_SIZE)
|
| 603 |
+
return log
|
| 604 |
+
|
| 605 |
+
|
| 606 |
+
cdef _setThreadErrorLog(name, _BaseErrorLog log):
|
| 607 |
+
"""Set the global error log of this thread."""
|
| 608 |
+
cdef python.PyObject* thread_dict
|
| 609 |
+
thread_dict = python.PyThreadState_GetDict()
|
| 610 |
+
if thread_dict is NULL:
|
| 611 |
+
if name == GLOBAL_ERROR_LOG:
|
| 612 |
+
global __GLOBAL_ERROR_LOG
|
| 613 |
+
__GLOBAL_ERROR_LOG = log
|
| 614 |
+
else:
|
| 615 |
+
(<object>thread_dict)[name] = log
|
| 616 |
+
|
| 617 |
+
|
| 618 |
+
cdef __copyGlobalErrorLog():
|
| 619 |
+
"Helper function for properties in exceptions."
|
| 620 |
+
return _getThreadErrorLog(GLOBAL_ERROR_LOG).copy()
|
| 621 |
+
|
| 622 |
+
|
| 623 |
+
def use_global_python_log(PyErrorLog log not None):
|
| 624 |
+
"""use_global_python_log(log)
|
| 625 |
+
|
| 626 |
+
Replace the global error log by an etree.PyErrorLog that uses the
|
| 627 |
+
standard Python logging package.
|
| 628 |
+
|
| 629 |
+
Note that this disables access to the global error log from exceptions.
|
| 630 |
+
Parsers, XSLT etc. will continue to provide their normal local error log.
|
| 631 |
+
|
| 632 |
+
Note: prior to lxml 2.2, this changed the error log globally.
|
| 633 |
+
Since lxml 2.2, the global error log is local to a thread and this
|
| 634 |
+
function will only set the global error log of the current thread.
|
| 635 |
+
"""
|
| 636 |
+
_setThreadErrorLog(GLOBAL_ERROR_LOG, log)
|
| 637 |
+
|
| 638 |
+
|
| 639 |
+
# local log functions: forward error to logger object
|
| 640 |
+
cdef void _forwardError(void* c_log_handler, const xmlerror.xmlError* error) noexcept with gil:
|
| 641 |
+
cdef _BaseErrorLog log_handler
|
| 642 |
+
if c_log_handler is not NULL:
|
| 643 |
+
log_handler = <_BaseErrorLog>c_log_handler
|
| 644 |
+
elif error.domain == xmlerror.XML_FROM_XSLT:
|
| 645 |
+
log_handler = _getThreadErrorLog(XSLT_ERROR_LOG)
|
| 646 |
+
else:
|
| 647 |
+
log_handler = _getThreadErrorLog(GLOBAL_ERROR_LOG)
|
| 648 |
+
log_handler._receive(error)
|
| 649 |
+
|
| 650 |
+
|
| 651 |
+
cdef void _receiveError(void* c_log_handler, const xmlerror.xmlError* error) noexcept nogil:
|
| 652 |
+
# no Python objects here, may be called without thread context !
|
| 653 |
+
if __DEBUG:
|
| 654 |
+
_forwardError(c_log_handler, error)
|
| 655 |
+
|
| 656 |
+
|
| 657 |
+
cdef void _receiveXSLTError(void* c_log_handler, char* msg, ...) noexcept nogil:
|
| 658 |
+
# no Python objects here, may be called without thread context !
|
| 659 |
+
cdef cvarargs.va_list args
|
| 660 |
+
cvarargs.va_start(args, msg)
|
| 661 |
+
_receiveGenericError(c_log_handler, xmlerror.XML_FROM_XSLT, msg, args)
|
| 662 |
+
cvarargs.va_end(args)
|
| 663 |
+
|
| 664 |
+
cdef void _receiveRelaxNGParseError(void* c_log_handler, char* msg, ...) noexcept nogil:
|
| 665 |
+
# no Python objects here, may be called without thread context !
|
| 666 |
+
cdef cvarargs.va_list args
|
| 667 |
+
cvarargs.va_start(args, msg)
|
| 668 |
+
_receiveGenericError(c_log_handler, xmlerror.XML_FROM_RELAXNGP, msg, args)
|
| 669 |
+
cvarargs.va_end(args)
|
| 670 |
+
|
| 671 |
+
cdef void _receiveRelaxNGValidationError(void* c_log_handler, char* msg, ...) noexcept nogil:
|
| 672 |
+
# no Python objects here, may be called without thread context !
|
| 673 |
+
cdef cvarargs.va_list args
|
| 674 |
+
cvarargs.va_start(args, msg)
|
| 675 |
+
_receiveGenericError(c_log_handler, xmlerror.XML_FROM_RELAXNGV, msg, args)
|
| 676 |
+
cvarargs.va_end(args)
|
| 677 |
+
|
| 678 |
+
# dummy function: no log output at all
|
| 679 |
+
cdef void _nullGenericErrorFunc(void* ctxt, char* msg, ...) noexcept nogil:
|
| 680 |
+
pass
|
| 681 |
+
|
| 682 |
+
|
| 683 |
+
cdef void _connectGenericErrorLog(log, int c_domain=-1) noexcept:
|
| 684 |
+
cdef xmlerror.xmlGenericErrorFunc error_func = NULL
|
| 685 |
+
c_log = <void*>log
|
| 686 |
+
if c_domain == xmlerror.XML_FROM_XSLT:
|
| 687 |
+
error_func = <xmlerror.xmlGenericErrorFunc>_receiveXSLTError
|
| 688 |
+
elif c_domain == xmlerror.XML_FROM_RELAXNGP:
|
| 689 |
+
error_func = <xmlerror.xmlGenericErrorFunc>_receiveRelaxNGParseError
|
| 690 |
+
elif c_domain == xmlerror.XML_FROM_RELAXNGV:
|
| 691 |
+
error_func = <xmlerror.xmlGenericErrorFunc>_receiveRelaxNGValidationError
|
| 692 |
+
|
| 693 |
+
if log is None or error_func is NULL:
|
| 694 |
+
c_log = NULL
|
| 695 |
+
error_func = <xmlerror.xmlGenericErrorFunc>_nullGenericErrorFunc
|
| 696 |
+
xmlerror.xmlSetGenericErrorFunc(c_log, error_func)
|
| 697 |
+
|
| 698 |
+
|
| 699 |
+
cdef void _receiveGenericError(void* c_log_handler, int c_domain,
|
| 700 |
+
char* msg, cvarargs.va_list args) noexcept nogil:
|
| 701 |
+
# no Python objects here, may be called without thread context !
|
| 702 |
+
cdef xmlerror.xmlError c_error
|
| 703 |
+
cdef char* c_text
|
| 704 |
+
cdef char* c_message
|
| 705 |
+
cdef char* c_element
|
| 706 |
+
cdef char* c_pos
|
| 707 |
+
cdef char* c_name_pos
|
| 708 |
+
cdef char* c_str
|
| 709 |
+
cdef int text_size, element_size, format_count, c_int
|
| 710 |
+
if not __DEBUG or msg is NULL:
|
| 711 |
+
return
|
| 712 |
+
if msg[0] in b'\n\0':
|
| 713 |
+
return
|
| 714 |
+
|
| 715 |
+
c_text = c_element = c_error.file = c_error.node = NULL
|
| 716 |
+
c_error.line = 0
|
| 717 |
+
|
| 718 |
+
# parse "NAME %s" chunks from the format string
|
| 719 |
+
c_name_pos = c_pos = msg
|
| 720 |
+
format_count = 0
|
| 721 |
+
while c_pos[0]:
|
| 722 |
+
if c_pos[0] == b'%':
|
| 723 |
+
c_pos += 1
|
| 724 |
+
if c_pos[0] == b's': # "%s"
|
| 725 |
+
format_count += 1
|
| 726 |
+
c_str = cvarargs.va_charptr(args)
|
| 727 |
+
if c_pos == msg + 1:
|
| 728 |
+
c_text = c_str # msg == "%s..."
|
| 729 |
+
elif c_name_pos[0] == b'e':
|
| 730 |
+
if cstring_h.strncmp(c_name_pos, 'element %s', 10) == 0:
|
| 731 |
+
c_element = c_str
|
| 732 |
+
elif c_name_pos[0] == b'f':
|
| 733 |
+
if cstring_h.strncmp(c_name_pos, 'file %s', 7) == 0:
|
| 734 |
+
if cstring_h.strncmp('string://__STRING__XSLT',
|
| 735 |
+
c_str, 23) == 0:
|
| 736 |
+
c_str = '<xslt>'
|
| 737 |
+
c_error.file = c_str
|
| 738 |
+
elif c_pos[0] == b'd': # "%d"
|
| 739 |
+
format_count += 1
|
| 740 |
+
c_int = cvarargs.va_int(args)
|
| 741 |
+
if cstring_h.strncmp(c_name_pos, 'line %d', 7) == 0:
|
| 742 |
+
c_error.line = c_int
|
| 743 |
+
elif c_pos[0] != b'%': # "%%" == "%"
|
| 744 |
+
format_count += 1
|
| 745 |
+
break # unexpected format or end of string => abort
|
| 746 |
+
elif c_pos[0] == b' ':
|
| 747 |
+
if c_pos[1] != b'%':
|
| 748 |
+
c_name_pos = c_pos + 1
|
| 749 |
+
c_pos += 1
|
| 750 |
+
|
| 751 |
+
c_message = NULL
|
| 752 |
+
if c_text is NULL:
|
| 753 |
+
if c_element is not NULL and format_count == 1:
|
| 754 |
+
# special case: a single occurrence of 'element %s'
|
| 755 |
+
text_size = cstring_h.strlen(msg)
|
| 756 |
+
element_size = cstring_h.strlen(c_element)
|
| 757 |
+
c_message = <char*>stdlib.malloc(
|
| 758 |
+
(text_size + element_size + 1) * sizeof(char))
|
| 759 |
+
stdio.sprintf(c_message, msg, c_element)
|
| 760 |
+
c_error.message = c_message
|
| 761 |
+
else:
|
| 762 |
+
c_error.message = ''
|
| 763 |
+
elif c_element is NULL:
|
| 764 |
+
c_error.message = c_text
|
| 765 |
+
else:
|
| 766 |
+
text_size = cstring_h.strlen(c_text)
|
| 767 |
+
element_size = cstring_h.strlen(c_element)
|
| 768 |
+
c_message = <char*>stdlib.malloc(
|
| 769 |
+
(text_size + 12 + element_size + 1) * sizeof(char))
|
| 770 |
+
if c_message is NULL:
|
| 771 |
+
c_error.message = c_text
|
| 772 |
+
else:
|
| 773 |
+
stdio.sprintf(c_message, "%s, element '%s'", c_text, c_element)
|
| 774 |
+
c_error.message = c_message
|
| 775 |
+
|
| 776 |
+
c_error.domain = c_domain
|
| 777 |
+
c_error.code = xmlerror.XML_ERR_OK # what else?
|
| 778 |
+
c_error.level = xmlerror.XML_ERR_ERROR # what else?
|
| 779 |
+
c_error.int2 = 0
|
| 780 |
+
|
| 781 |
+
_forwardError(c_log_handler, &c_error)
|
| 782 |
+
|
| 783 |
+
if c_message is not NULL:
|
| 784 |
+
stdlib.free(c_message)
|
| 785 |
+
|
| 786 |
+
################################################################################
|
| 787 |
+
## CONSTANTS FROM "xmlerror.h" (or rather libxml-xmlerror.html)
|
| 788 |
+
################################################################################
|
| 789 |
+
|
| 790 |
+
cdef __initErrorConstants():
|
| 791 |
+
"Called at setup time to parse the constants and build the classes below."
|
| 792 |
+
global __ERROR_LEVELS, __ERROR_DOMAINS, __PARSER_ERROR_TYPES, __RELAXNG_ERROR_TYPES
|
| 793 |
+
const_defs = ((ErrorLevels, __ERROR_LEVELS),
|
| 794 |
+
(ErrorDomains, __ERROR_DOMAINS),
|
| 795 |
+
(ErrorTypes, __PARSER_ERROR_TYPES),
|
| 796 |
+
(RelaxNGErrorTypes, __RELAXNG_ERROR_TYPES))
|
| 797 |
+
|
| 798 |
+
for cls, constants in const_defs:
|
| 799 |
+
reverse_dict = {}
|
| 800 |
+
cls._names = reverse_dict
|
| 801 |
+
cls._getName = reverse_dict.get
|
| 802 |
+
for line in constants.splitlines():
|
| 803 |
+
if not line:
|
| 804 |
+
continue
|
| 805 |
+
name, value = line.split('=')
|
| 806 |
+
value = int(value)
|
| 807 |
+
setattr(cls, name, value)
|
| 808 |
+
reverse_dict[value] = name
|
| 809 |
+
|
| 810 |
+
# discard the global string references after use
|
| 811 |
+
__ERROR_LEVELS = __ERROR_DOMAINS = __PARSER_ERROR_TYPES = __RELAXNG_ERROR_TYPES = None
|
| 812 |
+
|
| 813 |
+
|
| 814 |
+
class ErrorLevels(object):
|
| 815 |
+
"""Libxml2 error levels"""
|
| 816 |
+
|
| 817 |
+
class ErrorDomains(object):
|
| 818 |
+
"""Libxml2 error domains"""
|
| 819 |
+
|
| 820 |
+
class ErrorTypes(object):
|
| 821 |
+
"""Libxml2 error types"""
|
| 822 |
+
|
| 823 |
+
class RelaxNGErrorTypes(object):
|
| 824 |
+
"""Libxml2 RelaxNG error types"""
|
| 825 |
+
|
| 826 |
+
|
| 827 |
+
# --- BEGIN: GENERATED CONSTANTS ---
|
| 828 |
+
|
| 829 |
+
# This section is generated by the script 'update-error-constants.py'.
|
| 830 |
+
|
| 831 |
+
cdef object __ERROR_LEVELS = """\
|
| 832 |
+
NONE=0
|
| 833 |
+
WARNING=1
|
| 834 |
+
ERROR=2
|
| 835 |
+
FATAL=3
|
| 836 |
+
"""
|
| 837 |
+
|
| 838 |
+
cdef object __ERROR_DOMAINS = """\
|
| 839 |
+
NONE=0
|
| 840 |
+
PARSER=1
|
| 841 |
+
TREE=2
|
| 842 |
+
NAMESPACE=3
|
| 843 |
+
DTD=4
|
| 844 |
+
HTML=5
|
| 845 |
+
MEMORY=6
|
| 846 |
+
OUTPUT=7
|
| 847 |
+
IO=8
|
| 848 |
+
FTP=9
|
| 849 |
+
HTTP=10
|
| 850 |
+
XINCLUDE=11
|
| 851 |
+
XPATH=12
|
| 852 |
+
XPOINTER=13
|
| 853 |
+
REGEXP=14
|
| 854 |
+
DATATYPE=15
|
| 855 |
+
SCHEMASP=16
|
| 856 |
+
SCHEMASV=17
|
| 857 |
+
RELAXNGP=18
|
| 858 |
+
RELAXNGV=19
|
| 859 |
+
CATALOG=20
|
| 860 |
+
C14N=21
|
| 861 |
+
XSLT=22
|
| 862 |
+
VALID=23
|
| 863 |
+
CHECK=24
|
| 864 |
+
WRITER=25
|
| 865 |
+
MODULE=26
|
| 866 |
+
I18N=27
|
| 867 |
+
SCHEMATRONV=28
|
| 868 |
+
BUFFER=29
|
| 869 |
+
URI=30
|
| 870 |
+
"""
|
| 871 |
+
|
| 872 |
+
cdef object __PARSER_ERROR_TYPES = """\
|
| 873 |
+
ERR_OK=0
|
| 874 |
+
ERR_INTERNAL_ERROR=1
|
| 875 |
+
ERR_NO_MEMORY=2
|
| 876 |
+
ERR_DOCUMENT_START=3
|
| 877 |
+
ERR_DOCUMENT_EMPTY=4
|
| 878 |
+
ERR_DOCUMENT_END=5
|
| 879 |
+
ERR_INVALID_HEX_CHARREF=6
|
| 880 |
+
ERR_INVALID_DEC_CHARREF=7
|
| 881 |
+
ERR_INVALID_CHARREF=8
|
| 882 |
+
ERR_INVALID_CHAR=9
|
| 883 |
+
ERR_CHARREF_AT_EOF=10
|
| 884 |
+
ERR_CHARREF_IN_PROLOG=11
|
| 885 |
+
ERR_CHARREF_IN_EPILOG=12
|
| 886 |
+
ERR_CHARREF_IN_DTD=13
|
| 887 |
+
ERR_ENTITYREF_AT_EOF=14
|
| 888 |
+
ERR_ENTITYREF_IN_PROLOG=15
|
| 889 |
+
ERR_ENTITYREF_IN_EPILOG=16
|
| 890 |
+
ERR_ENTITYREF_IN_DTD=17
|
| 891 |
+
ERR_PEREF_AT_EOF=18
|
| 892 |
+
ERR_PEREF_IN_PROLOG=19
|
| 893 |
+
ERR_PEREF_IN_EPILOG=20
|
| 894 |
+
ERR_PEREF_IN_INT_SUBSET=21
|
| 895 |
+
ERR_ENTITYREF_NO_NAME=22
|
| 896 |
+
ERR_ENTITYREF_SEMICOL_MISSING=23
|
| 897 |
+
ERR_PEREF_NO_NAME=24
|
| 898 |
+
ERR_PEREF_SEMICOL_MISSING=25
|
| 899 |
+
ERR_UNDECLARED_ENTITY=26
|
| 900 |
+
WAR_UNDECLARED_ENTITY=27
|
| 901 |
+
ERR_UNPARSED_ENTITY=28
|
| 902 |
+
ERR_ENTITY_IS_EXTERNAL=29
|
| 903 |
+
ERR_ENTITY_IS_PARAMETER=30
|
| 904 |
+
ERR_UNKNOWN_ENCODING=31
|
| 905 |
+
ERR_UNSUPPORTED_ENCODING=32
|
| 906 |
+
ERR_STRING_NOT_STARTED=33
|
| 907 |
+
ERR_STRING_NOT_CLOSED=34
|
| 908 |
+
ERR_NS_DECL_ERROR=35
|
| 909 |
+
ERR_ENTITY_NOT_STARTED=36
|
| 910 |
+
ERR_ENTITY_NOT_FINISHED=37
|
| 911 |
+
ERR_LT_IN_ATTRIBUTE=38
|
| 912 |
+
ERR_ATTRIBUTE_NOT_STARTED=39
|
| 913 |
+
ERR_ATTRIBUTE_NOT_FINISHED=40
|
| 914 |
+
ERR_ATTRIBUTE_WITHOUT_VALUE=41
|
| 915 |
+
ERR_ATTRIBUTE_REDEFINED=42
|
| 916 |
+
ERR_LITERAL_NOT_STARTED=43
|
| 917 |
+
ERR_LITERAL_NOT_FINISHED=44
|
| 918 |
+
ERR_COMMENT_NOT_FINISHED=45
|
| 919 |
+
ERR_PI_NOT_STARTED=46
|
| 920 |
+
ERR_PI_NOT_FINISHED=47
|
| 921 |
+
ERR_NOTATION_NOT_STARTED=48
|
| 922 |
+
ERR_NOTATION_NOT_FINISHED=49
|
| 923 |
+
ERR_ATTLIST_NOT_STARTED=50
|
| 924 |
+
ERR_ATTLIST_NOT_FINISHED=51
|
| 925 |
+
ERR_MIXED_NOT_STARTED=52
|
| 926 |
+
ERR_MIXED_NOT_FINISHED=53
|
| 927 |
+
ERR_ELEMCONTENT_NOT_STARTED=54
|
| 928 |
+
ERR_ELEMCONTENT_NOT_FINISHED=55
|
| 929 |
+
ERR_XMLDECL_NOT_STARTED=56
|
| 930 |
+
ERR_XMLDECL_NOT_FINISHED=57
|
| 931 |
+
ERR_CONDSEC_NOT_STARTED=58
|
| 932 |
+
ERR_CONDSEC_NOT_FINISHED=59
|
| 933 |
+
ERR_EXT_SUBSET_NOT_FINISHED=60
|
| 934 |
+
ERR_DOCTYPE_NOT_FINISHED=61
|
| 935 |
+
ERR_MISPLACED_CDATA_END=62
|
| 936 |
+
ERR_CDATA_NOT_FINISHED=63
|
| 937 |
+
ERR_RESERVED_XML_NAME=64
|
| 938 |
+
ERR_SPACE_REQUIRED=65
|
| 939 |
+
ERR_SEPARATOR_REQUIRED=66
|
| 940 |
+
ERR_NMTOKEN_REQUIRED=67
|
| 941 |
+
ERR_NAME_REQUIRED=68
|
| 942 |
+
ERR_PCDATA_REQUIRED=69
|
| 943 |
+
ERR_URI_REQUIRED=70
|
| 944 |
+
ERR_PUBID_REQUIRED=71
|
| 945 |
+
ERR_LT_REQUIRED=72
|
| 946 |
+
ERR_GT_REQUIRED=73
|
| 947 |
+
ERR_LTSLASH_REQUIRED=74
|
| 948 |
+
ERR_EQUAL_REQUIRED=75
|
| 949 |
+
ERR_TAG_NAME_MISMATCH=76
|
| 950 |
+
ERR_TAG_NOT_FINISHED=77
|
| 951 |
+
ERR_STANDALONE_VALUE=78
|
| 952 |
+
ERR_ENCODING_NAME=79
|
| 953 |
+
ERR_HYPHEN_IN_COMMENT=80
|
| 954 |
+
ERR_INVALID_ENCODING=81
|
| 955 |
+
ERR_EXT_ENTITY_STANDALONE=82
|
| 956 |
+
ERR_CONDSEC_INVALID=83
|
| 957 |
+
ERR_VALUE_REQUIRED=84
|
| 958 |
+
ERR_NOT_WELL_BALANCED=85
|
| 959 |
+
ERR_EXTRA_CONTENT=86
|
| 960 |
+
ERR_ENTITY_CHAR_ERROR=87
|
| 961 |
+
ERR_ENTITY_PE_INTERNAL=88
|
| 962 |
+
ERR_ENTITY_LOOP=89
|
| 963 |
+
ERR_ENTITY_BOUNDARY=90
|
| 964 |
+
ERR_INVALID_URI=91
|
| 965 |
+
ERR_URI_FRAGMENT=92
|
| 966 |
+
WAR_CATALOG_PI=93
|
| 967 |
+
ERR_NO_DTD=94
|
| 968 |
+
ERR_CONDSEC_INVALID_KEYWORD=95
|
| 969 |
+
ERR_VERSION_MISSING=96
|
| 970 |
+
WAR_UNKNOWN_VERSION=97
|
| 971 |
+
WAR_LANG_VALUE=98
|
| 972 |
+
WAR_NS_URI=99
|
| 973 |
+
WAR_NS_URI_RELATIVE=100
|
| 974 |
+
ERR_MISSING_ENCODING=101
|
| 975 |
+
WAR_SPACE_VALUE=102
|
| 976 |
+
ERR_NOT_STANDALONE=103
|
| 977 |
+
ERR_ENTITY_PROCESSING=104
|
| 978 |
+
ERR_NOTATION_PROCESSING=105
|
| 979 |
+
WAR_NS_COLUMN=106
|
| 980 |
+
WAR_ENTITY_REDEFINED=107
|
| 981 |
+
ERR_UNKNOWN_VERSION=108
|
| 982 |
+
ERR_VERSION_MISMATCH=109
|
| 983 |
+
ERR_NAME_TOO_LONG=110
|
| 984 |
+
ERR_USER_STOP=111
|
| 985 |
+
ERR_COMMENT_ABRUPTLY_ENDED=112
|
| 986 |
+
WAR_ENCODING_MISMATCH=113
|
| 987 |
+
ERR_RESOURCE_LIMIT=114
|
| 988 |
+
ERR_ARGUMENT=115
|
| 989 |
+
ERR_SYSTEM=116
|
| 990 |
+
ERR_REDECL_PREDEF_ENTITY=117
|
| 991 |
+
ERR_INT_SUBSET_NOT_FINISHED=118
|
| 992 |
+
NS_ERR_XML_NAMESPACE=200
|
| 993 |
+
NS_ERR_UNDEFINED_NAMESPACE=201
|
| 994 |
+
NS_ERR_QNAME=202
|
| 995 |
+
NS_ERR_ATTRIBUTE_REDEFINED=203
|
| 996 |
+
NS_ERR_EMPTY=204
|
| 997 |
+
NS_ERR_COLON=205
|
| 998 |
+
DTD_ATTRIBUTE_DEFAULT=500
|
| 999 |
+
DTD_ATTRIBUTE_REDEFINED=501
|
| 1000 |
+
DTD_ATTRIBUTE_VALUE=502
|
| 1001 |
+
DTD_CONTENT_ERROR=503
|
| 1002 |
+
DTD_CONTENT_MODEL=504
|
| 1003 |
+
DTD_CONTENT_NOT_DETERMINIST=505
|
| 1004 |
+
DTD_DIFFERENT_PREFIX=506
|
| 1005 |
+
DTD_ELEM_DEFAULT_NAMESPACE=507
|
| 1006 |
+
DTD_ELEM_NAMESPACE=508
|
| 1007 |
+
DTD_ELEM_REDEFINED=509
|
| 1008 |
+
DTD_EMPTY_NOTATION=510
|
| 1009 |
+
DTD_ENTITY_TYPE=511
|
| 1010 |
+
DTD_ID_FIXED=512
|
| 1011 |
+
DTD_ID_REDEFINED=513
|
| 1012 |
+
DTD_ID_SUBSET=514
|
| 1013 |
+
DTD_INVALID_CHILD=515
|
| 1014 |
+
DTD_INVALID_DEFAULT=516
|
| 1015 |
+
DTD_LOAD_ERROR=517
|
| 1016 |
+
DTD_MISSING_ATTRIBUTE=518
|
| 1017 |
+
DTD_MIXED_CORRUPT=519
|
| 1018 |
+
DTD_MULTIPLE_ID=520
|
| 1019 |
+
DTD_NO_DOC=521
|
| 1020 |
+
DTD_NO_DTD=522
|
| 1021 |
+
DTD_NO_ELEM_NAME=523
|
| 1022 |
+
DTD_NO_PREFIX=524
|
| 1023 |
+
DTD_NO_ROOT=525
|
| 1024 |
+
DTD_NOTATION_REDEFINED=526
|
| 1025 |
+
DTD_NOTATION_VALUE=527
|
| 1026 |
+
DTD_NOT_EMPTY=528
|
| 1027 |
+
DTD_NOT_PCDATA=529
|
| 1028 |
+
DTD_NOT_STANDALONE=530
|
| 1029 |
+
DTD_ROOT_NAME=531
|
| 1030 |
+
DTD_STANDALONE_WHITE_SPACE=532
|
| 1031 |
+
DTD_UNKNOWN_ATTRIBUTE=533
|
| 1032 |
+
DTD_UNKNOWN_ELEM=534
|
| 1033 |
+
DTD_UNKNOWN_ENTITY=535
|
| 1034 |
+
DTD_UNKNOWN_ID=536
|
| 1035 |
+
DTD_UNKNOWN_NOTATION=537
|
| 1036 |
+
DTD_STANDALONE_DEFAULTED=538
|
| 1037 |
+
DTD_XMLID_VALUE=539
|
| 1038 |
+
DTD_XMLID_TYPE=540
|
| 1039 |
+
DTD_DUP_TOKEN=541
|
| 1040 |
+
HTML_STRUCURE_ERROR=800
|
| 1041 |
+
HTML_UNKNOWN_TAG=801
|
| 1042 |
+
HTML_INCORRECTLY_OPENED_COMMENT=802
|
| 1043 |
+
RNGP_ANYNAME_ATTR_ANCESTOR=1000
|
| 1044 |
+
RNGP_ATTR_CONFLICT=1001
|
| 1045 |
+
RNGP_ATTRIBUTE_CHILDREN=1002
|
| 1046 |
+
RNGP_ATTRIBUTE_CONTENT=1003
|
| 1047 |
+
RNGP_ATTRIBUTE_EMPTY=1004
|
| 1048 |
+
RNGP_ATTRIBUTE_NOOP=1005
|
| 1049 |
+
RNGP_CHOICE_CONTENT=1006
|
| 1050 |
+
RNGP_CHOICE_EMPTY=1007
|
| 1051 |
+
RNGP_CREATE_FAILURE=1008
|
| 1052 |
+
RNGP_DATA_CONTENT=1009
|
| 1053 |
+
RNGP_DEF_CHOICE_AND_INTERLEAVE=1010
|
| 1054 |
+
RNGP_DEFINE_CREATE_FAILED=1011
|
| 1055 |
+
RNGP_DEFINE_EMPTY=1012
|
| 1056 |
+
RNGP_DEFINE_MISSING=1013
|
| 1057 |
+
RNGP_DEFINE_NAME_MISSING=1014
|
| 1058 |
+
RNGP_ELEM_CONTENT_EMPTY=1015
|
| 1059 |
+
RNGP_ELEM_CONTENT_ERROR=1016
|
| 1060 |
+
RNGP_ELEMENT_EMPTY=1017
|
| 1061 |
+
RNGP_ELEMENT_CONTENT=1018
|
| 1062 |
+
RNGP_ELEMENT_NAME=1019
|
| 1063 |
+
RNGP_ELEMENT_NO_CONTENT=1020
|
| 1064 |
+
RNGP_ELEM_TEXT_CONFLICT=1021
|
| 1065 |
+
RNGP_EMPTY=1022
|
| 1066 |
+
RNGP_EMPTY_CONSTRUCT=1023
|
| 1067 |
+
RNGP_EMPTY_CONTENT=1024
|
| 1068 |
+
RNGP_EMPTY_NOT_EMPTY=1025
|
| 1069 |
+
RNGP_ERROR_TYPE_LIB=1026
|
| 1070 |
+
RNGP_EXCEPT_EMPTY=1027
|
| 1071 |
+
RNGP_EXCEPT_MISSING=1028
|
| 1072 |
+
RNGP_EXCEPT_MULTIPLE=1029
|
| 1073 |
+
RNGP_EXCEPT_NO_CONTENT=1030
|
| 1074 |
+
RNGP_EXTERNALREF_EMTPY=1031
|
| 1075 |
+
RNGP_EXTERNAL_REF_FAILURE=1032
|
| 1076 |
+
RNGP_EXTERNALREF_RECURSE=1033
|
| 1077 |
+
RNGP_FORBIDDEN_ATTRIBUTE=1034
|
| 1078 |
+
RNGP_FOREIGN_ELEMENT=1035
|
| 1079 |
+
RNGP_GRAMMAR_CONTENT=1036
|
| 1080 |
+
RNGP_GRAMMAR_EMPTY=1037
|
| 1081 |
+
RNGP_GRAMMAR_MISSING=1038
|
| 1082 |
+
RNGP_GRAMMAR_NO_START=1039
|
| 1083 |
+
RNGP_GROUP_ATTR_CONFLICT=1040
|
| 1084 |
+
RNGP_HREF_ERROR=1041
|
| 1085 |
+
RNGP_INCLUDE_EMPTY=1042
|
| 1086 |
+
RNGP_INCLUDE_FAILURE=1043
|
| 1087 |
+
RNGP_INCLUDE_RECURSE=1044
|
| 1088 |
+
RNGP_INTERLEAVE_ADD=1045
|
| 1089 |
+
RNGP_INTERLEAVE_CREATE_FAILED=1046
|
| 1090 |
+
RNGP_INTERLEAVE_EMPTY=1047
|
| 1091 |
+
RNGP_INTERLEAVE_NO_CONTENT=1048
|
| 1092 |
+
RNGP_INVALID_DEFINE_NAME=1049
|
| 1093 |
+
RNGP_INVALID_URI=1050
|
| 1094 |
+
RNGP_INVALID_VALUE=1051
|
| 1095 |
+
RNGP_MISSING_HREF=1052
|
| 1096 |
+
RNGP_NAME_MISSING=1053
|
| 1097 |
+
RNGP_NEED_COMBINE=1054
|
| 1098 |
+
RNGP_NOTALLOWED_NOT_EMPTY=1055
|
| 1099 |
+
RNGP_NSNAME_ATTR_ANCESTOR=1056
|
| 1100 |
+
RNGP_NSNAME_NO_NS=1057
|
| 1101 |
+
RNGP_PARAM_FORBIDDEN=1058
|
| 1102 |
+
RNGP_PARAM_NAME_MISSING=1059
|
| 1103 |
+
RNGP_PARENTREF_CREATE_FAILED=1060
|
| 1104 |
+
RNGP_PARENTREF_NAME_INVALID=1061
|
| 1105 |
+
RNGP_PARENTREF_NO_NAME=1062
|
| 1106 |
+
RNGP_PARENTREF_NO_PARENT=1063
|
| 1107 |
+
RNGP_PARENTREF_NOT_EMPTY=1064
|
| 1108 |
+
RNGP_PARSE_ERROR=1065
|
| 1109 |
+
RNGP_PAT_ANYNAME_EXCEPT_ANYNAME=1066
|
| 1110 |
+
RNGP_PAT_ATTR_ATTR=1067
|
| 1111 |
+
RNGP_PAT_ATTR_ELEM=1068
|
| 1112 |
+
RNGP_PAT_DATA_EXCEPT_ATTR=1069
|
| 1113 |
+
RNGP_PAT_DATA_EXCEPT_ELEM=1070
|
| 1114 |
+
RNGP_PAT_DATA_EXCEPT_EMPTY=1071
|
| 1115 |
+
RNGP_PAT_DATA_EXCEPT_GROUP=1072
|
| 1116 |
+
RNGP_PAT_DATA_EXCEPT_INTERLEAVE=1073
|
| 1117 |
+
RNGP_PAT_DATA_EXCEPT_LIST=1074
|
| 1118 |
+
RNGP_PAT_DATA_EXCEPT_ONEMORE=1075
|
| 1119 |
+
RNGP_PAT_DATA_EXCEPT_REF=1076
|
| 1120 |
+
RNGP_PAT_DATA_EXCEPT_TEXT=1077
|
| 1121 |
+
RNGP_PAT_LIST_ATTR=1078
|
| 1122 |
+
RNGP_PAT_LIST_ELEM=1079
|
| 1123 |
+
RNGP_PAT_LIST_INTERLEAVE=1080
|
| 1124 |
+
RNGP_PAT_LIST_LIST=1081
|
| 1125 |
+
RNGP_PAT_LIST_REF=1082
|
| 1126 |
+
RNGP_PAT_LIST_TEXT=1083
|
| 1127 |
+
RNGP_PAT_NSNAME_EXCEPT_ANYNAME=1084
|
| 1128 |
+
RNGP_PAT_NSNAME_EXCEPT_NSNAME=1085
|
| 1129 |
+
RNGP_PAT_ONEMORE_GROUP_ATTR=1086
|
| 1130 |
+
RNGP_PAT_ONEMORE_INTERLEAVE_ATTR=1087
|
| 1131 |
+
RNGP_PAT_START_ATTR=1088
|
| 1132 |
+
RNGP_PAT_START_DATA=1089
|
| 1133 |
+
RNGP_PAT_START_EMPTY=1090
|
| 1134 |
+
RNGP_PAT_START_GROUP=1091
|
| 1135 |
+
RNGP_PAT_START_INTERLEAVE=1092
|
| 1136 |
+
RNGP_PAT_START_LIST=1093
|
| 1137 |
+
RNGP_PAT_START_ONEMORE=1094
|
| 1138 |
+
RNGP_PAT_START_TEXT=1095
|
| 1139 |
+
RNGP_PAT_START_VALUE=1096
|
| 1140 |
+
RNGP_PREFIX_UNDEFINED=1097
|
| 1141 |
+
RNGP_REF_CREATE_FAILED=1098
|
| 1142 |
+
RNGP_REF_CYCLE=1099
|
| 1143 |
+
RNGP_REF_NAME_INVALID=1100
|
| 1144 |
+
RNGP_REF_NO_DEF=1101
|
| 1145 |
+
RNGP_REF_NO_NAME=1102
|
| 1146 |
+
RNGP_REF_NOT_EMPTY=1103
|
| 1147 |
+
RNGP_START_CHOICE_AND_INTERLEAVE=1104
|
| 1148 |
+
RNGP_START_CONTENT=1105
|
| 1149 |
+
RNGP_START_EMPTY=1106
|
| 1150 |
+
RNGP_START_MISSING=1107
|
| 1151 |
+
RNGP_TEXT_EXPECTED=1108
|
| 1152 |
+
RNGP_TEXT_HAS_CHILD=1109
|
| 1153 |
+
RNGP_TYPE_MISSING=1110
|
| 1154 |
+
RNGP_TYPE_NOT_FOUND=1111
|
| 1155 |
+
RNGP_TYPE_VALUE=1112
|
| 1156 |
+
RNGP_UNKNOWN_ATTRIBUTE=1113
|
| 1157 |
+
RNGP_UNKNOWN_COMBINE=1114
|
| 1158 |
+
RNGP_UNKNOWN_CONSTRUCT=1115
|
| 1159 |
+
RNGP_UNKNOWN_TYPE_LIB=1116
|
| 1160 |
+
RNGP_URI_FRAGMENT=1117
|
| 1161 |
+
RNGP_URI_NOT_ABSOLUTE=1118
|
| 1162 |
+
RNGP_VALUE_EMPTY=1119
|
| 1163 |
+
RNGP_VALUE_NO_CONTENT=1120
|
| 1164 |
+
RNGP_XMLNS_NAME=1121
|
| 1165 |
+
RNGP_XML_NS=1122
|
| 1166 |
+
XPATH_EXPRESSION_OK=1200
|
| 1167 |
+
XPATH_NUMBER_ERROR=1201
|
| 1168 |
+
XPATH_UNFINISHED_LITERAL_ERROR=1202
|
| 1169 |
+
XPATH_START_LITERAL_ERROR=1203
|
| 1170 |
+
XPATH_VARIABLE_REF_ERROR=1204
|
| 1171 |
+
XPATH_UNDEF_VARIABLE_ERROR=1205
|
| 1172 |
+
XPATH_INVALID_PREDICATE_ERROR=1206
|
| 1173 |
+
XPATH_EXPR_ERROR=1207
|
| 1174 |
+
XPATH_UNCLOSED_ERROR=1208
|
| 1175 |
+
XPATH_UNKNOWN_FUNC_ERROR=1209
|
| 1176 |
+
XPATH_INVALID_OPERAND=1210
|
| 1177 |
+
XPATH_INVALID_TYPE=1211
|
| 1178 |
+
XPATH_INVALID_ARITY=1212
|
| 1179 |
+
XPATH_INVALID_CTXT_SIZE=1213
|
| 1180 |
+
XPATH_INVALID_CTXT_POSITION=1214
|
| 1181 |
+
XPATH_MEMORY_ERROR=1215
|
| 1182 |
+
XPTR_SYNTAX_ERROR=1216
|
| 1183 |
+
XPTR_RESOURCE_ERROR=1217
|
| 1184 |
+
XPTR_SUB_RESOURCE_ERROR=1218
|
| 1185 |
+
XPATH_UNDEF_PREFIX_ERROR=1219
|
| 1186 |
+
XPATH_ENCODING_ERROR=1220
|
| 1187 |
+
XPATH_INVALID_CHAR_ERROR=1221
|
| 1188 |
+
TREE_INVALID_HEX=1300
|
| 1189 |
+
TREE_INVALID_DEC=1301
|
| 1190 |
+
TREE_UNTERMINATED_ENTITY=1302
|
| 1191 |
+
TREE_NOT_UTF8=1303
|
| 1192 |
+
SAVE_NOT_UTF8=1400
|
| 1193 |
+
SAVE_CHAR_INVALID=1401
|
| 1194 |
+
SAVE_NO_DOCTYPE=1402
|
| 1195 |
+
SAVE_UNKNOWN_ENCODING=1403
|
| 1196 |
+
REGEXP_COMPILE_ERROR=1450
|
| 1197 |
+
IO_UNKNOWN=1500
|
| 1198 |
+
IO_EACCES=1501
|
| 1199 |
+
IO_EAGAIN=1502
|
| 1200 |
+
IO_EBADF=1503
|
| 1201 |
+
IO_EBADMSG=1504
|
| 1202 |
+
IO_EBUSY=1505
|
| 1203 |
+
IO_ECANCELED=1506
|
| 1204 |
+
IO_ECHILD=1507
|
| 1205 |
+
IO_EDEADLK=1508
|
| 1206 |
+
IO_EDOM=1509
|
| 1207 |
+
IO_EEXIST=1510
|
| 1208 |
+
IO_EFAULT=1511
|
| 1209 |
+
IO_EFBIG=1512
|
| 1210 |
+
IO_EINPROGRESS=1513
|
| 1211 |
+
IO_EINTR=1514
|
| 1212 |
+
IO_EINVAL=1515
|
| 1213 |
+
IO_EIO=1516
|
| 1214 |
+
IO_EISDIR=1517
|
| 1215 |
+
IO_EMFILE=1518
|
| 1216 |
+
IO_EMLINK=1519
|
| 1217 |
+
IO_EMSGSIZE=1520
|
| 1218 |
+
IO_ENAMETOOLONG=1521
|
| 1219 |
+
IO_ENFILE=1522
|
| 1220 |
+
IO_ENODEV=1523
|
| 1221 |
+
IO_ENOENT=1524
|
| 1222 |
+
IO_ENOEXEC=1525
|
| 1223 |
+
IO_ENOLCK=1526
|
| 1224 |
+
IO_ENOMEM=1527
|
| 1225 |
+
IO_ENOSPC=1528
|
| 1226 |
+
IO_ENOSYS=1529
|
| 1227 |
+
IO_ENOTDIR=1530
|
| 1228 |
+
IO_ENOTEMPTY=1531
|
| 1229 |
+
IO_ENOTSUP=1532
|
| 1230 |
+
IO_ENOTTY=1533
|
| 1231 |
+
IO_ENXIO=1534
|
| 1232 |
+
IO_EPERM=1535
|
| 1233 |
+
IO_EPIPE=1536
|
| 1234 |
+
IO_ERANGE=1537
|
| 1235 |
+
IO_EROFS=1538
|
| 1236 |
+
IO_ESPIPE=1539
|
| 1237 |
+
IO_ESRCH=1540
|
| 1238 |
+
IO_ETIMEDOUT=1541
|
| 1239 |
+
IO_EXDEV=1542
|
| 1240 |
+
IO_NETWORK_ATTEMPT=1543
|
| 1241 |
+
IO_ENCODER=1544
|
| 1242 |
+
IO_FLUSH=1545
|
| 1243 |
+
IO_WRITE=1546
|
| 1244 |
+
IO_NO_INPUT=1547
|
| 1245 |
+
IO_BUFFER_FULL=1548
|
| 1246 |
+
IO_LOAD_ERROR=1549
|
| 1247 |
+
IO_ENOTSOCK=1550
|
| 1248 |
+
IO_EISCONN=1551
|
| 1249 |
+
IO_ECONNREFUSED=1552
|
| 1250 |
+
IO_ENETUNREACH=1553
|
| 1251 |
+
IO_EADDRINUSE=1554
|
| 1252 |
+
IO_EALREADY=1555
|
| 1253 |
+
IO_EAFNOSUPPORT=1556
|
| 1254 |
+
IO_UNSUPPORTED_PROTOCOL=1557
|
| 1255 |
+
XINCLUDE_RECURSION=1600
|
| 1256 |
+
XINCLUDE_PARSE_VALUE=1601
|
| 1257 |
+
XINCLUDE_ENTITY_DEF_MISMATCH=1602
|
| 1258 |
+
XINCLUDE_NO_HREF=1603
|
| 1259 |
+
XINCLUDE_NO_FALLBACK=1604
|
| 1260 |
+
XINCLUDE_HREF_URI=1605
|
| 1261 |
+
XINCLUDE_TEXT_FRAGMENT=1606
|
| 1262 |
+
XINCLUDE_TEXT_DOCUMENT=1607
|
| 1263 |
+
XINCLUDE_INVALID_CHAR=1608
|
| 1264 |
+
XINCLUDE_BUILD_FAILED=1609
|
| 1265 |
+
XINCLUDE_UNKNOWN_ENCODING=1610
|
| 1266 |
+
XINCLUDE_MULTIPLE_ROOT=1611
|
| 1267 |
+
XINCLUDE_XPTR_FAILED=1612
|
| 1268 |
+
XINCLUDE_XPTR_RESULT=1613
|
| 1269 |
+
XINCLUDE_INCLUDE_IN_INCLUDE=1614
|
| 1270 |
+
XINCLUDE_FALLBACKS_IN_INCLUDE=1615
|
| 1271 |
+
XINCLUDE_FALLBACK_NOT_IN_INCLUDE=1616
|
| 1272 |
+
XINCLUDE_DEPRECATED_NS=1617
|
| 1273 |
+
XINCLUDE_FRAGMENT_ID=1618
|
| 1274 |
+
CATALOG_MISSING_ATTR=1650
|
| 1275 |
+
CATALOG_ENTRY_BROKEN=1651
|
| 1276 |
+
CATALOG_PREFER_VALUE=1652
|
| 1277 |
+
CATALOG_NOT_CATALOG=1653
|
| 1278 |
+
CATALOG_RECURSION=1654
|
| 1279 |
+
SCHEMAP_PREFIX_UNDEFINED=1700
|
| 1280 |
+
SCHEMAP_ATTRFORMDEFAULT_VALUE=1701
|
| 1281 |
+
SCHEMAP_ATTRGRP_NONAME_NOREF=1702
|
| 1282 |
+
SCHEMAP_ATTR_NONAME_NOREF=1703
|
| 1283 |
+
SCHEMAP_COMPLEXTYPE_NONAME_NOREF=1704
|
| 1284 |
+
SCHEMAP_ELEMFORMDEFAULT_VALUE=1705
|
| 1285 |
+
SCHEMAP_ELEM_NONAME_NOREF=1706
|
| 1286 |
+
SCHEMAP_EXTENSION_NO_BASE=1707
|
| 1287 |
+
SCHEMAP_FACET_NO_VALUE=1708
|
| 1288 |
+
SCHEMAP_FAILED_BUILD_IMPORT=1709
|
| 1289 |
+
SCHEMAP_GROUP_NONAME_NOREF=1710
|
| 1290 |
+
SCHEMAP_IMPORT_NAMESPACE_NOT_URI=1711
|
| 1291 |
+
SCHEMAP_IMPORT_REDEFINE_NSNAME=1712
|
| 1292 |
+
SCHEMAP_IMPORT_SCHEMA_NOT_URI=1713
|
| 1293 |
+
SCHEMAP_INVALID_BOOLEAN=1714
|
| 1294 |
+
SCHEMAP_INVALID_ENUM=1715
|
| 1295 |
+
SCHEMAP_INVALID_FACET=1716
|
| 1296 |
+
SCHEMAP_INVALID_FACET_VALUE=1717
|
| 1297 |
+
SCHEMAP_INVALID_MAXOCCURS=1718
|
| 1298 |
+
SCHEMAP_INVALID_MINOCCURS=1719
|
| 1299 |
+
SCHEMAP_INVALID_REF_AND_SUBTYPE=1720
|
| 1300 |
+
SCHEMAP_INVALID_WHITE_SPACE=1721
|
| 1301 |
+
SCHEMAP_NOATTR_NOREF=1722
|
| 1302 |
+
SCHEMAP_NOTATION_NO_NAME=1723
|
| 1303 |
+
SCHEMAP_NOTYPE_NOREF=1724
|
| 1304 |
+
SCHEMAP_REF_AND_SUBTYPE=1725
|
| 1305 |
+
SCHEMAP_RESTRICTION_NONAME_NOREF=1726
|
| 1306 |
+
SCHEMAP_SIMPLETYPE_NONAME=1727
|
| 1307 |
+
SCHEMAP_TYPE_AND_SUBTYPE=1728
|
| 1308 |
+
SCHEMAP_UNKNOWN_ALL_CHILD=1729
|
| 1309 |
+
SCHEMAP_UNKNOWN_ANYATTRIBUTE_CHILD=1730
|
| 1310 |
+
SCHEMAP_UNKNOWN_ATTR_CHILD=1731
|
| 1311 |
+
SCHEMAP_UNKNOWN_ATTRGRP_CHILD=1732
|
| 1312 |
+
SCHEMAP_UNKNOWN_ATTRIBUTE_GROUP=1733
|
| 1313 |
+
SCHEMAP_UNKNOWN_BASE_TYPE=1734
|
| 1314 |
+
SCHEMAP_UNKNOWN_CHOICE_CHILD=1735
|
| 1315 |
+
SCHEMAP_UNKNOWN_COMPLEXCONTENT_CHILD=1736
|
| 1316 |
+
SCHEMAP_UNKNOWN_COMPLEXTYPE_CHILD=1737
|
| 1317 |
+
SCHEMAP_UNKNOWN_ELEM_CHILD=1738
|
| 1318 |
+
SCHEMAP_UNKNOWN_EXTENSION_CHILD=1739
|
| 1319 |
+
SCHEMAP_UNKNOWN_FACET_CHILD=1740
|
| 1320 |
+
SCHEMAP_UNKNOWN_FACET_TYPE=1741
|
| 1321 |
+
SCHEMAP_UNKNOWN_GROUP_CHILD=1742
|
| 1322 |
+
SCHEMAP_UNKNOWN_IMPORT_CHILD=1743
|
| 1323 |
+
SCHEMAP_UNKNOWN_LIST_CHILD=1744
|
| 1324 |
+
SCHEMAP_UNKNOWN_NOTATION_CHILD=1745
|
| 1325 |
+
SCHEMAP_UNKNOWN_PROCESSCONTENT_CHILD=1746
|
| 1326 |
+
SCHEMAP_UNKNOWN_REF=1747
|
| 1327 |
+
SCHEMAP_UNKNOWN_RESTRICTION_CHILD=1748
|
| 1328 |
+
SCHEMAP_UNKNOWN_SCHEMAS_CHILD=1749
|
| 1329 |
+
SCHEMAP_UNKNOWN_SEQUENCE_CHILD=1750
|
| 1330 |
+
SCHEMAP_UNKNOWN_SIMPLECONTENT_CHILD=1751
|
| 1331 |
+
SCHEMAP_UNKNOWN_SIMPLETYPE_CHILD=1752
|
| 1332 |
+
SCHEMAP_UNKNOWN_TYPE=1753
|
| 1333 |
+
SCHEMAP_UNKNOWN_UNION_CHILD=1754
|
| 1334 |
+
SCHEMAP_ELEM_DEFAULT_FIXED=1755
|
| 1335 |
+
SCHEMAP_REGEXP_INVALID=1756
|
| 1336 |
+
SCHEMAP_FAILED_LOAD=1757
|
| 1337 |
+
SCHEMAP_NOTHING_TO_PARSE=1758
|
| 1338 |
+
SCHEMAP_NOROOT=1759
|
| 1339 |
+
SCHEMAP_REDEFINED_GROUP=1760
|
| 1340 |
+
SCHEMAP_REDEFINED_TYPE=1761
|
| 1341 |
+
SCHEMAP_REDEFINED_ELEMENT=1762
|
| 1342 |
+
SCHEMAP_REDEFINED_ATTRGROUP=1763
|
| 1343 |
+
SCHEMAP_REDEFINED_ATTR=1764
|
| 1344 |
+
SCHEMAP_REDEFINED_NOTATION=1765
|
| 1345 |
+
SCHEMAP_FAILED_PARSE=1766
|
| 1346 |
+
SCHEMAP_UNKNOWN_PREFIX=1767
|
| 1347 |
+
SCHEMAP_DEF_AND_PREFIX=1768
|
| 1348 |
+
SCHEMAP_UNKNOWN_INCLUDE_CHILD=1769
|
| 1349 |
+
SCHEMAP_INCLUDE_SCHEMA_NOT_URI=1770
|
| 1350 |
+
SCHEMAP_INCLUDE_SCHEMA_NO_URI=1771
|
| 1351 |
+
SCHEMAP_NOT_SCHEMA=1772
|
| 1352 |
+
SCHEMAP_UNKNOWN_MEMBER_TYPE=1773
|
| 1353 |
+
SCHEMAP_INVALID_ATTR_USE=1774
|
| 1354 |
+
SCHEMAP_RECURSIVE=1775
|
| 1355 |
+
SCHEMAP_SUPERNUMEROUS_LIST_ITEM_TYPE=1776
|
| 1356 |
+
SCHEMAP_INVALID_ATTR_COMBINATION=1777
|
| 1357 |
+
SCHEMAP_INVALID_ATTR_INLINE_COMBINATION=1778
|
| 1358 |
+
SCHEMAP_MISSING_SIMPLETYPE_CHILD=1779
|
| 1359 |
+
SCHEMAP_INVALID_ATTR_NAME=1780
|
| 1360 |
+
SCHEMAP_REF_AND_CONTENT=1781
|
| 1361 |
+
SCHEMAP_CT_PROPS_CORRECT_1=1782
|
| 1362 |
+
SCHEMAP_CT_PROPS_CORRECT_2=1783
|
| 1363 |
+
SCHEMAP_CT_PROPS_CORRECT_3=1784
|
| 1364 |
+
SCHEMAP_CT_PROPS_CORRECT_4=1785
|
| 1365 |
+
SCHEMAP_CT_PROPS_CORRECT_5=1786
|
| 1366 |
+
SCHEMAP_DERIVATION_OK_RESTRICTION_1=1787
|
| 1367 |
+
SCHEMAP_DERIVATION_OK_RESTRICTION_2_1_1=1788
|
| 1368 |
+
SCHEMAP_DERIVATION_OK_RESTRICTION_2_1_2=1789
|
| 1369 |
+
SCHEMAP_DERIVATION_OK_RESTRICTION_2_2=1790
|
| 1370 |
+
SCHEMAP_DERIVATION_OK_RESTRICTION_3=1791
|
| 1371 |
+
SCHEMAP_WILDCARD_INVALID_NS_MEMBER=1792
|
| 1372 |
+
SCHEMAP_INTERSECTION_NOT_EXPRESSIBLE=1793
|
| 1373 |
+
SCHEMAP_UNION_NOT_EXPRESSIBLE=1794
|
| 1374 |
+
SCHEMAP_SRC_IMPORT_3_1=1795
|
| 1375 |
+
SCHEMAP_SRC_IMPORT_3_2=1796
|
| 1376 |
+
SCHEMAP_DERIVATION_OK_RESTRICTION_4_1=1797
|
| 1377 |
+
SCHEMAP_DERIVATION_OK_RESTRICTION_4_2=1798
|
| 1378 |
+
SCHEMAP_DERIVATION_OK_RESTRICTION_4_3=1799
|
| 1379 |
+
SCHEMAP_COS_CT_EXTENDS_1_3=1800
|
| 1380 |
+
SCHEMAV_NOROOT=1801
|
| 1381 |
+
SCHEMAV_UNDECLAREDELEM=1802
|
| 1382 |
+
SCHEMAV_NOTTOPLEVEL=1803
|
| 1383 |
+
SCHEMAV_MISSING=1804
|
| 1384 |
+
SCHEMAV_WRONGELEM=1805
|
| 1385 |
+
SCHEMAV_NOTYPE=1806
|
| 1386 |
+
SCHEMAV_NOROLLBACK=1807
|
| 1387 |
+
SCHEMAV_ISABSTRACT=1808
|
| 1388 |
+
SCHEMAV_NOTEMPTY=1809
|
| 1389 |
+
SCHEMAV_ELEMCONT=1810
|
| 1390 |
+
SCHEMAV_HAVEDEFAULT=1811
|
| 1391 |
+
SCHEMAV_NOTNILLABLE=1812
|
| 1392 |
+
SCHEMAV_EXTRACONTENT=1813
|
| 1393 |
+
SCHEMAV_INVALIDATTR=1814
|
| 1394 |
+
SCHEMAV_INVALIDELEM=1815
|
| 1395 |
+
SCHEMAV_NOTDETERMINIST=1816
|
| 1396 |
+
SCHEMAV_CONSTRUCT=1817
|
| 1397 |
+
SCHEMAV_INTERNAL=1818
|
| 1398 |
+
SCHEMAV_NOTSIMPLE=1819
|
| 1399 |
+
SCHEMAV_ATTRUNKNOWN=1820
|
| 1400 |
+
SCHEMAV_ATTRINVALID=1821
|
| 1401 |
+
SCHEMAV_VALUE=1822
|
| 1402 |
+
SCHEMAV_FACET=1823
|
| 1403 |
+
SCHEMAV_CVC_DATATYPE_VALID_1_2_1=1824
|
| 1404 |
+
SCHEMAV_CVC_DATATYPE_VALID_1_2_2=1825
|
| 1405 |
+
SCHEMAV_CVC_DATATYPE_VALID_1_2_3=1826
|
| 1406 |
+
SCHEMAV_CVC_TYPE_3_1_1=1827
|
| 1407 |
+
SCHEMAV_CVC_TYPE_3_1_2=1828
|
| 1408 |
+
SCHEMAV_CVC_FACET_VALID=1829
|
| 1409 |
+
SCHEMAV_CVC_LENGTH_VALID=1830
|
| 1410 |
+
SCHEMAV_CVC_MINLENGTH_VALID=1831
|
| 1411 |
+
SCHEMAV_CVC_MAXLENGTH_VALID=1832
|
| 1412 |
+
SCHEMAV_CVC_MININCLUSIVE_VALID=1833
|
| 1413 |
+
SCHEMAV_CVC_MAXINCLUSIVE_VALID=1834
|
| 1414 |
+
SCHEMAV_CVC_MINEXCLUSIVE_VALID=1835
|
| 1415 |
+
SCHEMAV_CVC_MAXEXCLUSIVE_VALID=1836
|
| 1416 |
+
SCHEMAV_CVC_TOTALDIGITS_VALID=1837
|
| 1417 |
+
SCHEMAV_CVC_FRACTIONDIGITS_VALID=1838
|
| 1418 |
+
SCHEMAV_CVC_PATTERN_VALID=1839
|
| 1419 |
+
SCHEMAV_CVC_ENUMERATION_VALID=1840
|
| 1420 |
+
SCHEMAV_CVC_COMPLEX_TYPE_2_1=1841
|
| 1421 |
+
SCHEMAV_CVC_COMPLEX_TYPE_2_2=1842
|
| 1422 |
+
SCHEMAV_CVC_COMPLEX_TYPE_2_3=1843
|
| 1423 |
+
SCHEMAV_CVC_COMPLEX_TYPE_2_4=1844
|
| 1424 |
+
SCHEMAV_CVC_ELT_1=1845
|
| 1425 |
+
SCHEMAV_CVC_ELT_2=1846
|
| 1426 |
+
SCHEMAV_CVC_ELT_3_1=1847
|
| 1427 |
+
SCHEMAV_CVC_ELT_3_2_1=1848
|
| 1428 |
+
SCHEMAV_CVC_ELT_3_2_2=1849
|
| 1429 |
+
SCHEMAV_CVC_ELT_4_1=1850
|
| 1430 |
+
SCHEMAV_CVC_ELT_4_2=1851
|
| 1431 |
+
SCHEMAV_CVC_ELT_4_3=1852
|
| 1432 |
+
SCHEMAV_CVC_ELT_5_1_1=1853
|
| 1433 |
+
SCHEMAV_CVC_ELT_5_1_2=1854
|
| 1434 |
+
SCHEMAV_CVC_ELT_5_2_1=1855
|
| 1435 |
+
SCHEMAV_CVC_ELT_5_2_2_1=1856
|
| 1436 |
+
SCHEMAV_CVC_ELT_5_2_2_2_1=1857
|
| 1437 |
+
SCHEMAV_CVC_ELT_5_2_2_2_2=1858
|
| 1438 |
+
SCHEMAV_CVC_ELT_6=1859
|
| 1439 |
+
SCHEMAV_CVC_ELT_7=1860
|
| 1440 |
+
SCHEMAV_CVC_ATTRIBUTE_1=1861
|
| 1441 |
+
SCHEMAV_CVC_ATTRIBUTE_2=1862
|
| 1442 |
+
SCHEMAV_CVC_ATTRIBUTE_3=1863
|
| 1443 |
+
SCHEMAV_CVC_ATTRIBUTE_4=1864
|
| 1444 |
+
SCHEMAV_CVC_COMPLEX_TYPE_3_1=1865
|
| 1445 |
+
SCHEMAV_CVC_COMPLEX_TYPE_3_2_1=1866
|
| 1446 |
+
SCHEMAV_CVC_COMPLEX_TYPE_3_2_2=1867
|
| 1447 |
+
SCHEMAV_CVC_COMPLEX_TYPE_4=1868
|
| 1448 |
+
SCHEMAV_CVC_COMPLEX_TYPE_5_1=1869
|
| 1449 |
+
SCHEMAV_CVC_COMPLEX_TYPE_5_2=1870
|
| 1450 |
+
SCHEMAV_ELEMENT_CONTENT=1871
|
| 1451 |
+
SCHEMAV_DOCUMENT_ELEMENT_MISSING=1872
|
| 1452 |
+
SCHEMAV_CVC_COMPLEX_TYPE_1=1873
|
| 1453 |
+
SCHEMAV_CVC_AU=1874
|
| 1454 |
+
SCHEMAV_CVC_TYPE_1=1875
|
| 1455 |
+
SCHEMAV_CVC_TYPE_2=1876
|
| 1456 |
+
SCHEMAV_CVC_IDC=1877
|
| 1457 |
+
SCHEMAV_CVC_WILDCARD=1878
|
| 1458 |
+
SCHEMAV_MISC=1879
|
| 1459 |
+
XPTR_UNKNOWN_SCHEME=1900
|
| 1460 |
+
XPTR_CHILDSEQ_START=1901
|
| 1461 |
+
XPTR_EVAL_FAILED=1902
|
| 1462 |
+
XPTR_EXTRA_OBJECTS=1903
|
| 1463 |
+
C14N_CREATE_CTXT=1950
|
| 1464 |
+
C14N_REQUIRES_UTF8=1951
|
| 1465 |
+
C14N_CREATE_STACK=1952
|
| 1466 |
+
C14N_INVALID_NODE=1953
|
| 1467 |
+
C14N_UNKNOW_NODE=1954
|
| 1468 |
+
C14N_RELATIVE_NAMESPACE=1955
|
| 1469 |
+
FTP_PASV_ANSWER=2000
|
| 1470 |
+
FTP_EPSV_ANSWER=2001
|
| 1471 |
+
FTP_ACCNT=2002
|
| 1472 |
+
FTP_URL_SYNTAX=2003
|
| 1473 |
+
HTTP_URL_SYNTAX=2020
|
| 1474 |
+
HTTP_USE_IP=2021
|
| 1475 |
+
HTTP_UNKNOWN_HOST=2022
|
| 1476 |
+
SCHEMAP_SRC_SIMPLE_TYPE_1=3000
|
| 1477 |
+
SCHEMAP_SRC_SIMPLE_TYPE_2=3001
|
| 1478 |
+
SCHEMAP_SRC_SIMPLE_TYPE_3=3002
|
| 1479 |
+
SCHEMAP_SRC_SIMPLE_TYPE_4=3003
|
| 1480 |
+
SCHEMAP_SRC_RESOLVE=3004
|
| 1481 |
+
SCHEMAP_SRC_RESTRICTION_BASE_OR_SIMPLETYPE=3005
|
| 1482 |
+
SCHEMAP_SRC_LIST_ITEMTYPE_OR_SIMPLETYPE=3006
|
| 1483 |
+
SCHEMAP_SRC_UNION_MEMBERTYPES_OR_SIMPLETYPES=3007
|
| 1484 |
+
SCHEMAP_ST_PROPS_CORRECT_1=3008
|
| 1485 |
+
SCHEMAP_ST_PROPS_CORRECT_2=3009
|
| 1486 |
+
SCHEMAP_ST_PROPS_CORRECT_3=3010
|
| 1487 |
+
SCHEMAP_COS_ST_RESTRICTS_1_1=3011
|
| 1488 |
+
SCHEMAP_COS_ST_RESTRICTS_1_2=3012
|
| 1489 |
+
SCHEMAP_COS_ST_RESTRICTS_1_3_1=3013
|
| 1490 |
+
SCHEMAP_COS_ST_RESTRICTS_1_3_2=3014
|
| 1491 |
+
SCHEMAP_COS_ST_RESTRICTS_2_1=3015
|
| 1492 |
+
SCHEMAP_COS_ST_RESTRICTS_2_3_1_1=3016
|
| 1493 |
+
SCHEMAP_COS_ST_RESTRICTS_2_3_1_2=3017
|
| 1494 |
+
SCHEMAP_COS_ST_RESTRICTS_2_3_2_1=3018
|
| 1495 |
+
SCHEMAP_COS_ST_RESTRICTS_2_3_2_2=3019
|
| 1496 |
+
SCHEMAP_COS_ST_RESTRICTS_2_3_2_3=3020
|
| 1497 |
+
SCHEMAP_COS_ST_RESTRICTS_2_3_2_4=3021
|
| 1498 |
+
SCHEMAP_COS_ST_RESTRICTS_2_3_2_5=3022
|
| 1499 |
+
SCHEMAP_COS_ST_RESTRICTS_3_1=3023
|
| 1500 |
+
SCHEMAP_COS_ST_RESTRICTS_3_3_1=3024
|
| 1501 |
+
SCHEMAP_COS_ST_RESTRICTS_3_3_1_2=3025
|
| 1502 |
+
SCHEMAP_COS_ST_RESTRICTS_3_3_2_2=3026
|
| 1503 |
+
SCHEMAP_COS_ST_RESTRICTS_3_3_2_1=3027
|
| 1504 |
+
SCHEMAP_COS_ST_RESTRICTS_3_3_2_3=3028
|
| 1505 |
+
SCHEMAP_COS_ST_RESTRICTS_3_3_2_4=3029
|
| 1506 |
+
SCHEMAP_COS_ST_RESTRICTS_3_3_2_5=3030
|
| 1507 |
+
SCHEMAP_COS_ST_DERIVED_OK_2_1=3031
|
| 1508 |
+
SCHEMAP_COS_ST_DERIVED_OK_2_2=3032
|
| 1509 |
+
SCHEMAP_S4S_ELEM_NOT_ALLOWED=3033
|
| 1510 |
+
SCHEMAP_S4S_ELEM_MISSING=3034
|
| 1511 |
+
SCHEMAP_S4S_ATTR_NOT_ALLOWED=3035
|
| 1512 |
+
SCHEMAP_S4S_ATTR_MISSING=3036
|
| 1513 |
+
SCHEMAP_S4S_ATTR_INVALID_VALUE=3037
|
| 1514 |
+
SCHEMAP_SRC_ELEMENT_1=3038
|
| 1515 |
+
SCHEMAP_SRC_ELEMENT_2_1=3039
|
| 1516 |
+
SCHEMAP_SRC_ELEMENT_2_2=3040
|
| 1517 |
+
SCHEMAP_SRC_ELEMENT_3=3041
|
| 1518 |
+
SCHEMAP_P_PROPS_CORRECT_1=3042
|
| 1519 |
+
SCHEMAP_P_PROPS_CORRECT_2_1=3043
|
| 1520 |
+
SCHEMAP_P_PROPS_CORRECT_2_2=3044
|
| 1521 |
+
SCHEMAP_E_PROPS_CORRECT_2=3045
|
| 1522 |
+
SCHEMAP_E_PROPS_CORRECT_3=3046
|
| 1523 |
+
SCHEMAP_E_PROPS_CORRECT_4=3047
|
| 1524 |
+
SCHEMAP_E_PROPS_CORRECT_5=3048
|
| 1525 |
+
SCHEMAP_E_PROPS_CORRECT_6=3049
|
| 1526 |
+
SCHEMAP_SRC_INCLUDE=3050
|
| 1527 |
+
SCHEMAP_SRC_ATTRIBUTE_1=3051
|
| 1528 |
+
SCHEMAP_SRC_ATTRIBUTE_2=3052
|
| 1529 |
+
SCHEMAP_SRC_ATTRIBUTE_3_1=3053
|
| 1530 |
+
SCHEMAP_SRC_ATTRIBUTE_3_2=3054
|
| 1531 |
+
SCHEMAP_SRC_ATTRIBUTE_4=3055
|
| 1532 |
+
SCHEMAP_NO_XMLNS=3056
|
| 1533 |
+
SCHEMAP_NO_XSI=3057
|
| 1534 |
+
SCHEMAP_COS_VALID_DEFAULT_1=3058
|
| 1535 |
+
SCHEMAP_COS_VALID_DEFAULT_2_1=3059
|
| 1536 |
+
SCHEMAP_COS_VALID_DEFAULT_2_2_1=3060
|
| 1537 |
+
SCHEMAP_COS_VALID_DEFAULT_2_2_2=3061
|
| 1538 |
+
SCHEMAP_CVC_SIMPLE_TYPE=3062
|
| 1539 |
+
SCHEMAP_COS_CT_EXTENDS_1_1=3063
|
| 1540 |
+
SCHEMAP_SRC_IMPORT_1_1=3064
|
| 1541 |
+
SCHEMAP_SRC_IMPORT_1_2=3065
|
| 1542 |
+
SCHEMAP_SRC_IMPORT_2=3066
|
| 1543 |
+
SCHEMAP_SRC_IMPORT_2_1=3067
|
| 1544 |
+
SCHEMAP_SRC_IMPORT_2_2=3068
|
| 1545 |
+
SCHEMAP_INTERNAL=3069
|
| 1546 |
+
SCHEMAP_NOT_DETERMINISTIC=3070
|
| 1547 |
+
SCHEMAP_SRC_ATTRIBUTE_GROUP_1=3071
|
| 1548 |
+
SCHEMAP_SRC_ATTRIBUTE_GROUP_2=3072
|
| 1549 |
+
SCHEMAP_SRC_ATTRIBUTE_GROUP_3=3073
|
| 1550 |
+
SCHEMAP_MG_PROPS_CORRECT_1=3074
|
| 1551 |
+
SCHEMAP_MG_PROPS_CORRECT_2=3075
|
| 1552 |
+
SCHEMAP_SRC_CT_1=3076
|
| 1553 |
+
SCHEMAP_DERIVATION_OK_RESTRICTION_2_1_3=3077
|
| 1554 |
+
SCHEMAP_AU_PROPS_CORRECT_2=3078
|
| 1555 |
+
SCHEMAP_A_PROPS_CORRECT_2=3079
|
| 1556 |
+
SCHEMAP_C_PROPS_CORRECT=3080
|
| 1557 |
+
SCHEMAP_SRC_REDEFINE=3081
|
| 1558 |
+
SCHEMAP_SRC_IMPORT=3082
|
| 1559 |
+
SCHEMAP_WARN_SKIP_SCHEMA=3083
|
| 1560 |
+
SCHEMAP_WARN_UNLOCATED_SCHEMA=3084
|
| 1561 |
+
SCHEMAP_WARN_ATTR_REDECL_PROH=3085
|
| 1562 |
+
SCHEMAP_WARN_ATTR_POINTLESS_PROH=3086
|
| 1563 |
+
SCHEMAP_AG_PROPS_CORRECT=3087
|
| 1564 |
+
SCHEMAP_COS_CT_EXTENDS_1_2=3088
|
| 1565 |
+
SCHEMAP_AU_PROPS_CORRECT=3089
|
| 1566 |
+
SCHEMAP_A_PROPS_CORRECT_3=3090
|
| 1567 |
+
SCHEMAP_COS_ALL_LIMITED=3091
|
| 1568 |
+
SCHEMATRONV_ASSERT=4000
|
| 1569 |
+
SCHEMATRONV_REPORT=4001
|
| 1570 |
+
MODULE_OPEN=4900
|
| 1571 |
+
MODULE_CLOSE=4901
|
| 1572 |
+
CHECK_FOUND_ELEMENT=5000
|
| 1573 |
+
CHECK_FOUND_ATTRIBUTE=5001
|
| 1574 |
+
CHECK_FOUND_TEXT=5002
|
| 1575 |
+
CHECK_FOUND_CDATA=5003
|
| 1576 |
+
CHECK_FOUND_ENTITYREF=5004
|
| 1577 |
+
CHECK_FOUND_ENTITY=5005
|
| 1578 |
+
CHECK_FOUND_PI=5006
|
| 1579 |
+
CHECK_FOUND_COMMENT=5007
|
| 1580 |
+
CHECK_FOUND_DOCTYPE=5008
|
| 1581 |
+
CHECK_FOUND_FRAGMENT=5009
|
| 1582 |
+
CHECK_FOUND_NOTATION=5010
|
| 1583 |
+
CHECK_UNKNOWN_NODE=5011
|
| 1584 |
+
CHECK_ENTITY_TYPE=5012
|
| 1585 |
+
CHECK_NO_PARENT=5013
|
| 1586 |
+
CHECK_NO_DOC=5014
|
| 1587 |
+
CHECK_NO_NAME=5015
|
| 1588 |
+
CHECK_NO_ELEM=5016
|
| 1589 |
+
CHECK_WRONG_DOC=5017
|
| 1590 |
+
CHECK_NO_PREV=5018
|
| 1591 |
+
CHECK_WRONG_PREV=5019
|
| 1592 |
+
CHECK_NO_NEXT=5020
|
| 1593 |
+
CHECK_WRONG_NEXT=5021
|
| 1594 |
+
CHECK_NOT_DTD=5022
|
| 1595 |
+
CHECK_NOT_ATTR=5023
|
| 1596 |
+
CHECK_NOT_ATTR_DECL=5024
|
| 1597 |
+
CHECK_NOT_ELEM_DECL=5025
|
| 1598 |
+
CHECK_NOT_ENTITY_DECL=5026
|
| 1599 |
+
CHECK_NOT_NS_DECL=5027
|
| 1600 |
+
CHECK_NO_HREF=5028
|
| 1601 |
+
CHECK_WRONG_PARENT=5029
|
| 1602 |
+
CHECK_NS_SCOPE=5030
|
| 1603 |
+
CHECK_NS_ANCESTOR=5031
|
| 1604 |
+
CHECK_NOT_UTF8=5032
|
| 1605 |
+
CHECK_NO_DICT=5033
|
| 1606 |
+
CHECK_NOT_NCNAME=5034
|
| 1607 |
+
CHECK_OUTSIDE_DICT=5035
|
| 1608 |
+
CHECK_WRONG_NAME=5036
|
| 1609 |
+
CHECK_NAME_NOT_NULL=5037
|
| 1610 |
+
I18N_NO_NAME=6000
|
| 1611 |
+
I18N_NO_HANDLER=6001
|
| 1612 |
+
I18N_EXCESS_HANDLER=6002
|
| 1613 |
+
I18N_CONV_FAILED=6003
|
| 1614 |
+
I18N_NO_OUTPUT=6004
|
| 1615 |
+
BUF_OVERFLOW=7000
|
| 1616 |
+
"""
|
| 1617 |
+
|
| 1618 |
+
cdef object __RELAXNG_ERROR_TYPES = """\
|
| 1619 |
+
RELAXNG_OK=0
|
| 1620 |
+
RELAXNG_ERR_MEMORY=1
|
| 1621 |
+
RELAXNG_ERR_TYPE=2
|
| 1622 |
+
RELAXNG_ERR_TYPEVAL=3
|
| 1623 |
+
RELAXNG_ERR_DUPID=4
|
| 1624 |
+
RELAXNG_ERR_TYPECMP=5
|
| 1625 |
+
RELAXNG_ERR_NOSTATE=6
|
| 1626 |
+
RELAXNG_ERR_NODEFINE=7
|
| 1627 |
+
RELAXNG_ERR_LISTEXTRA=8
|
| 1628 |
+
RELAXNG_ERR_LISTEMPTY=9
|
| 1629 |
+
RELAXNG_ERR_INTERNODATA=10
|
| 1630 |
+
RELAXNG_ERR_INTERSEQ=11
|
| 1631 |
+
RELAXNG_ERR_INTEREXTRA=12
|
| 1632 |
+
RELAXNG_ERR_ELEMNAME=13
|
| 1633 |
+
RELAXNG_ERR_ATTRNAME=14
|
| 1634 |
+
RELAXNG_ERR_ELEMNONS=15
|
| 1635 |
+
RELAXNG_ERR_ATTRNONS=16
|
| 1636 |
+
RELAXNG_ERR_ELEMWRONGNS=17
|
| 1637 |
+
RELAXNG_ERR_ATTRWRONGNS=18
|
| 1638 |
+
RELAXNG_ERR_ELEMEXTRANS=19
|
| 1639 |
+
RELAXNG_ERR_ATTREXTRANS=20
|
| 1640 |
+
RELAXNG_ERR_ELEMNOTEMPTY=21
|
| 1641 |
+
RELAXNG_ERR_NOELEM=22
|
| 1642 |
+
RELAXNG_ERR_NOTELEM=23
|
| 1643 |
+
RELAXNG_ERR_ATTRVALID=24
|
| 1644 |
+
RELAXNG_ERR_CONTENTVALID=25
|
| 1645 |
+
RELAXNG_ERR_EXTRACONTENT=26
|
| 1646 |
+
RELAXNG_ERR_INVALIDATTR=27
|
| 1647 |
+
RELAXNG_ERR_DATAELEM=28
|
| 1648 |
+
RELAXNG_ERR_VALELEM=29
|
| 1649 |
+
RELAXNG_ERR_LISTELEM=30
|
| 1650 |
+
RELAXNG_ERR_DATATYPE=31
|
| 1651 |
+
RELAXNG_ERR_VALUE=32
|
| 1652 |
+
RELAXNG_ERR_LIST=33
|
| 1653 |
+
RELAXNG_ERR_NOGRAMMAR=34
|
| 1654 |
+
RELAXNG_ERR_EXTRADATA=35
|
| 1655 |
+
RELAXNG_ERR_LACKDATA=36
|
| 1656 |
+
RELAXNG_ERR_INTERNAL=37
|
| 1657 |
+
RELAXNG_ERR_ELEMWRONG=38
|
| 1658 |
+
RELAXNG_ERR_TEXTWRONG=39
|
| 1659 |
+
"""
|
| 1660 |
+
# --- END: GENERATED CONSTANTS ---
|
| 1661 |
+
|
| 1662 |
+
__initErrorConstants()
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/xpath.pxi
ADDED
|
@@ -0,0 +1,487 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# XPath evaluation
|
| 2 |
+
|
| 3 |
+
class XPathSyntaxError(LxmlSyntaxError, XPathError):
|
| 4 |
+
pass
|
| 5 |
+
|
| 6 |
+
################################################################################
|
| 7 |
+
# XPath
|
| 8 |
+
|
| 9 |
+
cdef object _XPATH_SYNTAX_ERRORS = (
|
| 10 |
+
xmlerror.XML_XPATH_NUMBER_ERROR,
|
| 11 |
+
xmlerror.XML_XPATH_UNFINISHED_LITERAL_ERROR,
|
| 12 |
+
xmlerror.XML_XPATH_VARIABLE_REF_ERROR,
|
| 13 |
+
xmlerror.XML_XPATH_INVALID_PREDICATE_ERROR,
|
| 14 |
+
xmlerror.XML_XPATH_UNCLOSED_ERROR,
|
| 15 |
+
xmlerror.XML_XPATH_INVALID_CHAR_ERROR
|
| 16 |
+
)
|
| 17 |
+
|
| 18 |
+
cdef object _XPATH_EVAL_ERRORS = (
|
| 19 |
+
xmlerror.XML_XPATH_UNDEF_VARIABLE_ERROR,
|
| 20 |
+
xmlerror.XML_XPATH_UNDEF_PREFIX_ERROR,
|
| 21 |
+
xmlerror.XML_XPATH_UNKNOWN_FUNC_ERROR,
|
| 22 |
+
xmlerror.XML_XPATH_INVALID_OPERAND,
|
| 23 |
+
xmlerror.XML_XPATH_INVALID_TYPE,
|
| 24 |
+
xmlerror.XML_XPATH_INVALID_ARITY,
|
| 25 |
+
xmlerror.XML_XPATH_INVALID_CTXT_SIZE,
|
| 26 |
+
xmlerror.XML_XPATH_INVALID_CTXT_POSITION
|
| 27 |
+
)
|
| 28 |
+
|
| 29 |
+
cdef int _register_xpath_function(void* ctxt, name_utf, ns_utf) noexcept:
|
| 30 |
+
if ns_utf is None:
|
| 31 |
+
return xpath.xmlXPathRegisterFunc(
|
| 32 |
+
<xpath.xmlXPathContext*>ctxt, _xcstr(name_utf),
|
| 33 |
+
_xpath_function_call)
|
| 34 |
+
else:
|
| 35 |
+
return xpath.xmlXPathRegisterFuncNS(
|
| 36 |
+
<xpath.xmlXPathContext*>ctxt, _xcstr(name_utf), _xcstr(ns_utf),
|
| 37 |
+
_xpath_function_call)
|
| 38 |
+
|
| 39 |
+
cdef int _unregister_xpath_function(void* ctxt, name_utf, ns_utf) noexcept:
|
| 40 |
+
if ns_utf is None:
|
| 41 |
+
return xpath.xmlXPathRegisterFunc(
|
| 42 |
+
<xpath.xmlXPathContext*>ctxt, _xcstr(name_utf), NULL)
|
| 43 |
+
else:
|
| 44 |
+
return xpath.xmlXPathRegisterFuncNS(
|
| 45 |
+
<xpath.xmlXPathContext*>ctxt, _xcstr(name_utf), _xcstr(ns_utf), NULL)
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
@cython.final
|
| 49 |
+
@cython.internal
|
| 50 |
+
cdef class _XPathContext(_BaseContext):
|
| 51 |
+
cdef object _variables
|
| 52 |
+
def __init__(self, namespaces, extensions, error_log, enable_regexp, variables,
|
| 53 |
+
build_smart_strings):
|
| 54 |
+
self._variables = variables
|
| 55 |
+
_BaseContext.__init__(self, namespaces, extensions, error_log, enable_regexp,
|
| 56 |
+
build_smart_strings)
|
| 57 |
+
|
| 58 |
+
cdef set_context(self, xpath.xmlXPathContext* xpathCtxt):
|
| 59 |
+
self._set_xpath_context(xpathCtxt)
|
| 60 |
+
# This would be a good place to set up the XPath parser dict, but
|
| 61 |
+
# we cannot use the current thread dict as we do not know which
|
| 62 |
+
# thread will execute the XPath evaluator - so, no dict for now.
|
| 63 |
+
self.registerLocalNamespaces()
|
| 64 |
+
self.registerLocalFunctions(xpathCtxt, _register_xpath_function)
|
| 65 |
+
|
| 66 |
+
cdef register_context(self, _Document doc):
|
| 67 |
+
self._register_context(doc)
|
| 68 |
+
self.registerGlobalNamespaces()
|
| 69 |
+
self.registerGlobalFunctions(self._xpathCtxt, _register_xpath_function)
|
| 70 |
+
self.registerExsltFunctions()
|
| 71 |
+
if self._variables is not None:
|
| 72 |
+
self.registerVariables(self._variables)
|
| 73 |
+
|
| 74 |
+
cdef unregister_context(self):
|
| 75 |
+
self.unregisterGlobalFunctions(
|
| 76 |
+
self._xpathCtxt, _unregister_xpath_function)
|
| 77 |
+
self.unregisterGlobalNamespaces()
|
| 78 |
+
xpath.xmlXPathRegisteredVariablesCleanup(self._xpathCtxt)
|
| 79 |
+
self._cleanup_context()
|
| 80 |
+
|
| 81 |
+
cdef void registerExsltFunctions(self) noexcept:
|
| 82 |
+
if xslt.LIBXSLT_VERSION < 10125:
|
| 83 |
+
# we'd only execute dummy functions anyway
|
| 84 |
+
return
|
| 85 |
+
tree.xmlHashScan(
|
| 86 |
+
self._xpathCtxt.nsHash, _registerExsltFunctionsForNamespaces,
|
| 87 |
+
self._xpathCtxt)
|
| 88 |
+
|
| 89 |
+
cdef registerVariables(self, variable_dict):
|
| 90 |
+
for name, value in variable_dict.items():
|
| 91 |
+
name_utf = self._to_utf(name)
|
| 92 |
+
xpath.xmlXPathRegisterVariable(
|
| 93 |
+
self._xpathCtxt, _xcstr(name_utf), _wrapXPathObject(value, None, None))
|
| 94 |
+
|
| 95 |
+
cdef registerVariable(self, name, value):
|
| 96 |
+
name_utf = self._to_utf(name)
|
| 97 |
+
xpath.xmlXPathRegisterVariable(
|
| 98 |
+
self._xpathCtxt, _xcstr(name_utf), _wrapXPathObject(value, None, None))
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
cdef void _registerExsltFunctionsForNamespaces(
|
| 102 |
+
void* _c_href, void* _ctxt, const_xmlChar* c_prefix) noexcept:
|
| 103 |
+
c_href = <const_xmlChar*> _c_href
|
| 104 |
+
ctxt = <xpath.xmlXPathContext*> _ctxt
|
| 105 |
+
|
| 106 |
+
if tree.xmlStrcmp(c_href, xslt.EXSLT_DATE_NAMESPACE) == 0:
|
| 107 |
+
xslt.exsltDateXpathCtxtRegister(ctxt, c_prefix)
|
| 108 |
+
elif tree.xmlStrcmp(c_href, xslt.EXSLT_SETS_NAMESPACE) == 0:
|
| 109 |
+
xslt.exsltSetsXpathCtxtRegister(ctxt, c_prefix)
|
| 110 |
+
elif tree.xmlStrcmp(c_href, xslt.EXSLT_MATH_NAMESPACE) == 0:
|
| 111 |
+
xslt.exsltMathXpathCtxtRegister(ctxt, c_prefix)
|
| 112 |
+
elif tree.xmlStrcmp(c_href, xslt.EXSLT_STRINGS_NAMESPACE) == 0:
|
| 113 |
+
xslt.exsltStrXpathCtxtRegister(ctxt, c_prefix)
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
cdef class _XPathEvaluatorBase:
|
| 117 |
+
cdef xpath.xmlXPathContext* _xpathCtxt
|
| 118 |
+
cdef _XPathContext _context
|
| 119 |
+
cdef python.PyThread_type_lock _eval_lock
|
| 120 |
+
cdef _ErrorLog _error_log
|
| 121 |
+
def __cinit__(self):
|
| 122 |
+
self._xpathCtxt = NULL
|
| 123 |
+
if config.ENABLE_THREADING:
|
| 124 |
+
self._eval_lock = python.PyThread_allocate_lock()
|
| 125 |
+
if self._eval_lock is NULL:
|
| 126 |
+
raise MemoryError()
|
| 127 |
+
self._error_log = _ErrorLog()
|
| 128 |
+
|
| 129 |
+
def __init__(self, namespaces, extensions, enable_regexp,
|
| 130 |
+
smart_strings):
|
| 131 |
+
self._context = _XPathContext(namespaces, extensions, self._error_log,
|
| 132 |
+
enable_regexp, None, smart_strings)
|
| 133 |
+
|
| 134 |
+
@property
|
| 135 |
+
def error_log(self):
|
| 136 |
+
assert self._error_log is not None, "XPath evaluator not initialised"
|
| 137 |
+
return self._error_log.copy()
|
| 138 |
+
|
| 139 |
+
def __dealloc__(self):
|
| 140 |
+
if self._xpathCtxt is not NULL:
|
| 141 |
+
xpath.xmlXPathFreeContext(self._xpathCtxt)
|
| 142 |
+
if config.ENABLE_THREADING:
|
| 143 |
+
if self._eval_lock is not NULL:
|
| 144 |
+
python.PyThread_free_lock(self._eval_lock)
|
| 145 |
+
|
| 146 |
+
cdef set_context(self, xpath.xmlXPathContext* xpathCtxt):
|
| 147 |
+
self._xpathCtxt = xpathCtxt
|
| 148 |
+
self._context.set_context(xpathCtxt)
|
| 149 |
+
|
| 150 |
+
cdef bint _checkAbsolutePath(self, char* path) noexcept:
|
| 151 |
+
cdef char c
|
| 152 |
+
if path is NULL:
|
| 153 |
+
return 0
|
| 154 |
+
c = path[0]
|
| 155 |
+
while c == c' ' or c == c'\t':
|
| 156 |
+
path = path + 1
|
| 157 |
+
c = path[0]
|
| 158 |
+
return c == c'/'
|
| 159 |
+
|
| 160 |
+
@cython.final
|
| 161 |
+
cdef int _lock(self) except -1:
|
| 162 |
+
cdef int result
|
| 163 |
+
if config.ENABLE_THREADING and self._eval_lock != NULL:
|
| 164 |
+
with nogil:
|
| 165 |
+
result = python.PyThread_acquire_lock(
|
| 166 |
+
self._eval_lock, python.WAIT_LOCK)
|
| 167 |
+
if result == 0:
|
| 168 |
+
raise XPathError, "XPath evaluator locking failed"
|
| 169 |
+
return 0
|
| 170 |
+
|
| 171 |
+
@cython.final
|
| 172 |
+
cdef void _unlock(self) noexcept:
|
| 173 |
+
if config.ENABLE_THREADING and self._eval_lock != NULL:
|
| 174 |
+
python.PyThread_release_lock(self._eval_lock)
|
| 175 |
+
|
| 176 |
+
cdef _build_parse_error(self):
|
| 177 |
+
cdef _BaseErrorLog entries
|
| 178 |
+
entries = self._error_log.filter_types(_XPATH_SYNTAX_ERRORS)
|
| 179 |
+
if entries:
|
| 180 |
+
message = entries._buildExceptionMessage(None)
|
| 181 |
+
if message is not None:
|
| 182 |
+
return XPathSyntaxError(message, self._error_log)
|
| 183 |
+
return XPathSyntaxError(
|
| 184 |
+
self._error_log._buildExceptionMessage("Error in xpath expression"),
|
| 185 |
+
self._error_log)
|
| 186 |
+
|
| 187 |
+
cdef _build_eval_error(self):
|
| 188 |
+
cdef _BaseErrorLog entries
|
| 189 |
+
entries = self._error_log.filter_types(_XPATH_EVAL_ERRORS)
|
| 190 |
+
if not entries:
|
| 191 |
+
entries = self._error_log.filter_types(_XPATH_SYNTAX_ERRORS)
|
| 192 |
+
if entries:
|
| 193 |
+
message = entries._buildExceptionMessage(None)
|
| 194 |
+
if message is not None:
|
| 195 |
+
return XPathEvalError(message, self._error_log)
|
| 196 |
+
return XPathEvalError(
|
| 197 |
+
self._error_log._buildExceptionMessage("Error in xpath expression"),
|
| 198 |
+
self._error_log)
|
| 199 |
+
|
| 200 |
+
cdef object _handle_result(self, xpath.xmlXPathObject* xpathObj, _Document doc):
|
| 201 |
+
if self._context._exc._has_raised():
|
| 202 |
+
if xpathObj is not NULL:
|
| 203 |
+
_freeXPathObject(xpathObj)
|
| 204 |
+
xpathObj = NULL
|
| 205 |
+
self._context._release_temp_refs()
|
| 206 |
+
self._context._exc._raise_if_stored()
|
| 207 |
+
|
| 208 |
+
if xpathObj is NULL:
|
| 209 |
+
self._context._release_temp_refs()
|
| 210 |
+
raise self._build_eval_error()
|
| 211 |
+
|
| 212 |
+
try:
|
| 213 |
+
result = _unwrapXPathObject(xpathObj, doc, self._context)
|
| 214 |
+
finally:
|
| 215 |
+
_freeXPathObject(xpathObj)
|
| 216 |
+
self._context._release_temp_refs()
|
| 217 |
+
|
| 218 |
+
return result
|
| 219 |
+
|
| 220 |
+
|
| 221 |
+
cdef class XPathElementEvaluator(_XPathEvaluatorBase):
|
| 222 |
+
"""XPathElementEvaluator(self, element, namespaces=None, extensions=None, regexp=True, smart_strings=True)
|
| 223 |
+
Create an XPath evaluator for an element.
|
| 224 |
+
|
| 225 |
+
Absolute XPath expressions (starting with '/') will be evaluated against
|
| 226 |
+
the ElementTree as returned by getroottree().
|
| 227 |
+
|
| 228 |
+
Additional namespace declarations can be passed with the
|
| 229 |
+
'namespace' keyword argument. EXSLT regular expression support
|
| 230 |
+
can be disabled with the 'regexp' boolean keyword (defaults to
|
| 231 |
+
True). Smart strings will be returned for string results unless
|
| 232 |
+
you pass ``smart_strings=False``.
|
| 233 |
+
"""
|
| 234 |
+
cdef _Element _element
|
| 235 |
+
def __init__(self, _Element element not None, *, namespaces=None,
|
| 236 |
+
extensions=None, regexp=True, smart_strings=True):
|
| 237 |
+
cdef xpath.xmlXPathContext* xpathCtxt
|
| 238 |
+
cdef int ns_register_status
|
| 239 |
+
cdef _Document doc
|
| 240 |
+
_assertValidNode(element)
|
| 241 |
+
_assertValidDoc(element._doc)
|
| 242 |
+
self._element = element
|
| 243 |
+
doc = element._doc
|
| 244 |
+
_XPathEvaluatorBase.__init__(self, namespaces, extensions,
|
| 245 |
+
regexp, smart_strings)
|
| 246 |
+
xpathCtxt = xpath.xmlXPathNewContext(doc._c_doc)
|
| 247 |
+
if xpathCtxt is NULL:
|
| 248 |
+
raise MemoryError()
|
| 249 |
+
self.set_context(xpathCtxt)
|
| 250 |
+
|
| 251 |
+
def register_namespace(self, prefix, uri):
|
| 252 |
+
"""Register a namespace with the XPath context.
|
| 253 |
+
"""
|
| 254 |
+
assert self._xpathCtxt is not NULL, "XPath context not initialised"
|
| 255 |
+
self._context.addNamespace(prefix, uri)
|
| 256 |
+
|
| 257 |
+
def register_namespaces(self, namespaces):
|
| 258 |
+
"""Register a prefix -> uri dict.
|
| 259 |
+
"""
|
| 260 |
+
assert self._xpathCtxt is not NULL, "XPath context not initialised"
|
| 261 |
+
for prefix, uri in namespaces.items():
|
| 262 |
+
self._context.addNamespace(prefix, uri)
|
| 263 |
+
|
| 264 |
+
def __call__(self, _path, **_variables):
|
| 265 |
+
"""__call__(self, _path, **_variables)
|
| 266 |
+
|
| 267 |
+
Evaluate an XPath expression on the document.
|
| 268 |
+
|
| 269 |
+
Variables may be provided as keyword arguments. Note that namespaces
|
| 270 |
+
are currently not supported for variables.
|
| 271 |
+
|
| 272 |
+
Absolute XPath expressions (starting with '/') will be evaluated
|
| 273 |
+
against the ElementTree as returned by getroottree().
|
| 274 |
+
"""
|
| 275 |
+
cdef xpath.xmlXPathObject* xpathObj
|
| 276 |
+
cdef _Document doc
|
| 277 |
+
assert self._xpathCtxt is not NULL, "XPath context not initialised"
|
| 278 |
+
path = _utf8(_path)
|
| 279 |
+
doc = self._element._doc
|
| 280 |
+
|
| 281 |
+
self._lock()
|
| 282 |
+
self._xpathCtxt.node = self._element._c_node
|
| 283 |
+
try:
|
| 284 |
+
self._context.register_context(doc)
|
| 285 |
+
self._context.registerVariables(_variables)
|
| 286 |
+
c_path = _xcstr(path)
|
| 287 |
+
with nogil:
|
| 288 |
+
xpathObj = xpath.xmlXPathEvalExpression(
|
| 289 |
+
c_path, self._xpathCtxt)
|
| 290 |
+
result = self._handle_result(xpathObj, doc)
|
| 291 |
+
finally:
|
| 292 |
+
self._context.unregister_context()
|
| 293 |
+
self._unlock()
|
| 294 |
+
|
| 295 |
+
return result
|
| 296 |
+
|
| 297 |
+
|
| 298 |
+
cdef class XPathDocumentEvaluator(XPathElementEvaluator):
|
| 299 |
+
"""XPathDocumentEvaluator(self, etree, namespaces=None, extensions=None, regexp=True, smart_strings=True)
|
| 300 |
+
Create an XPath evaluator for an ElementTree.
|
| 301 |
+
|
| 302 |
+
Additional namespace declarations can be passed with the
|
| 303 |
+
'namespace' keyword argument. EXSLT regular expression support
|
| 304 |
+
can be disabled with the 'regexp' boolean keyword (defaults to
|
| 305 |
+
True). Smart strings will be returned for string results unless
|
| 306 |
+
you pass ``smart_strings=False``.
|
| 307 |
+
"""
|
| 308 |
+
def __init__(self, _ElementTree etree not None, *, namespaces=None,
|
| 309 |
+
extensions=None, regexp=True, smart_strings=True):
|
| 310 |
+
XPathElementEvaluator.__init__(
|
| 311 |
+
self, etree._context_node, namespaces=namespaces,
|
| 312 |
+
extensions=extensions, regexp=regexp,
|
| 313 |
+
smart_strings=smart_strings)
|
| 314 |
+
|
| 315 |
+
def __call__(self, _path, **_variables):
|
| 316 |
+
"""__call__(self, _path, **_variables)
|
| 317 |
+
|
| 318 |
+
Evaluate an XPath expression on the document.
|
| 319 |
+
|
| 320 |
+
Variables may be provided as keyword arguments. Note that namespaces
|
| 321 |
+
are currently not supported for variables.
|
| 322 |
+
"""
|
| 323 |
+
cdef xpath.xmlXPathObject* xpathObj
|
| 324 |
+
cdef xmlDoc* c_doc
|
| 325 |
+
cdef _Document doc
|
| 326 |
+
assert self._xpathCtxt is not NULL, "XPath context not initialised"
|
| 327 |
+
path = _utf8(_path)
|
| 328 |
+
doc = self._element._doc
|
| 329 |
+
|
| 330 |
+
self._lock()
|
| 331 |
+
try:
|
| 332 |
+
self._context.register_context(doc)
|
| 333 |
+
c_doc = _fakeRootDoc(doc._c_doc, self._element._c_node)
|
| 334 |
+
try:
|
| 335 |
+
self._context.registerVariables(_variables)
|
| 336 |
+
c_path = _xcstr(path)
|
| 337 |
+
with nogil:
|
| 338 |
+
self._xpathCtxt.doc = c_doc
|
| 339 |
+
self._xpathCtxt.node = tree.xmlDocGetRootElement(c_doc)
|
| 340 |
+
xpathObj = xpath.xmlXPathEvalExpression(
|
| 341 |
+
c_path, self._xpathCtxt)
|
| 342 |
+
result = self._handle_result(xpathObj, doc)
|
| 343 |
+
finally:
|
| 344 |
+
_destroyFakeDoc(doc._c_doc, c_doc)
|
| 345 |
+
self._context.unregister_context()
|
| 346 |
+
finally:
|
| 347 |
+
self._unlock()
|
| 348 |
+
|
| 349 |
+
return result
|
| 350 |
+
|
| 351 |
+
|
| 352 |
+
def XPathEvaluator(etree_or_element, *, namespaces=None, extensions=None,
|
| 353 |
+
regexp=True, smart_strings=True):
|
| 354 |
+
"""XPathEvaluator(etree_or_element, namespaces=None, extensions=None, regexp=True, smart_strings=True)
|
| 355 |
+
|
| 356 |
+
Creates an XPath evaluator for an ElementTree or an Element.
|
| 357 |
+
|
| 358 |
+
The resulting object can be called with an XPath expression as argument
|
| 359 |
+
and XPath variables provided as keyword arguments.
|
| 360 |
+
|
| 361 |
+
Additional namespace declarations can be passed with the
|
| 362 |
+
'namespace' keyword argument. EXSLT regular expression support
|
| 363 |
+
can be disabled with the 'regexp' boolean keyword (defaults to
|
| 364 |
+
True). Smart strings will be returned for string results unless
|
| 365 |
+
you pass ``smart_strings=False``.
|
| 366 |
+
"""
|
| 367 |
+
if isinstance(etree_or_element, _ElementTree):
|
| 368 |
+
return XPathDocumentEvaluator(
|
| 369 |
+
etree_or_element, namespaces=namespaces,
|
| 370 |
+
extensions=extensions, regexp=regexp, smart_strings=smart_strings)
|
| 371 |
+
else:
|
| 372 |
+
return XPathElementEvaluator(
|
| 373 |
+
etree_or_element, namespaces=namespaces,
|
| 374 |
+
extensions=extensions, regexp=regexp, smart_strings=smart_strings)
|
| 375 |
+
|
| 376 |
+
|
| 377 |
+
cdef class XPath(_XPathEvaluatorBase):
|
| 378 |
+
"""XPath(self, path, namespaces=None, extensions=None, regexp=True, smart_strings=True)
|
| 379 |
+
A compiled XPath expression that can be called on Elements and ElementTrees.
|
| 380 |
+
|
| 381 |
+
Besides the XPath expression, you can pass prefix-namespace
|
| 382 |
+
mappings and extension functions to the constructor through the
|
| 383 |
+
keyword arguments ``namespaces`` and ``extensions``. EXSLT
|
| 384 |
+
regular expression support can be disabled with the 'regexp'
|
| 385 |
+
boolean keyword (defaults to True). Smart strings will be
|
| 386 |
+
returned for string results unless you pass
|
| 387 |
+
``smart_strings=False``.
|
| 388 |
+
"""
|
| 389 |
+
cdef xpath.xmlXPathCompExpr* _xpath
|
| 390 |
+
cdef bytes _path
|
| 391 |
+
def __cinit__(self):
|
| 392 |
+
self._xpath = NULL
|
| 393 |
+
|
| 394 |
+
def __init__(self, path, *, namespaces=None, extensions=None,
|
| 395 |
+
regexp=True, smart_strings=True):
|
| 396 |
+
cdef xpath.xmlXPathContext* xpathCtxt
|
| 397 |
+
_XPathEvaluatorBase.__init__(self, namespaces, extensions,
|
| 398 |
+
regexp, smart_strings)
|
| 399 |
+
self._path = _utf8(path)
|
| 400 |
+
xpathCtxt = xpath.xmlXPathNewContext(NULL)
|
| 401 |
+
if xpathCtxt is NULL:
|
| 402 |
+
raise MemoryError()
|
| 403 |
+
self.set_context(xpathCtxt)
|
| 404 |
+
self._xpath = xpath.xmlXPathCtxtCompile(xpathCtxt, _xcstr(self._path))
|
| 405 |
+
if self._xpath is NULL:
|
| 406 |
+
raise self._build_parse_error()
|
| 407 |
+
|
| 408 |
+
def __call__(self, _etree_or_element, **_variables):
|
| 409 |
+
"__call__(self, _etree_or_element, **_variables)"
|
| 410 |
+
cdef xpath.xmlXPathObject* xpathObj
|
| 411 |
+
cdef _Document document
|
| 412 |
+
cdef _Element element
|
| 413 |
+
|
| 414 |
+
assert self._xpathCtxt is not NULL, "XPath context not initialised"
|
| 415 |
+
document = _documentOrRaise(_etree_or_element)
|
| 416 |
+
element = _rootNodeOrRaise(_etree_or_element)
|
| 417 |
+
|
| 418 |
+
self._lock()
|
| 419 |
+
self._xpathCtxt.doc = document._c_doc
|
| 420 |
+
self._xpathCtxt.node = element._c_node
|
| 421 |
+
|
| 422 |
+
try:
|
| 423 |
+
self._context.register_context(document)
|
| 424 |
+
self._context.registerVariables(_variables)
|
| 425 |
+
with nogil:
|
| 426 |
+
xpathObj = xpath.xmlXPathCompiledEval(
|
| 427 |
+
self._xpath, self._xpathCtxt)
|
| 428 |
+
result = self._handle_result(xpathObj, document)
|
| 429 |
+
finally:
|
| 430 |
+
self._context.unregister_context()
|
| 431 |
+
self._unlock()
|
| 432 |
+
return result
|
| 433 |
+
|
| 434 |
+
@property
|
| 435 |
+
def path(self):
|
| 436 |
+
"""The literal XPath expression.
|
| 437 |
+
"""
|
| 438 |
+
return self._path.decode('UTF-8')
|
| 439 |
+
|
| 440 |
+
def __dealloc__(self):
|
| 441 |
+
if self._xpath is not NULL:
|
| 442 |
+
xpath.xmlXPathFreeCompExpr(self._xpath)
|
| 443 |
+
|
| 444 |
+
def __repr__(self):
|
| 445 |
+
return self.path
|
| 446 |
+
|
| 447 |
+
|
| 448 |
+
cdef object _replace_strings = re.compile(b'("[^"]*")|(\'[^\']*\')').sub
|
| 449 |
+
cdef object _find_namespaces = re.compile(b'({[^}]+})').findall
|
| 450 |
+
|
| 451 |
+
cdef class ETXPath(XPath):
|
| 452 |
+
"""ETXPath(self, path, extensions=None, regexp=True, smart_strings=True)
|
| 453 |
+
Special XPath class that supports the ElementTree {uri} notation for namespaces.
|
| 454 |
+
|
| 455 |
+
Note that this class does not accept the ``namespace`` keyword
|
| 456 |
+
argument. All namespaces must be passed as part of the path
|
| 457 |
+
string. Smart strings will be returned for string results unless
|
| 458 |
+
you pass ``smart_strings=False``.
|
| 459 |
+
"""
|
| 460 |
+
def __init__(self, path, *, extensions=None, regexp=True,
|
| 461 |
+
smart_strings=True):
|
| 462 |
+
path, namespaces = self._nsextract_path(path)
|
| 463 |
+
XPath.__init__(self, path, namespaces=namespaces,
|
| 464 |
+
extensions=extensions, regexp=regexp,
|
| 465 |
+
smart_strings=smart_strings)
|
| 466 |
+
|
| 467 |
+
cdef _nsextract_path(self, path):
|
| 468 |
+
# replace {namespaces} by new prefixes
|
| 469 |
+
cdef dict namespaces = {}
|
| 470 |
+
cdef list namespace_defs = []
|
| 471 |
+
cdef int i
|
| 472 |
+
path_utf = _utf8(path)
|
| 473 |
+
stripped_path = _replace_strings(b'', path_utf) # remove string literals
|
| 474 |
+
i = 1
|
| 475 |
+
for namespace_def in _find_namespaces(stripped_path):
|
| 476 |
+
if namespace_def not in namespace_defs:
|
| 477 |
+
prefix = python.PyBytes_FromFormat("__xpp%02d", i)
|
| 478 |
+
i += 1
|
| 479 |
+
namespace_defs.append(namespace_def)
|
| 480 |
+
namespace = namespace_def[1:-1] # remove '{}'
|
| 481 |
+
namespace = (<bytes>namespace).decode('utf8')
|
| 482 |
+
namespaces[prefix.decode('utf8')] = namespace
|
| 483 |
+
prefix_str = prefix + b':'
|
| 484 |
+
# FIXME: this also replaces {namespaces} within strings!
|
| 485 |
+
path_utf = path_utf.replace(namespace_def, prefix_str)
|
| 486 |
+
path = path_utf.decode('utf8')
|
| 487 |
+
return path, namespaces
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/lxml/xsltext.pxi
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# XSLT extension elements
|
| 2 |
+
|
| 3 |
+
cdef class XSLTExtension:
|
| 4 |
+
"""Base class of an XSLT extension element.
|
| 5 |
+
"""
|
| 6 |
+
def execute(self, context, self_node, input_node, output_parent):
|
| 7 |
+
"""execute(self, context, self_node, input_node, output_parent)
|
| 8 |
+
Execute this extension element.
|
| 9 |
+
|
| 10 |
+
Subclasses must override this method. They may append
|
| 11 |
+
elements to the `output_parent` element here, or set its text
|
| 12 |
+
content. To this end, the `input_node` provides read-only
|
| 13 |
+
access to the current node in the input document, and the
|
| 14 |
+
`self_node` points to the extension element in the stylesheet.
|
| 15 |
+
|
| 16 |
+
Note that the `output_parent` parameter may be `None` if there
|
| 17 |
+
is no parent element in the current context (e.g. no content
|
| 18 |
+
was added to the output tree yet).
|
| 19 |
+
"""
|
| 20 |
+
pass
|
| 21 |
+
|
| 22 |
+
def apply_templates(self, _XSLTContext context not None, node, output_parent=None,
|
| 23 |
+
*, elements_only=False, remove_blank_text=False):
|
| 24 |
+
"""apply_templates(self, context, node, output_parent=None, elements_only=False, remove_blank_text=False)
|
| 25 |
+
|
| 26 |
+
Call this method to retrieve the result of applying templates
|
| 27 |
+
to an element.
|
| 28 |
+
|
| 29 |
+
The return value is a list of elements or text strings that
|
| 30 |
+
were generated by the XSLT processor. If you pass
|
| 31 |
+
``elements_only=True``, strings will be discarded from the result
|
| 32 |
+
list. The option ``remove_blank_text=True`` will only discard
|
| 33 |
+
strings that consist entirely of whitespace (e.g. formatting).
|
| 34 |
+
These options do not apply to Elements, only to bare string results.
|
| 35 |
+
|
| 36 |
+
If you pass an Element as `output_parent` parameter, the result
|
| 37 |
+
will instead be appended to the element (including attributes
|
| 38 |
+
etc.) and the return value will be `None`. This is a safe way
|
| 39 |
+
to generate content into the output document directly, without
|
| 40 |
+
having to take care of special values like text or attributes.
|
| 41 |
+
Note that the string discarding options will be ignored in this
|
| 42 |
+
case.
|
| 43 |
+
"""
|
| 44 |
+
cdef xmlNode* c_parent
|
| 45 |
+
cdef xmlNode* c_node
|
| 46 |
+
cdef xmlNode* c_context_node
|
| 47 |
+
assert context._xsltCtxt is not NULL, "XSLT context not initialised"
|
| 48 |
+
c_context_node = _roNodeOf(node)
|
| 49 |
+
#assert c_context_node.doc is context._xsltContext.node.doc, \
|
| 50 |
+
# "switching input documents during transformation is not currently supported"
|
| 51 |
+
|
| 52 |
+
if output_parent is not None:
|
| 53 |
+
c_parent = _nonRoNodeOf(output_parent)
|
| 54 |
+
else:
|
| 55 |
+
c_parent = tree.xmlNewDocNode(
|
| 56 |
+
context._xsltCtxt.output, NULL, <unsigned char*>"fake-parent", NULL)
|
| 57 |
+
|
| 58 |
+
c_node = context._xsltCtxt.insert
|
| 59 |
+
context._xsltCtxt.insert = c_parent
|
| 60 |
+
xslt.xsltProcessOneNode(
|
| 61 |
+
context._xsltCtxt, c_context_node, NULL)
|
| 62 |
+
context._xsltCtxt.insert = c_node
|
| 63 |
+
|
| 64 |
+
if output_parent is not None:
|
| 65 |
+
return None
|
| 66 |
+
|
| 67 |
+
try:
|
| 68 |
+
return self._collectXSLTResultContent(
|
| 69 |
+
context, c_parent, elements_only, remove_blank_text)
|
| 70 |
+
finally:
|
| 71 |
+
# free all intermediate nodes that will not be freed by proxies
|
| 72 |
+
tree.xmlFreeNode(c_parent)
|
| 73 |
+
|
| 74 |
+
def process_children(self, _XSLTContext context not None, output_parent=None,
|
| 75 |
+
*, elements_only=False, remove_blank_text=False):
|
| 76 |
+
"""process_children(self, context, output_parent=None, elements_only=False, remove_blank_text=False)
|
| 77 |
+
|
| 78 |
+
Call this method to process the XSLT content of the extension
|
| 79 |
+
element itself.
|
| 80 |
+
|
| 81 |
+
The return value is a list of elements or text strings that
|
| 82 |
+
were generated by the XSLT processor. If you pass
|
| 83 |
+
``elements_only=True``, strings will be discarded from the result
|
| 84 |
+
list. The option ``remove_blank_text=True`` will only discard
|
| 85 |
+
strings that consist entirely of whitespace (e.g. formatting).
|
| 86 |
+
These options do not apply to Elements, only to bare string results.
|
| 87 |
+
|
| 88 |
+
If you pass an Element as `output_parent` parameter, the result
|
| 89 |
+
will instead be appended to the element (including attributes
|
| 90 |
+
etc.) and the return value will be `None`. This is a safe way
|
| 91 |
+
to generate content into the output document directly, without
|
| 92 |
+
having to take care of special values like text or attributes.
|
| 93 |
+
Note that the string discarding options will be ignored in this
|
| 94 |
+
case.
|
| 95 |
+
"""
|
| 96 |
+
cdef xmlNode* c_parent
|
| 97 |
+
cdef xslt.xsltTransformContext* c_ctxt = context._xsltCtxt
|
| 98 |
+
cdef xmlNode* c_old_output_parent = c_ctxt.insert
|
| 99 |
+
assert context._xsltCtxt is not NULL, "XSLT context not initialised"
|
| 100 |
+
|
| 101 |
+
# output_parent node is used for adding results instead of
|
| 102 |
+
# elements list used in apply_templates, that's easier and allows to
|
| 103 |
+
# use attributes added to extension element with <xsl:attribute>.
|
| 104 |
+
|
| 105 |
+
if output_parent is not None:
|
| 106 |
+
c_parent = _nonRoNodeOf(output_parent)
|
| 107 |
+
else:
|
| 108 |
+
c_parent = tree.xmlNewDocNode(
|
| 109 |
+
context._xsltCtxt.output, NULL, <unsigned char*>"fake-parent", NULL)
|
| 110 |
+
|
| 111 |
+
c_ctxt.insert = c_parent
|
| 112 |
+
xslt.xsltApplyOneTemplate(c_ctxt,
|
| 113 |
+
c_ctxt.node, c_ctxt.inst.children, NULL, NULL)
|
| 114 |
+
c_ctxt.insert = c_old_output_parent
|
| 115 |
+
|
| 116 |
+
if output_parent is not None:
|
| 117 |
+
return None
|
| 118 |
+
|
| 119 |
+
try:
|
| 120 |
+
return self._collectXSLTResultContent(
|
| 121 |
+
context, c_parent, elements_only, remove_blank_text)
|
| 122 |
+
finally:
|
| 123 |
+
# free all intermediate nodes that will not be freed by proxies
|
| 124 |
+
tree.xmlFreeNode(c_parent)
|
| 125 |
+
|
| 126 |
+
cdef _collectXSLTResultContent(self, _XSLTContext context, xmlNode* c_parent,
|
| 127 |
+
bint elements_only, bint remove_blank_text):
|
| 128 |
+
cdef xmlNode* c_node
|
| 129 |
+
cdef xmlNode* c_next
|
| 130 |
+
cdef _ReadOnlyProxy proxy
|
| 131 |
+
cdef list results = [] # or maybe _collectAttributes(c_parent, 2) ?
|
| 132 |
+
c_node = c_parent.children
|
| 133 |
+
while c_node is not NULL:
|
| 134 |
+
c_next = c_node.next
|
| 135 |
+
if c_node.type == tree.XML_TEXT_NODE:
|
| 136 |
+
if not elements_only:
|
| 137 |
+
s = funicode(c_node.content)
|
| 138 |
+
if not remove_blank_text or s.strip():
|
| 139 |
+
results.append(s)
|
| 140 |
+
s = None
|
| 141 |
+
elif c_node.type == tree.XML_ELEMENT_NODE:
|
| 142 |
+
proxy = _newReadOnlyProxy(
|
| 143 |
+
context._extension_element_proxy, c_node)
|
| 144 |
+
results.append(proxy)
|
| 145 |
+
# unlink node and make sure it will be freed later on
|
| 146 |
+
tree.xmlUnlinkNode(c_node)
|
| 147 |
+
proxy.free_after_use()
|
| 148 |
+
else:
|
| 149 |
+
raise TypeError, \
|
| 150 |
+
f"unsupported XSLT result type: {c_node.type}"
|
| 151 |
+
c_node = c_next
|
| 152 |
+
return results
|
| 153 |
+
|
| 154 |
+
|
| 155 |
+
cdef _registerXSLTExtensions(xslt.xsltTransformContext* c_ctxt,
|
| 156 |
+
extension_dict):
|
| 157 |
+
for ns_utf, name_utf in extension_dict:
|
| 158 |
+
xslt.xsltRegisterExtElement(
|
| 159 |
+
c_ctxt, _xcstr(name_utf), _xcstr(ns_utf),
|
| 160 |
+
<xslt.xsltTransformFunction>_callExtensionElement)
|
| 161 |
+
|
| 162 |
+
cdef void _callExtensionElement(xslt.xsltTransformContext* c_ctxt,
|
| 163 |
+
xmlNode* c_context_node,
|
| 164 |
+
xmlNode* c_inst_node,
|
| 165 |
+
void* dummy) noexcept with gil:
|
| 166 |
+
cdef _XSLTContext context
|
| 167 |
+
cdef XSLTExtension extension
|
| 168 |
+
cdef python.PyObject* dict_result
|
| 169 |
+
cdef xmlNode* c_node
|
| 170 |
+
cdef _ReadOnlyProxy context_node = None, self_node = None
|
| 171 |
+
cdef object output_parent # not restricted to ro-nodes
|
| 172 |
+
c_uri = _getNs(c_inst_node)
|
| 173 |
+
if c_uri is NULL:
|
| 174 |
+
# not allowed, and should never happen
|
| 175 |
+
return
|
| 176 |
+
if c_ctxt.xpathCtxt.userData is NULL:
|
| 177 |
+
# just for safety, should never happen
|
| 178 |
+
return
|
| 179 |
+
context = <_XSLTContext>c_ctxt.xpathCtxt.userData
|
| 180 |
+
try:
|
| 181 |
+
try:
|
| 182 |
+
dict_result = python.PyDict_GetItem(
|
| 183 |
+
context._extension_elements, (c_uri, c_inst_node.name))
|
| 184 |
+
if dict_result is NULL:
|
| 185 |
+
raise KeyError, f"extension element {funicode(c_inst_node.name)} not found"
|
| 186 |
+
extension = <object>dict_result
|
| 187 |
+
|
| 188 |
+
try:
|
| 189 |
+
# build the context proxy nodes
|
| 190 |
+
self_node = _newReadOnlyProxy(None, c_inst_node)
|
| 191 |
+
if _isElement(c_ctxt.insert):
|
| 192 |
+
output_parent = _newAppendOnlyProxy(self_node, c_ctxt.insert)
|
| 193 |
+
else:
|
| 194 |
+
# may be the document node or other stuff
|
| 195 |
+
output_parent = _newOpaqueAppendOnlyNodeWrapper(c_ctxt.insert)
|
| 196 |
+
if c_context_node.type in (tree.XML_DOCUMENT_NODE,
|
| 197 |
+
tree.XML_HTML_DOCUMENT_NODE):
|
| 198 |
+
c_node = tree.xmlDocGetRootElement(<xmlDoc*>c_context_node)
|
| 199 |
+
if c_node is not NULL:
|
| 200 |
+
context_node = _newReadOnlyProxy(self_node, c_node)
|
| 201 |
+
else:
|
| 202 |
+
context_node = None
|
| 203 |
+
elif c_context_node.type in (tree.XML_ATTRIBUTE_NODE,
|
| 204 |
+
tree.XML_TEXT_NODE,
|
| 205 |
+
tree.XML_CDATA_SECTION_NODE):
|
| 206 |
+
# this isn't easy to support using read-only
|
| 207 |
+
# nodes, as the smart-string factory must
|
| 208 |
+
# instantiate the parent proxy somehow...
|
| 209 |
+
raise TypeError(f"Unsupported element type: {c_context_node.type}")
|
| 210 |
+
else:
|
| 211 |
+
context_node = _newReadOnlyProxy(self_node, c_context_node)
|
| 212 |
+
|
| 213 |
+
# run the XSLT extension
|
| 214 |
+
context._extension_element_proxy = self_node
|
| 215 |
+
extension.execute(context, self_node, context_node, output_parent)
|
| 216 |
+
finally:
|
| 217 |
+
context._extension_element_proxy = None
|
| 218 |
+
if self_node is not None:
|
| 219 |
+
_freeReadOnlyProxies(self_node)
|
| 220 |
+
except Exception as e:
|
| 221 |
+
try:
|
| 222 |
+
e = unicode(e).encode("UTF-8")
|
| 223 |
+
except:
|
| 224 |
+
e = repr(e).encode("UTF-8")
|
| 225 |
+
message = python.PyBytes_FromFormat(
|
| 226 |
+
"Error executing extension element '%s': %s",
|
| 227 |
+
c_inst_node.name, _cstr(e))
|
| 228 |
+
xslt.xsltTransformError(c_ctxt, NULL, c_inst_node, "%s", message)
|
| 229 |
+
context._exc._store_raised()
|
| 230 |
+
except:
|
| 231 |
+
# just in case
|
| 232 |
+
message = python.PyBytes_FromFormat(
|
| 233 |
+
"Error executing extension element '%s'", c_inst_node.name)
|
| 234 |
+
xslt.xsltTransformError(c_ctxt, NULL, c_inst_node, "%s", message)
|
| 235 |
+
context._exc._store_raised()
|
| 236 |
+
except:
|
| 237 |
+
# no Python functions here - everything can fail...
|
| 238 |
+
xslt.xsltTransformError(c_ctxt, NULL, c_inst_node,
|
| 239 |
+
"Error during XSLT extension element evaluation")
|
| 240 |
+
context._exc._store_raised()
|
| 241 |
+
finally:
|
| 242 |
+
return # swallow any further exceptions
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/__init__.py
ADDED
|
@@ -0,0 +1,975 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2020 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
# When adding a new object to this init, remember to add it twice: once inside the `_import_structure` dictionary and
|
| 16 |
+
# once inside the `if TYPE_CHECKING` branch. The `TYPE_CHECKING` should have import statements as usual, but they are
|
| 17 |
+
# only there for type checking. The `_import_structure` is a dictionary submodule to list of object names, and is used
|
| 18 |
+
# to defer the actual importing for when the objects are requested. This way `import transformers` provides the names
|
| 19 |
+
# in the namespace without actually importing anything (and especially none of the backends).
|
| 20 |
+
|
| 21 |
+
__version__ = "4.57.6"
|
| 22 |
+
|
| 23 |
+
from pathlib import Path
|
| 24 |
+
from typing import TYPE_CHECKING
|
| 25 |
+
|
| 26 |
+
# Check the dependencies satisfy the minimal versions required.
|
| 27 |
+
from . import dependency_versions_check
|
| 28 |
+
from .utils import (
|
| 29 |
+
OptionalDependencyNotAvailable,
|
| 30 |
+
_LazyModule,
|
| 31 |
+
is_essentia_available,
|
| 32 |
+
is_g2p_en_available,
|
| 33 |
+
is_librosa_available,
|
| 34 |
+
is_mistral_common_available,
|
| 35 |
+
is_mlx_available,
|
| 36 |
+
is_pretty_midi_available,
|
| 37 |
+
)
|
| 38 |
+
|
| 39 |
+
# Note: the following symbols are deliberately exported with `as`
|
| 40 |
+
# so that mypy, pylint or other static linters can recognize them,
|
| 41 |
+
# given that they are not exported using `__all__` in this file.
|
| 42 |
+
from .utils import is_bitsandbytes_available as is_bitsandbytes_available
|
| 43 |
+
from .utils import is_flax_available as is_flax_available
|
| 44 |
+
from .utils import is_keras_nlp_available as is_keras_nlp_available
|
| 45 |
+
from .utils import is_scipy_available as is_scipy_available
|
| 46 |
+
from .utils import is_sentencepiece_available as is_sentencepiece_available
|
| 47 |
+
from .utils import is_speech_available as is_speech_available
|
| 48 |
+
from .utils import is_tensorflow_text_available as is_tensorflow_text_available
|
| 49 |
+
from .utils import is_tf_available as is_tf_available
|
| 50 |
+
from .utils import is_timm_available as is_timm_available
|
| 51 |
+
from .utils import is_tokenizers_available as is_tokenizers_available
|
| 52 |
+
from .utils import is_torch_available as is_torch_available
|
| 53 |
+
from .utils import is_torchaudio_available as is_torchaudio_available
|
| 54 |
+
from .utils import is_torchvision_available as is_torchvision_available
|
| 55 |
+
from .utils import is_vision_available as is_vision_available
|
| 56 |
+
from .utils import logging as logging
|
| 57 |
+
from .utils.import_utils import define_import_structure
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
logger = logging.get_logger(__name__) # pylint: disable=invalid-name
|
| 61 |
+
|
| 62 |
+
# Base objects, independent of any specific backend
|
| 63 |
+
_import_structure = {
|
| 64 |
+
"audio_utils": [],
|
| 65 |
+
"commands": [],
|
| 66 |
+
"configuration_utils": ["PretrainedConfig"],
|
| 67 |
+
"convert_graph_to_onnx": [],
|
| 68 |
+
"convert_slow_tokenizers_checkpoints_to_fast": [],
|
| 69 |
+
"convert_tf_hub_seq_to_seq_bert_to_pytorch": [],
|
| 70 |
+
"data": [
|
| 71 |
+
"DataProcessor",
|
| 72 |
+
"InputExample",
|
| 73 |
+
"InputFeatures",
|
| 74 |
+
"SingleSentenceClassificationProcessor",
|
| 75 |
+
"SquadExample",
|
| 76 |
+
"SquadFeatures",
|
| 77 |
+
"SquadV1Processor",
|
| 78 |
+
"SquadV2Processor",
|
| 79 |
+
"glue_compute_metrics",
|
| 80 |
+
"glue_convert_examples_to_features",
|
| 81 |
+
"glue_output_modes",
|
| 82 |
+
"glue_processors",
|
| 83 |
+
"glue_tasks_num_labels",
|
| 84 |
+
"squad_convert_examples_to_features",
|
| 85 |
+
"xnli_compute_metrics",
|
| 86 |
+
"xnli_output_modes",
|
| 87 |
+
"xnli_processors",
|
| 88 |
+
"xnli_tasks_num_labels",
|
| 89 |
+
],
|
| 90 |
+
"data.data_collator": [
|
| 91 |
+
"DataCollator",
|
| 92 |
+
"DataCollatorForLanguageModeling",
|
| 93 |
+
"DataCollatorForMultipleChoice",
|
| 94 |
+
"DataCollatorForPermutationLanguageModeling",
|
| 95 |
+
"DataCollatorForSeq2Seq",
|
| 96 |
+
"DataCollatorForSOP",
|
| 97 |
+
"DataCollatorForTokenClassification",
|
| 98 |
+
"DataCollatorForWholeWordMask",
|
| 99 |
+
"DataCollatorWithFlattening",
|
| 100 |
+
"DataCollatorWithPadding",
|
| 101 |
+
"DefaultDataCollator",
|
| 102 |
+
"default_data_collator",
|
| 103 |
+
],
|
| 104 |
+
"data.metrics": [],
|
| 105 |
+
"data.processors": [],
|
| 106 |
+
"debug_utils": [],
|
| 107 |
+
"dependency_versions_check": [],
|
| 108 |
+
"dependency_versions_table": [],
|
| 109 |
+
"dynamic_module_utils": [],
|
| 110 |
+
"feature_extraction_sequence_utils": ["SequenceFeatureExtractor"],
|
| 111 |
+
"feature_extraction_utils": ["BatchFeature", "FeatureExtractionMixin"],
|
| 112 |
+
"file_utils": [],
|
| 113 |
+
"generation": [
|
| 114 |
+
"AsyncTextIteratorStreamer",
|
| 115 |
+
"CompileConfig",
|
| 116 |
+
"GenerationConfig",
|
| 117 |
+
"TextIteratorStreamer",
|
| 118 |
+
"TextStreamer",
|
| 119 |
+
"WatermarkingConfig",
|
| 120 |
+
],
|
| 121 |
+
"hf_argparser": ["HfArgumentParser"],
|
| 122 |
+
"hyperparameter_search": [],
|
| 123 |
+
"image_transforms": [],
|
| 124 |
+
"integrations": [
|
| 125 |
+
"is_clearml_available",
|
| 126 |
+
"is_comet_available",
|
| 127 |
+
"is_dvclive_available",
|
| 128 |
+
"is_neptune_available",
|
| 129 |
+
"is_optuna_available",
|
| 130 |
+
"is_ray_available",
|
| 131 |
+
"is_ray_tune_available",
|
| 132 |
+
"is_sigopt_available",
|
| 133 |
+
"is_swanlab_available",
|
| 134 |
+
"is_tensorboard_available",
|
| 135 |
+
"is_trackio_available",
|
| 136 |
+
"is_wandb_available",
|
| 137 |
+
],
|
| 138 |
+
"loss": [],
|
| 139 |
+
"modelcard": ["ModelCard"],
|
| 140 |
+
# Losses
|
| 141 |
+
"modeling_tf_pytorch_utils": [
|
| 142 |
+
"convert_tf_weight_name_to_pt_weight_name",
|
| 143 |
+
"load_pytorch_checkpoint_in_tf2_model",
|
| 144 |
+
"load_pytorch_model_in_tf2_model",
|
| 145 |
+
"load_pytorch_weights_in_tf2_model",
|
| 146 |
+
"load_tf2_checkpoint_in_pytorch_model",
|
| 147 |
+
"load_tf2_model_in_pytorch_model",
|
| 148 |
+
"load_tf2_weights_in_pytorch_model",
|
| 149 |
+
],
|
| 150 |
+
# Models
|
| 151 |
+
"onnx": [],
|
| 152 |
+
"pipelines": [
|
| 153 |
+
"AudioClassificationPipeline",
|
| 154 |
+
"AutomaticSpeechRecognitionPipeline",
|
| 155 |
+
"CsvPipelineDataFormat",
|
| 156 |
+
"DepthEstimationPipeline",
|
| 157 |
+
"DocumentQuestionAnsweringPipeline",
|
| 158 |
+
"FeatureExtractionPipeline",
|
| 159 |
+
"FillMaskPipeline",
|
| 160 |
+
"ImageClassificationPipeline",
|
| 161 |
+
"ImageFeatureExtractionPipeline",
|
| 162 |
+
"ImageSegmentationPipeline",
|
| 163 |
+
"ImageTextToTextPipeline",
|
| 164 |
+
"ImageToImagePipeline",
|
| 165 |
+
"ImageToTextPipeline",
|
| 166 |
+
"JsonPipelineDataFormat",
|
| 167 |
+
"KeypointMatchingPipeline",
|
| 168 |
+
"MaskGenerationPipeline",
|
| 169 |
+
"NerPipeline",
|
| 170 |
+
"ObjectDetectionPipeline",
|
| 171 |
+
"PipedPipelineDataFormat",
|
| 172 |
+
"Pipeline",
|
| 173 |
+
"PipelineDataFormat",
|
| 174 |
+
"QuestionAnsweringPipeline",
|
| 175 |
+
"SummarizationPipeline",
|
| 176 |
+
"TableQuestionAnsweringPipeline",
|
| 177 |
+
"Text2TextGenerationPipeline",
|
| 178 |
+
"TextClassificationPipeline",
|
| 179 |
+
"TextGenerationPipeline",
|
| 180 |
+
"TextToAudioPipeline",
|
| 181 |
+
"TokenClassificationPipeline",
|
| 182 |
+
"TranslationPipeline",
|
| 183 |
+
"VideoClassificationPipeline",
|
| 184 |
+
"VisualQuestionAnsweringPipeline",
|
| 185 |
+
"ZeroShotAudioClassificationPipeline",
|
| 186 |
+
"ZeroShotClassificationPipeline",
|
| 187 |
+
"ZeroShotImageClassificationPipeline",
|
| 188 |
+
"ZeroShotObjectDetectionPipeline",
|
| 189 |
+
"pipeline",
|
| 190 |
+
],
|
| 191 |
+
"processing_utils": ["ProcessorMixin"],
|
| 192 |
+
"quantizers": [],
|
| 193 |
+
"testing_utils": [],
|
| 194 |
+
"tokenization_utils": ["PreTrainedTokenizer"],
|
| 195 |
+
"tokenization_utils_base": [
|
| 196 |
+
"AddedToken",
|
| 197 |
+
"BatchEncoding",
|
| 198 |
+
"CharSpan",
|
| 199 |
+
"PreTrainedTokenizerBase",
|
| 200 |
+
"SpecialTokensMixin",
|
| 201 |
+
"TokenSpan",
|
| 202 |
+
],
|
| 203 |
+
"trainer_callback": [
|
| 204 |
+
"DefaultFlowCallback",
|
| 205 |
+
"EarlyStoppingCallback",
|
| 206 |
+
"PrinterCallback",
|
| 207 |
+
"ProgressCallback",
|
| 208 |
+
"TrainerCallback",
|
| 209 |
+
"TrainerControl",
|
| 210 |
+
"TrainerState",
|
| 211 |
+
],
|
| 212 |
+
"trainer_utils": [
|
| 213 |
+
"EvalPrediction",
|
| 214 |
+
"IntervalStrategy",
|
| 215 |
+
"SchedulerType",
|
| 216 |
+
"enable_full_determinism",
|
| 217 |
+
"set_seed",
|
| 218 |
+
],
|
| 219 |
+
"training_args": ["TrainingArguments"],
|
| 220 |
+
"training_args_seq2seq": ["Seq2SeqTrainingArguments"],
|
| 221 |
+
"training_args_tf": ["TFTrainingArguments"],
|
| 222 |
+
"utils": [
|
| 223 |
+
"CONFIG_NAME",
|
| 224 |
+
"MODEL_CARD_NAME",
|
| 225 |
+
"PYTORCH_PRETRAINED_BERT_CACHE",
|
| 226 |
+
"PYTORCH_TRANSFORMERS_CACHE",
|
| 227 |
+
"SPIECE_UNDERLINE",
|
| 228 |
+
"TF2_WEIGHTS_NAME",
|
| 229 |
+
"TF_WEIGHTS_NAME",
|
| 230 |
+
"TRANSFORMERS_CACHE",
|
| 231 |
+
"WEIGHTS_NAME",
|
| 232 |
+
"TensorType",
|
| 233 |
+
"add_end_docstrings",
|
| 234 |
+
"add_start_docstrings",
|
| 235 |
+
"is_apex_available",
|
| 236 |
+
"is_av_available",
|
| 237 |
+
"is_bitsandbytes_available",
|
| 238 |
+
"is_datasets_available",
|
| 239 |
+
"is_faiss_available",
|
| 240 |
+
"is_flax_available",
|
| 241 |
+
"is_keras_nlp_available",
|
| 242 |
+
"is_matplotlib_available",
|
| 243 |
+
"is_mlx_available",
|
| 244 |
+
"is_phonemizer_available",
|
| 245 |
+
"is_psutil_available",
|
| 246 |
+
"is_py3nvml_available",
|
| 247 |
+
"is_pyctcdecode_available",
|
| 248 |
+
"is_sacremoses_available",
|
| 249 |
+
"is_safetensors_available",
|
| 250 |
+
"is_scipy_available",
|
| 251 |
+
"is_sentencepiece_available",
|
| 252 |
+
"is_sklearn_available",
|
| 253 |
+
"is_speech_available",
|
| 254 |
+
"is_tensorflow_text_available",
|
| 255 |
+
"is_tf_available",
|
| 256 |
+
"is_timm_available",
|
| 257 |
+
"is_tokenizers_available",
|
| 258 |
+
"is_torch_available",
|
| 259 |
+
"is_torch_hpu_available",
|
| 260 |
+
"is_torch_mlu_available",
|
| 261 |
+
"is_torch_musa_available",
|
| 262 |
+
"is_torch_neuroncore_available",
|
| 263 |
+
"is_torch_npu_available",
|
| 264 |
+
"is_torchvision_available",
|
| 265 |
+
"is_torch_xla_available",
|
| 266 |
+
"is_torch_xpu_available",
|
| 267 |
+
"is_vision_available",
|
| 268 |
+
"logging",
|
| 269 |
+
],
|
| 270 |
+
"utils.quantization_config": [
|
| 271 |
+
"AqlmConfig",
|
| 272 |
+
"AutoRoundConfig",
|
| 273 |
+
"AwqConfig",
|
| 274 |
+
"BitNetQuantConfig",
|
| 275 |
+
"BitsAndBytesConfig",
|
| 276 |
+
"CompressedTensorsConfig",
|
| 277 |
+
"EetqConfig",
|
| 278 |
+
"FbgemmFp8Config",
|
| 279 |
+
"FineGrainedFP8Config",
|
| 280 |
+
"GPTQConfig",
|
| 281 |
+
"HiggsConfig",
|
| 282 |
+
"HqqConfig",
|
| 283 |
+
"Mxfp4Config",
|
| 284 |
+
"QuantoConfig",
|
| 285 |
+
"QuarkConfig",
|
| 286 |
+
"FPQuantConfig",
|
| 287 |
+
"SpQRConfig",
|
| 288 |
+
"TorchAoConfig",
|
| 289 |
+
"VptqConfig",
|
| 290 |
+
],
|
| 291 |
+
"video_utils": [],
|
| 292 |
+
}
|
| 293 |
+
|
| 294 |
+
# tokenizers-backed objects
|
| 295 |
+
try:
|
| 296 |
+
if not is_tokenizers_available():
|
| 297 |
+
raise OptionalDependencyNotAvailable()
|
| 298 |
+
except OptionalDependencyNotAvailable:
|
| 299 |
+
from .utils import dummy_tokenizers_objects
|
| 300 |
+
|
| 301 |
+
_import_structure["utils.dummy_tokenizers_objects"] = [
|
| 302 |
+
name for name in dir(dummy_tokenizers_objects) if not name.startswith("_")
|
| 303 |
+
]
|
| 304 |
+
else:
|
| 305 |
+
# Fast tokenizers structure
|
| 306 |
+
_import_structure["tokenization_utils_fast"] = ["PreTrainedTokenizerFast"]
|
| 307 |
+
|
| 308 |
+
|
| 309 |
+
try:
|
| 310 |
+
if not (is_sentencepiece_available() and is_tokenizers_available()):
|
| 311 |
+
raise OptionalDependencyNotAvailable()
|
| 312 |
+
except OptionalDependencyNotAvailable:
|
| 313 |
+
from .utils import dummy_sentencepiece_and_tokenizers_objects
|
| 314 |
+
|
| 315 |
+
_import_structure["utils.dummy_sentencepiece_and_tokenizers_objects"] = [
|
| 316 |
+
name for name in dir(dummy_sentencepiece_and_tokenizers_objects) if not name.startswith("_")
|
| 317 |
+
]
|
| 318 |
+
else:
|
| 319 |
+
_import_structure["convert_slow_tokenizer"] = [
|
| 320 |
+
"SLOW_TO_FAST_CONVERTERS",
|
| 321 |
+
"convert_slow_tokenizer",
|
| 322 |
+
]
|
| 323 |
+
|
| 324 |
+
try:
|
| 325 |
+
if not (is_mistral_common_available()):
|
| 326 |
+
raise OptionalDependencyNotAvailable()
|
| 327 |
+
except OptionalDependencyNotAvailable:
|
| 328 |
+
from .utils import dummy_mistral_common_objects
|
| 329 |
+
|
| 330 |
+
_import_structure["utils.dummy_mistral_common_objects"] = [
|
| 331 |
+
name for name in dir(dummy_mistral_common_objects) if not name.startswith("_")
|
| 332 |
+
]
|
| 333 |
+
else:
|
| 334 |
+
_import_structure["tokenization_mistral_common"] = ["MistralCommonTokenizer"]
|
| 335 |
+
|
| 336 |
+
# Vision-specific objects
|
| 337 |
+
try:
|
| 338 |
+
if not is_vision_available():
|
| 339 |
+
raise OptionalDependencyNotAvailable()
|
| 340 |
+
except OptionalDependencyNotAvailable:
|
| 341 |
+
from .utils import dummy_vision_objects
|
| 342 |
+
|
| 343 |
+
_import_structure["utils.dummy_vision_objects"] = [
|
| 344 |
+
name for name in dir(dummy_vision_objects) if not name.startswith("_")
|
| 345 |
+
]
|
| 346 |
+
else:
|
| 347 |
+
_import_structure["image_processing_base"] = ["ImageProcessingMixin"]
|
| 348 |
+
_import_structure["image_processing_utils"] = ["BaseImageProcessor"]
|
| 349 |
+
_import_structure["image_utils"] = ["ImageFeatureExtractionMixin"]
|
| 350 |
+
|
| 351 |
+
try:
|
| 352 |
+
if not is_torchvision_available():
|
| 353 |
+
raise OptionalDependencyNotAvailable()
|
| 354 |
+
except OptionalDependencyNotAvailable:
|
| 355 |
+
from .utils import dummy_torchvision_objects
|
| 356 |
+
|
| 357 |
+
_import_structure["utils.dummy_torchvision_objects"] = [
|
| 358 |
+
name for name in dir(dummy_torchvision_objects) if not name.startswith("_")
|
| 359 |
+
]
|
| 360 |
+
else:
|
| 361 |
+
_import_structure["image_processing_utils_fast"] = ["BaseImageProcessorFast"]
|
| 362 |
+
_import_structure["video_processing_utils"] = ["BaseVideoProcessor"]
|
| 363 |
+
|
| 364 |
+
# PyTorch-backed objects
|
| 365 |
+
try:
|
| 366 |
+
if not is_torch_available():
|
| 367 |
+
raise OptionalDependencyNotAvailable()
|
| 368 |
+
except OptionalDependencyNotAvailable:
|
| 369 |
+
from .utils import dummy_pt_objects
|
| 370 |
+
|
| 371 |
+
_import_structure["utils.dummy_pt_objects"] = [name for name in dir(dummy_pt_objects) if not name.startswith("_")]
|
| 372 |
+
else:
|
| 373 |
+
_import_structure["model_debugging_utils"] = [
|
| 374 |
+
"model_addition_debugger_context",
|
| 375 |
+
]
|
| 376 |
+
_import_structure["activations"] = []
|
| 377 |
+
_import_structure["cache_utils"] = [
|
| 378 |
+
"CacheLayerMixin",
|
| 379 |
+
"DynamicLayer",
|
| 380 |
+
"StaticLayer",
|
| 381 |
+
"StaticSlidingWindowLayer",
|
| 382 |
+
"SlidingWindowLayer",
|
| 383 |
+
"ChunkedSlidingLayer",
|
| 384 |
+
"QuantoQuantizedLayer",
|
| 385 |
+
"HQQQuantizedLayer",
|
| 386 |
+
"Cache",
|
| 387 |
+
"DynamicCache",
|
| 388 |
+
"EncoderDecoderCache",
|
| 389 |
+
"HQQQuantizedCache",
|
| 390 |
+
"HybridCache",
|
| 391 |
+
"HybridChunkedCache",
|
| 392 |
+
"OffloadedCache",
|
| 393 |
+
"OffloadedStaticCache",
|
| 394 |
+
"QuantizedCache",
|
| 395 |
+
"QuantoQuantizedCache",
|
| 396 |
+
"SinkCache",
|
| 397 |
+
"SlidingWindowCache",
|
| 398 |
+
"StaticCache",
|
| 399 |
+
]
|
| 400 |
+
_import_structure["data.datasets"] = [
|
| 401 |
+
"GlueDataset",
|
| 402 |
+
"GlueDataTrainingArguments",
|
| 403 |
+
"LineByLineTextDataset",
|
| 404 |
+
"LineByLineWithRefDataset",
|
| 405 |
+
"LineByLineWithSOPTextDataset",
|
| 406 |
+
"SquadDataset",
|
| 407 |
+
"SquadDataTrainingArguments",
|
| 408 |
+
"TextDataset",
|
| 409 |
+
"TextDatasetForNextSentencePrediction",
|
| 410 |
+
]
|
| 411 |
+
_import_structure["generation"].extend(
|
| 412 |
+
[
|
| 413 |
+
"AlternatingCodebooksLogitsProcessor",
|
| 414 |
+
"BayesianDetectorConfig",
|
| 415 |
+
"BayesianDetectorModel",
|
| 416 |
+
"BeamScorer",
|
| 417 |
+
"ClassifierFreeGuidanceLogitsProcessor",
|
| 418 |
+
"ConstrainedBeamSearchScorer",
|
| 419 |
+
"Constraint",
|
| 420 |
+
"ConstraintListState",
|
| 421 |
+
"DisjunctiveConstraint",
|
| 422 |
+
"EncoderNoRepeatNGramLogitsProcessor",
|
| 423 |
+
"EncoderRepetitionPenaltyLogitsProcessor",
|
| 424 |
+
"EosTokenCriteria",
|
| 425 |
+
"EpsilonLogitsWarper",
|
| 426 |
+
"EtaLogitsWarper",
|
| 427 |
+
"ExponentialDecayLengthPenalty",
|
| 428 |
+
"ForcedBOSTokenLogitsProcessor",
|
| 429 |
+
"ForcedEOSTokenLogitsProcessor",
|
| 430 |
+
"GenerationMixin",
|
| 431 |
+
"InfNanRemoveLogitsProcessor",
|
| 432 |
+
"LogitNormalization",
|
| 433 |
+
"LogitsProcessor",
|
| 434 |
+
"LogitsProcessorList",
|
| 435 |
+
"MaxLengthCriteria",
|
| 436 |
+
"MaxTimeCriteria",
|
| 437 |
+
"MinLengthLogitsProcessor",
|
| 438 |
+
"MinNewTokensLengthLogitsProcessor",
|
| 439 |
+
"MinPLogitsWarper",
|
| 440 |
+
"NoBadWordsLogitsProcessor",
|
| 441 |
+
"NoRepeatNGramLogitsProcessor",
|
| 442 |
+
"PhrasalConstraint",
|
| 443 |
+
"PrefixConstrainedLogitsProcessor",
|
| 444 |
+
"RepetitionPenaltyLogitsProcessor",
|
| 445 |
+
"SequenceBiasLogitsProcessor",
|
| 446 |
+
"StoppingCriteria",
|
| 447 |
+
"StoppingCriteriaList",
|
| 448 |
+
"StopStringCriteria",
|
| 449 |
+
"SuppressTokensAtBeginLogitsProcessor",
|
| 450 |
+
"SuppressTokensLogitsProcessor",
|
| 451 |
+
"SynthIDTextWatermarkDetector",
|
| 452 |
+
"SynthIDTextWatermarkingConfig",
|
| 453 |
+
"SynthIDTextWatermarkLogitsProcessor",
|
| 454 |
+
"TemperatureLogitsWarper",
|
| 455 |
+
"TopKLogitsWarper",
|
| 456 |
+
"TopPLogitsWarper",
|
| 457 |
+
"TypicalLogitsWarper",
|
| 458 |
+
"UnbatchedClassifierFreeGuidanceLogitsProcessor",
|
| 459 |
+
"WatermarkDetector",
|
| 460 |
+
"WatermarkLogitsProcessor",
|
| 461 |
+
"WhisperTimeStampLogitsProcessor",
|
| 462 |
+
]
|
| 463 |
+
)
|
| 464 |
+
|
| 465 |
+
# PyTorch domain libraries integration
|
| 466 |
+
_import_structure["integrations.executorch"] = [
|
| 467 |
+
"TorchExportableModuleWithStaticCache",
|
| 468 |
+
"convert_and_export_with_cache",
|
| 469 |
+
]
|
| 470 |
+
|
| 471 |
+
_import_structure["modeling_flash_attention_utils"] = []
|
| 472 |
+
_import_structure["modeling_layers"] = ["GradientCheckpointingLayer"]
|
| 473 |
+
_import_structure["modeling_outputs"] = []
|
| 474 |
+
_import_structure["modeling_rope_utils"] = ["ROPE_INIT_FUNCTIONS", "dynamic_rope_update"]
|
| 475 |
+
_import_structure["modeling_utils"] = ["PreTrainedModel", "AttentionInterface"]
|
| 476 |
+
_import_structure["masking_utils"] = ["AttentionMaskInterface"]
|
| 477 |
+
_import_structure["optimization"] = [
|
| 478 |
+
"Adafactor",
|
| 479 |
+
"get_constant_schedule",
|
| 480 |
+
"get_constant_schedule_with_warmup",
|
| 481 |
+
"get_cosine_schedule_with_warmup",
|
| 482 |
+
"get_cosine_with_hard_restarts_schedule_with_warmup",
|
| 483 |
+
"get_cosine_with_min_lr_schedule_with_warmup",
|
| 484 |
+
"get_cosine_with_min_lr_schedule_with_warmup_lr_rate",
|
| 485 |
+
"get_inverse_sqrt_schedule",
|
| 486 |
+
"get_linear_schedule_with_warmup",
|
| 487 |
+
"get_polynomial_decay_schedule_with_warmup",
|
| 488 |
+
"get_scheduler",
|
| 489 |
+
"get_wsd_schedule",
|
| 490 |
+
"get_reduce_on_plateau_schedule",
|
| 491 |
+
]
|
| 492 |
+
_import_structure["pytorch_utils"] = [
|
| 493 |
+
"Conv1D",
|
| 494 |
+
"apply_chunking_to_forward",
|
| 495 |
+
"prune_layer",
|
| 496 |
+
"infer_device",
|
| 497 |
+
]
|
| 498 |
+
_import_structure["sagemaker"] = []
|
| 499 |
+
_import_structure["time_series_utils"] = []
|
| 500 |
+
_import_structure["trainer"] = ["Trainer"]
|
| 501 |
+
_import_structure["trainer_pt_utils"] = ["torch_distributed_zero_first"]
|
| 502 |
+
_import_structure["trainer_seq2seq"] = ["Seq2SeqTrainer"]
|
| 503 |
+
|
| 504 |
+
# TensorFlow-backed objects
|
| 505 |
+
try:
|
| 506 |
+
if not is_tf_available():
|
| 507 |
+
raise OptionalDependencyNotAvailable()
|
| 508 |
+
except OptionalDependencyNotAvailable:
|
| 509 |
+
from .utils import dummy_tf_objects
|
| 510 |
+
|
| 511 |
+
_import_structure["utils.dummy_tf_objects"] = [name for name in dir(dummy_tf_objects) if not name.startswith("_")]
|
| 512 |
+
else:
|
| 513 |
+
_import_structure["activations_tf"] = []
|
| 514 |
+
_import_structure["generation"].extend(
|
| 515 |
+
[
|
| 516 |
+
"TFForcedBOSTokenLogitsProcessor",
|
| 517 |
+
"TFForcedEOSTokenLogitsProcessor",
|
| 518 |
+
"TFForceTokensLogitsProcessor",
|
| 519 |
+
"TFGenerationMixin",
|
| 520 |
+
"TFLogitsProcessor",
|
| 521 |
+
"TFLogitsProcessorList",
|
| 522 |
+
"TFLogitsWarper",
|
| 523 |
+
"TFMinLengthLogitsProcessor",
|
| 524 |
+
"TFNoBadWordsLogitsProcessor",
|
| 525 |
+
"TFNoRepeatNGramLogitsProcessor",
|
| 526 |
+
"TFRepetitionPenaltyLogitsProcessor",
|
| 527 |
+
"TFSuppressTokensAtBeginLogitsProcessor",
|
| 528 |
+
"TFSuppressTokensLogitsProcessor",
|
| 529 |
+
"TFTemperatureLogitsWarper",
|
| 530 |
+
"TFTopKLogitsWarper",
|
| 531 |
+
"TFTopPLogitsWarper",
|
| 532 |
+
]
|
| 533 |
+
)
|
| 534 |
+
_import_structure["keras_callbacks"] = ["KerasMetricCallback", "PushToHubCallback"]
|
| 535 |
+
_import_structure["modeling_tf_outputs"] = []
|
| 536 |
+
_import_structure["modeling_tf_utils"] = [
|
| 537 |
+
"TFPreTrainedModel",
|
| 538 |
+
"TFSequenceSummary",
|
| 539 |
+
"TFSharedEmbeddings",
|
| 540 |
+
"shape_list",
|
| 541 |
+
]
|
| 542 |
+
_import_structure["optimization_tf"] = [
|
| 543 |
+
"AdamWeightDecay",
|
| 544 |
+
"GradientAccumulator",
|
| 545 |
+
"WarmUp",
|
| 546 |
+
"create_optimizer",
|
| 547 |
+
]
|
| 548 |
+
_import_structure["tf_utils"] = []
|
| 549 |
+
|
| 550 |
+
|
| 551 |
+
# FLAX-backed objects
|
| 552 |
+
try:
|
| 553 |
+
if not is_flax_available():
|
| 554 |
+
raise OptionalDependencyNotAvailable()
|
| 555 |
+
except OptionalDependencyNotAvailable:
|
| 556 |
+
from .utils import dummy_flax_objects
|
| 557 |
+
|
| 558 |
+
_import_structure["utils.dummy_flax_objects"] = [
|
| 559 |
+
name for name in dir(dummy_flax_objects) if not name.startswith("_")
|
| 560 |
+
]
|
| 561 |
+
else:
|
| 562 |
+
_import_structure["generation"].extend(
|
| 563 |
+
[
|
| 564 |
+
"FlaxForcedBOSTokenLogitsProcessor",
|
| 565 |
+
"FlaxForcedEOSTokenLogitsProcessor",
|
| 566 |
+
"FlaxForceTokensLogitsProcessor",
|
| 567 |
+
"FlaxGenerationMixin",
|
| 568 |
+
"FlaxLogitsProcessor",
|
| 569 |
+
"FlaxLogitsProcessorList",
|
| 570 |
+
"FlaxLogitsWarper",
|
| 571 |
+
"FlaxMinLengthLogitsProcessor",
|
| 572 |
+
"FlaxTemperatureLogitsWarper",
|
| 573 |
+
"FlaxSuppressTokensAtBeginLogitsProcessor",
|
| 574 |
+
"FlaxSuppressTokensLogitsProcessor",
|
| 575 |
+
"FlaxTopKLogitsWarper",
|
| 576 |
+
"FlaxTopPLogitsWarper",
|
| 577 |
+
"FlaxWhisperTimeStampLogitsProcessor",
|
| 578 |
+
]
|
| 579 |
+
)
|
| 580 |
+
_import_structure["modeling_flax_outputs"] = []
|
| 581 |
+
_import_structure["modeling_flax_utils"] = ["FlaxPreTrainedModel"]
|
| 582 |
+
|
| 583 |
+
# Direct imports for type-checking
|
| 584 |
+
if TYPE_CHECKING:
|
| 585 |
+
# All modeling imports
|
| 586 |
+
from .cache_utils import Cache as Cache
|
| 587 |
+
from .cache_utils import ChunkedSlidingLayer as ChunkedSlidingLayer
|
| 588 |
+
from .cache_utils import DynamicCache as DynamicCache
|
| 589 |
+
from .cache_utils import DynamicLayer as DynamicLayer
|
| 590 |
+
from .cache_utils import EncoderDecoderCache as EncoderDecoderCache
|
| 591 |
+
from .cache_utils import HQQQuantizedCache as HQQQuantizedCache
|
| 592 |
+
from .cache_utils import HQQQuantizedLayer as HQQQuantizedLayer
|
| 593 |
+
from .cache_utils import HybridCache as HybridCache
|
| 594 |
+
from .cache_utils import OffloadedCache as OffloadedCache
|
| 595 |
+
from .cache_utils import OffloadedStaticCache as OffloadedStaticCache
|
| 596 |
+
from .cache_utils import QuantizedCache as QuantizedCache
|
| 597 |
+
from .cache_utils import QuantoQuantizedCache as QuantoQuantizedCache
|
| 598 |
+
from .cache_utils import QuantoQuantizedLayer as QuantoQuantizedLayer
|
| 599 |
+
from .cache_utils import SinkCache as SinkCache
|
| 600 |
+
from .cache_utils import SlidingWindowCache as SlidingWindowCache
|
| 601 |
+
from .cache_utils import SlidingWindowLayer as SlidingWindowLayer
|
| 602 |
+
from .cache_utils import StaticCache as StaticCache
|
| 603 |
+
from .cache_utils import StaticLayer as StaticLayer
|
| 604 |
+
from .cache_utils import StaticSlidingWindowLayer as StaticSlidingWindowLayer
|
| 605 |
+
from .configuration_utils import PretrainedConfig as PretrainedConfig
|
| 606 |
+
from .convert_slow_tokenizer import SLOW_TO_FAST_CONVERTERS as SLOW_TO_FAST_CONVERTERS
|
| 607 |
+
from .convert_slow_tokenizer import convert_slow_tokenizer as convert_slow_tokenizer
|
| 608 |
+
|
| 609 |
+
# Data
|
| 610 |
+
from .data import DataProcessor as DataProcessor
|
| 611 |
+
from .data import InputExample as InputExample
|
| 612 |
+
from .data import InputFeatures as InputFeatures
|
| 613 |
+
from .data import SingleSentenceClassificationProcessor as SingleSentenceClassificationProcessor
|
| 614 |
+
from .data import SquadExample as SquadExample
|
| 615 |
+
from .data import SquadFeatures as SquadFeatures
|
| 616 |
+
from .data import SquadV1Processor as SquadV1Processor
|
| 617 |
+
from .data import SquadV2Processor as SquadV2Processor
|
| 618 |
+
from .data import glue_compute_metrics as glue_compute_metrics
|
| 619 |
+
from .data import glue_convert_examples_to_features as glue_convert_examples_to_features
|
| 620 |
+
from .data import glue_output_modes as glue_output_modes
|
| 621 |
+
from .data import glue_processors as glue_processors
|
| 622 |
+
from .data import glue_tasks_num_labels as glue_tasks_num_labels
|
| 623 |
+
from .data import squad_convert_examples_to_features as squad_convert_examples_to_features
|
| 624 |
+
from .data import xnli_compute_metrics as xnli_compute_metrics
|
| 625 |
+
from .data import xnli_output_modes as xnli_output_modes
|
| 626 |
+
from .data import xnli_processors as xnli_processors
|
| 627 |
+
from .data import xnli_tasks_num_labels as xnli_tasks_num_labels
|
| 628 |
+
from .data.data_collator import DataCollator as DataCollator
|
| 629 |
+
from .data.data_collator import DataCollatorForLanguageModeling as DataCollatorForLanguageModeling
|
| 630 |
+
from .data.data_collator import DataCollatorForMultipleChoice as DataCollatorForMultipleChoice
|
| 631 |
+
from .data.data_collator import (
|
| 632 |
+
DataCollatorForPermutationLanguageModeling as DataCollatorForPermutationLanguageModeling,
|
| 633 |
+
)
|
| 634 |
+
from .data.data_collator import DataCollatorForSeq2Seq as DataCollatorForSeq2Seq
|
| 635 |
+
from .data.data_collator import DataCollatorForSOP as DataCollatorForSOP
|
| 636 |
+
from .data.data_collator import DataCollatorForTokenClassification as DataCollatorForTokenClassification
|
| 637 |
+
from .data.data_collator import DataCollatorForWholeWordMask as DataCollatorForWholeWordMask
|
| 638 |
+
from .data.data_collator import DataCollatorWithFlattening as DataCollatorWithFlattening
|
| 639 |
+
from .data.data_collator import DataCollatorWithPadding as DataCollatorWithPadding
|
| 640 |
+
from .data.data_collator import DefaultDataCollator as DefaultDataCollator
|
| 641 |
+
from .data.data_collator import default_data_collator as default_data_collator
|
| 642 |
+
from .data.datasets import GlueDataset as GlueDataset
|
| 643 |
+
from .data.datasets import GlueDataTrainingArguments as GlueDataTrainingArguments
|
| 644 |
+
from .data.datasets import LineByLineTextDataset as LineByLineTextDataset
|
| 645 |
+
from .data.datasets import LineByLineWithRefDataset as LineByLineWithRefDataset
|
| 646 |
+
from .data.datasets import LineByLineWithSOPTextDataset as LineByLineWithSOPTextDataset
|
| 647 |
+
from .data.datasets import SquadDataset as SquadDataset
|
| 648 |
+
from .data.datasets import SquadDataTrainingArguments as SquadDataTrainingArguments
|
| 649 |
+
from .data.datasets import TextDataset as TextDataset
|
| 650 |
+
from .data.datasets import TextDatasetForNextSentencePrediction as TextDatasetForNextSentencePrediction
|
| 651 |
+
from .feature_extraction_sequence_utils import SequenceFeatureExtractor as SequenceFeatureExtractor
|
| 652 |
+
|
| 653 |
+
# Feature Extractor
|
| 654 |
+
from .feature_extraction_utils import BatchFeature as BatchFeature
|
| 655 |
+
from .feature_extraction_utils import FeatureExtractionMixin as FeatureExtractionMixin
|
| 656 |
+
|
| 657 |
+
# Generation
|
| 658 |
+
from .generation import AlternatingCodebooksLogitsProcessor as AlternatingCodebooksLogitsProcessor
|
| 659 |
+
from .generation import AsyncTextIteratorStreamer as AsyncTextIteratorStreamer
|
| 660 |
+
from .generation import BayesianDetectorConfig as BayesianDetectorConfig
|
| 661 |
+
from .generation import BayesianDetectorModel as BayesianDetectorModel
|
| 662 |
+
from .generation import BeamScorer as BeamScorer
|
| 663 |
+
from .generation import ClassifierFreeGuidanceLogitsProcessor as ClassifierFreeGuidanceLogitsProcessor
|
| 664 |
+
from .generation import CompileConfig as CompileConfig
|
| 665 |
+
from .generation import ConstrainedBeamSearchScorer as ConstrainedBeamSearchScorer
|
| 666 |
+
from .generation import Constraint as Constraint
|
| 667 |
+
from .generation import ConstraintListState as ConstraintListState
|
| 668 |
+
from .generation import DisjunctiveConstraint as DisjunctiveConstraint
|
| 669 |
+
from .generation import EncoderNoRepeatNGramLogitsProcessor as EncoderNoRepeatNGramLogitsProcessor
|
| 670 |
+
from .generation import EncoderRepetitionPenaltyLogitsProcessor as EncoderRepetitionPenaltyLogitsProcessor
|
| 671 |
+
from .generation import EosTokenCriteria as EosTokenCriteria
|
| 672 |
+
from .generation import EpsilonLogitsWarper as EpsilonLogitsWarper
|
| 673 |
+
from .generation import EtaLogitsWarper as EtaLogitsWarper
|
| 674 |
+
from .generation import ExponentialDecayLengthPenalty as ExponentialDecayLengthPenalty
|
| 675 |
+
from .generation import FlaxForcedBOSTokenLogitsProcessor as FlaxForcedBOSTokenLogitsProcessor
|
| 676 |
+
from .generation import FlaxForcedEOSTokenLogitsProcessor as FlaxForcedEOSTokenLogitsProcessor
|
| 677 |
+
from .generation import FlaxForceTokensLogitsProcessor as FlaxForceTokensLogitsProcessor
|
| 678 |
+
from .generation import FlaxGenerationMixin as FlaxGenerationMixin
|
| 679 |
+
from .generation import FlaxLogitsProcessor as FlaxLogitsProcessor
|
| 680 |
+
from .generation import FlaxLogitsProcessorList as FlaxLogitsProcessorList
|
| 681 |
+
from .generation import FlaxLogitsWarper as FlaxLogitsWarper
|
| 682 |
+
from .generation import FlaxMinLengthLogitsProcessor as FlaxMinLengthLogitsProcessor
|
| 683 |
+
from .generation import FlaxSuppressTokensAtBeginLogitsProcessor as FlaxSuppressTokensAtBeginLogitsProcessor
|
| 684 |
+
from .generation import FlaxSuppressTokensLogitsProcessor as FlaxSuppressTokensLogitsProcessor
|
| 685 |
+
from .generation import FlaxTemperatureLogitsWarper as FlaxTemperatureLogitsWarper
|
| 686 |
+
from .generation import FlaxTopKLogitsWarper as FlaxTopKLogitsWarper
|
| 687 |
+
from .generation import FlaxTopPLogitsWarper as FlaxTopPLogitsWarper
|
| 688 |
+
from .generation import FlaxWhisperTimeStampLogitsProcessor as FlaxWhisperTimeStampLogitsProcessor
|
| 689 |
+
from .generation import ForcedBOSTokenLogitsProcessor as ForcedBOSTokenLogitsProcessor
|
| 690 |
+
from .generation import ForcedEOSTokenLogitsProcessor as ForcedEOSTokenLogitsProcessor
|
| 691 |
+
from .generation import GenerationConfig as GenerationConfig
|
| 692 |
+
from .generation import GenerationMixin as GenerationMixin
|
| 693 |
+
from .generation import InfNanRemoveLogitsProcessor as InfNanRemoveLogitsProcessor
|
| 694 |
+
from .generation import LogitNormalization as LogitNormalization
|
| 695 |
+
from .generation import LogitsProcessor as LogitsProcessor
|
| 696 |
+
from .generation import LogitsProcessorList as LogitsProcessorList
|
| 697 |
+
from .generation import MaxLengthCriteria as MaxLengthCriteria
|
| 698 |
+
from .generation import MaxTimeCriteria as MaxTimeCriteria
|
| 699 |
+
from .generation import MinLengthLogitsProcessor as MinLengthLogitsProcessor
|
| 700 |
+
from .generation import MinNewTokensLengthLogitsProcessor as MinNewTokensLengthLogitsProcessor
|
| 701 |
+
from .generation import MinPLogitsWarper as MinPLogitsWarper
|
| 702 |
+
from .generation import NoBadWordsLogitsProcessor as NoBadWordsLogitsProcessor
|
| 703 |
+
from .generation import NoRepeatNGramLogitsProcessor as NoRepeatNGramLogitsProcessor
|
| 704 |
+
from .generation import PhrasalConstraint as PhrasalConstraint
|
| 705 |
+
from .generation import PrefixConstrainedLogitsProcessor as PrefixConstrainedLogitsProcessor
|
| 706 |
+
from .generation import RepetitionPenaltyLogitsProcessor as RepetitionPenaltyLogitsProcessor
|
| 707 |
+
from .generation import SequenceBiasLogitsProcessor as SequenceBiasLogitsProcessor
|
| 708 |
+
from .generation import StoppingCriteria as StoppingCriteria
|
| 709 |
+
from .generation import StoppingCriteriaList as StoppingCriteriaList
|
| 710 |
+
from .generation import StopStringCriteria as StopStringCriteria
|
| 711 |
+
from .generation import SuppressTokensAtBeginLogitsProcessor as SuppressTokensAtBeginLogitsProcessor
|
| 712 |
+
from .generation import SuppressTokensLogitsProcessor as SuppressTokensLogitsProcessor
|
| 713 |
+
from .generation import SynthIDTextWatermarkDetector as SynthIDTextWatermarkDetector
|
| 714 |
+
from .generation import SynthIDTextWatermarkingConfig as SynthIDTextWatermarkingConfig
|
| 715 |
+
from .generation import SynthIDTextWatermarkLogitsProcessor as SynthIDTextWatermarkLogitsProcessor
|
| 716 |
+
from .generation import TemperatureLogitsWarper as TemperatureLogitsWarper
|
| 717 |
+
from .generation import TextIteratorStreamer as TextIteratorStreamer
|
| 718 |
+
from .generation import TextStreamer as TextStreamer
|
| 719 |
+
from .generation import TFForcedBOSTokenLogitsProcessor as TFForcedBOSTokenLogitsProcessor
|
| 720 |
+
from .generation import TFForcedEOSTokenLogitsProcessor as TFForcedEOSTokenLogitsProcessor
|
| 721 |
+
from .generation import TFForceTokensLogitsProcessor as TFForceTokensLogitsProcessor
|
| 722 |
+
from .generation import TFGenerationMixin as TFGenerationMixin
|
| 723 |
+
from .generation import TFLogitsProcessor as TFLogitsProcessor
|
| 724 |
+
from .generation import TFLogitsProcessorList as TFLogitsProcessorList
|
| 725 |
+
from .generation import TFLogitsWarper as TFLogitsWarper
|
| 726 |
+
from .generation import TFMinLengthLogitsProcessor as TFMinLengthLogitsProcessor
|
| 727 |
+
from .generation import TFNoBadWordsLogitsProcessor as TFNoBadWordsLogitsProcessor
|
| 728 |
+
from .generation import TFNoRepeatNGramLogitsProcessor as TFNoRepeatNGramLogitsProcessor
|
| 729 |
+
from .generation import TFRepetitionPenaltyLogitsProcessor as TFRepetitionPenaltyLogitsProcessor
|
| 730 |
+
from .generation import TFSuppressTokensAtBeginLogitsProcessor as TFSuppressTokensAtBeginLogitsProcessor
|
| 731 |
+
from .generation import TFSuppressTokensLogitsProcessor as TFSuppressTokensLogitsProcessor
|
| 732 |
+
from .generation import TFTemperatureLogitsWarper as TFTemperatureLogitsWarper
|
| 733 |
+
from .generation import TFTopKLogitsWarper as TFTopKLogitsWarper
|
| 734 |
+
from .generation import TFTopPLogitsWarper as TFTopPLogitsWarper
|
| 735 |
+
from .generation import TopKLogitsWarper as TopKLogitsWarper
|
| 736 |
+
from .generation import TopPLogitsWarper as TopPLogitsWarper
|
| 737 |
+
from .generation import TypicalLogitsWarper as TypicalLogitsWarper
|
| 738 |
+
from .generation import (
|
| 739 |
+
UnbatchedClassifierFreeGuidanceLogitsProcessor as UnbatchedClassifierFreeGuidanceLogitsProcessor,
|
| 740 |
+
)
|
| 741 |
+
from .generation import WatermarkDetector as WatermarkDetector
|
| 742 |
+
from .generation import WatermarkingConfig as WatermarkingConfig
|
| 743 |
+
from .generation import WatermarkLogitsProcessor as WatermarkLogitsProcessor
|
| 744 |
+
from .generation import WhisperTimeStampLogitsProcessor as WhisperTimeStampLogitsProcessor
|
| 745 |
+
from .hf_argparser import HfArgumentParser as HfArgumentParser
|
| 746 |
+
from .image_processing_base import ImageProcessingMixin as ImageProcessingMixin
|
| 747 |
+
from .image_processing_utils import BaseImageProcessor as BaseImageProcessor
|
| 748 |
+
from .image_processing_utils_fast import BaseImageProcessorFast as BaseImageProcessorFast
|
| 749 |
+
from .image_utils import ImageFeatureExtractionMixin as ImageFeatureExtractionMixin
|
| 750 |
+
|
| 751 |
+
# Integrations
|
| 752 |
+
from .integrations import is_clearml_available as is_clearml_available
|
| 753 |
+
from .integrations import is_comet_available as is_comet_available
|
| 754 |
+
from .integrations import is_dvclive_available as is_dvclive_available
|
| 755 |
+
from .integrations import is_neptune_available as is_neptune_available
|
| 756 |
+
from .integrations import is_optuna_available as is_optuna_available
|
| 757 |
+
from .integrations import is_ray_available as is_ray_available
|
| 758 |
+
from .integrations import is_ray_tune_available as is_ray_tune_available
|
| 759 |
+
from .integrations import is_sigopt_available as is_sigopt_available
|
| 760 |
+
from .integrations import is_swanlab_available as is_swanlab_available
|
| 761 |
+
from .integrations import is_tensorboard_available as is_tensorboard_available
|
| 762 |
+
from .integrations import is_trackio_available as is_trackio_available
|
| 763 |
+
from .integrations import is_wandb_available as is_wandb_available
|
| 764 |
+
from .integrations.executorch import TorchExportableModuleWithStaticCache as TorchExportableModuleWithStaticCache
|
| 765 |
+
from .integrations.executorch import convert_and_export_with_cache as convert_and_export_with_cache
|
| 766 |
+
from .keras_callbacks import KerasMetricCallback as KerasMetricCallback
|
| 767 |
+
from .keras_callbacks import PushToHubCallback as PushToHubCallback
|
| 768 |
+
from .masking_utils import AttentionMaskInterface as AttentionMaskInterface
|
| 769 |
+
from .model_debugging_utils import model_addition_debugger_context as model_addition_debugger_context
|
| 770 |
+
|
| 771 |
+
# Model Cards
|
| 772 |
+
from .modelcard import ModelCard as ModelCard
|
| 773 |
+
from .modeling_flax_utils import FlaxPreTrainedModel as FlaxPreTrainedModel
|
| 774 |
+
from .modeling_layers import GradientCheckpointingLayer as GradientCheckpointingLayer
|
| 775 |
+
from .modeling_rope_utils import ROPE_INIT_FUNCTIONS as ROPE_INIT_FUNCTIONS
|
| 776 |
+
from .modeling_rope_utils import dynamic_rope_update as dynamic_rope_update
|
| 777 |
+
|
| 778 |
+
# TF 2.0 <=> PyTorch conversion utilities
|
| 779 |
+
from .modeling_tf_pytorch_utils import (
|
| 780 |
+
convert_tf_weight_name_to_pt_weight_name as convert_tf_weight_name_to_pt_weight_name,
|
| 781 |
+
)
|
| 782 |
+
from .modeling_tf_pytorch_utils import load_pytorch_checkpoint_in_tf2_model as load_pytorch_checkpoint_in_tf2_model
|
| 783 |
+
from .modeling_tf_pytorch_utils import load_pytorch_model_in_tf2_model as load_pytorch_model_in_tf2_model
|
| 784 |
+
from .modeling_tf_pytorch_utils import load_pytorch_weights_in_tf2_model as load_pytorch_weights_in_tf2_model
|
| 785 |
+
from .modeling_tf_pytorch_utils import load_tf2_checkpoint_in_pytorch_model as load_tf2_checkpoint_in_pytorch_model
|
| 786 |
+
from .modeling_tf_pytorch_utils import load_tf2_model_in_pytorch_model as load_tf2_model_in_pytorch_model
|
| 787 |
+
from .modeling_tf_pytorch_utils import load_tf2_weights_in_pytorch_model as load_tf2_weights_in_pytorch_model
|
| 788 |
+
from .modeling_tf_utils import TFPreTrainedModel as TFPreTrainedModel
|
| 789 |
+
from .modeling_tf_utils import TFSequenceSummary as TFSequenceSummary
|
| 790 |
+
from .modeling_tf_utils import TFSharedEmbeddings as TFSharedEmbeddings
|
| 791 |
+
from .modeling_tf_utils import shape_list as shape_list
|
| 792 |
+
from .modeling_utils import AttentionInterface as AttentionInterface
|
| 793 |
+
from .modeling_utils import PreTrainedModel as PreTrainedModel
|
| 794 |
+
from .models import *
|
| 795 |
+
from .models.mamba.modeling_mamba import MambaCache as MambaCache
|
| 796 |
+
from .models.timm_wrapper import TimmWrapperImageProcessor as TimmWrapperImageProcessor
|
| 797 |
+
|
| 798 |
+
# Optimization
|
| 799 |
+
from .optimization import Adafactor as Adafactor
|
| 800 |
+
from .optimization import get_constant_schedule as get_constant_schedule
|
| 801 |
+
from .optimization import get_constant_schedule_with_warmup as get_constant_schedule_with_warmup
|
| 802 |
+
from .optimization import get_cosine_schedule_with_warmup as get_cosine_schedule_with_warmup
|
| 803 |
+
from .optimization import (
|
| 804 |
+
get_cosine_with_hard_restarts_schedule_with_warmup as get_cosine_with_hard_restarts_schedule_with_warmup,
|
| 805 |
+
)
|
| 806 |
+
from .optimization import (
|
| 807 |
+
get_cosine_with_min_lr_schedule_with_warmup as get_cosine_with_min_lr_schedule_with_warmup,
|
| 808 |
+
)
|
| 809 |
+
from .optimization import (
|
| 810 |
+
get_cosine_with_min_lr_schedule_with_warmup_lr_rate as get_cosine_with_min_lr_schedule_with_warmup_lr_rate,
|
| 811 |
+
)
|
| 812 |
+
from .optimization import get_inverse_sqrt_schedule as get_inverse_sqrt_schedule
|
| 813 |
+
from .optimization import get_linear_schedule_with_warmup as get_linear_schedule_with_warmup
|
| 814 |
+
from .optimization import get_polynomial_decay_schedule_with_warmup as get_polynomial_decay_schedule_with_warmup
|
| 815 |
+
from .optimization import get_scheduler as get_scheduler
|
| 816 |
+
from .optimization import get_wsd_schedule as get_wsd_schedule
|
| 817 |
+
|
| 818 |
+
# Optimization
|
| 819 |
+
from .optimization_tf import AdamWeightDecay as AdamWeightDecay
|
| 820 |
+
from .optimization_tf import GradientAccumulator as GradientAccumulator
|
| 821 |
+
from .optimization_tf import WarmUp as WarmUp
|
| 822 |
+
from .optimization_tf import create_optimizer as create_optimizer
|
| 823 |
+
|
| 824 |
+
# Pipelines
|
| 825 |
+
from .pipelines import AudioClassificationPipeline as AudioClassificationPipeline
|
| 826 |
+
from .pipelines import AutomaticSpeechRecognitionPipeline as AutomaticSpeechRecognitionPipeline
|
| 827 |
+
from .pipelines import CsvPipelineDataFormat as CsvPipelineDataFormat
|
| 828 |
+
from .pipelines import DepthEstimationPipeline as DepthEstimationPipeline
|
| 829 |
+
from .pipelines import DocumentQuestionAnsweringPipeline as DocumentQuestionAnsweringPipeline
|
| 830 |
+
from .pipelines import FeatureExtractionPipeline as FeatureExtractionPipeline
|
| 831 |
+
from .pipelines import FillMaskPipeline as FillMaskPipeline
|
| 832 |
+
from .pipelines import ImageClassificationPipeline as ImageClassificationPipeline
|
| 833 |
+
from .pipelines import ImageFeatureExtractionPipeline as ImageFeatureExtractionPipeline
|
| 834 |
+
from .pipelines import ImageSegmentationPipeline as ImageSegmentationPipeline
|
| 835 |
+
from .pipelines import ImageTextToTextPipeline as ImageTextToTextPipeline
|
| 836 |
+
from .pipelines import ImageToImagePipeline as ImageToImagePipeline
|
| 837 |
+
from .pipelines import ImageToTextPipeline as ImageToTextPipeline
|
| 838 |
+
from .pipelines import JsonPipelineDataFormat as JsonPipelineDataFormat
|
| 839 |
+
from .pipelines import KeypointMatchingPipeline as KeypointMatchingPipeline
|
| 840 |
+
from .pipelines import MaskGenerationPipeline as MaskGenerationPipeline
|
| 841 |
+
from .pipelines import NerPipeline as NerPipeline
|
| 842 |
+
from .pipelines import ObjectDetectionPipeline as ObjectDetectionPipeline
|
| 843 |
+
from .pipelines import PipedPipelineDataFormat as PipedPipelineDataFormat
|
| 844 |
+
from .pipelines import Pipeline as Pipeline
|
| 845 |
+
from .pipelines import PipelineDataFormat as PipelineDataFormat
|
| 846 |
+
from .pipelines import QuestionAnsweringPipeline as QuestionAnsweringPipeline
|
| 847 |
+
from .pipelines import SummarizationPipeline as SummarizationPipeline
|
| 848 |
+
from .pipelines import TableQuestionAnsweringPipeline as TableQuestionAnsweringPipeline
|
| 849 |
+
from .pipelines import Text2TextGenerationPipeline as Text2TextGenerationPipeline
|
| 850 |
+
from .pipelines import TextClassificationPipeline as TextClassificationPipeline
|
| 851 |
+
from .pipelines import TextGenerationPipeline as TextGenerationPipeline
|
| 852 |
+
from .pipelines import TextToAudioPipeline as TextToAudioPipeline
|
| 853 |
+
from .pipelines import TokenClassificationPipeline as TokenClassificationPipeline
|
| 854 |
+
from .pipelines import TranslationPipeline as TranslationPipeline
|
| 855 |
+
from .pipelines import VideoClassificationPipeline as VideoClassificationPipeline
|
| 856 |
+
from .pipelines import VisualQuestionAnsweringPipeline as VisualQuestionAnsweringPipeline
|
| 857 |
+
from .pipelines import ZeroShotAudioClassificationPipeline as ZeroShotAudioClassificationPipeline
|
| 858 |
+
from .pipelines import ZeroShotClassificationPipeline as ZeroShotClassificationPipeline
|
| 859 |
+
from .pipelines import ZeroShotImageClassificationPipeline as ZeroShotImageClassificationPipeline
|
| 860 |
+
from .pipelines import ZeroShotObjectDetectionPipeline as ZeroShotObjectDetectionPipeline
|
| 861 |
+
from .pipelines import pipeline as pipeline
|
| 862 |
+
from .processing_utils import ProcessorMixin as ProcessorMixin
|
| 863 |
+
from .pytorch_utils import Conv1D as Conv1D
|
| 864 |
+
from .pytorch_utils import apply_chunking_to_forward as apply_chunking_to_forward
|
| 865 |
+
from .pytorch_utils import prune_layer as prune_layer
|
| 866 |
+
|
| 867 |
+
# Tokenization
|
| 868 |
+
from .tokenization_utils import PreTrainedTokenizer as PreTrainedTokenizer
|
| 869 |
+
from .tokenization_utils_base import AddedToken as AddedToken
|
| 870 |
+
from .tokenization_utils_base import BatchEncoding as BatchEncoding
|
| 871 |
+
from .tokenization_utils_base import CharSpan as CharSpan
|
| 872 |
+
from .tokenization_utils_base import PreTrainedTokenizerBase as PreTrainedTokenizerBase
|
| 873 |
+
from .tokenization_utils_base import SpecialTokensMixin as SpecialTokensMixin
|
| 874 |
+
from .tokenization_utils_base import TokenSpan as TokenSpan
|
| 875 |
+
from .tokenization_utils_fast import PreTrainedTokenizerFast as PreTrainedTokenizerFast
|
| 876 |
+
|
| 877 |
+
# Trainer
|
| 878 |
+
from .trainer import Trainer as Trainer
|
| 879 |
+
|
| 880 |
+
# Trainer
|
| 881 |
+
from .trainer_callback import DefaultFlowCallback as DefaultFlowCallback
|
| 882 |
+
from .trainer_callback import EarlyStoppingCallback as EarlyStoppingCallback
|
| 883 |
+
from .trainer_callback import PrinterCallback as PrinterCallback
|
| 884 |
+
from .trainer_callback import ProgressCallback as ProgressCallback
|
| 885 |
+
from .trainer_callback import TrainerCallback as TrainerCallback
|
| 886 |
+
from .trainer_callback import TrainerControl as TrainerControl
|
| 887 |
+
from .trainer_callback import TrainerState as TrainerState
|
| 888 |
+
from .trainer_pt_utils import torch_distributed_zero_first as torch_distributed_zero_first
|
| 889 |
+
from .trainer_seq2seq import Seq2SeqTrainer as Seq2SeqTrainer
|
| 890 |
+
from .trainer_utils import EvalPrediction as EvalPrediction
|
| 891 |
+
from .trainer_utils import IntervalStrategy as IntervalStrategy
|
| 892 |
+
from .trainer_utils import SchedulerType as SchedulerType
|
| 893 |
+
from .trainer_utils import enable_full_determinism as enable_full_determinism
|
| 894 |
+
from .trainer_utils import set_seed as set_seed
|
| 895 |
+
from .training_args import TrainingArguments as TrainingArguments
|
| 896 |
+
from .training_args_seq2seq import Seq2SeqTrainingArguments as Seq2SeqTrainingArguments
|
| 897 |
+
from .training_args_tf import TFTrainingArguments as TFTrainingArguments
|
| 898 |
+
|
| 899 |
+
# Files and general utilities
|
| 900 |
+
from .utils import CONFIG_NAME as CONFIG_NAME
|
| 901 |
+
from .utils import MODEL_CARD_NAME as MODEL_CARD_NAME
|
| 902 |
+
from .utils import PYTORCH_PRETRAINED_BERT_CACHE as PYTORCH_PRETRAINED_BERT_CACHE
|
| 903 |
+
from .utils import PYTORCH_TRANSFORMERS_CACHE as PYTORCH_TRANSFORMERS_CACHE
|
| 904 |
+
from .utils import SPIECE_UNDERLINE as SPIECE_UNDERLINE
|
| 905 |
+
from .utils import TF2_WEIGHTS_NAME as TF2_WEIGHTS_NAME
|
| 906 |
+
from .utils import TF_WEIGHTS_NAME as TF_WEIGHTS_NAME
|
| 907 |
+
from .utils import TRANSFORMERS_CACHE as TRANSFORMERS_CACHE
|
| 908 |
+
from .utils import WEIGHTS_NAME as WEIGHTS_NAME
|
| 909 |
+
from .utils import TensorType as TensorType
|
| 910 |
+
from .utils import add_end_docstrings as add_end_docstrings
|
| 911 |
+
from .utils import add_start_docstrings as add_start_docstrings
|
| 912 |
+
from .utils import is_apex_available as is_apex_available
|
| 913 |
+
from .utils import is_av_available as is_av_available
|
| 914 |
+
from .utils import is_datasets_available as is_datasets_available
|
| 915 |
+
from .utils import is_faiss_available as is_faiss_available
|
| 916 |
+
from .utils import is_matplotlib_available as is_matplotlib_available
|
| 917 |
+
from .utils import is_phonemizer_available as is_phonemizer_available
|
| 918 |
+
from .utils import is_psutil_available as is_psutil_available
|
| 919 |
+
from .utils import is_py3nvml_available as is_py3nvml_available
|
| 920 |
+
from .utils import is_pyctcdecode_available as is_pyctcdecode_available
|
| 921 |
+
from .utils import is_sacremoses_available as is_sacremoses_available
|
| 922 |
+
from .utils import is_safetensors_available as is_safetensors_available
|
| 923 |
+
from .utils import is_sklearn_available as is_sklearn_available
|
| 924 |
+
from .utils import is_torch_hpu_available as is_torch_hpu_available
|
| 925 |
+
from .utils import is_torch_mlu_available as is_torch_mlu_available
|
| 926 |
+
from .utils import is_torch_musa_available as is_torch_musa_available
|
| 927 |
+
from .utils import is_torch_neuroncore_available as is_torch_neuroncore_available
|
| 928 |
+
from .utils import is_torch_npu_available as is_torch_npu_available
|
| 929 |
+
from .utils import is_torch_xla_available as is_torch_xla_available
|
| 930 |
+
from .utils import is_torch_xpu_available as is_torch_xpu_available
|
| 931 |
+
|
| 932 |
+
# bitsandbytes config
|
| 933 |
+
from .utils.quantization_config import AqlmConfig as AqlmConfig
|
| 934 |
+
from .utils.quantization_config import AutoRoundConfig as AutoRoundConfig
|
| 935 |
+
from .utils.quantization_config import AwqConfig as AwqConfig
|
| 936 |
+
from .utils.quantization_config import BitNetQuantConfig as BitNetQuantConfig
|
| 937 |
+
from .utils.quantization_config import BitsAndBytesConfig as BitsAndBytesConfig
|
| 938 |
+
from .utils.quantization_config import CompressedTensorsConfig as CompressedTensorsConfig
|
| 939 |
+
from .utils.quantization_config import EetqConfig as EetqConfig
|
| 940 |
+
from .utils.quantization_config import FbgemmFp8Config as FbgemmFp8Config
|
| 941 |
+
from .utils.quantization_config import FineGrainedFP8Config as FineGrainedFP8Config
|
| 942 |
+
from .utils.quantization_config import FPQuantConfig as FPQuantConfig
|
| 943 |
+
from .utils.quantization_config import GPTQConfig as GPTQConfig
|
| 944 |
+
from .utils.quantization_config import HiggsConfig as HiggsConfig
|
| 945 |
+
from .utils.quantization_config import HqqConfig as HqqConfig
|
| 946 |
+
from .utils.quantization_config import QuantoConfig as QuantoConfig
|
| 947 |
+
from .utils.quantization_config import QuarkConfig as QuarkConfig
|
| 948 |
+
from .utils.quantization_config import SpQRConfig as SpQRConfig
|
| 949 |
+
from .utils.quantization_config import TorchAoConfig as TorchAoConfig
|
| 950 |
+
from .utils.quantization_config import VptqConfig as VptqConfig
|
| 951 |
+
from .video_processing_utils import BaseVideoProcessor as BaseVideoProcessor
|
| 952 |
+
|
| 953 |
+
else:
|
| 954 |
+
import sys
|
| 955 |
+
|
| 956 |
+
_import_structure = {k: set(v) for k, v in _import_structure.items()}
|
| 957 |
+
|
| 958 |
+
import_structure = define_import_structure(Path(__file__).parent / "models", prefix="models")
|
| 959 |
+
import_structure[frozenset({})].update(_import_structure)
|
| 960 |
+
|
| 961 |
+
sys.modules[__name__] = _LazyModule(
|
| 962 |
+
__name__,
|
| 963 |
+
globals()["__file__"],
|
| 964 |
+
import_structure,
|
| 965 |
+
module_spec=__spec__,
|
| 966 |
+
extra_objects={"__version__": __version__},
|
| 967 |
+
)
|
| 968 |
+
|
| 969 |
+
|
| 970 |
+
if not is_tf_available() and not is_torch_available() and not is_flax_available():
|
| 971 |
+
logger.warning_advice(
|
| 972 |
+
"None of PyTorch, TensorFlow >= 2.0, or Flax have been found. "
|
| 973 |
+
"Models won't be available and only tokenizers, configuration "
|
| 974 |
+
"and file/data utilities can be used."
|
| 975 |
+
)
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/activations.py
ADDED
|
@@ -0,0 +1,356 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2020 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
import functools
|
| 16 |
+
import math
|
| 17 |
+
from collections import OrderedDict
|
| 18 |
+
|
| 19 |
+
import torch
|
| 20 |
+
from torch import Tensor, nn
|
| 21 |
+
|
| 22 |
+
from .integrations.hub_kernels import use_kernel_forward_from_hub
|
| 23 |
+
from .utils import logging
|
| 24 |
+
from .utils.import_utils import is_torchdynamo_compiling
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
logger = logging.get_logger(__name__)
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
@use_kernel_forward_from_hub("GeluTanh")
|
| 31 |
+
class GELUTanh(nn.Module):
|
| 32 |
+
"""
|
| 33 |
+
A fast C implementation of the tanh approximation of the GeLU activation function. See
|
| 34 |
+
https://huggingface.co/papers/1606.08415.
|
| 35 |
+
|
| 36 |
+
This implementation is equivalent to NewGELU and FastGELU but much faster. However, it is not an exact numerical
|
| 37 |
+
match due to rounding errors.
|
| 38 |
+
"""
|
| 39 |
+
|
| 40 |
+
def __init__(self, use_gelu_tanh_python: bool = False):
|
| 41 |
+
super().__init__()
|
| 42 |
+
if use_gelu_tanh_python:
|
| 43 |
+
self.act = self._gelu_tanh_python
|
| 44 |
+
else:
|
| 45 |
+
self.act = functools.partial(nn.functional.gelu, approximate="tanh")
|
| 46 |
+
|
| 47 |
+
def _gelu_tanh_python(self, input: Tensor) -> Tensor:
|
| 48 |
+
return input * 0.5 * (1.0 + torch.tanh(math.sqrt(2.0 / math.pi) * (input + 0.044715 * torch.pow(input, 3.0))))
|
| 49 |
+
|
| 50 |
+
def forward(self, input: Tensor) -> Tensor:
|
| 51 |
+
return self.act(input)
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
@use_kernel_forward_from_hub("NewGELU")
|
| 55 |
+
class NewGELUActivation(nn.Module):
|
| 56 |
+
"""
|
| 57 |
+
Implementation of the GELU activation function currently in Google BERT repo (identical to OpenAI GPT). Also see
|
| 58 |
+
the Gaussian Error Linear Units paper: https://huggingface.co/papers/1606.08415
|
| 59 |
+
"""
|
| 60 |
+
|
| 61 |
+
def forward(self, input: Tensor) -> Tensor:
|
| 62 |
+
return 0.5 * input * (1.0 + torch.tanh(math.sqrt(2.0 / math.pi) * (input + 0.044715 * torch.pow(input, 3.0))))
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
@use_kernel_forward_from_hub("GeLU")
|
| 66 |
+
class GELUActivation(nn.Module):
|
| 67 |
+
"""
|
| 68 |
+
Original Implementation of the GELU activation function in Google BERT repo when initially created. For
|
| 69 |
+
information: OpenAI GPT's GELU is slightly different (and gives slightly different results): 0.5 * x * (1 +
|
| 70 |
+
torch.tanh(math.sqrt(2 / math.pi) * (x + 0.044715 * torch.pow(x, 3)))) This is now written in C in nn.functional
|
| 71 |
+
Also see the Gaussian Error Linear Units paper: https://huggingface.co/papers/1606.08415
|
| 72 |
+
"""
|
| 73 |
+
|
| 74 |
+
def __init__(self, use_gelu_python: bool = False):
|
| 75 |
+
super().__init__()
|
| 76 |
+
if use_gelu_python:
|
| 77 |
+
self.act = self._gelu_python
|
| 78 |
+
else:
|
| 79 |
+
self.act = nn.functional.gelu
|
| 80 |
+
|
| 81 |
+
def _gelu_python(self, input: Tensor) -> Tensor:
|
| 82 |
+
return input * 0.5 * (1.0 + torch.erf(input / math.sqrt(2.0)))
|
| 83 |
+
|
| 84 |
+
def forward(self, input: Tensor) -> Tensor:
|
| 85 |
+
return self.act(input)
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
@use_kernel_forward_from_hub("SiLU")
|
| 89 |
+
class SiLUActivation(nn.Module):
|
| 90 |
+
"""
|
| 91 |
+
See Gaussian Error Linear Units (Hendrycks et al., https://arxiv.org/abs/1606.08415) where the SiLU (Sigmoid Linear
|
| 92 |
+
Unit) was originally introduced and coined, and see Sigmoid-Weighted Linear Units for Neural Network Function
|
| 93 |
+
Approximation in Reinforcement Learning (Elfwing et al., https://arxiv.org/abs/1702.03118) and Swish: a Self-Gated
|
| 94 |
+
Activation Function (Ramachandran et al., https://arxiv.org/abs/1710.05941v1) where the SiLU was experimented with
|
| 95 |
+
later.
|
| 96 |
+
"""
|
| 97 |
+
|
| 98 |
+
def forward(self, input: Tensor) -> Tensor:
|
| 99 |
+
return nn.functional.silu(input)
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
@use_kernel_forward_from_hub("FastGELU")
|
| 103 |
+
class FastGELUActivation(nn.Module):
|
| 104 |
+
"""
|
| 105 |
+
Applies GELU approximation that is slower than QuickGELU but more accurate. See: https://github.com/hendrycks/GELUs
|
| 106 |
+
"""
|
| 107 |
+
|
| 108 |
+
def forward(self, input: Tensor) -> Tensor:
|
| 109 |
+
return 0.5 * input * (1.0 + torch.tanh(input * 0.7978845608 * (1.0 + 0.044715 * input * input)))
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
@use_kernel_forward_from_hub("QuickGELU")
|
| 113 |
+
class QuickGELUActivation(nn.Module):
|
| 114 |
+
"""
|
| 115 |
+
Applies GELU approximation that is fast but somewhat inaccurate. See: https://github.com/hendrycks/GELUs
|
| 116 |
+
"""
|
| 117 |
+
|
| 118 |
+
def forward(self, input: Tensor) -> Tensor:
|
| 119 |
+
return input * torch.sigmoid(1.702 * input)
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
class ClippedGELUActivation(nn.Module):
|
| 123 |
+
"""
|
| 124 |
+
Clip the range of possible GeLU outputs between [min, max]. This is especially useful for quantization purpose, as
|
| 125 |
+
it allows mapping negatives values in the GeLU spectrum. For more information on this trick, please refer to
|
| 126 |
+
https://huggingface.co/papers/2004.09602.
|
| 127 |
+
|
| 128 |
+
Gaussian Error Linear Unit. Original Implementation of the gelu activation function in Google Bert repo when
|
| 129 |
+
initially created.
|
| 130 |
+
|
| 131 |
+
For information: OpenAI GPT's gelu is slightly different (and gives slightly different results): 0.5 * x * (1 +
|
| 132 |
+
torch.tanh(math.sqrt(2 / math.pi) * (x + 0.044715 * torch.pow(x, 3)))). See https://huggingface.co/papers/1606.08415
|
| 133 |
+
"""
|
| 134 |
+
|
| 135 |
+
def __init__(self, min: float, max: float):
|
| 136 |
+
if min > max:
|
| 137 |
+
raise ValueError(f"min should be < max (got min: {min}, max: {max})")
|
| 138 |
+
|
| 139 |
+
super().__init__()
|
| 140 |
+
self.min = min
|
| 141 |
+
self.max = max
|
| 142 |
+
|
| 143 |
+
def forward(self, x: Tensor) -> Tensor:
|
| 144 |
+
return torch.clip(gelu(x), self.min, self.max)
|
| 145 |
+
|
| 146 |
+
|
| 147 |
+
class AccurateGELUActivation(nn.Module):
|
| 148 |
+
"""
|
| 149 |
+
Applies GELU approximation that is faster than default and more accurate than QuickGELU. See:
|
| 150 |
+
https://github.com/hendrycks/GELUs
|
| 151 |
+
|
| 152 |
+
Implemented along with MEGA (Moving Average Equipped Gated Attention)
|
| 153 |
+
"""
|
| 154 |
+
|
| 155 |
+
def __init__(self):
|
| 156 |
+
super().__init__()
|
| 157 |
+
self.precomputed_constant = math.sqrt(2 / math.pi)
|
| 158 |
+
|
| 159 |
+
def forward(self, input: Tensor) -> Tensor:
|
| 160 |
+
return 0.5 * input * (1 + torch.tanh(self.precomputed_constant * (input + 0.044715 * torch.pow(input, 3))))
|
| 161 |
+
|
| 162 |
+
|
| 163 |
+
class MishActivation(nn.Module):
|
| 164 |
+
"""
|
| 165 |
+
See Mish: A Self-Regularized Non-Monotonic Activation Function (Misra., https://huggingface.co/papers/1908.08681). Also
|
| 166 |
+
visit the official repository for the paper: https://github.com/digantamisra98/Mish
|
| 167 |
+
"""
|
| 168 |
+
|
| 169 |
+
def __init__(self):
|
| 170 |
+
super().__init__()
|
| 171 |
+
self.act = nn.functional.mish
|
| 172 |
+
|
| 173 |
+
def _mish_python(self, input: Tensor) -> Tensor:
|
| 174 |
+
return input * torch.tanh(nn.functional.softplus(input))
|
| 175 |
+
|
| 176 |
+
def forward(self, input: Tensor) -> Tensor:
|
| 177 |
+
return self.act(input)
|
| 178 |
+
|
| 179 |
+
|
| 180 |
+
class LinearActivation(nn.Module):
|
| 181 |
+
"""
|
| 182 |
+
Applies the linear activation function, i.e. forwarding input directly to output.
|
| 183 |
+
"""
|
| 184 |
+
|
| 185 |
+
def forward(self, input: Tensor) -> Tensor:
|
| 186 |
+
return input
|
| 187 |
+
|
| 188 |
+
|
| 189 |
+
class LaplaceActivation(nn.Module):
|
| 190 |
+
"""
|
| 191 |
+
Applies elementwise activation based on Laplace function, introduced in MEGA as an attention activation. See
|
| 192 |
+
https://huggingface.co/papers/2209.10655
|
| 193 |
+
|
| 194 |
+
Inspired by squared relu, but with bounded range and gradient for better stability
|
| 195 |
+
"""
|
| 196 |
+
|
| 197 |
+
def forward(self, input, mu=0.707107, sigma=0.282095):
|
| 198 |
+
input = (input - mu).div(sigma * math.sqrt(2.0))
|
| 199 |
+
return 0.5 * (1.0 + torch.erf(input))
|
| 200 |
+
|
| 201 |
+
|
| 202 |
+
class ReLUSquaredActivation(nn.Module):
|
| 203 |
+
"""
|
| 204 |
+
Applies the relu^2 activation introduced in https://huggingface.co/papers/2109.08668v2
|
| 205 |
+
"""
|
| 206 |
+
|
| 207 |
+
def forward(self, input):
|
| 208 |
+
relu_applied = nn.functional.relu(input)
|
| 209 |
+
squared = torch.square(relu_applied)
|
| 210 |
+
return squared
|
| 211 |
+
|
| 212 |
+
|
| 213 |
+
class ClassInstantier(OrderedDict):
|
| 214 |
+
def __getitem__(self, key):
|
| 215 |
+
content = super().__getitem__(key)
|
| 216 |
+
cls, kwargs = content if isinstance(content, tuple) else (content, {})
|
| 217 |
+
return cls(**kwargs)
|
| 218 |
+
|
| 219 |
+
|
| 220 |
+
class XIELUActivation(nn.Module):
|
| 221 |
+
"""
|
| 222 |
+
Applies the xIELU activation function introduced in https://arxiv.org/abs/2411.13010
|
| 223 |
+
|
| 224 |
+
If the user has installed the nickjbrowning/XIELU wheel, we import xIELU CUDA
|
| 225 |
+
Otherwise, we emit a single warning and use xIELU Python
|
| 226 |
+
"""
|
| 227 |
+
|
| 228 |
+
def __init__(
|
| 229 |
+
self,
|
| 230 |
+
alpha_p_init=0.8,
|
| 231 |
+
alpha_n_init=0.8,
|
| 232 |
+
beta=0.5,
|
| 233 |
+
eps=-1e-6,
|
| 234 |
+
dtype=torch.bfloat16,
|
| 235 |
+
with_vector_loads=False,
|
| 236 |
+
):
|
| 237 |
+
super().__init__()
|
| 238 |
+
self.alpha_p = nn.Parameter(torch.log(torch.expm1(torch.tensor(alpha_p_init, dtype=dtype))).unsqueeze(0))
|
| 239 |
+
self.alpha_n = nn.Parameter(
|
| 240 |
+
torch.log(torch.expm1(torch.tensor(alpha_n_init - beta, dtype=dtype))).unsqueeze(0)
|
| 241 |
+
)
|
| 242 |
+
self.register_buffer("beta", torch.tensor(beta, dtype=dtype))
|
| 243 |
+
self.register_buffer("eps", torch.tensor(eps, dtype=dtype))
|
| 244 |
+
self.with_vector_loads = with_vector_loads
|
| 245 |
+
# Temporary until xIELU CUDA fully implemented
|
| 246 |
+
self._beta_scalar = float(self.beta.detach().cpu().float().item())
|
| 247 |
+
self._eps_scalar = float(self.eps.detach().cpu().float().item())
|
| 248 |
+
|
| 249 |
+
self._xielu_cuda_obj = None
|
| 250 |
+
try:
|
| 251 |
+
import xielu.ops # noqa: F401
|
| 252 |
+
|
| 253 |
+
self._xielu_cuda_obj = torch.classes.xielu.XIELU()
|
| 254 |
+
msg = "Using experimental xIELU CUDA."
|
| 255 |
+
try:
|
| 256 |
+
from torch._dynamo import allow_in_graph
|
| 257 |
+
|
| 258 |
+
self._xielu_cuda_fn = allow_in_graph(self._xielu_cuda)
|
| 259 |
+
msg += " Enabled torch._dynamo for xIELU CUDA."
|
| 260 |
+
except Exception as err:
|
| 261 |
+
msg += f" Could not enable torch._dynamo for xIELU ({err}) - this may result in slower performance."
|
| 262 |
+
self._xielu_cuda_fn = self._xielu_cuda
|
| 263 |
+
logger.warning_once(msg)
|
| 264 |
+
except Exception as err:
|
| 265 |
+
logger.warning_once(
|
| 266 |
+
"CUDA-fused xIELU not available (%s) – falling back to a Python version.\n"
|
| 267 |
+
"For CUDA xIELU (experimental), `pip install git+https://github.com/nickjbrowning/XIELU`",
|
| 268 |
+
str(err),
|
| 269 |
+
)
|
| 270 |
+
|
| 271 |
+
def _xielu_python(self, x: Tensor) -> Tensor:
|
| 272 |
+
alpha_p = nn.functional.softplus(self.alpha_p)
|
| 273 |
+
alpha_n = self.beta + nn.functional.softplus(self.alpha_n)
|
| 274 |
+
return torch.where(
|
| 275 |
+
x > 0,
|
| 276 |
+
alpha_p * x * x + self.beta * x,
|
| 277 |
+
(torch.expm1(torch.min(x, self.eps)) - x) * alpha_n + self.beta * x,
|
| 278 |
+
)
|
| 279 |
+
|
| 280 |
+
def _xielu_cuda(self, x: Tensor) -> Tensor:
|
| 281 |
+
"""Firewall function to prevent torch.compile from seeing .item() calls"""
|
| 282 |
+
original_shape = x.shape
|
| 283 |
+
# CUDA kernel expects 3D tensors, reshape if needed
|
| 284 |
+
while x.dim() < 3:
|
| 285 |
+
x = x.unsqueeze(0)
|
| 286 |
+
if x.dim() > 3:
|
| 287 |
+
x = x.view(-1, 1, x.size(-1))
|
| 288 |
+
if original_shape != x.shape:
|
| 289 |
+
logger.warning_once(
|
| 290 |
+
"Warning: xIELU input tensor expects 3 dimensions but got (shape: %s). Reshaping to (shape: %s).",
|
| 291 |
+
original_shape,
|
| 292 |
+
x.shape,
|
| 293 |
+
)
|
| 294 |
+
result = self._xielu_cuda_obj.forward(
|
| 295 |
+
x,
|
| 296 |
+
self.alpha_p.to(x.dtype),
|
| 297 |
+
self.alpha_n.to(x.dtype),
|
| 298 |
+
# Temporary until xIELU CUDA fully implemented -> self.{beta,eps}.item()
|
| 299 |
+
self._beta_scalar,
|
| 300 |
+
self._eps_scalar,
|
| 301 |
+
self.with_vector_loads,
|
| 302 |
+
)
|
| 303 |
+
return result.view(original_shape)
|
| 304 |
+
|
| 305 |
+
def forward(self, input: Tensor) -> Tensor:
|
| 306 |
+
if self._xielu_cuda_obj is not None and input.is_cuda:
|
| 307 |
+
if not is_torchdynamo_compiling():
|
| 308 |
+
return self._xielu_cuda_fn(input)
|
| 309 |
+
else:
|
| 310 |
+
logger.warning_once("torch._dynamo is compiling, using Python version of xIELU.")
|
| 311 |
+
return self._xielu_python(input)
|
| 312 |
+
|
| 313 |
+
|
| 314 |
+
ACT2CLS = {
|
| 315 |
+
"gelu": GELUActivation,
|
| 316 |
+
"gelu_10": (ClippedGELUActivation, {"min": -10, "max": 10}),
|
| 317 |
+
"gelu_fast": FastGELUActivation,
|
| 318 |
+
"gelu_new": NewGELUActivation,
|
| 319 |
+
"gelu_python": (GELUActivation, {"use_gelu_python": True}),
|
| 320 |
+
"gelu_pytorch_tanh": GELUTanh,
|
| 321 |
+
"gelu_python_tanh": (GELUTanh, {"use_gelu_tanh_python": True}),
|
| 322 |
+
"gelu_accurate": AccurateGELUActivation,
|
| 323 |
+
"laplace": LaplaceActivation,
|
| 324 |
+
"leaky_relu": nn.LeakyReLU,
|
| 325 |
+
"linear": LinearActivation,
|
| 326 |
+
"mish": MishActivation,
|
| 327 |
+
"quick_gelu": QuickGELUActivation,
|
| 328 |
+
"relu": nn.ReLU,
|
| 329 |
+
"relu2": ReLUSquaredActivation,
|
| 330 |
+
"relu6": nn.ReLU6,
|
| 331 |
+
"sigmoid": nn.Sigmoid,
|
| 332 |
+
"silu": SiLUActivation,
|
| 333 |
+
"swish": nn.SiLU,
|
| 334 |
+
"tanh": nn.Tanh,
|
| 335 |
+
"prelu": nn.PReLU,
|
| 336 |
+
"xielu": XIELUActivation,
|
| 337 |
+
}
|
| 338 |
+
ACT2FN = ClassInstantier(ACT2CLS)
|
| 339 |
+
|
| 340 |
+
|
| 341 |
+
def get_activation(activation_string):
|
| 342 |
+
if activation_string in ACT2FN:
|
| 343 |
+
return ACT2FN[activation_string]
|
| 344 |
+
else:
|
| 345 |
+
raise KeyError(f"function {activation_string} not found in ACT2FN mapping {list(ACT2FN.keys())}")
|
| 346 |
+
|
| 347 |
+
|
| 348 |
+
# For backwards compatibility with: from activations import gelu_python
|
| 349 |
+
gelu_python = get_activation("gelu_python")
|
| 350 |
+
gelu_new = get_activation("gelu_new")
|
| 351 |
+
gelu = get_activation("gelu")
|
| 352 |
+
gelu_fast = get_activation("gelu_fast")
|
| 353 |
+
quick_gelu = get_activation("quick_gelu")
|
| 354 |
+
silu = get_activation("silu")
|
| 355 |
+
mish = get_activation("mish")
|
| 356 |
+
linear_act = get_activation("linear")
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/activations_tf.py
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2020 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
import math
|
| 16 |
+
|
| 17 |
+
import tensorflow as tf
|
| 18 |
+
from packaging.version import parse
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
try:
|
| 22 |
+
import tf_keras as keras
|
| 23 |
+
except (ModuleNotFoundError, ImportError):
|
| 24 |
+
import keras
|
| 25 |
+
|
| 26 |
+
if parse(keras.__version__).major > 2:
|
| 27 |
+
raise ValueError(
|
| 28 |
+
"Your currently installed version of Keras is Keras 3, but this is not yet supported in "
|
| 29 |
+
"Transformers. Please install the backwards-compatible tf-keras package with "
|
| 30 |
+
"`pip install tf-keras`."
|
| 31 |
+
)
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def _gelu(x):
|
| 35 |
+
"""
|
| 36 |
+
Gaussian Error Linear Unit. Original Implementation of the gelu activation function in Google Bert repo when
|
| 37 |
+
initially created. For information: OpenAI GPT's gelu is slightly different (and gives slightly different results):
|
| 38 |
+
0.5 * x * (1 + torch.tanh(math.sqrt(2 / math.pi) * (x + 0.044715 * torch.pow(x, 3)))) Also see
|
| 39 |
+
https://huggingface.co/papers/1606.08415
|
| 40 |
+
"""
|
| 41 |
+
x = tf.convert_to_tensor(x)
|
| 42 |
+
cdf = 0.5 * (1.0 + tf.math.erf(x / tf.cast(tf.sqrt(2.0), x.dtype)))
|
| 43 |
+
|
| 44 |
+
return x * cdf
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
def _gelu_new(x):
|
| 48 |
+
"""
|
| 49 |
+
Gaussian Error Linear Unit. This is a smoother version of the GELU. Original paper: https://huggingface.co/papers/1606.0841
|
| 50 |
+
|
| 51 |
+
Args:
|
| 52 |
+
x: float Tensor to perform activation
|
| 53 |
+
|
| 54 |
+
Returns:
|
| 55 |
+
`x` with the GELU activation applied.
|
| 56 |
+
"""
|
| 57 |
+
x = tf.convert_to_tensor(x)
|
| 58 |
+
pi = tf.cast(math.pi, x.dtype)
|
| 59 |
+
coeff = tf.cast(0.044715, x.dtype)
|
| 60 |
+
cdf = 0.5 * (1.0 + tf.tanh(tf.sqrt(2.0 / pi) * (x + coeff * tf.pow(x, 3))))
|
| 61 |
+
|
| 62 |
+
return x * cdf
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
def mish(x):
|
| 66 |
+
x = tf.convert_to_tensor(x)
|
| 67 |
+
|
| 68 |
+
return x * tf.tanh(tf.math.softplus(x))
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
def gelu_fast(x):
|
| 72 |
+
x = tf.convert_to_tensor(x)
|
| 73 |
+
coeff1 = tf.cast(0.044715, x.dtype)
|
| 74 |
+
coeff2 = tf.cast(0.7978845608, x.dtype)
|
| 75 |
+
|
| 76 |
+
return 0.5 * x * (1.0 + tf.tanh(x * coeff2 * (1.0 + coeff1 * x * x)))
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def quick_gelu(x):
|
| 80 |
+
x = tf.convert_to_tensor(x)
|
| 81 |
+
coeff = tf.cast(1.702, x.dtype)
|
| 82 |
+
return x * tf.math.sigmoid(coeff * x)
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
def gelu_10(x):
|
| 86 |
+
"""
|
| 87 |
+
Clip the range of possible GeLU outputs between [-10, 10]. This is especially useful for quantization purpose, as
|
| 88 |
+
it allows mapping 2 negatives values in the GeLU spectrum. For more information on this trick, please refer to
|
| 89 |
+
https://huggingface.co/papers/2004.09602
|
| 90 |
+
|
| 91 |
+
Gaussian Error Linear Unit. Original Implementation of the gelu activation function in Google Bert repo when
|
| 92 |
+
initially created. For information: OpenAI GPT's gelu is slightly different (and gives slightly different results):
|
| 93 |
+
0.5 * x * (1 + torch.tanh(math.sqrt(2 / math.pi) * (x + 0.044715 * torch.pow(x, 3)))) Also see
|
| 94 |
+
https://huggingface.co/papers/1606.08415 :param x: :return:
|
| 95 |
+
"""
|
| 96 |
+
return tf.clip_by_value(_gelu(x), -10, 10)
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
def glu(x, axis=-1):
|
| 100 |
+
"""
|
| 101 |
+
Gated Linear Unit. Implementation as defined in the original paper (see https://huggingface.co/papers/1612.08083), where
|
| 102 |
+
the input `x` is split in two halves across a dimension (`axis`), A and B, returning A * sigmoid(B).
|
| 103 |
+
|
| 104 |
+
Args:
|
| 105 |
+
`x`: float Tensor to perform activation
|
| 106 |
+
`axis`: dimension across which `x` be split in half
|
| 107 |
+
|
| 108 |
+
Returns:
|
| 109 |
+
`x` with the GLU activation applied (with its size halved across the dimension `axis`).
|
| 110 |
+
"""
|
| 111 |
+
a, b = tf.split(x, 2, axis=axis)
|
| 112 |
+
return a * tf.math.sigmoid(b)
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
if parse(tf.version.VERSION) >= parse("2.4"):
|
| 116 |
+
|
| 117 |
+
def approximate_gelu_wrap(x):
|
| 118 |
+
return keras.activations.gelu(x, approximate=True)
|
| 119 |
+
|
| 120 |
+
gelu = keras.activations.gelu
|
| 121 |
+
gelu_new = approximate_gelu_wrap
|
| 122 |
+
else:
|
| 123 |
+
gelu = _gelu
|
| 124 |
+
gelu_new = _gelu_new
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
ACT2FN = {
|
| 128 |
+
"gelu": gelu,
|
| 129 |
+
"gelu_10": gelu_10,
|
| 130 |
+
"gelu_fast": gelu_fast,
|
| 131 |
+
"gelu_new": gelu_new,
|
| 132 |
+
"glu": glu,
|
| 133 |
+
"mish": mish,
|
| 134 |
+
"quick_gelu": quick_gelu,
|
| 135 |
+
"relu": keras.activations.relu,
|
| 136 |
+
"sigmoid": keras.activations.sigmoid,
|
| 137 |
+
"silu": keras.activations.swish,
|
| 138 |
+
"swish": keras.activations.swish,
|
| 139 |
+
"tanh": keras.activations.tanh,
|
| 140 |
+
}
|
| 141 |
+
|
| 142 |
+
|
| 143 |
+
def get_tf_activation(activation_string):
|
| 144 |
+
if activation_string in ACT2FN:
|
| 145 |
+
return ACT2FN[activation_string]
|
| 146 |
+
else:
|
| 147 |
+
raise KeyError(f"function {activation_string} not found in ACT2FN mapping {list(ACT2FN.keys())}")
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/audio_utils.py
ADDED
|
@@ -0,0 +1,1224 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2023 The HuggingFace Inc. team and the librosa & torchaudio authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""
|
| 15 |
+
Audio processing functions to extract features from audio waveforms. This code is pure numpy to support all frameworks
|
| 16 |
+
and remove unnecessary dependencies.
|
| 17 |
+
"""
|
| 18 |
+
|
| 19 |
+
import base64
|
| 20 |
+
import importlib
|
| 21 |
+
import io
|
| 22 |
+
import os
|
| 23 |
+
import warnings
|
| 24 |
+
from collections.abc import Sequence
|
| 25 |
+
from io import BytesIO
|
| 26 |
+
from typing import TYPE_CHECKING, Any, Optional, Union
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
if TYPE_CHECKING:
|
| 30 |
+
import torch
|
| 31 |
+
import numpy as np
|
| 32 |
+
import requests
|
| 33 |
+
from packaging import version
|
| 34 |
+
|
| 35 |
+
from .utils import (
|
| 36 |
+
is_librosa_available,
|
| 37 |
+
is_numpy_array,
|
| 38 |
+
is_soundfile_available,
|
| 39 |
+
is_torch_tensor,
|
| 40 |
+
is_torchcodec_available,
|
| 41 |
+
requires_backends,
|
| 42 |
+
)
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
if is_soundfile_available():
|
| 46 |
+
import soundfile as sf
|
| 47 |
+
|
| 48 |
+
if is_librosa_available():
|
| 49 |
+
import librosa
|
| 50 |
+
|
| 51 |
+
# TODO: @eustlb, we actually don't need librosa but soxr is installed with librosa
|
| 52 |
+
import soxr
|
| 53 |
+
|
| 54 |
+
if is_torchcodec_available():
|
| 55 |
+
TORCHCODEC_VERSION = version.parse(importlib.metadata.version("torchcodec"))
|
| 56 |
+
|
| 57 |
+
AudioInput = Union[np.ndarray, "torch.Tensor", Sequence[np.ndarray], Sequence["torch.Tensor"]]
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
def load_audio(audio: Union[str, np.ndarray], sampling_rate=16000, timeout=None) -> np.ndarray:
|
| 61 |
+
"""
|
| 62 |
+
Loads `audio` to an np.ndarray object.
|
| 63 |
+
|
| 64 |
+
Args:
|
| 65 |
+
audio (`str` or `np.ndarray`):
|
| 66 |
+
The audio to be loaded to the numpy array format.
|
| 67 |
+
sampling_rate (`int`, *optional*, defaults to 16000):
|
| 68 |
+
The sampling rate to be used when loading the audio. It should be same as the
|
| 69 |
+
sampling rate the model you will be using further was trained with.
|
| 70 |
+
timeout (`float`, *optional*):
|
| 71 |
+
The timeout value in seconds for the URL request.
|
| 72 |
+
|
| 73 |
+
Returns:
|
| 74 |
+
`np.ndarray`: A numpy array representing the audio.
|
| 75 |
+
"""
|
| 76 |
+
if isinstance(audio, str):
|
| 77 |
+
# Try to load with `torchcodec` but do not enforce users to install it. If not found
|
| 78 |
+
# fallback to `librosa`. If using an audio-only model, most probably `torchcodec` won't be
|
| 79 |
+
# needed. Do not raise any errors if not installed or versions do not match
|
| 80 |
+
if is_torchcodec_available() and TORCHCODEC_VERSION >= version.parse("0.3.0"):
|
| 81 |
+
audio = load_audio_torchcodec(audio, sampling_rate=sampling_rate)
|
| 82 |
+
else:
|
| 83 |
+
audio = load_audio_librosa(audio, sampling_rate=sampling_rate, timeout=timeout)
|
| 84 |
+
elif not isinstance(audio, np.ndarray):
|
| 85 |
+
raise TypeError(
|
| 86 |
+
"Incorrect format used for `audio`. Should be an url linking to an audio, a local path, or numpy array."
|
| 87 |
+
)
|
| 88 |
+
return audio
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
def load_audio_torchcodec(audio: Union[str, np.ndarray], sampling_rate=16000) -> np.ndarray:
|
| 92 |
+
"""
|
| 93 |
+
Loads `audio` to an np.ndarray object using `torchcodec`.
|
| 94 |
+
|
| 95 |
+
Args:
|
| 96 |
+
audio (`str` or `np.ndarray`):
|
| 97 |
+
The audio to be loaded to the numpy array format.
|
| 98 |
+
sampling_rate (`int`, *optional*, defaults to 16000):
|
| 99 |
+
The sampling rate to be used when loading the audio. It should be same as the
|
| 100 |
+
sampling rate the model you will be using further was trained with.
|
| 101 |
+
|
| 102 |
+
Returns:
|
| 103 |
+
`np.ndarray`: A numpy array representing the audio.
|
| 104 |
+
"""
|
| 105 |
+
# Lazy import so that issues in torchcodec compatibility don't crash the whole library
|
| 106 |
+
requires_backends(load_audio_torchcodec, ["torchcodec"])
|
| 107 |
+
from torchcodec.decoders import AudioDecoder
|
| 108 |
+
|
| 109 |
+
# Set `num_channels` to `1` which is what most models expects and the default in librosa
|
| 110 |
+
decoder = AudioDecoder(audio, sample_rate=sampling_rate, num_channels=1)
|
| 111 |
+
audio = decoder.get_all_samples().data[0].numpy() # NOTE: feature extractors don't accept torch tensors
|
| 112 |
+
return audio
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
def load_audio_librosa(audio: Union[str, np.ndarray], sampling_rate=16000, timeout=None) -> np.ndarray:
|
| 116 |
+
"""
|
| 117 |
+
Loads `audio` to an np.ndarray object using `librosa`.
|
| 118 |
+
|
| 119 |
+
Args:
|
| 120 |
+
audio (`str` or `np.ndarray`):
|
| 121 |
+
The audio to be loaded to the numpy array format.
|
| 122 |
+
sampling_rate (`int`, *optional*, defaults to 16000):
|
| 123 |
+
The sampling rate to be used when loading the audio. It should be same as the
|
| 124 |
+
sampling rate the model you will be using further was trained with.
|
| 125 |
+
timeout (`float`, *optional*):
|
| 126 |
+
The timeout value in seconds for the URL request.
|
| 127 |
+
|
| 128 |
+
Returns:
|
| 129 |
+
`np.ndarray`: A numpy array representing the audio.
|
| 130 |
+
"""
|
| 131 |
+
requires_backends(load_audio_librosa, ["librosa"])
|
| 132 |
+
|
| 133 |
+
# Load audio from URL (e.g https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen2-Audio/audio/translate_to_chinese.wav)
|
| 134 |
+
if audio.startswith("http://") or audio.startswith("https://"):
|
| 135 |
+
audio = librosa.load(BytesIO(requests.get(audio, timeout=timeout).content), sr=sampling_rate)[0]
|
| 136 |
+
elif os.path.isfile(audio):
|
| 137 |
+
audio = librosa.load(audio, sr=sampling_rate)[0]
|
| 138 |
+
return audio
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
def load_audio_as(
|
| 142 |
+
audio: str,
|
| 143 |
+
return_format: str,
|
| 144 |
+
timeout: Optional[int] = None,
|
| 145 |
+
force_mono: bool = False,
|
| 146 |
+
sampling_rate: Optional[int] = None,
|
| 147 |
+
) -> Union[str, dict[str, Any], io.BytesIO, None]:
|
| 148 |
+
"""
|
| 149 |
+
Load audio from either a local file path or URL and return in specified format.
|
| 150 |
+
|
| 151 |
+
Args:
|
| 152 |
+
audio (`str`): Either a local file path or a URL to an audio file
|
| 153 |
+
return_format (`str`): Format to return the audio in:
|
| 154 |
+
- "base64": Base64 encoded string
|
| 155 |
+
- "dict": Dictionary with data and format
|
| 156 |
+
- "buffer": BytesIO object
|
| 157 |
+
timeout (`int`, *optional*): Timeout for URL requests in seconds
|
| 158 |
+
force_mono (`bool`): Whether to convert stereo audio to mono
|
| 159 |
+
sampling_rate (`int`, *optional*): If provided, the audio will be resampled to the specified sampling rate.
|
| 160 |
+
|
| 161 |
+
Returns:
|
| 162 |
+
`Union[str, Dict[str, Any], io.BytesIO, None]`:
|
| 163 |
+
- `str`: Base64 encoded audio data (if return_format="base64")
|
| 164 |
+
- `dict`: Dictionary with 'data' (base64 encoded audio data) and 'format' keys (if return_format="dict")
|
| 165 |
+
- `io.BytesIO`: BytesIO object containing audio data (if return_format="buffer")
|
| 166 |
+
"""
|
| 167 |
+
# TODO: @eustlb, we actually don't need librosa but soxr is installed with librosa
|
| 168 |
+
requires_backends(load_audio_as, ["librosa"])
|
| 169 |
+
|
| 170 |
+
if return_format not in ["base64", "dict", "buffer"]:
|
| 171 |
+
raise ValueError(f"Invalid return_format: {return_format}. Must be 'base64', 'dict', or 'buffer'")
|
| 172 |
+
|
| 173 |
+
try:
|
| 174 |
+
# Load audio bytes from URL or file
|
| 175 |
+
audio_bytes = None
|
| 176 |
+
if audio.startswith(("http://", "https://")):
|
| 177 |
+
response = requests.get(audio, timeout=timeout)
|
| 178 |
+
response.raise_for_status()
|
| 179 |
+
audio_bytes = response.content
|
| 180 |
+
elif os.path.isfile(audio):
|
| 181 |
+
with open(audio, "rb") as audio_file:
|
| 182 |
+
audio_bytes = audio_file.read()
|
| 183 |
+
else:
|
| 184 |
+
raise ValueError(f"File not found: {audio}")
|
| 185 |
+
|
| 186 |
+
# Process audio data
|
| 187 |
+
with io.BytesIO(audio_bytes) as audio_file:
|
| 188 |
+
with sf.SoundFile(audio_file) as f:
|
| 189 |
+
audio_array = f.read(dtype="float32")
|
| 190 |
+
original_sr = f.samplerate
|
| 191 |
+
audio_format = f.format
|
| 192 |
+
if sampling_rate is not None and sampling_rate != original_sr:
|
| 193 |
+
# Resample audio to target sampling rate
|
| 194 |
+
audio_array = soxr.resample(audio_array, original_sr, sampling_rate, quality="HQ")
|
| 195 |
+
else:
|
| 196 |
+
sampling_rate = original_sr
|
| 197 |
+
|
| 198 |
+
# Convert to mono if needed
|
| 199 |
+
if force_mono and audio_array.ndim != 1:
|
| 200 |
+
audio_array = audio_array.mean(axis=1)
|
| 201 |
+
|
| 202 |
+
buffer = io.BytesIO()
|
| 203 |
+
sf.write(buffer, audio_array, sampling_rate, format=audio_format.upper())
|
| 204 |
+
buffer.seek(0)
|
| 205 |
+
|
| 206 |
+
if return_format == "buffer":
|
| 207 |
+
return buffer
|
| 208 |
+
elif return_format == "base64":
|
| 209 |
+
return base64.b64encode(buffer.read()).decode("utf-8")
|
| 210 |
+
elif return_format == "dict":
|
| 211 |
+
return {
|
| 212 |
+
"data": base64.b64encode(buffer.read()).decode("utf-8"),
|
| 213 |
+
"format": audio_format.lower(),
|
| 214 |
+
}
|
| 215 |
+
|
| 216 |
+
except Exception as e:
|
| 217 |
+
raise ValueError(f"Error loading audio: {e}")
|
| 218 |
+
|
| 219 |
+
|
| 220 |
+
def is_valid_audio(audio):
|
| 221 |
+
return is_numpy_array(audio) or is_torch_tensor(audio)
|
| 222 |
+
|
| 223 |
+
|
| 224 |
+
def is_valid_list_of_audio(audio):
|
| 225 |
+
return audio and all(is_valid_audio(audio_i) for audio_i in audio)
|
| 226 |
+
|
| 227 |
+
|
| 228 |
+
def make_list_of_audio(
|
| 229 |
+
audio: Union[list[AudioInput], AudioInput],
|
| 230 |
+
) -> AudioInput:
|
| 231 |
+
"""
|
| 232 |
+
Ensure that the output is a list of audio.
|
| 233 |
+
Args:
|
| 234 |
+
audio (`Union[list[AudioInput], AudioInput]`):
|
| 235 |
+
The input audio.
|
| 236 |
+
Returns:
|
| 237 |
+
list: A list of audio.
|
| 238 |
+
"""
|
| 239 |
+
# If it's a list of audios, it's already in the right format
|
| 240 |
+
if isinstance(audio, (list, tuple)) and is_valid_list_of_audio(audio):
|
| 241 |
+
return audio
|
| 242 |
+
|
| 243 |
+
# If it's a single audio, convert it to a list of
|
| 244 |
+
if is_valid_audio(audio):
|
| 245 |
+
return [audio]
|
| 246 |
+
|
| 247 |
+
raise ValueError("Invalid input type. Must be a single audio or a list of audio")
|
| 248 |
+
|
| 249 |
+
|
| 250 |
+
def hertz_to_mel(freq: Union[float, np.ndarray], mel_scale: str = "htk") -> Union[float, np.ndarray]:
|
| 251 |
+
"""
|
| 252 |
+
Convert frequency from hertz to mels.
|
| 253 |
+
|
| 254 |
+
Args:
|
| 255 |
+
freq (`float` or `np.ndarray`):
|
| 256 |
+
The frequency, or multiple frequencies, in hertz (Hz).
|
| 257 |
+
mel_scale (`str`, *optional*, defaults to `"htk"`):
|
| 258 |
+
The mel frequency scale to use, `"htk"`, `"kaldi"` or `"slaney"`.
|
| 259 |
+
|
| 260 |
+
Returns:
|
| 261 |
+
`float` or `np.ndarray`: The frequencies on the mel scale.
|
| 262 |
+
"""
|
| 263 |
+
|
| 264 |
+
if mel_scale not in ["slaney", "htk", "kaldi"]:
|
| 265 |
+
raise ValueError('mel_scale should be one of "htk", "slaney" or "kaldi".')
|
| 266 |
+
|
| 267 |
+
if mel_scale == "htk":
|
| 268 |
+
return 2595.0 * np.log10(1.0 + (freq / 700.0))
|
| 269 |
+
elif mel_scale == "kaldi":
|
| 270 |
+
return 1127.0 * np.log(1.0 + (freq / 700.0))
|
| 271 |
+
|
| 272 |
+
min_log_hertz = 1000.0
|
| 273 |
+
min_log_mel = 15.0
|
| 274 |
+
logstep = 27.0 / np.log(6.4)
|
| 275 |
+
mels = 3.0 * freq / 200.0
|
| 276 |
+
|
| 277 |
+
if isinstance(freq, np.ndarray):
|
| 278 |
+
log_region = freq >= min_log_hertz
|
| 279 |
+
mels[log_region] = min_log_mel + np.log(freq[log_region] / min_log_hertz) * logstep
|
| 280 |
+
elif freq >= min_log_hertz:
|
| 281 |
+
mels = min_log_mel + np.log(freq / min_log_hertz) * logstep
|
| 282 |
+
|
| 283 |
+
return mels
|
| 284 |
+
|
| 285 |
+
|
| 286 |
+
def mel_to_hertz(mels: Union[float, np.ndarray], mel_scale: str = "htk") -> Union[float, np.ndarray]:
|
| 287 |
+
"""
|
| 288 |
+
Convert frequency from mels to hertz.
|
| 289 |
+
|
| 290 |
+
Args:
|
| 291 |
+
mels (`float` or `np.ndarray`):
|
| 292 |
+
The frequency, or multiple frequencies, in mels.
|
| 293 |
+
mel_scale (`str`, *optional*, `"htk"`):
|
| 294 |
+
The mel frequency scale to use, `"htk"`, `"kaldi"` or `"slaney"`.
|
| 295 |
+
|
| 296 |
+
Returns:
|
| 297 |
+
`float` or `np.ndarray`: The frequencies in hertz.
|
| 298 |
+
"""
|
| 299 |
+
|
| 300 |
+
if mel_scale not in ["slaney", "htk", "kaldi"]:
|
| 301 |
+
raise ValueError('mel_scale should be one of "htk", "slaney" or "kaldi".')
|
| 302 |
+
|
| 303 |
+
if mel_scale == "htk":
|
| 304 |
+
return 700.0 * (np.power(10, mels / 2595.0) - 1.0)
|
| 305 |
+
elif mel_scale == "kaldi":
|
| 306 |
+
return 700.0 * (np.exp(mels / 1127.0) - 1.0)
|
| 307 |
+
|
| 308 |
+
min_log_hertz = 1000.0
|
| 309 |
+
min_log_mel = 15.0
|
| 310 |
+
logstep = np.log(6.4) / 27.0
|
| 311 |
+
freq = 200.0 * mels / 3.0
|
| 312 |
+
|
| 313 |
+
if isinstance(mels, np.ndarray):
|
| 314 |
+
log_region = mels >= min_log_mel
|
| 315 |
+
freq[log_region] = min_log_hertz * np.exp(logstep * (mels[log_region] - min_log_mel))
|
| 316 |
+
elif mels >= min_log_mel:
|
| 317 |
+
freq = min_log_hertz * np.exp(logstep * (mels - min_log_mel))
|
| 318 |
+
|
| 319 |
+
return freq
|
| 320 |
+
|
| 321 |
+
|
| 322 |
+
def hertz_to_octave(freq: Union[float, np.ndarray], tuning: float = 0.0, bins_per_octave: int = 12):
|
| 323 |
+
"""
|
| 324 |
+
Convert frequency from hertz to fractional octave numbers.
|
| 325 |
+
Adapted from *librosa*.
|
| 326 |
+
|
| 327 |
+
Args:
|
| 328 |
+
freq (`float` or `np.ndarray`):
|
| 329 |
+
The frequency, or multiple frequencies, in hertz (Hz).
|
| 330 |
+
tuning (`float`, defaults to `0.`):
|
| 331 |
+
Tuning deviation from the Stuttgart pitch (A440) in (fractional) bins per octave.
|
| 332 |
+
bins_per_octave (`int`, defaults to `12`):
|
| 333 |
+
Number of bins per octave.
|
| 334 |
+
|
| 335 |
+
Returns:
|
| 336 |
+
`float` or `np.ndarray`: The frequencies on the octave scale.
|
| 337 |
+
"""
|
| 338 |
+
stuttgart_pitch = 440.0 * 2.0 ** (tuning / bins_per_octave)
|
| 339 |
+
octave = np.log2(freq / (float(stuttgart_pitch) / 16))
|
| 340 |
+
return octave
|
| 341 |
+
|
| 342 |
+
|
| 343 |
+
def _create_triangular_filter_bank(fft_freqs: np.ndarray, filter_freqs: np.ndarray) -> np.ndarray:
|
| 344 |
+
"""
|
| 345 |
+
Creates a triangular filter bank.
|
| 346 |
+
|
| 347 |
+
Adapted from *torchaudio* and *librosa*.
|
| 348 |
+
|
| 349 |
+
Args:
|
| 350 |
+
fft_freqs (`np.ndarray` of shape `(num_frequency_bins,)`):
|
| 351 |
+
Discrete frequencies of the FFT bins in Hz.
|
| 352 |
+
filter_freqs (`np.ndarray` of shape `(num_mel_filters,)`):
|
| 353 |
+
Center frequencies of the triangular filters to create, in Hz.
|
| 354 |
+
|
| 355 |
+
Returns:
|
| 356 |
+
`np.ndarray` of shape `(num_frequency_bins, num_mel_filters)`
|
| 357 |
+
"""
|
| 358 |
+
filter_diff = np.diff(filter_freqs)
|
| 359 |
+
slopes = np.expand_dims(filter_freqs, 0) - np.expand_dims(fft_freqs, 1)
|
| 360 |
+
down_slopes = -slopes[:, :-2] / filter_diff[:-1]
|
| 361 |
+
up_slopes = slopes[:, 2:] / filter_diff[1:]
|
| 362 |
+
return np.maximum(np.zeros(1), np.minimum(down_slopes, up_slopes))
|
| 363 |
+
|
| 364 |
+
|
| 365 |
+
def chroma_filter_bank(
|
| 366 |
+
num_frequency_bins: int,
|
| 367 |
+
num_chroma: int,
|
| 368 |
+
sampling_rate: int,
|
| 369 |
+
tuning: float = 0.0,
|
| 370 |
+
power: Optional[float] = 2.0,
|
| 371 |
+
weighting_parameters: Optional[tuple[float, float]] = (5.0, 2.0),
|
| 372 |
+
start_at_c_chroma: bool = True,
|
| 373 |
+
):
|
| 374 |
+
"""
|
| 375 |
+
Creates a chroma filter bank, i.e a linear transformation to project spectrogram bins onto chroma bins.
|
| 376 |
+
|
| 377 |
+
Adapted from *librosa*.
|
| 378 |
+
|
| 379 |
+
Args:
|
| 380 |
+
num_frequency_bins (`int`):
|
| 381 |
+
Number of frequencies used to compute the spectrogram (should be the same as in `stft`).
|
| 382 |
+
num_chroma (`int`):
|
| 383 |
+
Number of chroma bins (i.e pitch classes).
|
| 384 |
+
sampling_rate (`float`):
|
| 385 |
+
Sample rate of the audio waveform.
|
| 386 |
+
tuning (`float`):
|
| 387 |
+
Tuning deviation from A440 in fractions of a chroma bin.
|
| 388 |
+
power (`float`, *optional*, defaults to 2.0):
|
| 389 |
+
If 12.0, normalizes each column with their L2 norm. If 1.0, normalizes each column with their L1 norm.
|
| 390 |
+
weighting_parameters (`tuple[float, float]`, *optional*, defaults to `(5., 2.)`):
|
| 391 |
+
If specified, apply a Gaussian weighting parameterized by the first element of the tuple being the center and
|
| 392 |
+
the second element being the Gaussian half-width.
|
| 393 |
+
start_at_c_chroma (`bool`, *optional*, defaults to `True`):
|
| 394 |
+
If True, the filter bank will start at the 'C' pitch class. Otherwise, it will start at 'A'.
|
| 395 |
+
Returns:
|
| 396 |
+
`np.ndarray` of shape `(num_frequency_bins, num_chroma)`
|
| 397 |
+
"""
|
| 398 |
+
# Get the FFT bins, not counting the DC component
|
| 399 |
+
frequencies = np.linspace(0, sampling_rate, num_frequency_bins, endpoint=False)[1:]
|
| 400 |
+
|
| 401 |
+
freq_bins = num_chroma * hertz_to_octave(frequencies, tuning=tuning, bins_per_octave=num_chroma)
|
| 402 |
+
|
| 403 |
+
# make up a value for the 0 Hz bin = 1.5 octaves below bin 1
|
| 404 |
+
# (so chroma is 50% rotated from bin 1, and bin width is broad)
|
| 405 |
+
freq_bins = np.concatenate(([freq_bins[0] - 1.5 * num_chroma], freq_bins))
|
| 406 |
+
|
| 407 |
+
bins_width = np.concatenate((np.maximum(freq_bins[1:] - freq_bins[:-1], 1.0), [1]))
|
| 408 |
+
|
| 409 |
+
chroma_filters = np.subtract.outer(freq_bins, np.arange(0, num_chroma, dtype="d")).T
|
| 410 |
+
|
| 411 |
+
num_chroma2 = np.round(float(num_chroma) / 2)
|
| 412 |
+
|
| 413 |
+
# Project into range -num_chroma/2 .. num_chroma/2
|
| 414 |
+
# add on fixed offset of 10*num_chroma to ensure all values passed to
|
| 415 |
+
# rem are positive
|
| 416 |
+
chroma_filters = np.remainder(chroma_filters + num_chroma2 + 10 * num_chroma, num_chroma) - num_chroma2
|
| 417 |
+
|
| 418 |
+
# Gaussian bumps - 2*D to make them narrower
|
| 419 |
+
chroma_filters = np.exp(-0.5 * (2 * chroma_filters / np.tile(bins_width, (num_chroma, 1))) ** 2)
|
| 420 |
+
|
| 421 |
+
# normalize each column
|
| 422 |
+
if power is not None:
|
| 423 |
+
chroma_filters = chroma_filters / np.sum(chroma_filters**power, axis=0, keepdims=True) ** (1.0 / power)
|
| 424 |
+
|
| 425 |
+
# Maybe apply scaling for fft bins
|
| 426 |
+
if weighting_parameters is not None:
|
| 427 |
+
center, half_width = weighting_parameters
|
| 428 |
+
chroma_filters *= np.tile(
|
| 429 |
+
np.exp(-0.5 * (((freq_bins / num_chroma - center) / half_width) ** 2)),
|
| 430 |
+
(num_chroma, 1),
|
| 431 |
+
)
|
| 432 |
+
|
| 433 |
+
if start_at_c_chroma:
|
| 434 |
+
chroma_filters = np.roll(chroma_filters, -3 * (num_chroma // 12), axis=0)
|
| 435 |
+
|
| 436 |
+
# remove aliasing columns, copy to ensure row-contiguity
|
| 437 |
+
return np.ascontiguousarray(chroma_filters[:, : int(1 + num_frequency_bins / 2)])
|
| 438 |
+
|
| 439 |
+
|
| 440 |
+
def mel_filter_bank(
|
| 441 |
+
num_frequency_bins: int,
|
| 442 |
+
num_mel_filters: int,
|
| 443 |
+
min_frequency: float,
|
| 444 |
+
max_frequency: float,
|
| 445 |
+
sampling_rate: int,
|
| 446 |
+
norm: Optional[str] = None,
|
| 447 |
+
mel_scale: str = "htk",
|
| 448 |
+
triangularize_in_mel_space: bool = False,
|
| 449 |
+
) -> np.ndarray:
|
| 450 |
+
"""
|
| 451 |
+
Creates a frequency bin conversion matrix used to obtain a mel spectrogram. This is called a *mel filter bank*, and
|
| 452 |
+
various implementation exist, which differ in the number of filters, the shape of the filters, the way the filters
|
| 453 |
+
are spaced, the bandwidth of the filters, and the manner in which the spectrum is warped. The goal of these
|
| 454 |
+
features is to approximate the non-linear human perception of the variation in pitch with respect to the frequency.
|
| 455 |
+
|
| 456 |
+
Different banks of mel filters were introduced in the literature. The following variations are supported:
|
| 457 |
+
|
| 458 |
+
- MFCC FB-20: introduced in 1980 by Davis and Mermelstein, it assumes a sampling frequency of 10 kHz and a speech
|
| 459 |
+
bandwidth of `[0, 4600]` Hz.
|
| 460 |
+
- MFCC FB-24 HTK: from the Cambridge HMM Toolkit (HTK) (1995) uses a filter bank of 24 filters for a speech
|
| 461 |
+
bandwidth of `[0, 8000]` Hz. This assumes sampling rate ≥ 16 kHz.
|
| 462 |
+
- MFCC FB-40: from the Auditory Toolbox for MATLAB written by Slaney in 1998, assumes a sampling rate of 16 kHz and
|
| 463 |
+
speech bandwidth of `[133, 6854]` Hz. This version also includes area normalization.
|
| 464 |
+
- HFCC-E FB-29 (Human Factor Cepstral Coefficients) of Skowronski and Harris (2004), assumes a sampling rate of
|
| 465 |
+
12.5 kHz and speech bandwidth of `[0, 6250]` Hz.
|
| 466 |
+
|
| 467 |
+
This code is adapted from *torchaudio* and *librosa*. Note that the default parameters of torchaudio's
|
| 468 |
+
`melscale_fbanks` implement the `"htk"` filters while librosa uses the `"slaney"` implementation.
|
| 469 |
+
|
| 470 |
+
Args:
|
| 471 |
+
num_frequency_bins (`int`):
|
| 472 |
+
Number of frequency bins (should be the same as `n_fft // 2 + 1` where `n_fft` is the size of the Fourier Transform used to compute the spectrogram).
|
| 473 |
+
num_mel_filters (`int`):
|
| 474 |
+
Number of mel filters to generate.
|
| 475 |
+
min_frequency (`float`):
|
| 476 |
+
Lowest frequency of interest in Hz.
|
| 477 |
+
max_frequency (`float`):
|
| 478 |
+
Highest frequency of interest in Hz. This should not exceed `sampling_rate / 2`.
|
| 479 |
+
sampling_rate (`int`):
|
| 480 |
+
Sample rate of the audio waveform.
|
| 481 |
+
norm (`str`, *optional*):
|
| 482 |
+
If `"slaney"`, divide the triangular mel weights by the width of the mel band (area normalization).
|
| 483 |
+
mel_scale (`str`, *optional*, defaults to `"htk"`):
|
| 484 |
+
The mel frequency scale to use, `"htk"`, `"kaldi"` or `"slaney"`.
|
| 485 |
+
triangularize_in_mel_space (`bool`, *optional*, defaults to `False`):
|
| 486 |
+
If this option is enabled, the triangular filter is applied in mel space rather than frequency space. This
|
| 487 |
+
should be set to `true` in order to get the same results as `torchaudio` when computing mel filters.
|
| 488 |
+
|
| 489 |
+
Returns:
|
| 490 |
+
`np.ndarray` of shape (`num_frequency_bins`, `num_mel_filters`): Triangular filter bank matrix. This is a
|
| 491 |
+
projection matrix to go from a spectrogram to a mel spectrogram.
|
| 492 |
+
"""
|
| 493 |
+
if norm is not None and norm != "slaney":
|
| 494 |
+
raise ValueError('norm must be one of None or "slaney"')
|
| 495 |
+
|
| 496 |
+
if num_frequency_bins < 2:
|
| 497 |
+
raise ValueError(f"Require num_frequency_bins: {num_frequency_bins} >= 2")
|
| 498 |
+
|
| 499 |
+
if min_frequency > max_frequency:
|
| 500 |
+
raise ValueError(f"Require min_frequency: {min_frequency} <= max_frequency: {max_frequency}")
|
| 501 |
+
|
| 502 |
+
# center points of the triangular mel filters
|
| 503 |
+
mel_min = hertz_to_mel(min_frequency, mel_scale=mel_scale)
|
| 504 |
+
mel_max = hertz_to_mel(max_frequency, mel_scale=mel_scale)
|
| 505 |
+
mel_freqs = np.linspace(mel_min, mel_max, num_mel_filters + 2)
|
| 506 |
+
filter_freqs = mel_to_hertz(mel_freqs, mel_scale=mel_scale)
|
| 507 |
+
|
| 508 |
+
if triangularize_in_mel_space:
|
| 509 |
+
# frequencies of FFT bins in Hz, but filters triangularized in mel space
|
| 510 |
+
fft_bin_width = sampling_rate / ((num_frequency_bins - 1) * 2)
|
| 511 |
+
fft_freqs = hertz_to_mel(fft_bin_width * np.arange(num_frequency_bins), mel_scale=mel_scale)
|
| 512 |
+
filter_freqs = mel_freqs
|
| 513 |
+
else:
|
| 514 |
+
# frequencies of FFT bins in Hz
|
| 515 |
+
fft_freqs = np.linspace(0, sampling_rate // 2, num_frequency_bins)
|
| 516 |
+
|
| 517 |
+
mel_filters = _create_triangular_filter_bank(fft_freqs, filter_freqs)
|
| 518 |
+
|
| 519 |
+
if norm is not None and norm == "slaney":
|
| 520 |
+
# Slaney-style mel is scaled to be approx constant energy per channel
|
| 521 |
+
enorm = 2.0 / (filter_freqs[2 : num_mel_filters + 2] - filter_freqs[:num_mel_filters])
|
| 522 |
+
mel_filters *= np.expand_dims(enorm, 0)
|
| 523 |
+
|
| 524 |
+
if (mel_filters.max(axis=0) == 0.0).any():
|
| 525 |
+
warnings.warn(
|
| 526 |
+
"At least one mel filter has all zero values. "
|
| 527 |
+
f"The value for `num_mel_filters` ({num_mel_filters}) may be set too high. "
|
| 528 |
+
f"Or, the value for `num_frequency_bins` ({num_frequency_bins}) may be set too low."
|
| 529 |
+
)
|
| 530 |
+
|
| 531 |
+
return mel_filters
|
| 532 |
+
|
| 533 |
+
|
| 534 |
+
def optimal_fft_length(window_length: int) -> int:
|
| 535 |
+
"""
|
| 536 |
+
Finds the best FFT input size for a given `window_length`. This function takes a given window length and, if not
|
| 537 |
+
already a power of two, rounds it up to the next power or two.
|
| 538 |
+
|
| 539 |
+
The FFT algorithm works fastest when the length of the input is a power of two, which may be larger than the size
|
| 540 |
+
of the window or analysis frame. For example, if the window is 400 samples, using an FFT input size of 512 samples
|
| 541 |
+
is more optimal than an FFT size of 400 samples. Using a larger FFT size does not affect the detected frequencies,
|
| 542 |
+
it simply gives a higher frequency resolution (i.e. the frequency bins are smaller).
|
| 543 |
+
"""
|
| 544 |
+
return 2 ** int(np.ceil(np.log2(window_length)))
|
| 545 |
+
|
| 546 |
+
|
| 547 |
+
def window_function(
|
| 548 |
+
window_length: int,
|
| 549 |
+
name: str = "hann",
|
| 550 |
+
periodic: bool = True,
|
| 551 |
+
frame_length: Optional[int] = None,
|
| 552 |
+
center: bool = True,
|
| 553 |
+
) -> np.ndarray:
|
| 554 |
+
"""
|
| 555 |
+
Returns an array containing the specified window. This window is intended to be used with `stft`.
|
| 556 |
+
|
| 557 |
+
The following window types are supported:
|
| 558 |
+
|
| 559 |
+
- `"boxcar"`: a rectangular window
|
| 560 |
+
- `"hamming"`: the Hamming window
|
| 561 |
+
- `"hann"`: the Hann window
|
| 562 |
+
- `"povey"`: the Povey window
|
| 563 |
+
|
| 564 |
+
Args:
|
| 565 |
+
window_length (`int`):
|
| 566 |
+
The length of the window in samples.
|
| 567 |
+
name (`str`, *optional*, defaults to `"hann"`):
|
| 568 |
+
The name of the window function.
|
| 569 |
+
periodic (`bool`, *optional*, defaults to `True`):
|
| 570 |
+
Whether the window is periodic or symmetric.
|
| 571 |
+
frame_length (`int`, *optional*):
|
| 572 |
+
The length of the analysis frames in samples. Provide a value for `frame_length` if the window is smaller
|
| 573 |
+
than the frame length, so that it will be zero-padded.
|
| 574 |
+
center (`bool`, *optional*, defaults to `True`):
|
| 575 |
+
Whether to center the window inside the FFT buffer. Only used when `frame_length` is provided.
|
| 576 |
+
|
| 577 |
+
Returns:
|
| 578 |
+
`np.ndarray` of shape `(window_length,)` or `(frame_length,)` containing the window.
|
| 579 |
+
"""
|
| 580 |
+
length = window_length + 1 if periodic else window_length
|
| 581 |
+
|
| 582 |
+
if name == "boxcar":
|
| 583 |
+
window = np.ones(length)
|
| 584 |
+
elif name in ["hamming", "hamming_window"]:
|
| 585 |
+
window = np.hamming(length)
|
| 586 |
+
elif name in ["hann", "hann_window"]:
|
| 587 |
+
window = np.hanning(length)
|
| 588 |
+
elif name == "povey":
|
| 589 |
+
window = np.power(np.hanning(length), 0.85)
|
| 590 |
+
else:
|
| 591 |
+
raise ValueError(f"Unknown window function '{name}'")
|
| 592 |
+
|
| 593 |
+
if periodic:
|
| 594 |
+
window = window[:-1]
|
| 595 |
+
|
| 596 |
+
if frame_length is None:
|
| 597 |
+
return window
|
| 598 |
+
|
| 599 |
+
if window_length > frame_length:
|
| 600 |
+
raise ValueError(
|
| 601 |
+
f"Length of the window ({window_length}) may not be larger than frame_length ({frame_length})"
|
| 602 |
+
)
|
| 603 |
+
|
| 604 |
+
padded_window = np.zeros(frame_length)
|
| 605 |
+
offset = (frame_length - window_length) // 2 if center else 0
|
| 606 |
+
padded_window[offset : offset + window_length] = window
|
| 607 |
+
return padded_window
|
| 608 |
+
|
| 609 |
+
|
| 610 |
+
# TODO This method does not support batching yet as we are mainly focused on inference.
|
| 611 |
+
def spectrogram(
|
| 612 |
+
waveform: np.ndarray,
|
| 613 |
+
window: np.ndarray,
|
| 614 |
+
frame_length: int,
|
| 615 |
+
hop_length: int,
|
| 616 |
+
fft_length: Optional[int] = None,
|
| 617 |
+
power: Optional[float] = 1.0,
|
| 618 |
+
center: bool = True,
|
| 619 |
+
pad_mode: str = "reflect",
|
| 620 |
+
onesided: bool = True,
|
| 621 |
+
dither: float = 0.0,
|
| 622 |
+
preemphasis: Optional[float] = None,
|
| 623 |
+
mel_filters: Optional[np.ndarray] = None,
|
| 624 |
+
mel_floor: float = 1e-10,
|
| 625 |
+
log_mel: Optional[str] = None,
|
| 626 |
+
reference: float = 1.0,
|
| 627 |
+
min_value: float = 1e-10,
|
| 628 |
+
db_range: Optional[float] = None,
|
| 629 |
+
remove_dc_offset: bool = False,
|
| 630 |
+
dtype: np.dtype = np.float32,
|
| 631 |
+
) -> np.ndarray:
|
| 632 |
+
"""
|
| 633 |
+
Calculates a spectrogram over one waveform using the Short-Time Fourier Transform.
|
| 634 |
+
|
| 635 |
+
This function can create the following kinds of spectrograms:
|
| 636 |
+
|
| 637 |
+
- amplitude spectrogram (`power = 1.0`)
|
| 638 |
+
- power spectrogram (`power = 2.0`)
|
| 639 |
+
- complex-valued spectrogram (`power = None`)
|
| 640 |
+
- log spectrogram (use `log_mel` argument)
|
| 641 |
+
- mel spectrogram (provide `mel_filters`)
|
| 642 |
+
- log-mel spectrogram (provide `mel_filters` and `log_mel`)
|
| 643 |
+
|
| 644 |
+
How this works:
|
| 645 |
+
|
| 646 |
+
1. The input waveform is split into frames of size `frame_length` that are partially overlapping by `frame_length
|
| 647 |
+
- hop_length` samples.
|
| 648 |
+
2. Each frame is multiplied by the window and placed into a buffer of size `fft_length`.
|
| 649 |
+
3. The DFT is taken of each windowed frame.
|
| 650 |
+
4. The results are stacked into a spectrogram.
|
| 651 |
+
|
| 652 |
+
We make a distinction between the following "blocks" of sample data, each of which may have a different lengths:
|
| 653 |
+
|
| 654 |
+
- The analysis frame. This is the size of the time slices that the input waveform is split into.
|
| 655 |
+
- The window. Each analysis frame is multiplied by the window to avoid spectral leakage.
|
| 656 |
+
- The FFT input buffer. The length of this determines how many frequency bins are in the spectrogram.
|
| 657 |
+
|
| 658 |
+
In this implementation, the window is assumed to be zero-padded to have the same size as the analysis frame. A
|
| 659 |
+
padded window can be obtained from `window_function()`. The FFT input buffer may be larger than the analysis frame,
|
| 660 |
+
typically the next power of two.
|
| 661 |
+
|
| 662 |
+
Note: This function is not optimized for speed yet. It should be mostly compatible with `librosa.stft` and
|
| 663 |
+
`torchaudio.functional.transforms.Spectrogram`, although it is more flexible due to the different ways spectrograms
|
| 664 |
+
can be constructed.
|
| 665 |
+
|
| 666 |
+
Args:
|
| 667 |
+
waveform (`np.ndarray` of shape `(length,)`):
|
| 668 |
+
The input waveform. This must be a single real-valued, mono waveform.
|
| 669 |
+
window (`np.ndarray` of shape `(frame_length,)`):
|
| 670 |
+
The windowing function to apply, including zero-padding if necessary. The actual window length may be
|
| 671 |
+
shorter than `frame_length`, but we're assuming the array has already been zero-padded.
|
| 672 |
+
frame_length (`int`):
|
| 673 |
+
The length of the analysis frames in samples. With librosa this is always equal to `fft_length` but we also
|
| 674 |
+
allow smaller sizes.
|
| 675 |
+
hop_length (`int`):
|
| 676 |
+
The stride between successive analysis frames in samples.
|
| 677 |
+
fft_length (`int`, *optional*):
|
| 678 |
+
The size of the FFT buffer in samples. This determines how many frequency bins the spectrogram will have.
|
| 679 |
+
For optimal speed, this should be a power of two. If `None`, uses `frame_length`.
|
| 680 |
+
power (`float`, *optional*, defaults to 1.0):
|
| 681 |
+
If 1.0, returns the amplitude spectrogram. If 2.0, returns the power spectrogram. If `None`, returns
|
| 682 |
+
complex numbers.
|
| 683 |
+
center (`bool`, *optional*, defaults to `True`):
|
| 684 |
+
Whether to pad the waveform so that frame `t` is centered around time `t * hop_length`. If `False`, frame
|
| 685 |
+
`t` will start at time `t * hop_length`.
|
| 686 |
+
pad_mode (`str`, *optional*, defaults to `"reflect"`):
|
| 687 |
+
Padding mode used when `center` is `True`. Possible values are: `"constant"` (pad with zeros), `"edge"`
|
| 688 |
+
(pad with edge values), `"reflect"` (pads with mirrored values).
|
| 689 |
+
onesided (`bool`, *optional*, defaults to `True`):
|
| 690 |
+
If True, only computes the positive frequencies and returns a spectrogram containing `fft_length // 2 + 1`
|
| 691 |
+
frequency bins. If False, also computes the negative frequencies and returns `fft_length` frequency bins.
|
| 692 |
+
dither (`float`, *optional*, defaults to 0.0):
|
| 693 |
+
Adds dithering. In other words, adds a small Gaussian noise to each frame.
|
| 694 |
+
E.g. use 4.0 to add dithering with a normal distribution centered
|
| 695 |
+
around 0.0 with standard deviation 4.0, 0.0 means no dithering.
|
| 696 |
+
Dithering has similar effect as `mel_floor`. It reduces the high log_mel_fbank
|
| 697 |
+
values for signals with hard-zero sections, when VAD cutoff is present in the signal.
|
| 698 |
+
preemphasis (`float`, *optional*)
|
| 699 |
+
Coefficient for a low-pass filter that applies pre-emphasis before the DFT.
|
| 700 |
+
mel_filters (`np.ndarray` of shape `(num_freq_bins, num_mel_filters)`, *optional*):
|
| 701 |
+
The mel filter bank. If supplied, applies a this filter bank to create a mel spectrogram.
|
| 702 |
+
mel_floor (`float`, *optional*, defaults to 1e-10):
|
| 703 |
+
Minimum value of mel frequency banks.
|
| 704 |
+
log_mel (`str`, *optional*):
|
| 705 |
+
How to convert the spectrogram to log scale. Possible options are: `None` (don't convert), `"log"` (take
|
| 706 |
+
the natural logarithm) `"log10"` (take the base-10 logarithm), `"dB"` (convert to decibels). Can only be
|
| 707 |
+
used when `power` is not `None`.
|
| 708 |
+
reference (`float`, *optional*, defaults to 1.0):
|
| 709 |
+
Sets the input spectrogram value that corresponds to 0 dB. For example, use `np.max(spectrogram)` to set
|
| 710 |
+
the loudest part to 0 dB. Must be greater than zero.
|
| 711 |
+
min_value (`float`, *optional*, defaults to `1e-10`):
|
| 712 |
+
The spectrogram will be clipped to this minimum value before conversion to decibels, to avoid taking
|
| 713 |
+
`log(0)`. For a power spectrogram, the default of `1e-10` corresponds to a minimum of -100 dB. For an
|
| 714 |
+
amplitude spectrogram, the value `1e-5` corresponds to -100 dB. Must be greater than zero.
|
| 715 |
+
db_range (`float`, *optional*):
|
| 716 |
+
Sets the maximum dynamic range in decibels. For example, if `db_range = 80`, the difference between the
|
| 717 |
+
peak value and the smallest value will never be more than 80 dB. Must be greater than zero.
|
| 718 |
+
remove_dc_offset (`bool`, *optional*):
|
| 719 |
+
Subtract mean from waveform on each frame, applied before pre-emphasis. This should be set to `true` in
|
| 720 |
+
order to get the same results as `torchaudio.compliance.kaldi.fbank` when computing mel filters.
|
| 721 |
+
dtype (`np.dtype`, *optional*, defaults to `np.float32`):
|
| 722 |
+
Data type of the spectrogram tensor. If `power` is None, this argument is ignored and the dtype will be
|
| 723 |
+
`np.complex64`.
|
| 724 |
+
|
| 725 |
+
Returns:
|
| 726 |
+
`nd.array` containing a spectrogram of shape `(num_frequency_bins, length)` for a regular spectrogram or shape
|
| 727 |
+
`(num_mel_filters, length)` for a mel spectrogram.
|
| 728 |
+
"""
|
| 729 |
+
window_length = len(window)
|
| 730 |
+
|
| 731 |
+
if fft_length is None:
|
| 732 |
+
fft_length = frame_length
|
| 733 |
+
|
| 734 |
+
if frame_length > fft_length:
|
| 735 |
+
raise ValueError(f"frame_length ({frame_length}) may not be larger than fft_length ({fft_length})")
|
| 736 |
+
|
| 737 |
+
if window_length != frame_length:
|
| 738 |
+
raise ValueError(f"Length of the window ({window_length}) must equal frame_length ({frame_length})")
|
| 739 |
+
|
| 740 |
+
if hop_length <= 0:
|
| 741 |
+
raise ValueError("hop_length must be greater than zero")
|
| 742 |
+
|
| 743 |
+
if waveform.ndim != 1:
|
| 744 |
+
raise ValueError(f"Input waveform must have only one dimension, shape is {waveform.shape}")
|
| 745 |
+
|
| 746 |
+
if np.iscomplexobj(waveform):
|
| 747 |
+
raise ValueError("Complex-valued input waveforms are not currently supported")
|
| 748 |
+
|
| 749 |
+
if power is None and mel_filters is not None:
|
| 750 |
+
raise ValueError(
|
| 751 |
+
"You have provided `mel_filters` but `power` is `None`. Mel spectrogram computation is not yet supported for complex-valued spectrogram."
|
| 752 |
+
"Specify `power` to fix this issue."
|
| 753 |
+
)
|
| 754 |
+
|
| 755 |
+
# center pad the waveform
|
| 756 |
+
if center:
|
| 757 |
+
padding = [(int(frame_length // 2), int(frame_length // 2))]
|
| 758 |
+
waveform = np.pad(waveform, padding, mode=pad_mode)
|
| 759 |
+
|
| 760 |
+
# promote to float64, since np.fft uses float64 internally
|
| 761 |
+
waveform = waveform.astype(np.float64)
|
| 762 |
+
window = window.astype(np.float64)
|
| 763 |
+
|
| 764 |
+
# split waveform into frames of frame_length size
|
| 765 |
+
num_frames = int(1 + np.floor((waveform.size - frame_length) / hop_length))
|
| 766 |
+
|
| 767 |
+
num_frequency_bins = (fft_length // 2) + 1 if onesided else fft_length
|
| 768 |
+
spectrogram = np.empty((num_frames, num_frequency_bins), dtype=np.complex64)
|
| 769 |
+
|
| 770 |
+
# rfft is faster than fft
|
| 771 |
+
fft_func = np.fft.rfft if onesided else np.fft.fft
|
| 772 |
+
buffer = np.zeros(fft_length)
|
| 773 |
+
|
| 774 |
+
timestep = 0
|
| 775 |
+
for frame_idx in range(num_frames):
|
| 776 |
+
buffer[:frame_length] = waveform[timestep : timestep + frame_length]
|
| 777 |
+
|
| 778 |
+
if dither != 0.0:
|
| 779 |
+
buffer[:frame_length] += dither * np.random.randn(frame_length)
|
| 780 |
+
|
| 781 |
+
if remove_dc_offset:
|
| 782 |
+
buffer[:frame_length] = buffer[:frame_length] - buffer[:frame_length].mean()
|
| 783 |
+
|
| 784 |
+
if preemphasis is not None:
|
| 785 |
+
buffer[1:frame_length] -= preemphasis * buffer[: frame_length - 1]
|
| 786 |
+
buffer[0] *= 1 - preemphasis
|
| 787 |
+
|
| 788 |
+
buffer[:frame_length] *= window
|
| 789 |
+
|
| 790 |
+
spectrogram[frame_idx] = fft_func(buffer)
|
| 791 |
+
timestep += hop_length
|
| 792 |
+
|
| 793 |
+
# note: ** is much faster than np.power
|
| 794 |
+
if power is not None:
|
| 795 |
+
spectrogram = np.abs(spectrogram, dtype=np.float64) ** power
|
| 796 |
+
|
| 797 |
+
spectrogram = spectrogram.T
|
| 798 |
+
|
| 799 |
+
if mel_filters is not None:
|
| 800 |
+
spectrogram = np.maximum(mel_floor, np.dot(mel_filters.T, spectrogram))
|
| 801 |
+
|
| 802 |
+
if power is not None and log_mel is not None:
|
| 803 |
+
if log_mel == "log":
|
| 804 |
+
spectrogram = np.log(spectrogram)
|
| 805 |
+
elif log_mel == "log10":
|
| 806 |
+
spectrogram = np.log10(spectrogram)
|
| 807 |
+
elif log_mel == "dB":
|
| 808 |
+
if power == 1.0:
|
| 809 |
+
spectrogram = amplitude_to_db(spectrogram, reference, min_value, db_range)
|
| 810 |
+
elif power == 2.0:
|
| 811 |
+
spectrogram = power_to_db(spectrogram, reference, min_value, db_range)
|
| 812 |
+
else:
|
| 813 |
+
raise ValueError(f"Cannot use log_mel option '{log_mel}' with power {power}")
|
| 814 |
+
else:
|
| 815 |
+
raise ValueError(f"Unknown log_mel option: {log_mel}")
|
| 816 |
+
|
| 817 |
+
spectrogram = np.asarray(spectrogram, dtype)
|
| 818 |
+
|
| 819 |
+
return spectrogram
|
| 820 |
+
|
| 821 |
+
|
| 822 |
+
def spectrogram_batch(
|
| 823 |
+
waveform_list: list[np.ndarray],
|
| 824 |
+
window: np.ndarray,
|
| 825 |
+
frame_length: int,
|
| 826 |
+
hop_length: int,
|
| 827 |
+
fft_length: Optional[int] = None,
|
| 828 |
+
power: Optional[float] = 1.0,
|
| 829 |
+
center: bool = True,
|
| 830 |
+
pad_mode: str = "reflect",
|
| 831 |
+
onesided: bool = True,
|
| 832 |
+
dither: float = 0.0,
|
| 833 |
+
preemphasis: Optional[float] = None,
|
| 834 |
+
mel_filters: Optional[np.ndarray] = None,
|
| 835 |
+
mel_floor: float = 1e-10,
|
| 836 |
+
log_mel: Optional[str] = None,
|
| 837 |
+
reference: float = 1.0,
|
| 838 |
+
min_value: float = 1e-10,
|
| 839 |
+
db_range: Optional[float] = None,
|
| 840 |
+
remove_dc_offset: bool = False,
|
| 841 |
+
dtype: np.dtype = np.float32,
|
| 842 |
+
) -> list[np.ndarray]:
|
| 843 |
+
"""
|
| 844 |
+
Calculates spectrograms for a list of waveforms using the Short-Time Fourier Transform, optimized for batch processing.
|
| 845 |
+
This function extends the capabilities of the `spectrogram` function to handle multiple waveforms efficiently by leveraging broadcasting.
|
| 846 |
+
|
| 847 |
+
It supports generating various types of spectrograms:
|
| 848 |
+
|
| 849 |
+
- amplitude spectrogram (`power = 1.0`)
|
| 850 |
+
- power spectrogram (`power = 2.0`)
|
| 851 |
+
- complex-valued spectrogram (`power = None`)
|
| 852 |
+
- log spectrogram (use `log_mel` argument)
|
| 853 |
+
- mel spectrogram (provide `mel_filters`)
|
| 854 |
+
- log-mel spectrogram (provide `mel_filters` and `log_mel`)
|
| 855 |
+
|
| 856 |
+
How this works:
|
| 857 |
+
|
| 858 |
+
1. The input waveform is split into frames of size `frame_length` that are partially overlapping by `frame_length
|
| 859 |
+
- hop_length` samples.
|
| 860 |
+
2. Each frame is multiplied by the window and placed into a buffer of size `fft_length`.
|
| 861 |
+
3. The DFT is taken of each windowed frame.
|
| 862 |
+
4. The results are stacked into a spectrogram.
|
| 863 |
+
|
| 864 |
+
We make a distinction between the following "blocks" of sample data, each of which may have a different lengths:
|
| 865 |
+
|
| 866 |
+
- The analysis frame. This is the size of the time slices that the input waveform is split into.
|
| 867 |
+
- The window. Each analysis frame is multiplied by the window to avoid spectral leakage.
|
| 868 |
+
- The FFT input buffer. The length of this determines how many frequency bins are in the spectrogram.
|
| 869 |
+
|
| 870 |
+
In this implementation, the window is assumed to be zero-padded to have the same size as the analysis frame. A
|
| 871 |
+
padded window can be obtained from `window_function()`. The FFT input buffer may be larger than the analysis frame,
|
| 872 |
+
typically the next power of two.
|
| 873 |
+
|
| 874 |
+
Note: This function is designed for efficient batch processing of multiple waveforms but retains compatibility with individual waveform processing methods like `librosa.stft`.
|
| 875 |
+
|
| 876 |
+
Args:
|
| 877 |
+
waveform_list (`list[np.ndarray]` with arrays of shape `(length,)`):
|
| 878 |
+
The list of input waveforms, each a single-channel (mono) signal.
|
| 879 |
+
window (`np.ndarray` of shape `(frame_length,)`):
|
| 880 |
+
The windowing function to apply, including zero-padding if necessary.
|
| 881 |
+
frame_length (`int`):
|
| 882 |
+
The length of each frame for analysis.
|
| 883 |
+
hop_length (`int`):
|
| 884 |
+
The step size between successive frames.
|
| 885 |
+
fft_length (`int`, *optional*):
|
| 886 |
+
The size of the FFT buffer, defining frequency bin resolution.
|
| 887 |
+
power (`float`, *optional*, defaults to 1.0):
|
| 888 |
+
Determines the type of spectrogram: 1.0 for amplitude, 2.0 for power, None for complex.
|
| 889 |
+
center (`bool`, *optional*, defaults to `True`):
|
| 890 |
+
Whether to center-pad the waveform frames.
|
| 891 |
+
pad_mode (`str`, *optional*, defaults to `"reflect"`):
|
| 892 |
+
The padding strategy when `center` is `True`.
|
| 893 |
+
onesided (`bool`, *optional*, defaults to `True`):
|
| 894 |
+
If True, returns a one-sided spectrogram for real input signals.
|
| 895 |
+
dither (`float`, *optional*, defaults to 0.0):
|
| 896 |
+
Adds dithering. In other words, adds a small Gaussian noise to each frame.
|
| 897 |
+
E.g. use 4.0 to add dithering with a normal distribution centered
|
| 898 |
+
around 0.0 with standard deviation 4.0, 0.0 means no dithering.
|
| 899 |
+
preemphasis (`float`, *optional*):
|
| 900 |
+
Applies a pre-emphasis filter to each frame.
|
| 901 |
+
mel_filters (`np.ndarray`, *optional*):
|
| 902 |
+
Mel filter bank for converting to mel spectrogram.
|
| 903 |
+
mel_floor (`float`, *optional*, defaults to 1e-10):
|
| 904 |
+
Floor value for mel spectrogram to avoid log(0).
|
| 905 |
+
log_mel (`str`, *optional*):
|
| 906 |
+
Specifies log scaling strategy; options are None, "log", "log10", "dB".
|
| 907 |
+
reference (`float`, *optional*, defaults to 1.0):
|
| 908 |
+
Reference value for dB conversion in log_mel.
|
| 909 |
+
min_value (`float`, *optional*, defaults to 1e-10):
|
| 910 |
+
Minimum floor value for log scale conversions.
|
| 911 |
+
db_range (`float`, *optional*):
|
| 912 |
+
Dynamic range for dB scale spectrograms.
|
| 913 |
+
remove_dc_offset (`bool`, *optional*):
|
| 914 |
+
Whether to remove the DC offset from each frame.
|
| 915 |
+
dtype (`np.dtype`, *optional*, defaults to `np.float32`):
|
| 916 |
+
Data type of the output spectrogram.
|
| 917 |
+
|
| 918 |
+
Returns:
|
| 919 |
+
list[`np.ndarray`]: A list of spectrogram arrays, one for each input waveform.
|
| 920 |
+
"""
|
| 921 |
+
window_length = len(window)
|
| 922 |
+
|
| 923 |
+
if fft_length is None:
|
| 924 |
+
fft_length = frame_length
|
| 925 |
+
|
| 926 |
+
if frame_length > fft_length:
|
| 927 |
+
raise ValueError(f"frame_length ({frame_length}) may not be larger than fft_length ({fft_length})")
|
| 928 |
+
|
| 929 |
+
if window_length != frame_length:
|
| 930 |
+
raise ValueError(f"Length of the window ({window_length}) must equal frame_length ({frame_length})")
|
| 931 |
+
|
| 932 |
+
if hop_length <= 0:
|
| 933 |
+
raise ValueError("hop_length must be greater than zero")
|
| 934 |
+
|
| 935 |
+
# Check the dimensions of the waveform , and if waveform is complex
|
| 936 |
+
for waveform in waveform_list:
|
| 937 |
+
if waveform.ndim != 1:
|
| 938 |
+
raise ValueError(f"Input waveform must have only one dimension, shape is {waveform.shape}")
|
| 939 |
+
if np.iscomplexobj(waveform):
|
| 940 |
+
raise ValueError("Complex-valued input waveforms are not currently supported")
|
| 941 |
+
# Center pad the waveform
|
| 942 |
+
if center:
|
| 943 |
+
padding = [(int(frame_length // 2), int(frame_length // 2))]
|
| 944 |
+
waveform_list = [
|
| 945 |
+
np.pad(
|
| 946 |
+
waveform,
|
| 947 |
+
padding,
|
| 948 |
+
mode=pad_mode,
|
| 949 |
+
)
|
| 950 |
+
for waveform in waveform_list
|
| 951 |
+
]
|
| 952 |
+
original_waveform_lengths = [
|
| 953 |
+
len(waveform) for waveform in waveform_list
|
| 954 |
+
] # these lengths will be used to remove padding later
|
| 955 |
+
|
| 956 |
+
# Batch pad the waveform
|
| 957 |
+
max_length = max(original_waveform_lengths)
|
| 958 |
+
padded_waveform_batch = np.array(
|
| 959 |
+
[
|
| 960 |
+
np.pad(waveform, (0, max_length - len(waveform)), mode="constant", constant_values=0)
|
| 961 |
+
for waveform in waveform_list
|
| 962 |
+
],
|
| 963 |
+
dtype=dtype,
|
| 964 |
+
)
|
| 965 |
+
|
| 966 |
+
# Promote to float64, since np.fft uses float64 internally
|
| 967 |
+
padded_waveform_batch = padded_waveform_batch.astype(np.float64)
|
| 968 |
+
window = window.astype(np.float64)
|
| 969 |
+
|
| 970 |
+
# Split waveform into frames of frame_length size
|
| 971 |
+
num_frames = int(1 + np.floor((padded_waveform_batch.shape[1] - frame_length) / hop_length))
|
| 972 |
+
# these lengths will be used to remove padding later
|
| 973 |
+
true_num_frames = [int(1 + np.floor((length - frame_length) / hop_length)) for length in original_waveform_lengths]
|
| 974 |
+
num_batches = padded_waveform_batch.shape[0]
|
| 975 |
+
|
| 976 |
+
num_frequency_bins = (fft_length // 2) + 1 if onesided else fft_length
|
| 977 |
+
spectrogram = np.empty((num_batches, num_frames, num_frequency_bins), dtype=np.complex64)
|
| 978 |
+
|
| 979 |
+
# rfft is faster than fft
|
| 980 |
+
fft_func = np.fft.rfft if onesided else np.fft.fft
|
| 981 |
+
buffer = np.zeros((num_batches, fft_length))
|
| 982 |
+
|
| 983 |
+
for frame_idx in range(num_frames):
|
| 984 |
+
timestep = frame_idx * hop_length
|
| 985 |
+
buffer[:, :frame_length] = padded_waveform_batch[:, timestep : timestep + frame_length]
|
| 986 |
+
|
| 987 |
+
if dither != 0.0:
|
| 988 |
+
buffer[:, :frame_length] += dither * np.random.randn(*buffer[:, :frame_length].shape)
|
| 989 |
+
|
| 990 |
+
if remove_dc_offset:
|
| 991 |
+
buffer[:, :frame_length] -= buffer[:, :frame_length].mean(axis=1, keepdims=True)
|
| 992 |
+
|
| 993 |
+
if preemphasis is not None:
|
| 994 |
+
buffer[:, 1:frame_length] -= preemphasis * buffer[:, : frame_length - 1]
|
| 995 |
+
buffer[:, 0] *= 1 - preemphasis
|
| 996 |
+
|
| 997 |
+
buffer[:, :frame_length] *= window
|
| 998 |
+
|
| 999 |
+
spectrogram[:, frame_idx] = fft_func(buffer)
|
| 1000 |
+
|
| 1001 |
+
# Note: ** is much faster than np.power
|
| 1002 |
+
if power is not None:
|
| 1003 |
+
spectrogram = np.abs(spectrogram, dtype=np.float64) ** power
|
| 1004 |
+
|
| 1005 |
+
# Apply mel filters if provided
|
| 1006 |
+
if mel_filters is not None:
|
| 1007 |
+
result = np.tensordot(spectrogram, mel_filters.T, axes=([2], [1]))
|
| 1008 |
+
spectrogram = np.maximum(mel_floor, result)
|
| 1009 |
+
|
| 1010 |
+
# Convert to log scale if specified
|
| 1011 |
+
if power is not None and log_mel is not None:
|
| 1012 |
+
if log_mel == "log":
|
| 1013 |
+
spectrogram = np.log(spectrogram)
|
| 1014 |
+
elif log_mel == "log10":
|
| 1015 |
+
spectrogram = np.log10(spectrogram)
|
| 1016 |
+
elif log_mel == "dB":
|
| 1017 |
+
if power == 1.0:
|
| 1018 |
+
spectrogram = amplitude_to_db_batch(spectrogram, reference, min_value, db_range)
|
| 1019 |
+
elif power == 2.0:
|
| 1020 |
+
spectrogram = power_to_db_batch(spectrogram, reference, min_value, db_range)
|
| 1021 |
+
else:
|
| 1022 |
+
raise ValueError(f"Cannot use log_mel option '{log_mel}' with power {power}")
|
| 1023 |
+
else:
|
| 1024 |
+
raise ValueError(f"Unknown log_mel option: {log_mel}")
|
| 1025 |
+
|
| 1026 |
+
spectrogram = np.asarray(spectrogram, dtype)
|
| 1027 |
+
|
| 1028 |
+
spectrogram_list = [spectrogram[i, : true_num_frames[i], :].T for i in range(len(true_num_frames))]
|
| 1029 |
+
|
| 1030 |
+
return spectrogram_list
|
| 1031 |
+
|
| 1032 |
+
|
| 1033 |
+
def power_to_db(
|
| 1034 |
+
spectrogram: np.ndarray,
|
| 1035 |
+
reference: float = 1.0,
|
| 1036 |
+
min_value: float = 1e-10,
|
| 1037 |
+
db_range: Optional[float] = None,
|
| 1038 |
+
) -> np.ndarray:
|
| 1039 |
+
"""
|
| 1040 |
+
Converts a power spectrogram to the decibel scale. This computes `10 * log10(spectrogram / reference)`, using basic
|
| 1041 |
+
logarithm properties for numerical stability.
|
| 1042 |
+
|
| 1043 |
+
The motivation behind applying the log function on the (mel) spectrogram is that humans do not hear loudness on a
|
| 1044 |
+
linear scale. Generally to double the perceived volume of a sound we need to put 8 times as much energy into it.
|
| 1045 |
+
This means that large variations in energy may not sound all that different if the sound is loud to begin with.
|
| 1046 |
+
This compression operation makes the (mel) spectrogram features match more closely what humans actually hear.
|
| 1047 |
+
|
| 1048 |
+
Based on the implementation of `librosa.power_to_db`.
|
| 1049 |
+
|
| 1050 |
+
Args:
|
| 1051 |
+
spectrogram (`np.ndarray`):
|
| 1052 |
+
The input power (mel) spectrogram. Note that a power spectrogram has the amplitudes squared!
|
| 1053 |
+
reference (`float`, *optional*, defaults to 1.0):
|
| 1054 |
+
Sets the input spectrogram value that corresponds to 0 dB. For example, use `np.max(spectrogram)` to set
|
| 1055 |
+
the loudest part to 0 dB. Must be greater than zero.
|
| 1056 |
+
min_value (`float`, *optional*, defaults to `1e-10`):
|
| 1057 |
+
The spectrogram will be clipped to this minimum value before conversion to decibels, to avoid taking
|
| 1058 |
+
`log(0)`. The default of `1e-10` corresponds to a minimum of -100 dB. Must be greater than zero.
|
| 1059 |
+
db_range (`float`, *optional*):
|
| 1060 |
+
Sets the maximum dynamic range in decibels. For example, if `db_range = 80`, the difference between the
|
| 1061 |
+
peak value and the smallest value will never be more than 80 dB. Must be greater than zero.
|
| 1062 |
+
|
| 1063 |
+
Returns:
|
| 1064 |
+
`np.ndarray`: the spectrogram in decibels
|
| 1065 |
+
"""
|
| 1066 |
+
if reference <= 0.0:
|
| 1067 |
+
raise ValueError("reference must be greater than zero")
|
| 1068 |
+
if min_value <= 0.0:
|
| 1069 |
+
raise ValueError("min_value must be greater than zero")
|
| 1070 |
+
|
| 1071 |
+
reference = max(min_value, reference)
|
| 1072 |
+
|
| 1073 |
+
spectrogram = np.clip(spectrogram, a_min=min_value, a_max=None)
|
| 1074 |
+
spectrogram = 10.0 * (np.log10(spectrogram) - np.log10(reference))
|
| 1075 |
+
|
| 1076 |
+
if db_range is not None:
|
| 1077 |
+
if db_range <= 0.0:
|
| 1078 |
+
raise ValueError("db_range must be greater than zero")
|
| 1079 |
+
spectrogram = np.clip(spectrogram, a_min=spectrogram.max() - db_range, a_max=None)
|
| 1080 |
+
|
| 1081 |
+
return spectrogram
|
| 1082 |
+
|
| 1083 |
+
|
| 1084 |
+
def power_to_db_batch(
|
| 1085 |
+
spectrogram: np.ndarray,
|
| 1086 |
+
reference: float = 1.0,
|
| 1087 |
+
min_value: float = 1e-10,
|
| 1088 |
+
db_range: Optional[float] = None,
|
| 1089 |
+
) -> np.ndarray:
|
| 1090 |
+
"""
|
| 1091 |
+
Converts a batch of power spectrograms to the decibel scale. This computes `10 * log10(spectrogram / reference)`,
|
| 1092 |
+
using basic logarithm properties for numerical stability.
|
| 1093 |
+
|
| 1094 |
+
This function supports batch processing, where each item in the batch is an individual power (mel) spectrogram.
|
| 1095 |
+
|
| 1096 |
+
Args:
|
| 1097 |
+
spectrogram (`np.ndarray`):
|
| 1098 |
+
The input batch of power (mel) spectrograms. Expected shape is (batch_size, *spectrogram_shape).
|
| 1099 |
+
Note that a power spectrogram has the amplitudes squared!
|
| 1100 |
+
reference (`float`, *optional*, defaults to 1.0):
|
| 1101 |
+
Sets the input spectrogram value that corresponds to 0 dB. For example, use `np.max(spectrogram)` to set
|
| 1102 |
+
the loudest part to 0 dB. Must be greater than zero.
|
| 1103 |
+
min_value (`float`, *optional*, defaults to `1e-10`):
|
| 1104 |
+
The spectrogram will be clipped to this minimum value before conversion to decibels, to avoid taking
|
| 1105 |
+
`log(0)`. The default of `1e-10` corresponds to a minimum of -100 dB. Must be greater than zero.
|
| 1106 |
+
db_range (`float`, *optional*):
|
| 1107 |
+
Sets the maximum dynamic range in decibels. For example, if `db_range = 80`, the difference between the
|
| 1108 |
+
peak value and the smallest value will never be more than 80 dB. Must be greater than zero.
|
| 1109 |
+
|
| 1110 |
+
Returns:
|
| 1111 |
+
`np.ndarray`: the batch of spectrograms in decibels
|
| 1112 |
+
"""
|
| 1113 |
+
if reference <= 0.0:
|
| 1114 |
+
raise ValueError("reference must be greater than zero")
|
| 1115 |
+
if min_value <= 0.0:
|
| 1116 |
+
raise ValueError("min_value must be greater than zero")
|
| 1117 |
+
|
| 1118 |
+
reference = max(min_value, reference)
|
| 1119 |
+
|
| 1120 |
+
spectrogram = np.clip(spectrogram, a_min=min_value, a_max=None)
|
| 1121 |
+
spectrogram = 10.0 * (np.log10(spectrogram) - np.log10(reference))
|
| 1122 |
+
|
| 1123 |
+
if db_range is not None:
|
| 1124 |
+
if db_range <= 0.0:
|
| 1125 |
+
raise ValueError("db_range must be greater than zero")
|
| 1126 |
+
# Apply db_range clipping per batch item
|
| 1127 |
+
max_values = spectrogram.max(axis=(1, 2), keepdims=True)
|
| 1128 |
+
spectrogram = np.clip(spectrogram, a_min=max_values - db_range, a_max=None)
|
| 1129 |
+
|
| 1130 |
+
return spectrogram
|
| 1131 |
+
|
| 1132 |
+
|
| 1133 |
+
def amplitude_to_db(
|
| 1134 |
+
spectrogram: np.ndarray,
|
| 1135 |
+
reference: float = 1.0,
|
| 1136 |
+
min_value: float = 1e-5,
|
| 1137 |
+
db_range: Optional[float] = None,
|
| 1138 |
+
) -> np.ndarray:
|
| 1139 |
+
"""
|
| 1140 |
+
Converts an amplitude spectrogram to the decibel scale. This computes `20 * log10(spectrogram / reference)`, using
|
| 1141 |
+
basic logarithm properties for numerical stability.
|
| 1142 |
+
|
| 1143 |
+
The motivation behind applying the log function on the (mel) spectrogram is that humans do not hear loudness on a
|
| 1144 |
+
linear scale. Generally to double the perceived volume of a sound we need to put 8 times as much energy into it.
|
| 1145 |
+
This means that large variations in energy may not sound all that different if the sound is loud to begin with.
|
| 1146 |
+
This compression operation makes the (mel) spectrogram features match more closely what humans actually hear.
|
| 1147 |
+
|
| 1148 |
+
Args:
|
| 1149 |
+
spectrogram (`np.ndarray`):
|
| 1150 |
+
The input amplitude (mel) spectrogram.
|
| 1151 |
+
reference (`float`, *optional*, defaults to 1.0):
|
| 1152 |
+
Sets the input spectrogram value that corresponds to 0 dB. For example, use `np.max(spectrogram)` to set
|
| 1153 |
+
the loudest part to 0 dB. Must be greater than zero.
|
| 1154 |
+
min_value (`float`, *optional*, defaults to `1e-5`):
|
| 1155 |
+
The spectrogram will be clipped to this minimum value before conversion to decibels, to avoid taking
|
| 1156 |
+
`log(0)`. The default of `1e-5` corresponds to a minimum of -100 dB. Must be greater than zero.
|
| 1157 |
+
db_range (`float`, *optional*):
|
| 1158 |
+
Sets the maximum dynamic range in decibels. For example, if `db_range = 80`, the difference between the
|
| 1159 |
+
peak value and the smallest value will never be more than 80 dB. Must be greater than zero.
|
| 1160 |
+
|
| 1161 |
+
Returns:
|
| 1162 |
+
`np.ndarray`: the spectrogram in decibels
|
| 1163 |
+
"""
|
| 1164 |
+
if reference <= 0.0:
|
| 1165 |
+
raise ValueError("reference must be greater than zero")
|
| 1166 |
+
if min_value <= 0.0:
|
| 1167 |
+
raise ValueError("min_value must be greater than zero")
|
| 1168 |
+
|
| 1169 |
+
reference = max(min_value, reference)
|
| 1170 |
+
|
| 1171 |
+
spectrogram = np.clip(spectrogram, a_min=min_value, a_max=None)
|
| 1172 |
+
spectrogram = 20.0 * (np.log10(spectrogram) - np.log10(reference))
|
| 1173 |
+
|
| 1174 |
+
if db_range is not None:
|
| 1175 |
+
if db_range <= 0.0:
|
| 1176 |
+
raise ValueError("db_range must be greater than zero")
|
| 1177 |
+
spectrogram = np.clip(spectrogram, a_min=spectrogram.max() - db_range, a_max=None)
|
| 1178 |
+
|
| 1179 |
+
return spectrogram
|
| 1180 |
+
|
| 1181 |
+
|
| 1182 |
+
def amplitude_to_db_batch(
|
| 1183 |
+
spectrogram: np.ndarray, reference: float = 1.0, min_value: float = 1e-5, db_range: Optional[float] = None
|
| 1184 |
+
) -> np.ndarray:
|
| 1185 |
+
"""
|
| 1186 |
+
Converts a batch of amplitude spectrograms to the decibel scale. This computes `20 * log10(spectrogram / reference)`,
|
| 1187 |
+
using basic logarithm properties for numerical stability.
|
| 1188 |
+
|
| 1189 |
+
The function supports batch processing, where each item in the batch is an individual amplitude (mel) spectrogram.
|
| 1190 |
+
|
| 1191 |
+
Args:
|
| 1192 |
+
spectrogram (`np.ndarray`):
|
| 1193 |
+
The input batch of amplitude (mel) spectrograms. Expected shape is (batch_size, *spectrogram_shape).
|
| 1194 |
+
reference (`float`, *optional*, defaults to 1.0):
|
| 1195 |
+
Sets the input spectrogram value that corresponds to 0 dB. For example, use `np.max(spectrogram)` to set
|
| 1196 |
+
the loudest part to 0 dB. Must be greater than zero.
|
| 1197 |
+
min_value (`float`, *optional*, defaults to `1e-5`):
|
| 1198 |
+
The spectrogram will be clipped to this minimum value before conversion to decibels, to avoid taking
|
| 1199 |
+
`log(0)`. The default of `1e-5` corresponds to a minimum of -100 dB. Must be greater than zero.
|
| 1200 |
+
db_range (`float`, *optional*):
|
| 1201 |
+
Sets the maximum dynamic range in decibels. For example, if `db_range = 80`, the difference between the
|
| 1202 |
+
peak value and the smallest value will never be more than 80 dB. Must be greater than zero.
|
| 1203 |
+
|
| 1204 |
+
Returns:
|
| 1205 |
+
`np.ndarray`: the batch of spectrograms in decibels
|
| 1206 |
+
"""
|
| 1207 |
+
if reference <= 0.0:
|
| 1208 |
+
raise ValueError("reference must be greater than zero")
|
| 1209 |
+
if min_value <= 0.0:
|
| 1210 |
+
raise ValueError("min_value must be greater than zero")
|
| 1211 |
+
|
| 1212 |
+
reference = max(min_value, reference)
|
| 1213 |
+
|
| 1214 |
+
spectrogram = np.clip(spectrogram, a_min=min_value, a_max=None)
|
| 1215 |
+
spectrogram = 20.0 * (np.log10(spectrogram) - np.log10(reference))
|
| 1216 |
+
|
| 1217 |
+
if db_range is not None:
|
| 1218 |
+
if db_range <= 0.0:
|
| 1219 |
+
raise ValueError("db_range must be greater than zero")
|
| 1220 |
+
# Apply db_range clipping per batch item
|
| 1221 |
+
max_values = spectrogram.max(axis=(1, 2), keepdims=True)
|
| 1222 |
+
spectrogram = np.clip(spectrogram, a_min=max_values - db_range, a_max=None)
|
| 1223 |
+
|
| 1224 |
+
return spectrogram
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/cache_utils.py
ADDED
|
@@ -0,0 +1,1493 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from abc import ABC, abstractmethod
|
| 2 |
+
from collections.abc import Iterable
|
| 3 |
+
from typing import Any, Optional
|
| 4 |
+
|
| 5 |
+
import torch
|
| 6 |
+
|
| 7 |
+
from .configuration_utils import PretrainedConfig
|
| 8 |
+
from .utils import (
|
| 9 |
+
is_hqq_available,
|
| 10 |
+
is_quanto_greater,
|
| 11 |
+
is_torch_greater_or_equal,
|
| 12 |
+
is_torchdynamo_compiling,
|
| 13 |
+
logging,
|
| 14 |
+
)
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
if is_hqq_available():
|
| 18 |
+
from hqq.core.quantize import Quantizer as HQQQuantizer
|
| 19 |
+
|
| 20 |
+
_is_torch_greater_or_equal_than_2_7 = is_torch_greater_or_equal("2.7", accept_dev=True)
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
logger = logging.get_logger(__name__)
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
class CacheLayerMixin(ABC):
|
| 27 |
+
"""Base, abstract class for a single layer's cache."""
|
| 28 |
+
|
| 29 |
+
is_compileable = False
|
| 30 |
+
|
| 31 |
+
def __init__(self):
|
| 32 |
+
self.keys: Optional[torch.Tensor] = None
|
| 33 |
+
self.values: Optional[torch.Tensor] = None
|
| 34 |
+
self.is_initialized = False
|
| 35 |
+
|
| 36 |
+
def __repr__(self):
|
| 37 |
+
return f"{self.__class__.__name__}"
|
| 38 |
+
|
| 39 |
+
@abstractmethod
|
| 40 |
+
def lazy_initialization(self, key_states: torch.Tensor): ...
|
| 41 |
+
|
| 42 |
+
@abstractmethod
|
| 43 |
+
def update(
|
| 44 |
+
self, key_states: torch.Tensor, value_states: torch.Tensor, cache_kwargs: Optional[dict[str, Any]] = None
|
| 45 |
+
) -> tuple[torch.Tensor, torch.Tensor]: ...
|
| 46 |
+
|
| 47 |
+
@abstractmethod
|
| 48 |
+
def get_mask_sizes(self, cache_position: torch.Tensor) -> tuple[int, int]: ...
|
| 49 |
+
|
| 50 |
+
@abstractmethod
|
| 51 |
+
def get_seq_length(self) -> int: ...
|
| 52 |
+
|
| 53 |
+
@abstractmethod
|
| 54 |
+
def get_max_cache_shape(self) -> int: ...
|
| 55 |
+
|
| 56 |
+
def offload(self):
|
| 57 |
+
"""Offload this layer's data to CPU device."""
|
| 58 |
+
if self.is_initialized:
|
| 59 |
+
self.keys = self.keys.to("cpu", non_blocking=True)
|
| 60 |
+
self.values = self.values.to("cpu", non_blocking=True)
|
| 61 |
+
|
| 62 |
+
def prefetch(self):
|
| 63 |
+
"""In case of layer offloading, this allows to move the data back to the layer's device ahead of time."""
|
| 64 |
+
if self.is_initialized and self.keys.device != self.device:
|
| 65 |
+
self.keys = self.keys.to(self.device, non_blocking=True)
|
| 66 |
+
self.values = self.values.to(self.device, non_blocking=True)
|
| 67 |
+
|
| 68 |
+
def reset(self) -> None:
|
| 69 |
+
"""Resets the cache values while preserving the objects"""
|
| 70 |
+
if self.is_initialized:
|
| 71 |
+
self.keys.zero_()
|
| 72 |
+
self.values.zero_()
|
| 73 |
+
# This attribute is set on several Layers
|
| 74 |
+
if hasattr(self, "cumulative_length"):
|
| 75 |
+
self.cumulative_length = 0
|
| 76 |
+
|
| 77 |
+
def reorder_cache(self, beam_idx: torch.LongTensor) -> None:
|
| 78 |
+
"""Reorders this layer's cache for beam search."""
|
| 79 |
+
if self.get_seq_length() > 0:
|
| 80 |
+
self.keys = self.keys.index_select(0, beam_idx.to(self.keys.device))
|
| 81 |
+
self.values = self.values.index_select(0, beam_idx.to(self.values.device))
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
class DynamicLayer(CacheLayerMixin):
|
| 85 |
+
"""
|
| 86 |
+
A cache layer that grows dynamically as more tokens are generated. This is the default for generative models.
|
| 87 |
+
It stores the key and value states as tensors of shape `[batch_size, num_heads, seq_len, head_dim]`.
|
| 88 |
+
"""
|
| 89 |
+
|
| 90 |
+
is_sliding = False
|
| 91 |
+
|
| 92 |
+
def lazy_initialization(self, key_states: torch.Tensor):
|
| 93 |
+
self.dtype, self.device = key_states.dtype, key_states.device
|
| 94 |
+
self.keys = torch.tensor([], dtype=self.dtype, device=self.device)
|
| 95 |
+
self.values = torch.tensor([], dtype=self.dtype, device=self.device)
|
| 96 |
+
self.is_initialized = True
|
| 97 |
+
|
| 98 |
+
def update(
|
| 99 |
+
self,
|
| 100 |
+
key_states: torch.Tensor,
|
| 101 |
+
value_states: torch.Tensor,
|
| 102 |
+
cache_kwargs: Optional[dict[str, Any]] = None,
|
| 103 |
+
) -> tuple[torch.Tensor, torch.Tensor]:
|
| 104 |
+
"""
|
| 105 |
+
Update the key and value caches in-place, and return the necessary keys and value states.
|
| 106 |
+
|
| 107 |
+
Args:
|
| 108 |
+
key_states (`torch.Tensor`): The new key states to cache.
|
| 109 |
+
value_states (`torch.Tensor`): The new value states to cache.
|
| 110 |
+
cache_kwargs (`dict[str, Any]`, *optional*): Additional arguments for the cache.
|
| 111 |
+
|
| 112 |
+
Returns:
|
| 113 |
+
tuple[`torch.Tensor`, `torch.Tensor`]: The key and value states.
|
| 114 |
+
"""
|
| 115 |
+
# Lazy initialization
|
| 116 |
+
if not self.is_initialized:
|
| 117 |
+
self.lazy_initialization(key_states)
|
| 118 |
+
|
| 119 |
+
self.keys = torch.cat([self.keys, key_states], dim=-2)
|
| 120 |
+
self.values = torch.cat([self.values, value_states], dim=-2)
|
| 121 |
+
return self.keys, self.values
|
| 122 |
+
|
| 123 |
+
def get_mask_sizes(self, cache_position: torch.Tensor) -> tuple[int, int]:
|
| 124 |
+
"""Return the length and offset of the cache, used to generate the mask"""
|
| 125 |
+
kv_offset = 0
|
| 126 |
+
query_length = cache_position.shape[0]
|
| 127 |
+
kv_length = self.get_seq_length() + query_length
|
| 128 |
+
return kv_length, kv_offset
|
| 129 |
+
|
| 130 |
+
def get_seq_length(self) -> int:
|
| 131 |
+
"""Returns the sequence length of the cached states."""
|
| 132 |
+
if not self.is_initialized or self.keys.numel() == 0:
|
| 133 |
+
return 0
|
| 134 |
+
return self.keys.shape[-2]
|
| 135 |
+
|
| 136 |
+
def get_max_cache_shape(self) -> int:
|
| 137 |
+
"""Returns the maximum sequence length of the cache object. DynamicLayer does not have a maximum length."""
|
| 138 |
+
return -1
|
| 139 |
+
|
| 140 |
+
def crop(self, max_length: int) -> None:
|
| 141 |
+
"""
|
| 142 |
+
Crop the past key values up to a new `max_length` in terms of tokens. `max_length` can also be negative
|
| 143 |
+
to remove `max_length` tokens.
|
| 144 |
+
"""
|
| 145 |
+
if max_length < 0:
|
| 146 |
+
max_length = self.get_seq_length() - abs(max_length)
|
| 147 |
+
|
| 148 |
+
if self.get_seq_length() <= max_length:
|
| 149 |
+
return
|
| 150 |
+
|
| 151 |
+
self.keys = self.keys[..., :max_length, :]
|
| 152 |
+
self.values = self.values[..., :max_length, :]
|
| 153 |
+
|
| 154 |
+
def batch_repeat_interleave(self, repeats: int) -> None:
|
| 155 |
+
"""Repeat the cache `repeats` times in the batch dimension."""
|
| 156 |
+
if self.get_seq_length() > 0:
|
| 157 |
+
self.keys = self.keys.repeat_interleave(repeats, dim=0)
|
| 158 |
+
self.values = self.values.repeat_interleave(repeats, dim=0)
|
| 159 |
+
|
| 160 |
+
def batch_select_indices(self, indices: torch.Tensor) -> None:
|
| 161 |
+
"""Only keep the `indices` in the batch dimension of the cache."""
|
| 162 |
+
if self.get_seq_length() > 0:
|
| 163 |
+
self.keys = self.keys[indices, ...]
|
| 164 |
+
self.values = self.values[indices, ...]
|
| 165 |
+
|
| 166 |
+
|
| 167 |
+
class DynamicSlidingWindowLayer(DynamicLayer):
|
| 168 |
+
"""
|
| 169 |
+
A cache layer that grows dynamically as more tokens are generated, up until the sliding window size.
|
| 170 |
+
It stores the key and value states as tensors of shape `[batch_size, num_heads, min(seq_len, sliding_window), head_dim]`.
|
| 171 |
+
"""
|
| 172 |
+
|
| 173 |
+
is_sliding = True
|
| 174 |
+
|
| 175 |
+
def __init__(self, sliding_window: int):
|
| 176 |
+
super().__init__()
|
| 177 |
+
self.sliding_window = sliding_window
|
| 178 |
+
self.cumulative_length = 0
|
| 179 |
+
|
| 180 |
+
def update(
|
| 181 |
+
self,
|
| 182 |
+
key_states: torch.Tensor,
|
| 183 |
+
value_states: torch.Tensor,
|
| 184 |
+
cache_kwargs: Optional[dict[str, Any]] = None,
|
| 185 |
+
) -> tuple[torch.Tensor, torch.Tensor]:
|
| 186 |
+
"""
|
| 187 |
+
Update the key and value caches in-place, and return the necessary keys and value states.
|
| 188 |
+
|
| 189 |
+
Args:
|
| 190 |
+
key_states (`torch.Tensor`): The new key states to cache.
|
| 191 |
+
value_states (`torch.Tensor`): The new value states to cache.
|
| 192 |
+
cache_kwargs (`dict[str, Any]`, *optional*): Additional arguments for the cache.
|
| 193 |
+
|
| 194 |
+
Returns:
|
| 195 |
+
tuple[`torch.Tensor`, `torch.Tensor`]: The key and value states.
|
| 196 |
+
"""
|
| 197 |
+
# Lazy initialization
|
| 198 |
+
if not self.is_initialized:
|
| 199 |
+
self.lazy_initialization(key_states)
|
| 200 |
+
|
| 201 |
+
self.cumulative_length += key_states.shape[-2]
|
| 202 |
+
|
| 203 |
+
# Compute the full states
|
| 204 |
+
full_key_states = torch.cat([self.keys, key_states], dim=-2)
|
| 205 |
+
full_value_states = torch.cat([self.values, value_states], dim=-2)
|
| 206 |
+
# Only cache the last `self.sliding_window - 1` tokens (or all of them if lower than that)
|
| 207 |
+
self.keys = full_key_states[:, :, -self.sliding_window + 1 :, :]
|
| 208 |
+
self.values = full_value_states[:, :, -self.sliding_window + 1 :, :]
|
| 209 |
+
|
| 210 |
+
# Return the full states
|
| 211 |
+
return full_key_states, full_value_states
|
| 212 |
+
|
| 213 |
+
def get_mask_sizes(self, cache_position: torch.Tensor) -> tuple[int, int]:
|
| 214 |
+
"""Return the length and offset of the cache, used to generate the attention mask"""
|
| 215 |
+
query_length = cache_position.shape[0]
|
| 216 |
+
is_full = self.cumulative_length >= self.sliding_window
|
| 217 |
+
|
| 218 |
+
kv_offset = max(self.cumulative_length - self.sliding_window + 1, 0)
|
| 219 |
+
if is_full:
|
| 220 |
+
kv_length = self.sliding_window - 1 + query_length
|
| 221 |
+
else:
|
| 222 |
+
kv_length = self.cumulative_length + query_length
|
| 223 |
+
|
| 224 |
+
return kv_length, kv_offset
|
| 225 |
+
|
| 226 |
+
def get_seq_length(self) -> int:
|
| 227 |
+
"""Returns the sequence length of the cached states."""
|
| 228 |
+
return self.cumulative_length
|
| 229 |
+
|
| 230 |
+
def get_max_cache_shape(self) -> int:
|
| 231 |
+
"""Return the maximum cache shape of the cache"""
|
| 232 |
+
return self.sliding_window
|
| 233 |
+
|
| 234 |
+
def crop(self, max_length: int) -> None:
|
| 235 |
+
"""
|
| 236 |
+
Crop the past key values up to a new `max_length` in terms of tokens. `max_length` can also be
|
| 237 |
+
negative to remove `max_length` tokens.
|
| 238 |
+
"""
|
| 239 |
+
if self.get_seq_length() >= self.sliding_window:
|
| 240 |
+
raise ValueError(
|
| 241 |
+
"Cannot `crop` a `DynamicSlidingWindowLayer` after it has seen more tokens than its"
|
| 242 |
+
"sliding window (otherwise some states are lost)"
|
| 243 |
+
)
|
| 244 |
+
super().crop(max_length)
|
| 245 |
+
self.cumulative_length = self.keys.shape[-2]
|
| 246 |
+
|
| 247 |
+
|
| 248 |
+
class StaticLayer(CacheLayerMixin):
|
| 249 |
+
"""
|
| 250 |
+
A static cache layer that stores the key and value states as static tensors of shape `[batch_size, num_heads, max_cache_len), head_dim]`.
|
| 251 |
+
It lazily allocates its full backing tensors, and then mutates them in-place. Built for `torch.compile` support.
|
| 252 |
+
|
| 253 |
+
Args:
|
| 254 |
+
max_cache_len (`int`):
|
| 255 |
+
Maximum number of tokens that can be stored, used for tensor preallocation.
|
| 256 |
+
"""
|
| 257 |
+
|
| 258 |
+
is_compileable = True
|
| 259 |
+
is_sliding = False
|
| 260 |
+
|
| 261 |
+
def __init__(self, max_cache_len: int):
|
| 262 |
+
super().__init__()
|
| 263 |
+
self.max_cache_len = max_cache_len
|
| 264 |
+
|
| 265 |
+
def lazy_initialization(self, key_states: torch.Tensor):
|
| 266 |
+
"""
|
| 267 |
+
Lazy initialization of the keys and values tensors. This allows to get all properties (dtype, device,
|
| 268 |
+
num_heads in case of TP etc...) at runtime directly, which is extremely practical as it avoids moving
|
| 269 |
+
devices, dtypes etc later on for each `update` (which could break the static dynamo addresses as well).
|
| 270 |
+
|
| 271 |
+
If this is unwanted, one can call `early_initialization(...)` on the Cache directly, which will call this
|
| 272 |
+
function ahead-of-time (this is required for `torch.export` for example). Note that for `compile`, as we
|
| 273 |
+
internally don't compile the prefill, this is guaranteed to have been called already when compiling.
|
| 274 |
+
If compiling the prefill as well, e.g. calling `model.compile(...)` before `generate` with a static cache,
|
| 275 |
+
it is still supported in general, but without guarantees depending on the compilation options (e.g. cuda graphs,
|
| 276 |
+
i.e. `mode="reduce-overhead"` is known to fail). But it will in general work correctly, and prefill should
|
| 277 |
+
not be compiled anyway for performances!
|
| 278 |
+
"""
|
| 279 |
+
self.max_batch_size, self.num_heads, _, self.head_dim = key_states.shape
|
| 280 |
+
self.dtype, self.device = key_states.dtype, key_states.device
|
| 281 |
+
|
| 282 |
+
self.keys = torch.zeros(
|
| 283 |
+
(self.max_batch_size, self.num_heads, self.max_cache_len, self.head_dim),
|
| 284 |
+
dtype=self.dtype,
|
| 285 |
+
device=self.device,
|
| 286 |
+
)
|
| 287 |
+
self.values = torch.zeros(
|
| 288 |
+
(self.max_batch_size, self.num_heads, self.max_cache_len, self.head_dim),
|
| 289 |
+
dtype=self.dtype,
|
| 290 |
+
device=self.device,
|
| 291 |
+
)
|
| 292 |
+
# Note: `mark_static_address` is used to tag the cache as a fixed data pointer, preventing compiled graph
|
| 293 |
+
# breaks when updating the cache. However, it is not supported when tracing the graph, so we skip it in this case.
|
| 294 |
+
# As prefill should never be compiled, this is not an issue and it will still be run (except when users compile
|
| 295 |
+
# prefill explicitly, but this should be avoided!)
|
| 296 |
+
if not is_torchdynamo_compiling():
|
| 297 |
+
torch._dynamo.mark_static_address(self.keys)
|
| 298 |
+
torch._dynamo.mark_static_address(self.values)
|
| 299 |
+
|
| 300 |
+
self.is_initialized = True
|
| 301 |
+
|
| 302 |
+
def update(
|
| 303 |
+
self,
|
| 304 |
+
key_states: torch.Tensor,
|
| 305 |
+
value_states: torch.Tensor,
|
| 306 |
+
cache_kwargs: Optional[dict[str, Any]] = None,
|
| 307 |
+
) -> tuple[torch.Tensor, torch.Tensor]:
|
| 308 |
+
"""
|
| 309 |
+
Update the key and value caches in-place, and return the necessary keys and value states.
|
| 310 |
+
|
| 311 |
+
Args:
|
| 312 |
+
key_states (`torch.Tensor`): The new key states to cache.
|
| 313 |
+
value_states (`torch.Tensor`): The new value states to cache.
|
| 314 |
+
cache_kwargs (`dict[str, Any]`, *optional*): Additional arguments for the cache.
|
| 315 |
+
|
| 316 |
+
Returns:
|
| 317 |
+
tuple[`torch.Tensor`, `torch.Tensor`]: The key and value states.
|
| 318 |
+
"""
|
| 319 |
+
# Lazy initialization
|
| 320 |
+
if not self.is_initialized:
|
| 321 |
+
self.lazy_initialization(key_states)
|
| 322 |
+
|
| 323 |
+
# Some old models give None for `cache_position` or even omit passing `cache_kwargs` when used as cross-attention,
|
| 324 |
+
# in which case we should copy the whole Layer (key_states.shape[-2] == self.max_cache_len)
|
| 325 |
+
cache_position = cache_kwargs.get("cache_position") if cache_kwargs is not None else None
|
| 326 |
+
cache_position = (
|
| 327 |
+
cache_position if cache_position is not None else torch.arange(key_states.shape[-2], device=self.device)
|
| 328 |
+
)
|
| 329 |
+
|
| 330 |
+
# Update the cache
|
| 331 |
+
try:
|
| 332 |
+
self.keys.index_copy_(2, cache_position, key_states)
|
| 333 |
+
self.values.index_copy_(2, cache_position, value_states)
|
| 334 |
+
except NotImplementedError:
|
| 335 |
+
# Fallback for devices like MPS where index_copy_ might not be supported.
|
| 336 |
+
self.keys[:, :, cache_position] = key_states
|
| 337 |
+
self.values[:, :, cache_position] = value_states
|
| 338 |
+
return self.keys, self.values
|
| 339 |
+
|
| 340 |
+
def get_mask_sizes(self, cache_position: torch.Tensor) -> tuple[int, int]:
|
| 341 |
+
"""Return the length and offset of the cache, used to generate the attention mask"""
|
| 342 |
+
kv_offset = 0
|
| 343 |
+
kv_length = self.max_cache_len
|
| 344 |
+
return kv_length, kv_offset
|
| 345 |
+
|
| 346 |
+
def get_seq_length(self) -> int:
|
| 347 |
+
"""Returns the sequence length of the cached states."""
|
| 348 |
+
# Occupied cache == any slot in the 3rd dim (sequence length) holds a non-zero value. To save on compute, let's
|
| 349 |
+
# limit the check to the first batch member and head dimension.
|
| 350 |
+
return (self.keys[0, 0].any(dim=-1)).sum() if self.is_initialized else 0
|
| 351 |
+
|
| 352 |
+
def get_max_cache_shape(self) -> int:
|
| 353 |
+
"""Return the maximum cache shape of the cache"""
|
| 354 |
+
return self.max_cache_len
|
| 355 |
+
|
| 356 |
+
|
| 357 |
+
class StaticSlidingWindowLayer(StaticLayer):
|
| 358 |
+
"""
|
| 359 |
+
A static cache layer that stores the key and value states as static tensors of shape
|
| 360 |
+
`[batch_size, num_heads, min(max_cache_len, sliding_window), head_dim]`. It lazily allocates its full backing
|
| 361 |
+
tensors, and then mutates them in-place. Built for `torch.compile` support.
|
| 362 |
+
|
| 363 |
+
Args:
|
| 364 |
+
max_cache_len (`int`):
|
| 365 |
+
Maximum number of tokens that can be stored, used for tensor preallocation.
|
| 366 |
+
sliding_window (`int`):
|
| 367 |
+
The size of the sliding window.
|
| 368 |
+
"""
|
| 369 |
+
|
| 370 |
+
is_sliding = True
|
| 371 |
+
|
| 372 |
+
def __init__(self, max_cache_len: int, sliding_window: int):
|
| 373 |
+
effective_max_cache_len = min(sliding_window, max_cache_len)
|
| 374 |
+
super().__init__(max_cache_len=effective_max_cache_len)
|
| 375 |
+
self.cumulative_length = 0
|
| 376 |
+
|
| 377 |
+
def update(
|
| 378 |
+
self,
|
| 379 |
+
key_states: torch.Tensor,
|
| 380 |
+
value_states: torch.Tensor,
|
| 381 |
+
cache_kwargs: Optional[dict[str, Any]] = None,
|
| 382 |
+
) -> tuple[torch.Tensor, torch.Tensor]:
|
| 383 |
+
"""
|
| 384 |
+
Update the key and value caches in-place, and return the necessary keys and value states.
|
| 385 |
+
|
| 386 |
+
Args:
|
| 387 |
+
key_states (`torch.Tensor`): The new key states to cache.
|
| 388 |
+
value_states (`torch.Tensor`): The new value states to cache.
|
| 389 |
+
cache_kwargs (`dict[str, Any]`, *optional*): Additional arguments for the cache.
|
| 390 |
+
|
| 391 |
+
Returns:
|
| 392 |
+
tuple[`torch.Tensor`, `torch.Tensor`]: The key and value states.
|
| 393 |
+
"""
|
| 394 |
+
# Lazy initialization
|
| 395 |
+
if not self.is_initialized:
|
| 396 |
+
self.lazy_initialization(key_states)
|
| 397 |
+
|
| 398 |
+
# Some old models give None for `cache_position` or even omit passing `cache_kwargs` when used as cross-attention,
|
| 399 |
+
# in which case we should copy the whole Layer (key_states.shape[-2] == self.max_cache_len)
|
| 400 |
+
cache_position = cache_kwargs.get("cache_position") if cache_kwargs is not None else None
|
| 401 |
+
cache_position = (
|
| 402 |
+
cache_position if cache_position is not None else torch.arange(key_states.shape[-2], device=self.device)
|
| 403 |
+
)
|
| 404 |
+
|
| 405 |
+
cumulative_length = self.cumulative_length
|
| 406 |
+
is_full = cumulative_length >= self.max_cache_len
|
| 407 |
+
# Update it now that we saved the value above
|
| 408 |
+
self.cumulative_length += key_states.shape[-2]
|
| 409 |
+
|
| 410 |
+
if is_full:
|
| 411 |
+
# In general, we should use a much simpler `cat` here as well, independently of the states size. However,
|
| 412 |
+
# dynamo is currently bugged when doing it - see https://github.com/pytorch/pytorch/issues/159855 for more details
|
| 413 |
+
if key_states.shape[-2] == 1:
|
| 414 |
+
# Roll all values to the left by 1 position
|
| 415 |
+
new_keys = self.keys.roll(-1, dims=-2)
|
| 416 |
+
new_values = self.values.roll(-1, dims=-2)
|
| 417 |
+
# Overwrite the last position with new states
|
| 418 |
+
# (note: very important to use a tensor to index here, see https://github.com/pytorch/pytorch/issues/159855)
|
| 419 |
+
index = torch.tensor([-1], dtype=int, device=self.device)
|
| 420 |
+
new_keys[:, :, index] = key_states
|
| 421 |
+
new_values[:, :, index] = value_states
|
| 422 |
+
|
| 423 |
+
# Copy back into `self` (do not just assign again) in order to keep the static dynamo address
|
| 424 |
+
self.keys.copy_(new_keys)
|
| 425 |
+
self.values.copy_(new_values)
|
| 426 |
+
# Very important to return the `self` tensors here, as they have the static dynamo address
|
| 427 |
+
return self.keys, self.values
|
| 428 |
+
# Already full but using more than 1 new token (e.g. prefill caching, chat continuation, etc...)
|
| 429 |
+
else:
|
| 430 |
+
full_key_states = torch.cat((self.keys[:, :, 1:, :], key_states), dim=-2)
|
| 431 |
+
full_value_states = torch.cat((self.values[:, :, 1:, :], value_states), dim=-2)
|
| 432 |
+
# Not yet full, but becoming full on this update
|
| 433 |
+
elif cumulative_length + key_states.shape[2] > self.max_cache_len:
|
| 434 |
+
# Fast prefill path, no need to cat() in this case, as the cache is currently empty
|
| 435 |
+
if cumulative_length == 0:
|
| 436 |
+
full_key_states = key_states
|
| 437 |
+
full_value_states = value_states
|
| 438 |
+
else:
|
| 439 |
+
full_key_states = torch.cat((self.keys[:, :, :cumulative_length, :], key_states), dim=-2)
|
| 440 |
+
full_value_states = torch.cat((self.values[:, :, :cumulative_length, :], value_states), dim=-2)
|
| 441 |
+
else:
|
| 442 |
+
try:
|
| 443 |
+
self.keys.index_copy_(2, cache_position, key_states)
|
| 444 |
+
self.values.index_copy_(2, cache_position, value_states)
|
| 445 |
+
except NotImplementedError:
|
| 446 |
+
self.keys[:, :, cache_position] = key_states
|
| 447 |
+
self.values[:, :, cache_position] = value_states
|
| 448 |
+
|
| 449 |
+
# Very important to return the `self` tensors here, as they have the static dynamo address
|
| 450 |
+
return self.keys, self.values
|
| 451 |
+
|
| 452 |
+
# We only cache the last `sliding_window` tokens
|
| 453 |
+
self.keys.copy_(full_key_states[:, :, -self.max_cache_len :, :])
|
| 454 |
+
self.values.copy_(full_value_states[:, :, -self.max_cache_len :, :])
|
| 455 |
+
# we should return the whole states instead of `self.keys/values` here, as otherwise we lose some context
|
| 456 |
+
return full_key_states, full_value_states
|
| 457 |
+
|
| 458 |
+
def get_mask_sizes(self, cache_position: torch.Tensor) -> tuple[int, int]:
|
| 459 |
+
"""Return the length and offset of the cache, used to generate the attention mask"""
|
| 460 |
+
query_length = cache_position.shape[0]
|
| 461 |
+
sliding_window = self.max_cache_len
|
| 462 |
+
is_full = self.cumulative_length >= self.max_cache_len
|
| 463 |
+
|
| 464 |
+
kv_offset = max(self.cumulative_length - sliding_window + 1, 0)
|
| 465 |
+
# The cache is already full
|
| 466 |
+
if is_full:
|
| 467 |
+
kv_length = sliding_window + query_length - 1
|
| 468 |
+
# Not yet full, but becoming full on this update
|
| 469 |
+
elif self.cumulative_length + query_length > sliding_window:
|
| 470 |
+
kv_length = self.cumulative_length + query_length
|
| 471 |
+
# Here the Cache is still smaller than the local size, but we return the local size as it's static
|
| 472 |
+
else:
|
| 473 |
+
kv_length = sliding_window
|
| 474 |
+
|
| 475 |
+
return kv_length, kv_offset
|
| 476 |
+
|
| 477 |
+
def get_seq_length(self) -> int:
|
| 478 |
+
"""Returns the sequence length of the cached states."""
|
| 479 |
+
return self.cumulative_length
|
| 480 |
+
|
| 481 |
+
|
| 482 |
+
class QuantizedLayer(DynamicLayer):
|
| 483 |
+
"""
|
| 484 |
+
A quantized layer similar to what is described in the [KIVI: A Tuning-Free Asymmetric 2bit Quantization for KV Cache paper](https://huggingface.co/papers/2402.02750).
|
| 485 |
+
It allows the model to generate longer sequence length without allocating too much memory for the key and value caches by
|
| 486 |
+
applying quantization.
|
| 487 |
+
|
| 488 |
+
The cache has two types of storage, one for original precision and one for the quantized cache. A `residual length`
|
| 489 |
+
is set as a maximum capacity for the original precision cache. When the length goes beyond maximum capacity, the original
|
| 490 |
+
precision cache is discarded and moved into the quantized cache. The quantization is done per-channel with a set `q_group_size`
|
| 491 |
+
for both Keys and Values, in contrast to what was described in the paper.
|
| 492 |
+
"""
|
| 493 |
+
|
| 494 |
+
def __init__(
|
| 495 |
+
self,
|
| 496 |
+
nbits: int = 4,
|
| 497 |
+
axis_key: int = 0,
|
| 498 |
+
axis_value: int = 0,
|
| 499 |
+
q_group_size: int = 64,
|
| 500 |
+
residual_length: int = 128,
|
| 501 |
+
):
|
| 502 |
+
super().__init__()
|
| 503 |
+
self.nbits = nbits
|
| 504 |
+
self.axis_key = axis_key
|
| 505 |
+
self.axis_value = axis_value
|
| 506 |
+
self.q_group_size = q_group_size
|
| 507 |
+
self.residual_length = residual_length
|
| 508 |
+
self.cumulative_length = 0
|
| 509 |
+
|
| 510 |
+
def update(
|
| 511 |
+
self,
|
| 512 |
+
key_states: torch.Tensor,
|
| 513 |
+
value_states: torch.Tensor,
|
| 514 |
+
cache_kwargs: Optional[dict[str, Any]] = None,
|
| 515 |
+
) -> tuple[torch.Tensor, torch.Tensor]:
|
| 516 |
+
"""
|
| 517 |
+
Update the key and value caches in-place, and return the necessary keys and value states.
|
| 518 |
+
|
| 519 |
+
Args:
|
| 520 |
+
key_states (`torch.Tensor`): The new key states to cache.
|
| 521 |
+
value_states (`torch.Tensor`): The new value states to cache.
|
| 522 |
+
cache_kwargs (`dict[str, Any]`, *optional*): Additional arguments for the cache.
|
| 523 |
+
|
| 524 |
+
Returns:
|
| 525 |
+
tuple[`torch.Tensor`, `torch.Tensor`]: The key and value states.
|
| 526 |
+
"""
|
| 527 |
+
self.cumulative_length += key_states.shape[-2]
|
| 528 |
+
|
| 529 |
+
# Lazy initialization
|
| 530 |
+
if not self.is_initialized:
|
| 531 |
+
self.lazy_initialization(key_states)
|
| 532 |
+
self._quantized_keys = self._quantize(key_states.contiguous(), axis=self.axis_key)
|
| 533 |
+
self._quantized_values = self._quantize(value_states.contiguous(), axis=self.axis_value)
|
| 534 |
+
return key_states, value_states
|
| 535 |
+
|
| 536 |
+
dequant_keys = self._dequantize(self._quantized_keys)
|
| 537 |
+
dequant_values = self._dequantize(self._quantized_values)
|
| 538 |
+
keys_to_return = torch.cat([dequant_keys, self.keys, key_states], dim=-2)
|
| 539 |
+
values_to_return = torch.cat([dequant_values, self.values, value_states], dim=-2)
|
| 540 |
+
if self.keys.dim() == 4 and self.keys.shape[-2] + 1 >= self.residual_length:
|
| 541 |
+
self._quantized_keys = self._quantize(keys_to_return.contiguous(), axis=self.axis_key)
|
| 542 |
+
self._quantized_values = self._quantize(values_to_return.contiguous(), axis=self.axis_value)
|
| 543 |
+
self.keys = torch.tensor([], dtype=key_states.dtype, device=key_states.device)
|
| 544 |
+
self.values = torch.tensor([], dtype=key_states.dtype, device=key_states.device)
|
| 545 |
+
else:
|
| 546 |
+
self.keys = torch.cat([self.keys, key_states], dim=-2)
|
| 547 |
+
self.values = torch.cat([self.values, value_states], dim=-2)
|
| 548 |
+
|
| 549 |
+
return keys_to_return, values_to_return
|
| 550 |
+
|
| 551 |
+
@abstractmethod
|
| 552 |
+
def _quantize(self, tensor, axis): ...
|
| 553 |
+
|
| 554 |
+
@abstractmethod
|
| 555 |
+
def _dequantize(self, q_tensor): ...
|
| 556 |
+
|
| 557 |
+
def get_seq_length(self) -> int:
|
| 558 |
+
"""Returns the sequence length of the cached states."""
|
| 559 |
+
return self.cumulative_length
|
| 560 |
+
|
| 561 |
+
|
| 562 |
+
class QuantoQuantizedLayer(QuantizedLayer):
|
| 563 |
+
def __init__(
|
| 564 |
+
self,
|
| 565 |
+
nbits: int = 4,
|
| 566 |
+
axis_key: int = 0,
|
| 567 |
+
axis_value: int = 0,
|
| 568 |
+
q_group_size: int = 64,
|
| 569 |
+
residual_length: int = 128,
|
| 570 |
+
):
|
| 571 |
+
super().__init__(
|
| 572 |
+
nbits=nbits,
|
| 573 |
+
axis_key=axis_key,
|
| 574 |
+
axis_value=axis_value,
|
| 575 |
+
q_group_size=q_group_size,
|
| 576 |
+
residual_length=residual_length,
|
| 577 |
+
)
|
| 578 |
+
|
| 579 |
+
# We need to import quanto here to avoid circular imports due to optimum/quanto/models/transformers_models.py
|
| 580 |
+
if is_quanto_greater("0.2.5", accept_dev=True):
|
| 581 |
+
from optimum.quanto import MaxOptimizer, qint2, qint4
|
| 582 |
+
else:
|
| 583 |
+
raise ImportError(
|
| 584 |
+
"You need optimum-quanto package version to be greater or equal than 0.2.5 to use `QuantoQuantizedCache`. "
|
| 585 |
+
)
|
| 586 |
+
|
| 587 |
+
if self.nbits not in [2, 4]:
|
| 588 |
+
raise ValueError(f"`nbits` for `quanto` backend has to be one of [`2`, `4`] but got {self.nbits}")
|
| 589 |
+
|
| 590 |
+
if self.axis_key not in [0, -1]:
|
| 591 |
+
raise ValueError(f"`axis_key` for `quanto` backend has to be one of [`0`, `-1`] but got {self.axis_key}")
|
| 592 |
+
|
| 593 |
+
if self.axis_value not in [0, -1]:
|
| 594 |
+
raise ValueError(
|
| 595 |
+
f"`axis_value` for `quanto` backend has to be one of [`0`, `-1`] but got {self.axis_value}"
|
| 596 |
+
)
|
| 597 |
+
|
| 598 |
+
self.qtype = qint4 if self.nbits == 4 else qint2
|
| 599 |
+
self.optimizer = MaxOptimizer() # hardcode as it's the only one for per-channel quantization
|
| 600 |
+
|
| 601 |
+
def _quantize(self, tensor, axis):
|
| 602 |
+
from optimum.quanto import quantize_weight
|
| 603 |
+
|
| 604 |
+
scale, zeropoint = self.optimizer(tensor, self.qtype, axis, self.q_group_size)
|
| 605 |
+
qtensor = quantize_weight(tensor, self.qtype, axis, scale, zeropoint, self.q_group_size)
|
| 606 |
+
return qtensor
|
| 607 |
+
|
| 608 |
+
def _dequantize(self, qtensor):
|
| 609 |
+
return qtensor.dequantize()
|
| 610 |
+
|
| 611 |
+
|
| 612 |
+
class HQQQuantizedLayer(QuantizedLayer):
|
| 613 |
+
def __init__(
|
| 614 |
+
self,
|
| 615 |
+
nbits: int = 4,
|
| 616 |
+
axis_key: int = 0,
|
| 617 |
+
axis_value: int = 0,
|
| 618 |
+
q_group_size: int = 64,
|
| 619 |
+
residual_length: int = 128,
|
| 620 |
+
):
|
| 621 |
+
super().__init__(
|
| 622 |
+
nbits=nbits,
|
| 623 |
+
axis_key=axis_key,
|
| 624 |
+
axis_value=axis_value,
|
| 625 |
+
q_group_size=q_group_size,
|
| 626 |
+
residual_length=residual_length,
|
| 627 |
+
)
|
| 628 |
+
|
| 629 |
+
if not is_hqq_available():
|
| 630 |
+
raise ImportError("You need to install `hqq` to use `HQQQuantizedLayer`")
|
| 631 |
+
|
| 632 |
+
if self.nbits not in [1, 2, 3, 4, 8]:
|
| 633 |
+
raise ValueError(
|
| 634 |
+
f"`nbits` for `HQQ` backend has to be one of [`1`, `2`, `3`, `4`, `8`] but got {self.nbits}"
|
| 635 |
+
)
|
| 636 |
+
|
| 637 |
+
if self.axis_key not in [0, 1]:
|
| 638 |
+
raise ValueError(f"`axis_key` for `HQQ` backend has to be one of [`0`, `1`] but got {self.axis_key}")
|
| 639 |
+
|
| 640 |
+
if self.axis_value not in [0, 1]:
|
| 641 |
+
raise ValueError(f"`axis_value` for `HQQ` backend has to be one of [`0`, `1`] but got {self.axis_value}")
|
| 642 |
+
|
| 643 |
+
self.quantizer = HQQQuantizer
|
| 644 |
+
|
| 645 |
+
def _quantize(self, tensor, axis):
|
| 646 |
+
qtensor, meta = self.quantizer.quantize(
|
| 647 |
+
tensor,
|
| 648 |
+
axis=axis,
|
| 649 |
+
device=self.keys.device,
|
| 650 |
+
compute_dtype=self.keys.dtype,
|
| 651 |
+
nbits=self.nbits,
|
| 652 |
+
group_size=self.q_group_size,
|
| 653 |
+
)
|
| 654 |
+
meta["compute_dtype"] = self.keys.dtype
|
| 655 |
+
self.quantizer.cuda(qtensor, meta=meta, device=self.keys.device) # Move to device and cast to dtype
|
| 656 |
+
meta["scale"] = meta["scale"].to(qtensor.device)
|
| 657 |
+
meta["zero"] = meta["zero"].to(qtensor.device)
|
| 658 |
+
return qtensor, meta
|
| 659 |
+
|
| 660 |
+
def _dequantize(self, qtensor):
|
| 661 |
+
quant_tensor, meta = qtensor
|
| 662 |
+
tensor = self.quantizer.dequantize(quant_tensor, meta)
|
| 663 |
+
return tensor
|
| 664 |
+
|
| 665 |
+
|
| 666 |
+
class Cache:
|
| 667 |
+
"""
|
| 668 |
+
A `Cache` is mostly a list of `CacheLayerMixin` objects, one per model layer. It serves as a container for
|
| 669 |
+
the Cache of each layer.
|
| 670 |
+
|
| 671 |
+
Args:
|
| 672 |
+
layers (`Optional`, *optional*):
|
| 673 |
+
A list of pre-created `CacheLayerMixin`. If omitted (`None`), then `layer_class_to_replicate` will
|
| 674 |
+
be used.
|
| 675 |
+
layer_class_to_replicate (`type[CacheLayerMixin]`, *optional*):
|
| 676 |
+
Only used if `layers` is omitted (`None`), in which case it will be used as the base class for each layer,
|
| 677 |
+
and the layers will be added lazily as soon as `update` is called with a `layer_idx` greater than the current
|
| 678 |
+
list of layers.
|
| 679 |
+
offloading (`bool`, *optional*, defaults to `False`):
|
| 680 |
+
Whether to perform offloading of the layers to `cpu`, to save GPU memory.
|
| 681 |
+
offload_only_non_sliding (`bool`, *optional*, defaults to `True`):
|
| 682 |
+
If `offloading` is `True`, this further decides if only the non-sliding layers will be offloaded (because
|
| 683 |
+
usually the sliding layers are small in size, so there is no need to offload them, and skipping it is faster).
|
| 684 |
+
"""
|
| 685 |
+
|
| 686 |
+
def __init__(
|
| 687 |
+
self,
|
| 688 |
+
layers: Optional[list[CacheLayerMixin]] = None,
|
| 689 |
+
layer_class_to_replicate: Optional[type[CacheLayerMixin]] = None,
|
| 690 |
+
offloading: bool = False,
|
| 691 |
+
offload_only_non_sliding: bool = True,
|
| 692 |
+
):
|
| 693 |
+
if layers is not None and layer_class_to_replicate is not None:
|
| 694 |
+
raise ValueError(
|
| 695 |
+
"You can construct a Cache either from a list `layers` of all the predefined `CacheLayer`, or from a "
|
| 696 |
+
"`layer_class_to_replicate`, in which case the Cache will append a new layer corresponding to "
|
| 697 |
+
"`layer_class_to_replicate` for each new call to `update` with an idx not already in the Cache."
|
| 698 |
+
)
|
| 699 |
+
if layers is None and layer_class_to_replicate is None:
|
| 700 |
+
raise ValueError(
|
| 701 |
+
"You should provide exactly one of `layers` or `layer_class_to_replicate` to initialize a Cache."
|
| 702 |
+
)
|
| 703 |
+
self.layers = layers if layers is not None else []
|
| 704 |
+
self.layer_class_to_replicate = layer_class_to_replicate
|
| 705 |
+
self.offloading = offloading
|
| 706 |
+
if self.offloading:
|
| 707 |
+
self.only_non_sliding = offload_only_non_sliding
|
| 708 |
+
self.prefetch_stream = torch.Stream() if _is_torch_greater_or_equal_than_2_7 else torch.cuda.Stream()
|
| 709 |
+
|
| 710 |
+
def __repr__(self):
|
| 711 |
+
return f"{self.__class__.__name__}(layers={self.layers})"
|
| 712 |
+
|
| 713 |
+
def prefetch(self, layer_idx: int, only_non_sliding: bool = True):
|
| 714 |
+
"""
|
| 715 |
+
Prefetch a given layer on its device. If `only_non_sliding` is True, it will try to prefetch only the layers
|
| 716 |
+
which are non-sliding. If the `layer_idx` is outside the range, this will circle back to the first layers.
|
| 717 |
+
Note that we use a non-default stream for this, to avoid blocking.
|
| 718 |
+
"""
|
| 719 |
+
if only_non_sliding:
|
| 720 |
+
# Try to find next non-sliding, starting at `layer_idx`
|
| 721 |
+
try:
|
| 722 |
+
layer_idx = layer_idx + self.is_sliding[layer_idx:].index(False)
|
| 723 |
+
# In this case, we need to circle back to the beginning
|
| 724 |
+
except ValueError:
|
| 725 |
+
layer_idx = self.is_sliding.index(False)
|
| 726 |
+
else:
|
| 727 |
+
layer_idx = layer_idx if layer_idx < len(self.layers) else 0
|
| 728 |
+
|
| 729 |
+
# Prefetch
|
| 730 |
+
with self.prefetch_stream if _is_torch_greater_or_equal_than_2_7 else torch.cuda.stream(self.prefetch_stream):
|
| 731 |
+
self.layers[layer_idx].prefetch()
|
| 732 |
+
|
| 733 |
+
def offload(self, layer_idx: int, only_non_sliding: bool = True):
|
| 734 |
+
"""
|
| 735 |
+
Offload a given `layer_idx`. If `only_non_sliding` is True, it will offload `layer_idx` only if it is a
|
| 736 |
+
non-sliding layer. Note that we do it on the default stream, so that we ensure all earlier
|
| 737 |
+
computation in the layer's `update` methods are finished.
|
| 738 |
+
"""
|
| 739 |
+
if not (only_non_sliding and self.is_sliding[layer_idx]):
|
| 740 |
+
self.layers[layer_idx].offload()
|
| 741 |
+
|
| 742 |
+
def update(
|
| 743 |
+
self,
|
| 744 |
+
key_states: torch.Tensor,
|
| 745 |
+
value_states: torch.Tensor,
|
| 746 |
+
layer_idx: int,
|
| 747 |
+
cache_kwargs: Optional[dict[str, Any]] = None,
|
| 748 |
+
) -> tuple[torch.Tensor, torch.Tensor]:
|
| 749 |
+
"""
|
| 750 |
+
Updates the cache with the new `key_states` and `value_states` for the layer `layer_idx`.
|
| 751 |
+
|
| 752 |
+
Parameters:
|
| 753 |
+
key_states (`torch.Tensor`):
|
| 754 |
+
The new key states to cache.
|
| 755 |
+
value_states (`torch.Tensor`):
|
| 756 |
+
The new value states to cache.
|
| 757 |
+
layer_idx (`int`):
|
| 758 |
+
The index of the layer to cache the states for.
|
| 759 |
+
cache_kwargs (`dict[str, Any]`, *optional*):
|
| 760 |
+
Additional arguments for the cache subclass. These are specific to each subclass and allow new types of
|
| 761 |
+
cache to be created.
|
| 762 |
+
|
| 763 |
+
Return:
|
| 764 |
+
A tuple containing the updated key and value states.
|
| 765 |
+
"""
|
| 766 |
+
# In this case, the `layers` were not provided, and we must append as much as `layer_idx`
|
| 767 |
+
if self.layer_class_to_replicate is not None:
|
| 768 |
+
while len(self.layers) <= layer_idx:
|
| 769 |
+
self.layers.append(self.layer_class_to_replicate())
|
| 770 |
+
|
| 771 |
+
if self.offloading:
|
| 772 |
+
# Wait for the stream to finish if needed, and start prefetching the next layer
|
| 773 |
+
torch.cuda.default_stream(key_states.device).wait_stream(self.prefetch_stream)
|
| 774 |
+
self.prefetch(layer_idx + 1, self.only_non_sliding)
|
| 775 |
+
|
| 776 |
+
keys, values = self.layers[layer_idx].update(key_states, value_states, cache_kwargs)
|
| 777 |
+
|
| 778 |
+
if self.offloading:
|
| 779 |
+
self.offload(layer_idx, self.only_non_sliding)
|
| 780 |
+
|
| 781 |
+
return keys, values
|
| 782 |
+
|
| 783 |
+
def early_initialization(
|
| 784 |
+
self, batch_size: int, num_heads: int, head_dim: int, dtype: torch.dtype, device: torch.device
|
| 785 |
+
):
|
| 786 |
+
"""
|
| 787 |
+
Initialize all the layers in advance (it's otherwise lazily initialized on the first `update` call).
|
| 788 |
+
This is useful for our `export` recipes, as `export` needs everything in advance.
|
| 789 |
+
"""
|
| 790 |
+
# Note that the initialization needs all dimensions (except -2), as well as device and dtype, so we use
|
| 791 |
+
# this fake tensor approach. It has size 0 on the -2 dimension, so it does not allocate any data (it only
|
| 792 |
+
# creates an empty tensor with correct shape, dtype and device), which is very efficient and practical
|
| 793 |
+
fake_keys_tensor = torch.zeros((batch_size, num_heads, 0, head_dim), dtype=dtype, device=device)
|
| 794 |
+
# Init all layers
|
| 795 |
+
for layer in self.layers:
|
| 796 |
+
layer.lazy_initialization(fake_keys_tensor)
|
| 797 |
+
|
| 798 |
+
def get_seq_length(self, layer_idx: int = 0) -> int:
|
| 799 |
+
"""Returns the sequence length of the cache for the given layer."""
|
| 800 |
+
if layer_idx >= len(self.layers):
|
| 801 |
+
return 0
|
| 802 |
+
return self.layers[layer_idx].get_seq_length()
|
| 803 |
+
|
| 804 |
+
def get_mask_sizes(self, cache_position: torch.Tensor, layer_idx: int) -> tuple[int, int]:
|
| 805 |
+
"""
|
| 806 |
+
Return a tuple (kv_length, kv_offset) corresponding to the length and offset that will be returned for
|
| 807 |
+
the given layer at `layer_idx`.
|
| 808 |
+
The masks are then prepared according to the given lengths (kv_length, kv_offset) and patterns for each layer.
|
| 809 |
+
"""
|
| 810 |
+
# For DynamicCache, where the layers are created at runtime -> if it was not yet created, the size is
|
| 811 |
+
# simply the shape of `cache_position`
|
| 812 |
+
if layer_idx >= len(self.layers):
|
| 813 |
+
return cache_position.shape[0], 0
|
| 814 |
+
return self.layers[layer_idx].get_mask_sizes(cache_position)
|
| 815 |
+
|
| 816 |
+
def get_max_cache_shape(self, layer_idx: int = 0) -> int:
|
| 817 |
+
"""Returns maximum sequence length of the cache object. Dynamic caches do not have a maximum length."""
|
| 818 |
+
# For DynamicCache, where the layers are created at runtime -> if it was not yet created, return -1
|
| 819 |
+
# as DynamicLayer does
|
| 820 |
+
if layer_idx >= len(self.layers):
|
| 821 |
+
return -1
|
| 822 |
+
return self.layers[layer_idx].get_max_cache_shape()
|
| 823 |
+
|
| 824 |
+
def reset(self):
|
| 825 |
+
"""Recursively reset all layers tensors"""
|
| 826 |
+
for layer_idx in range(len(self.layers)):
|
| 827 |
+
self.layers[layer_idx].reset()
|
| 828 |
+
|
| 829 |
+
def reorder_cache(self, beam_idx: torch.LongTensor):
|
| 830 |
+
"""Reorder the cache for beam search"""
|
| 831 |
+
for layer_idx in range(len(self.layers)):
|
| 832 |
+
self.layers[layer_idx].reorder_cache(beam_idx)
|
| 833 |
+
|
| 834 |
+
def crop(self, max_length: int):
|
| 835 |
+
"""Crop the cache to the given length"""
|
| 836 |
+
for layer_idx in range(len(self.layers)):
|
| 837 |
+
self.layers[layer_idx].crop(max_length)
|
| 838 |
+
|
| 839 |
+
def batch_repeat_interleave(self, repeats: int):
|
| 840 |
+
"""Repeat and interleave the cache"""
|
| 841 |
+
for layer_idx in range(len(self.layers)):
|
| 842 |
+
self.layers[layer_idx].batch_repeat_interleave(repeats)
|
| 843 |
+
|
| 844 |
+
def batch_select_indices(self, indices: torch.Tensor):
|
| 845 |
+
"""Select indices from the cache"""
|
| 846 |
+
for layer_idx in range(len(self.layers)):
|
| 847 |
+
self.layers[layer_idx].batch_select_indices(indices)
|
| 848 |
+
|
| 849 |
+
@property
|
| 850 |
+
def max_batch_size(self) -> int:
|
| 851 |
+
"""Return the maximum batch size of the cache"""
|
| 852 |
+
values = [layer.max_batch_size for layer in self.layers]
|
| 853 |
+
if len(set(values)) > 1:
|
| 854 |
+
raise ValueError(f"Max batch size is not consistent across layers: {values}")
|
| 855 |
+
return values[0]
|
| 856 |
+
|
| 857 |
+
@property
|
| 858 |
+
def max_cache_len(self) -> int:
|
| 859 |
+
"""Return the maximum cache length of the cache"""
|
| 860 |
+
values = [layer.max_cache_len for layer in self.layers]
|
| 861 |
+
return max(values)
|
| 862 |
+
|
| 863 |
+
@property
|
| 864 |
+
def is_compileable(self) -> bool:
|
| 865 |
+
"""Return whether the cache is compileable"""
|
| 866 |
+
# For DynamicCache dispatching the layers lazily (otherwise, all([]) is True)
|
| 867 |
+
if len(self.layers) == 0:
|
| 868 |
+
return False
|
| 869 |
+
return all(layer.is_compileable for layer in self.layers)
|
| 870 |
+
|
| 871 |
+
@property
|
| 872 |
+
def is_initialized(self) -> bool:
|
| 873 |
+
"""Return whether the cache data is initialized"""
|
| 874 |
+
return len(self.layers) > 0 and all(layer.is_initialized for layer in self.layers)
|
| 875 |
+
|
| 876 |
+
@property
|
| 877 |
+
def is_sliding(self) -> list[bool]:
|
| 878 |
+
"""Return whether the layers of the cache are sliding window"""
|
| 879 |
+
return [getattr(layer, "is_sliding", False) for layer in self.layers]
|
| 880 |
+
|
| 881 |
+
def __getitem__(self, layer_idx: int) -> tuple[torch.Tensor, torch.Tensor]:
|
| 882 |
+
"""
|
| 883 |
+
Support for backwards-compatible `past_key_values` indexing, e.g. `past_key_values[0][0].shape[2]` to get the
|
| 884 |
+
sequence length.
|
| 885 |
+
"""
|
| 886 |
+
if layer_idx < len(self.layers):
|
| 887 |
+
return self.layers[layer_idx].keys, self.layers[layer_idx].values
|
| 888 |
+
else:
|
| 889 |
+
raise KeyError(
|
| 890 |
+
f"Cache only has {len(self.layers)} layers, attempted to access layer with index {layer_idx}"
|
| 891 |
+
)
|
| 892 |
+
|
| 893 |
+
def __iter__(self):
|
| 894 |
+
"""
|
| 895 |
+
Support for backwards-compatible `past_key_values` iteration, e.g. `for x in past_key_values:` to iterate over
|
| 896 |
+
keys and values
|
| 897 |
+
"""
|
| 898 |
+
for layer_idx in range(len(self)):
|
| 899 |
+
yield (self.layers[layer_idx].keys, self.layers[layer_idx].values)
|
| 900 |
+
|
| 901 |
+
def __len__(self):
|
| 902 |
+
"""
|
| 903 |
+
This value corresponds to the number of layers in the model.
|
| 904 |
+
"""
|
| 905 |
+
# Note: for DynamicCache, layers are initialized lazily, so this will not be accurate before the first
|
| 906 |
+
# forward through all the layers
|
| 907 |
+
return len(self.layers)
|
| 908 |
+
|
| 909 |
+
|
| 910 |
+
class DynamicCache(Cache):
|
| 911 |
+
"""
|
| 912 |
+
A cache that grows dynamically as more tokens are generated. This is the default for generative models.
|
| 913 |
+
It stores the key and value states as a list of `CacheLayer`, one for each layer. The expected shape for each tensor
|
| 914 |
+
in the `CacheLayer`s is `[batch_size, num_heads, seq_len, head_dim]`.
|
| 915 |
+
If a config is passed, it will additionally check for sliding or hybrid cache structure, greatly reducing the
|
| 916 |
+
memory requirement of the cached tensors to `[batch_size, num_heads, min(seq_len, sliding_window), head_dim]`.
|
| 917 |
+
|
| 918 |
+
See `Cache` for details on common methods that are implemented by all cache classes.
|
| 919 |
+
|
| 920 |
+
Args:
|
| 921 |
+
ddp_cache_data (`Iterable[tuple[torch.Tensor, torch.Tensor]]`, *optional*):
|
| 922 |
+
It was originally added for compatibility with `torch.distributed` (DDP). In a nutshell, it is
|
| 923 |
+
`map(gather_map, zip(*caches))`, i.e. each item in the iterable contains the key and value states
|
| 924 |
+
for a layer gathered across replicas by torch.distributed (shape=[global batch size, num_heads, seq_len, head_dim]).
|
| 925 |
+
Note: it needs to be the 1st arg as well to work correctly
|
| 926 |
+
config (`PretrainedConfig`, *optional*):
|
| 927 |
+
The config of the model for which this Cache will be used. If passed, it will be used to check for sliding
|
| 928 |
+
or hybrid layer structure, greatly reducing the memory requirement of the cached tensors to
|
| 929 |
+
`[batch_size, num_heads, min(seq_len, sliding_window), head_dim]`.
|
| 930 |
+
offloading (`bool`, *optional*, defaults to `False`):
|
| 931 |
+
Whether to perform offloading of the layers to `cpu`, to save GPU memory.
|
| 932 |
+
offload_only_non_sliding (`bool`, *optional*, defaults to `False`):
|
| 933 |
+
If `offloading` is `True`, this further decides if only the non-sliding layers will be offloaded (because
|
| 934 |
+
usually the sliding layers are small in size, so there is no need to offload them, and skipping it is faster).
|
| 935 |
+
|
| 936 |
+
Example:
|
| 937 |
+
|
| 938 |
+
```python
|
| 939 |
+
>>> from transformers import AutoTokenizer, AutoModelForCausalLM, DynamicCache
|
| 940 |
+
|
| 941 |
+
>>> model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2-0.5B-Instruct")
|
| 942 |
+
>>> tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2-0.5B-Instruct")
|
| 943 |
+
|
| 944 |
+
>>> inputs = tokenizer(text="My name is Qwen2", return_tensors="pt")
|
| 945 |
+
|
| 946 |
+
>>> # Prepare a cache class and pass it to model's forward
|
| 947 |
+
>>> past_key_values = DynamicCache(config=model.config)
|
| 948 |
+
>>> outputs = model(**inputs, past_key_values=past_key_values, use_cache=True)
|
| 949 |
+
>>> outputs.past_key_values # access cache filled with key/values from generation
|
| 950 |
+
```
|
| 951 |
+
"""
|
| 952 |
+
|
| 953 |
+
def __init__(
|
| 954 |
+
self,
|
| 955 |
+
ddp_cache_data: Optional[Iterable[tuple[torch.Tensor, torch.Tensor]]] = None,
|
| 956 |
+
config: Optional[PretrainedConfig] = None,
|
| 957 |
+
offloading: bool = False,
|
| 958 |
+
offload_only_non_sliding: bool = False,
|
| 959 |
+
):
|
| 960 |
+
layers = []
|
| 961 |
+
# If a config is passed, use it to infer the layer types and initialize accordingly
|
| 962 |
+
if config is not None:
|
| 963 |
+
decoder_config = config.get_text_config(decoder=True)
|
| 964 |
+
sliding_window = getattr(decoder_config, "sliding_window", None) or getattr(
|
| 965 |
+
decoder_config, "attention_chunk_size", None
|
| 966 |
+
)
|
| 967 |
+
layer_types = getattr(decoder_config, "layer_types", None)
|
| 968 |
+
if layer_types is None:
|
| 969 |
+
layer_types = [
|
| 970 |
+
"sliding_attention" if sliding_window is not None else "full_attention"
|
| 971 |
+
for _ in range(decoder_config.num_hidden_layers)
|
| 972 |
+
]
|
| 973 |
+
# Some models have shared layers thus no cache is needed for them (e.g. Gemma3n)
|
| 974 |
+
if hasattr(decoder_config, "num_kv_shared_layers"):
|
| 975 |
+
layer_types = layer_types[: -decoder_config.num_kv_shared_layers]
|
| 976 |
+
|
| 977 |
+
for layer_type in layer_types:
|
| 978 |
+
# From a cache point of view, both sliding and chunked are the same in how they should behave and how many
|
| 979 |
+
# states they should return - only the mask changes to make them different at the end!
|
| 980 |
+
if layer_type in ("sliding_attention", "chunked_attention"):
|
| 981 |
+
layers.append(DynamicSlidingWindowLayer(sliding_window=sliding_window))
|
| 982 |
+
else:
|
| 983 |
+
layers.append(DynamicLayer())
|
| 984 |
+
|
| 985 |
+
# In this case, use the passed data to already fill in the Cache
|
| 986 |
+
if ddp_cache_data is not None:
|
| 987 |
+
# Init all the layers with the data
|
| 988 |
+
for layer_idx, (key_states, value_states) in enumerate(ddp_cache_data):
|
| 989 |
+
# If the config was not passed above, initialize a DynamicLayer for each entry of the ddp_data
|
| 990 |
+
if config is None:
|
| 991 |
+
layers.append(DynamicLayer())
|
| 992 |
+
# Update the layer with the data
|
| 993 |
+
_, _ = layers[layer_idx].update(key_states, value_states)
|
| 994 |
+
|
| 995 |
+
# If neither of config nor ddp_data was passed, then simply lazy init a full cache of DynamicLayer
|
| 996 |
+
if len(layers) == 0:
|
| 997 |
+
super().__init__(
|
| 998 |
+
layer_class_to_replicate=DynamicLayer,
|
| 999 |
+
offloading=offloading,
|
| 1000 |
+
offload_only_non_sliding=offload_only_non_sliding,
|
| 1001 |
+
)
|
| 1002 |
+
else:
|
| 1003 |
+
super().__init__(layers=layers, offloading=offloading, offload_only_non_sliding=offload_only_non_sliding)
|
| 1004 |
+
|
| 1005 |
+
def to_legacy_cache(self) -> tuple[tuple[torch.Tensor, torch.Tensor]]:
|
| 1006 |
+
"""
|
| 1007 |
+
Converts the `Cache` instance into the its equivalent in the legacy cache format. Used for
|
| 1008 |
+
backward compatibility.
|
| 1009 |
+
"""
|
| 1010 |
+
legacy_cache = ()
|
| 1011 |
+
for layer in self.layers:
|
| 1012 |
+
legacy_cache += ((layer.keys, layer.values),)
|
| 1013 |
+
return legacy_cache
|
| 1014 |
+
|
| 1015 |
+
@classmethod
|
| 1016 |
+
def from_legacy_cache(cls, past_key_values: tuple[tuple[torch.Tensor, torch.Tensor]]) -> "DynamicCache":
|
| 1017 |
+
"""
|
| 1018 |
+
Converts a cache in the legacy cache format into an equivalent `Cache`. Used for
|
| 1019 |
+
backward compatibility.
|
| 1020 |
+
"""
|
| 1021 |
+
cache = cls()
|
| 1022 |
+
if past_key_values is None:
|
| 1023 |
+
logger.warning_once("past_key_values should not be None in from_legacy_cache()")
|
| 1024 |
+
if past_key_values is not None:
|
| 1025 |
+
for layer_idx in range(len(past_key_values)):
|
| 1026 |
+
key_states, value_states = past_key_values[layer_idx]
|
| 1027 |
+
cache.update(key_states, value_states, layer_idx)
|
| 1028 |
+
return cache
|
| 1029 |
+
|
| 1030 |
+
|
| 1031 |
+
class StaticCache(Cache):
|
| 1032 |
+
"""
|
| 1033 |
+
Static Cache class to be used with `torch.compile(model)` and `torch.export()`. It will check the `config`
|
| 1034 |
+
for potential hybrid cache structure, and initialize each layer accordingly.
|
| 1035 |
+
|
| 1036 |
+
See `Cache` for details on common methods that are implemented by all cache classes.
|
| 1037 |
+
|
| 1038 |
+
Args:
|
| 1039 |
+
config (`PretrainedConfig`):
|
| 1040 |
+
The config of the model for which this Cache will be used. It will be used to check for sliding
|
| 1041 |
+
or hybrid layer structure, and initialize each layer accordingly.
|
| 1042 |
+
max_cache_len (`int`):
|
| 1043 |
+
The maximum number of tokens that this Cache should hold.
|
| 1044 |
+
offloading (`bool`, *optional*, defaults to `False`):
|
| 1045 |
+
Whether to perform offloading of the layers to `cpu`, to save GPU memory.
|
| 1046 |
+
offload_only_non_sliding (`bool`, *optional*, defaults to `True`):
|
| 1047 |
+
If `offloading` is `True`, this further decides if only the non-sliding layers will be offloaded (because
|
| 1048 |
+
usually the sliding layers are small in size, so there is no need to offload them, and skipping it is faster).
|
| 1049 |
+
|
| 1050 |
+
Example:
|
| 1051 |
+
|
| 1052 |
+
```python
|
| 1053 |
+
>>> from transformers import AutoTokenizer, AutoModelForCausalLM, StaticCache
|
| 1054 |
+
|
| 1055 |
+
>>> model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-2-7b-chat-hf")
|
| 1056 |
+
>>> tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-2-7b-chat-hf")
|
| 1057 |
+
|
| 1058 |
+
>>> inputs = tokenizer(text="My name is Llama", return_tensors="pt")
|
| 1059 |
+
|
| 1060 |
+
>>> # Prepare a cache class and pass it to model's forward
|
| 1061 |
+
>>> # Leave empty space for 10 new tokens, which can be used when calling forward iteratively 10 times to generate
|
| 1062 |
+
>>> max_generated_length = inputs.input_ids.shape[1] + 10
|
| 1063 |
+
>>> past_key_values = StaticCache(config=model.config, max_cache_len=max_generated_length)
|
| 1064 |
+
>>> outputs = model(**inputs, past_key_values=past_key_values, use_cache=True)
|
| 1065 |
+
>>> outputs.past_key_values # access cache filled with key/values from generation
|
| 1066 |
+
StaticCache()
|
| 1067 |
+
```
|
| 1068 |
+
"""
|
| 1069 |
+
|
| 1070 |
+
# Pass-in kwargs as well to avoid crashing for BC (it used more arguments before)
|
| 1071 |
+
def __init__(
|
| 1072 |
+
self,
|
| 1073 |
+
config: PretrainedConfig,
|
| 1074 |
+
max_cache_len: int,
|
| 1075 |
+
offloading: bool = False,
|
| 1076 |
+
offload_only_non_sliding: bool = True,
|
| 1077 |
+
**kwargs,
|
| 1078 |
+
):
|
| 1079 |
+
config = config.get_text_config(decoder=True)
|
| 1080 |
+
layer_types = getattr(config, "layer_types", None)
|
| 1081 |
+
# If `layer_types` is not explicitly provided, infer if the model is fully sliding
|
| 1082 |
+
if layer_types is None:
|
| 1083 |
+
if getattr(config, "sliding_window", None) is not None:
|
| 1084 |
+
layer_types = ["sliding_attention" for _ in range(config.num_hidden_layers)]
|
| 1085 |
+
elif getattr(config, "attention_chunk_size", None) is not None:
|
| 1086 |
+
layer_types = ["chunked_attention" for _ in range(config.num_hidden_layers)]
|
| 1087 |
+
else:
|
| 1088 |
+
layer_types = ["full_attention" for _ in range(config.num_hidden_layers)]
|
| 1089 |
+
# Some models have shared layers thus no cache is needed for them (e.g. Gemma3n)
|
| 1090 |
+
if hasattr(config, "num_kv_shared_layers"):
|
| 1091 |
+
layer_types = layer_types[: -config.num_kv_shared_layers]
|
| 1092 |
+
|
| 1093 |
+
layers = []
|
| 1094 |
+
for layer_type in layer_types:
|
| 1095 |
+
if layer_type == "sliding_attention":
|
| 1096 |
+
layer = StaticSlidingWindowLayer(max_cache_len=max_cache_len, sliding_window=config.sliding_window)
|
| 1097 |
+
elif layer_type == "chunked_attention":
|
| 1098 |
+
# From a cache point of view, both sliding and chunked are the same in how they should behave and how many
|
| 1099 |
+
# states they should return - only the mask changes to make them different at the end!
|
| 1100 |
+
layer = StaticSlidingWindowLayer(
|
| 1101 |
+
max_cache_len=max_cache_len, sliding_window=config.attention_chunk_size
|
| 1102 |
+
)
|
| 1103 |
+
else:
|
| 1104 |
+
layer = StaticLayer(max_cache_len=max_cache_len)
|
| 1105 |
+
layers.append(layer)
|
| 1106 |
+
|
| 1107 |
+
super().__init__(layers=layers, offloading=offloading, offload_only_non_sliding=offload_only_non_sliding)
|
| 1108 |
+
|
| 1109 |
+
|
| 1110 |
+
class QuantizedCache(Cache):
|
| 1111 |
+
"""
|
| 1112 |
+
A quantizer cache similar to what is described in the
|
| 1113 |
+
[KIVI: A Tuning-Free Asymmetric 2bit Quantization for KV Cache paper](https://huggingface.co/papers/2402.02750).
|
| 1114 |
+
It allows the model to generate longer sequence length without allocating too much memory for keys and values
|
| 1115 |
+
by applying quantization.
|
| 1116 |
+
The cache has two types of storage, one for original precision and one for the
|
| 1117 |
+
quantized cache. A `residual length` is set as a maximum capacity for the original precision cache. When the
|
| 1118 |
+
length goes beyond maximum capacity, the original precision cache is discarded and moved into the quantized cache.
|
| 1119 |
+
The quantization is done per-channel with a set `q_group_size` for both keys and values, in contrast to what was
|
| 1120 |
+
described in the paper.
|
| 1121 |
+
|
| 1122 |
+
See `Cache` for details on common methods that are implemented by all cache classes.
|
| 1123 |
+
|
| 1124 |
+
Args:
|
| 1125 |
+
backend (`str`):
|
| 1126 |
+
The quantization backend to use. One of `("quanto", "hqq").
|
| 1127 |
+
config (`PretrainedConfig`):
|
| 1128 |
+
The config of the model for which this Cache will be used.
|
| 1129 |
+
nbits (`int`, *optional*, defaults to 4):
|
| 1130 |
+
The number of bits for quantization.
|
| 1131 |
+
axis_key (`int`, *optional*, defaults to 0):
|
| 1132 |
+
The axis on which to quantize the keys.
|
| 1133 |
+
axis_value (`int`, *optional*, defaults to 0):
|
| 1134 |
+
The axis on which to quantize the values.
|
| 1135 |
+
q_group_size (`int`, *optional*, defaults to 64):
|
| 1136 |
+
Quantization is done per-channel according to a set `q_group_size` for both keys and values.
|
| 1137 |
+
residual_length (`int`, *optional*, defaults to 128):
|
| 1138 |
+
Maximum capacity for the original precision cache
|
| 1139 |
+
"""
|
| 1140 |
+
|
| 1141 |
+
def __init__(
|
| 1142 |
+
self,
|
| 1143 |
+
backend: str,
|
| 1144 |
+
config: PretrainedConfig,
|
| 1145 |
+
nbits: int = 4,
|
| 1146 |
+
axis_key: int = 0,
|
| 1147 |
+
axis_value: int = 0,
|
| 1148 |
+
q_group_size: int = 64,
|
| 1149 |
+
residual_length: int = 128,
|
| 1150 |
+
):
|
| 1151 |
+
if backend == "quanto":
|
| 1152 |
+
layer_class = QuantoQuantizedLayer
|
| 1153 |
+
elif backend == "hqq":
|
| 1154 |
+
layer_class = HQQQuantizedLayer
|
| 1155 |
+
else:
|
| 1156 |
+
raise ValueError(f"Unknown quantization backend `{backend}`")
|
| 1157 |
+
|
| 1158 |
+
config = config.get_text_config(decoder=True)
|
| 1159 |
+
layers = [
|
| 1160 |
+
layer_class(nbits, axis_key, axis_value, q_group_size, residual_length)
|
| 1161 |
+
for _ in range(config.num_hidden_layers)
|
| 1162 |
+
]
|
| 1163 |
+
super().__init__(layers=layers)
|
| 1164 |
+
|
| 1165 |
+
|
| 1166 |
+
class EncoderDecoderCache(Cache):
|
| 1167 |
+
"""
|
| 1168 |
+
Base, abstract class for all encoder-decoder caches. Can be used to hold combinations of self-attention and
|
| 1169 |
+
cross-attention caches.
|
| 1170 |
+
|
| 1171 |
+
See `Cache` for details on common methods that are implemented by all cache classes.
|
| 1172 |
+
|
| 1173 |
+
Args:
|
| 1174 |
+
caches (`Iterable`):
|
| 1175 |
+
Usually an iterable of length 2, containing 2 `Cache` objects, the first one for self-attention, the
|
| 1176 |
+
second one for cross-attention. Can optionally also be an iterable of length 1, containing a
|
| 1177 |
+
`tuple[tuple[torch.Tensor]]` (usually used for compatibility with torch dp and ddp).
|
| 1178 |
+
|
| 1179 |
+
Example:
|
| 1180 |
+
|
| 1181 |
+
```python
|
| 1182 |
+
>>> from transformers import AutoProcessor, AutoModelForCausalLM, DynamicCache, EncoderDecoderCache
|
| 1183 |
+
|
| 1184 |
+
>>> model = AutoModelForCausalLM.from_pretrained("openai/whisper-small")
|
| 1185 |
+
>>> processor = AutoProcessor.from_pretrained("openai/whisper-small")
|
| 1186 |
+
|
| 1187 |
+
>>> inputs = processor(audio=YOUR-AUDIO, return_tensors="pt")
|
| 1188 |
+
|
| 1189 |
+
>>> # Prepare cache classes for encoder and decoder and pass it to model's forward
|
| 1190 |
+
>>> self_attention_cache = DynamicCache(config=self.config)
|
| 1191 |
+
>>> cross_attention_cache = DynamicCache(config=self.config)
|
| 1192 |
+
>>> past_key_values = EncoderDecoderCache(self_attention_cache, cross_attention_cache)
|
| 1193 |
+
>>> outputs = model(**inputs, past_key_values=past_key_values, use_cache=True)
|
| 1194 |
+
>>> outputs.past_key_values # access cache filled with key/values from generation
|
| 1195 |
+
EncoderDecoderCache()
|
| 1196 |
+
```
|
| 1197 |
+
"""
|
| 1198 |
+
|
| 1199 |
+
def __init__(self, *caches) -> None:
|
| 1200 |
+
# For dp and ddp support, if only one argument is passed, it should be an iterable of tuples of tensors
|
| 1201 |
+
if len(caches) == 1:
|
| 1202 |
+
self.self_attention_cache = DynamicCache()
|
| 1203 |
+
self.cross_attention_cache = DynamicCache()
|
| 1204 |
+
# Populate cache from the iterable
|
| 1205 |
+
for layer_idx, key_value_states in enumerate(caches[0]):
|
| 1206 |
+
key_states, value_states = key_value_states[:2]
|
| 1207 |
+
self.self_attention_cache.update(key_states, value_states, layer_idx)
|
| 1208 |
+
if len(key_value_states) > 2:
|
| 1209 |
+
key_states, value_states = key_value_states[2:]
|
| 1210 |
+
self.cross_attention_cache.update(key_states, value_states, layer_idx)
|
| 1211 |
+
# Otherwise, we should get two arguments, a self-attention cache and a cross-attention cache
|
| 1212 |
+
elif len(caches) == 2:
|
| 1213 |
+
if not isinstance(caches[0], Cache) or not isinstance(caches[1], Cache):
|
| 1214 |
+
raise TypeError(f"One of the two arguments is not a Cache: {type(caches[0]) = }, {type(caches[1]) = }")
|
| 1215 |
+
self.self_attention_cache = caches[0]
|
| 1216 |
+
self.cross_attention_cache = caches[1]
|
| 1217 |
+
# Error case
|
| 1218 |
+
else:
|
| 1219 |
+
raise ValueError(f"Expected 1 or 2 arguments, got {len(caches)}")
|
| 1220 |
+
|
| 1221 |
+
self.is_updated = {}
|
| 1222 |
+
for layer_idx in range(len(self.cross_attention_cache)):
|
| 1223 |
+
self.is_updated[layer_idx] = bool(self.cross_attention_cache.get_seq_length(layer_idx) > 0)
|
| 1224 |
+
|
| 1225 |
+
def __repr__(self) -> str:
|
| 1226 |
+
return (
|
| 1227 |
+
f"{self.__class__.__name__}(self_attention_cache={self.self_attention_cache}, cross_attention_cache="
|
| 1228 |
+
f"{self.cross_attention_cache})"
|
| 1229 |
+
)
|
| 1230 |
+
|
| 1231 |
+
def __iter__(self):
|
| 1232 |
+
"""
|
| 1233 |
+
Support for backwards-compatible `past_key_values` iteration, e.g. `for x in past_key_values:` to iterate over
|
| 1234 |
+
keys and values
|
| 1235 |
+
"""
|
| 1236 |
+
for layer_idx in range(len(self)):
|
| 1237 |
+
yield (
|
| 1238 |
+
self.self_attention_cache.layers[layer_idx].keys,
|
| 1239 |
+
self.self_attention_cache.layers[layer_idx].values,
|
| 1240 |
+
self.cross_attention_cache.layers[layer_idx].keys,
|
| 1241 |
+
self.cross_attention_cache.layers[layer_idx].values,
|
| 1242 |
+
)
|
| 1243 |
+
|
| 1244 |
+
def __getitem__(self, layer_idx: int) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
|
| 1245 |
+
"""
|
| 1246 |
+
Support for backwards-compatible `past_key_values` indexing, e.g. `past_key_values[0][0].shape[2]` to get the
|
| 1247 |
+
sequence length.
|
| 1248 |
+
"""
|
| 1249 |
+
if layer_idx < len(self):
|
| 1250 |
+
return (
|
| 1251 |
+
self.self_attention_cache.layers[layer_idx].keys,
|
| 1252 |
+
self.self_attention_cache.layers[layer_idx].values,
|
| 1253 |
+
self.cross_attention_cache.layers[layer_idx].keys,
|
| 1254 |
+
self.cross_attention_cache.layers[layer_idx].values,
|
| 1255 |
+
)
|
| 1256 |
+
else:
|
| 1257 |
+
raise KeyError(f"Cache only has {len(self)} layers, attempted to access layer with index {layer_idx}")
|
| 1258 |
+
|
| 1259 |
+
def __len__(self):
|
| 1260 |
+
"""
|
| 1261 |
+
Support for backwards-compatible `past_key_values` length, e.g. `len(past_key_values)`. This value corresponds
|
| 1262 |
+
to the number of layers in the model.
|
| 1263 |
+
"""
|
| 1264 |
+
return len(self.self_attention_cache)
|
| 1265 |
+
|
| 1266 |
+
def to_legacy_cache(self) -> tuple[tuple[torch.Tensor]]:
|
| 1267 |
+
"""Converts the `EncoderDecoderCache` instance into its equivalent in the legacy cache format."""
|
| 1268 |
+
legacy_cache = ()
|
| 1269 |
+
if len(self.cross_attention_cache) > 0:
|
| 1270 |
+
for self_attn, cross_attn in zip(
|
| 1271 |
+
self.self_attention_cache.to_legacy_cache(), self.cross_attention_cache.to_legacy_cache()
|
| 1272 |
+
):
|
| 1273 |
+
legacy_cache += (self_attn + cross_attn,)
|
| 1274 |
+
else:
|
| 1275 |
+
legacy_cache = self.self_attention_cache.to_legacy_cache()
|
| 1276 |
+
return legacy_cache
|
| 1277 |
+
|
| 1278 |
+
@classmethod
|
| 1279 |
+
def from_legacy_cache(
|
| 1280 |
+
cls, past_key_values: Optional[Iterable[tuple[torch.FloatTensor, ...]]]
|
| 1281 |
+
) -> "EncoderDecoderCache":
|
| 1282 |
+
"""Converts a cache in the legacy cache format into an equivalent `EncoderDecoderCache`."""
|
| 1283 |
+
cache = cls(DynamicCache(), DynamicCache())
|
| 1284 |
+
if past_key_values is None:
|
| 1285 |
+
logger.warning_once("past_key_values should not be None in from_legacy_cache()")
|
| 1286 |
+
else:
|
| 1287 |
+
for layer_idx, key_value_states in enumerate(past_key_values):
|
| 1288 |
+
key_states, value_states = key_value_states[:2]
|
| 1289 |
+
cache.self_attention_cache.update(key_states, value_states, layer_idx)
|
| 1290 |
+
if len(key_value_states) > 2:
|
| 1291 |
+
key_states, value_states = key_value_states[2:]
|
| 1292 |
+
cache.cross_attention_cache.update(key_states, value_states, layer_idx)
|
| 1293 |
+
cache.is_updated[layer_idx] = True
|
| 1294 |
+
return cache
|
| 1295 |
+
|
| 1296 |
+
def get_seq_length(self, layer_idx: int = 0) -> int:
|
| 1297 |
+
"""Returns the sequence length of the cached states. A layer index can be optionally passed."""
|
| 1298 |
+
return self.self_attention_cache.get_seq_length(layer_idx)
|
| 1299 |
+
|
| 1300 |
+
def reset(self):
|
| 1301 |
+
self.self_attention_cache.reset()
|
| 1302 |
+
self.cross_attention_cache.reset()
|
| 1303 |
+
for layer_idx in self.is_updated:
|
| 1304 |
+
self.is_updated[layer_idx] = False
|
| 1305 |
+
|
| 1306 |
+
def reorder_cache(self, beam_idx: torch.LongTensor):
|
| 1307 |
+
"""Reorders the cache for beam search, given the selected beam indices."""
|
| 1308 |
+
self.self_attention_cache.reorder_cache(beam_idx)
|
| 1309 |
+
self.cross_attention_cache.reorder_cache(beam_idx)
|
| 1310 |
+
|
| 1311 |
+
def check_dynamic_cache(self, method: str):
|
| 1312 |
+
if not (
|
| 1313 |
+
isinstance(self.self_attention_cache, DynamicCache)
|
| 1314 |
+
and isinstance(self.cross_attention_cache, DynamicCache)
|
| 1315 |
+
):
|
| 1316 |
+
raise ValueError(
|
| 1317 |
+
f"`{method}` is only defined for dynamic cache, got {self.self_attention_cache.__str__()} for the self "
|
| 1318 |
+
f"attention cache and {self.cross_attention_cache.__str__()} for the cross attention cache."
|
| 1319 |
+
)
|
| 1320 |
+
|
| 1321 |
+
# TODO(gante, sanchit-gandhi): move following functionality into `.generate`
|
| 1322 |
+
def crop(self, maximum_length: int):
|
| 1323 |
+
"""
|
| 1324 |
+
Crop the past key values up to a new `maximum_length` in terms of tokens. `maximum_length` can also be
|
| 1325 |
+
negative to remove `maximum_length` tokens. This is used in assisted decoding and contrastive search (on the Hub).
|
| 1326 |
+
"""
|
| 1327 |
+
self.check_dynamic_cache(self.crop.__name__)
|
| 1328 |
+
self.self_attention_cache.crop(maximum_length)
|
| 1329 |
+
|
| 1330 |
+
def batch_split(self, full_batch_size: int, split_size: int) -> "list[EncoderDecoderCache]":
|
| 1331 |
+
"""
|
| 1332 |
+
Split the current instance into a list of `DynamicCache` by the batch size. This will be used by
|
| 1333 |
+
`_split_model_inputs()` in `generation.utils`
|
| 1334 |
+
"""
|
| 1335 |
+
self.check_dynamic_cache(self.batch_split.__name__)
|
| 1336 |
+
self_attention_cache = self.self_attention_cache.batch_split(full_batch_size, split_size)
|
| 1337 |
+
cross_attention_cache = self.cross_attention_cache.batch_split(full_batch_size, split_size)
|
| 1338 |
+
|
| 1339 |
+
out = []
|
| 1340 |
+
for self_attn, cross_attn in zip(self_attention_cache, cross_attention_cache):
|
| 1341 |
+
out.append(EncoderDecoderCache(self_attn, cross_attn))
|
| 1342 |
+
return out
|
| 1343 |
+
|
| 1344 |
+
def batch_repeat_interleave(self, repeats: int):
|
| 1345 |
+
"""Repeat the cache `repeats` times in the batch dimension. Used in contrastive search (on the Hub)."""
|
| 1346 |
+
self.check_dynamic_cache(self.batch_repeat_interleave.__name__)
|
| 1347 |
+
self.self_attention_cache.batch_repeat_interleave(repeats)
|
| 1348 |
+
self.cross_attention_cache.batch_repeat_interleave(repeats)
|
| 1349 |
+
|
| 1350 |
+
def batch_select_indices(self, indices: torch.Tensor):
|
| 1351 |
+
"""Only keep the `indices` in the batch dimension of the cache. Used in contrastive search (on the Hub)."""
|
| 1352 |
+
self.check_dynamic_cache(self.batch_select_indices.__name__)
|
| 1353 |
+
self.self_attention_cache.batch_select_indices(indices)
|
| 1354 |
+
self.cross_attention_cache.batch_select_indices(indices)
|
| 1355 |
+
|
| 1356 |
+
def get_max_cache_shape(self) -> int:
|
| 1357 |
+
"""Returns the maximum sequence length (i.e. max capacity) of the cache object"""
|
| 1358 |
+
return self.self_attention_cache.get_max_cache_shape()
|
| 1359 |
+
|
| 1360 |
+
def get_mask_sizes(self, cache_position: torch.Tensor, layer_idx: int) -> tuple[int, int]:
|
| 1361 |
+
return self.self_attention_cache.get_mask_sizes(cache_position, layer_idx)
|
| 1362 |
+
|
| 1363 |
+
@property
|
| 1364 |
+
def is_sliding(self):
|
| 1365 |
+
return self.self_attention_cache.is_sliding
|
| 1366 |
+
|
| 1367 |
+
@property
|
| 1368 |
+
def is_compileable(self) -> bool:
|
| 1369 |
+
return self.self_attention_cache.is_compileable
|
| 1370 |
+
|
| 1371 |
+
|
| 1372 |
+
### Deprecated classes
|
| 1373 |
+
|
| 1374 |
+
|
| 1375 |
+
class SlidingWindowLayer(StaticSlidingWindowLayer):
|
| 1376 |
+
def __init__(self, max_cache_len: int, sliding_window: int):
|
| 1377 |
+
logger.warning_once(
|
| 1378 |
+
"`SlidingWindowLayer` is deprecated and will be removed in version v4.59 "
|
| 1379 |
+
"Use `StaticSlidingWindowLayer` instead, which is a better name for it."
|
| 1380 |
+
)
|
| 1381 |
+
super().__init__(max_cache_len, sliding_window)
|
| 1382 |
+
|
| 1383 |
+
|
| 1384 |
+
class ChunkedSlidingLayer(StaticSlidingWindowLayer):
|
| 1385 |
+
def __init__(self, max_cache_len: int, sliding_window: int):
|
| 1386 |
+
logger.warning_once(
|
| 1387 |
+
"`ChunkedSlidingLayer` is deprecated and will be removed in version v4.59 "
|
| 1388 |
+
"Use `StaticSlidingWindowLayer` instead, which has the exact same functionalities."
|
| 1389 |
+
)
|
| 1390 |
+
super().__init__(max_cache_len, sliding_window)
|
| 1391 |
+
|
| 1392 |
+
|
| 1393 |
+
class OffloadedCache(DynamicCache):
|
| 1394 |
+
def __init__(self) -> None:
|
| 1395 |
+
logger.warning_once(
|
| 1396 |
+
"`OffloadedCache` is deprecated and will be removed in version v4.59 "
|
| 1397 |
+
"Use `DynamicCache(offloading=True)` instead"
|
| 1398 |
+
)
|
| 1399 |
+
super().__init__(offloading=True)
|
| 1400 |
+
|
| 1401 |
+
|
| 1402 |
+
class OffloadedStaticCache(StaticCache):
|
| 1403 |
+
def __init__(self, config: PretrainedConfig, max_cache_len: int, *args, **kwargs):
|
| 1404 |
+
logger.warning_once(
|
| 1405 |
+
"`OffloadedStaticCache` is deprecated and will be removed in version v4.59 "
|
| 1406 |
+
"Use `StaticCache(..., offloading=True)` instead"
|
| 1407 |
+
)
|
| 1408 |
+
super().__init__(config=config, max_cache_len=max_cache_len, offloading=True)
|
| 1409 |
+
|
| 1410 |
+
|
| 1411 |
+
class SlidingWindowCache(StaticCache):
|
| 1412 |
+
def __init__(self, config: PretrainedConfig, max_cache_len: int, *args, **kwargs):
|
| 1413 |
+
logger.warning_once(
|
| 1414 |
+
"`SlidingWindowCache` is deprecated and will be removed in version v4.59 "
|
| 1415 |
+
"Use `StaticCache(...)` instead which will correctly infer the type of each layer."
|
| 1416 |
+
)
|
| 1417 |
+
super().__init__(config=config, max_cache_len=max_cache_len)
|
| 1418 |
+
|
| 1419 |
+
|
| 1420 |
+
class HybridCache(StaticCache):
|
| 1421 |
+
def __init__(self, config: PretrainedConfig, max_cache_len: int, *args, **kwargs):
|
| 1422 |
+
logger.warning_once(
|
| 1423 |
+
"`HybridCache` is deprecated and will be removed in version v4.59 "
|
| 1424 |
+
"Use `StaticCache(...)` instead which will correctly infer the type of each layer."
|
| 1425 |
+
)
|
| 1426 |
+
super().__init__(config=config, max_cache_len=max_cache_len)
|
| 1427 |
+
|
| 1428 |
+
|
| 1429 |
+
class HybridChunkedCache(StaticCache):
|
| 1430 |
+
def __init__(self, config: PretrainedConfig, max_cache_len: int, *args, **kwargs):
|
| 1431 |
+
logger.warning_once(
|
| 1432 |
+
"`HybridChunkedCache` is deprecated and will be removed in version v4.59 "
|
| 1433 |
+
"Use `StaticCache(...)` instead which will correctly infer the type of each layer."
|
| 1434 |
+
)
|
| 1435 |
+
super().__init__(config=config, max_cache_len=max_cache_len)
|
| 1436 |
+
|
| 1437 |
+
|
| 1438 |
+
class OffloadedHybridCache(StaticCache):
|
| 1439 |
+
def __init__(self, config: PretrainedConfig, max_cache_len: int, *args, **kwargs):
|
| 1440 |
+
logger.warning_once(
|
| 1441 |
+
"`OffloadedHybridCache` is deprecated and will be removed in version v4.59 "
|
| 1442 |
+
"Use `StaticCache(..., offload=True)` instead which will correctly infer the type of each layer."
|
| 1443 |
+
)
|
| 1444 |
+
super().__init__(config=config, max_cache_len=max_cache_len, offloading=True)
|
| 1445 |
+
|
| 1446 |
+
|
| 1447 |
+
class QuantoQuantizedCache(QuantizedCache):
|
| 1448 |
+
def __init__(
|
| 1449 |
+
self,
|
| 1450 |
+
config: PretrainedConfig,
|
| 1451 |
+
nbits: int = 4,
|
| 1452 |
+
axis_key: int = 0,
|
| 1453 |
+
axis_value: int = 0,
|
| 1454 |
+
q_group_size: int = 64,
|
| 1455 |
+
residual_length: int = 128,
|
| 1456 |
+
):
|
| 1457 |
+
logger.warning_once(
|
| 1458 |
+
"`QuantoQuantizedCache` is deprecated and will be removed in version v4.59 "
|
| 1459 |
+
"Use `QuantizedCache(backend='quanto', ...)` instead."
|
| 1460 |
+
)
|
| 1461 |
+
super().__init__("quanto", config, nbits, axis_key, axis_value, q_group_size, residual_length)
|
| 1462 |
+
|
| 1463 |
+
|
| 1464 |
+
class HQQQuantizedCache(QuantizedCache):
|
| 1465 |
+
def __init__(
|
| 1466 |
+
self,
|
| 1467 |
+
config: PretrainedConfig,
|
| 1468 |
+
nbits: int = 4,
|
| 1469 |
+
axis_key: int = 0,
|
| 1470 |
+
axis_value: int = 0,
|
| 1471 |
+
q_group_size: int = 64,
|
| 1472 |
+
residual_length: int = 128,
|
| 1473 |
+
):
|
| 1474 |
+
logger.warning_once(
|
| 1475 |
+
"`HQQQuantizedCache` is deprecated and will be removed in version v4.59 "
|
| 1476 |
+
"Use `QuantizedCache(backend='hqq', ...)` instead."
|
| 1477 |
+
)
|
| 1478 |
+
super().__init__("hqq", config, nbits, axis_key, axis_value, q_group_size, residual_length)
|
| 1479 |
+
|
| 1480 |
+
|
| 1481 |
+
class SinkCache(Cache):
|
| 1482 |
+
"""
|
| 1483 |
+
It is now a `custom_generate` repository on the Hub: https://huggingface.co/transformers-community/sink_cache.
|
| 1484 |
+
See [these docs](https://huggingface.co/docs/transformers/generation_strategies#custom-decoding-methods) for
|
| 1485 |
+
general `custom_generate`usage.
|
| 1486 |
+
"""
|
| 1487 |
+
|
| 1488 |
+
# TODO (joao, manuel): Remove this class in v4.59.0
|
| 1489 |
+
def __init__(self, **kwargs) -> None:
|
| 1490 |
+
raise NotImplementedError(
|
| 1491 |
+
"`SinkCache` has been moved as a `custom_generate` repository on the Hub: "
|
| 1492 |
+
"https://huggingface.co/transformers-community/sink_cache. See the repository for usage examples."
|
| 1493 |
+
)
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/convert_tf_hub_seq_to_seq_bert_to_pytorch.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2020 The HuggingFace Inc. team.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""Convert Seq2Seq TF Hub checkpoint."""
|
| 15 |
+
|
| 16 |
+
import argparse
|
| 17 |
+
|
| 18 |
+
from . import (
|
| 19 |
+
BertConfig,
|
| 20 |
+
BertGenerationConfig,
|
| 21 |
+
BertGenerationDecoder,
|
| 22 |
+
BertGenerationEncoder,
|
| 23 |
+
load_tf_weights_in_bert_generation,
|
| 24 |
+
logging,
|
| 25 |
+
)
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
logging.set_verbosity_info()
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def convert_tf_checkpoint_to_pytorch(tf_hub_path, pytorch_dump_path, is_encoder_named_decoder, vocab_size, is_encoder):
|
| 32 |
+
# Initialise PyTorch model
|
| 33 |
+
bert_config = BertConfig.from_pretrained(
|
| 34 |
+
"google-bert/bert-large-cased",
|
| 35 |
+
vocab_size=vocab_size,
|
| 36 |
+
max_position_embeddings=512,
|
| 37 |
+
is_decoder=True,
|
| 38 |
+
add_cross_attention=True,
|
| 39 |
+
)
|
| 40 |
+
bert_config_dict = bert_config.to_dict()
|
| 41 |
+
del bert_config_dict["type_vocab_size"]
|
| 42 |
+
config = BertGenerationConfig(**bert_config_dict)
|
| 43 |
+
if is_encoder:
|
| 44 |
+
model = BertGenerationEncoder(config)
|
| 45 |
+
else:
|
| 46 |
+
model = BertGenerationDecoder(config)
|
| 47 |
+
print(f"Building PyTorch model from configuration: {config}")
|
| 48 |
+
|
| 49 |
+
# Load weights from tf checkpoint
|
| 50 |
+
load_tf_weights_in_bert_generation(
|
| 51 |
+
model,
|
| 52 |
+
tf_hub_path,
|
| 53 |
+
model_class="bert",
|
| 54 |
+
is_encoder_named_decoder=is_encoder_named_decoder,
|
| 55 |
+
is_encoder=is_encoder,
|
| 56 |
+
)
|
| 57 |
+
|
| 58 |
+
# Save pytorch-model
|
| 59 |
+
print(f"Save PyTorch model and config to {pytorch_dump_path}")
|
| 60 |
+
model.save_pretrained(pytorch_dump_path)
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
if __name__ == "__main__":
|
| 64 |
+
parser = argparse.ArgumentParser()
|
| 65 |
+
# Required parameters
|
| 66 |
+
parser.add_argument(
|
| 67 |
+
"--tf_hub_path", default=None, type=str, required=True, help="Path to the TensorFlow checkpoint path."
|
| 68 |
+
)
|
| 69 |
+
parser.add_argument(
|
| 70 |
+
"--pytorch_dump_path", default=None, type=str, required=True, help="Path to the output PyTorch model."
|
| 71 |
+
)
|
| 72 |
+
parser.add_argument(
|
| 73 |
+
"--is_encoder_named_decoder",
|
| 74 |
+
action="store_true",
|
| 75 |
+
help="If decoder has to be renamed to encoder in PyTorch model.",
|
| 76 |
+
)
|
| 77 |
+
parser.add_argument("--is_encoder", action="store_true", help="If model is an encoder.")
|
| 78 |
+
parser.add_argument("--vocab_size", default=50358, type=int, help="Vocab size of model")
|
| 79 |
+
args = parser.parse_args()
|
| 80 |
+
convert_tf_checkpoint_to_pytorch(
|
| 81 |
+
args.tf_hub_path,
|
| 82 |
+
args.pytorch_dump_path,
|
| 83 |
+
args.is_encoder_named_decoder,
|
| 84 |
+
args.vocab_size,
|
| 85 |
+
is_encoder=args.is_encoder,
|
| 86 |
+
)
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/debug_utils.py
ADDED
|
@@ -0,0 +1,346 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2020 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
import collections
|
| 16 |
+
|
| 17 |
+
from .utils import ExplicitEnum, is_torch_available, logging
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
if is_torch_available():
|
| 21 |
+
import torch
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
logger = logging.get_logger(__name__)
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
class DebugUnderflowOverflow:
|
| 28 |
+
"""
|
| 29 |
+
This debug class helps detect and understand where the model starts getting very large or very small, and more
|
| 30 |
+
importantly `nan` or `inf` weight and activation elements.
|
| 31 |
+
|
| 32 |
+
There are 2 working modes:
|
| 33 |
+
|
| 34 |
+
1. Underflow/overflow detection (default)
|
| 35 |
+
2. Specific batch absolute min/max tracing without detection
|
| 36 |
+
|
| 37 |
+
Mode 1: Underflow/overflow detection
|
| 38 |
+
|
| 39 |
+
To activate the underflow/overflow detection, initialize the object with the model :
|
| 40 |
+
|
| 41 |
+
```python
|
| 42 |
+
debug_overflow = DebugUnderflowOverflow(model)
|
| 43 |
+
```
|
| 44 |
+
|
| 45 |
+
then run the training as normal and if `nan` or `inf` gets detected in at least one of the weight, input or output
|
| 46 |
+
elements this module will throw an exception and will print `max_frames_to_save` frames that lead to this event,
|
| 47 |
+
each frame reporting
|
| 48 |
+
|
| 49 |
+
1. the fully qualified module name plus the class name whose `forward` was run
|
| 50 |
+
2. the absolute min and max value of all elements for each module weights, and the inputs and output
|
| 51 |
+
|
| 52 |
+
For example, here is the header and the last few frames in detection report for `google/mt5-small` run in fp16
|
| 53 |
+
mixed precision :
|
| 54 |
+
|
| 55 |
+
```
|
| 56 |
+
Detected inf/nan during batch_number=0
|
| 57 |
+
Last 21 forward frames:
|
| 58 |
+
abs min abs max metadata
|
| 59 |
+
[...]
|
| 60 |
+
encoder.block.2.layer.1.DenseReluDense.wi_0 Linear
|
| 61 |
+
2.17e-07 4.50e+00 weight
|
| 62 |
+
1.79e-06 4.65e+00 input[0]
|
| 63 |
+
2.68e-06 3.70e+01 output
|
| 64 |
+
encoder.block.2.layer.1.DenseReluDense.wi_1 Linear
|
| 65 |
+
8.08e-07 2.66e+01 weight
|
| 66 |
+
1.79e-06 4.65e+00 input[0]
|
| 67 |
+
1.27e-04 2.37e+02 output
|
| 68 |
+
encoder.block.2.layer.1.DenseReluDense.wo Linear
|
| 69 |
+
1.01e-06 6.44e+00 weight
|
| 70 |
+
0.00e+00 9.74e+03 input[0]
|
| 71 |
+
3.18e-04 6.27e+04 output
|
| 72 |
+
encoder.block.2.layer.1.DenseReluDense T5DenseGatedGeluDense
|
| 73 |
+
1.79e-06 4.65e+00 input[0]
|
| 74 |
+
3.18e-04 6.27e+04 output
|
| 75 |
+
encoder.block.2.layer.1.dropout Dropout
|
| 76 |
+
3.18e-04 6.27e+04 input[0]
|
| 77 |
+
0.00e+00 inf output
|
| 78 |
+
```
|
| 79 |
+
|
| 80 |
+
You can see here, that `T5DenseGatedGeluDense.forward` resulted in output activations, whose absolute max value was
|
| 81 |
+
around 62.7K, which is very close to fp16's top limit of 64K. In the next frame we have `Dropout` which
|
| 82 |
+
renormalizes the weights, after it zeroed some of the elements, which pushes the absolute max value to more than
|
| 83 |
+
64K, and we get an overflow.
|
| 84 |
+
|
| 85 |
+
As you can see it's the previous frames that we need to look into when the numbers start going into very large for
|
| 86 |
+
fp16 numbers.
|
| 87 |
+
|
| 88 |
+
The tracking is done in a forward hook, which gets invoked immediately after `forward` has completed.
|
| 89 |
+
|
| 90 |
+
By default the last 21 frames are printed. You can change the default to adjust for your needs. For example :
|
| 91 |
+
|
| 92 |
+
```python
|
| 93 |
+
debug_overflow = DebugUnderflowOverflow(model, max_frames_to_save=100)
|
| 94 |
+
```
|
| 95 |
+
|
| 96 |
+
To validate that you have set up this debugging feature correctly, and you intend to use it in a training that
|
| 97 |
+
may take hours to complete, first run it with normal tracing enabled for one of a few batches as explained in
|
| 98 |
+
the next section.
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
Mode 2. Specific batch absolute min/max tracing without detection
|
| 102 |
+
|
| 103 |
+
The second work mode is per-batch tracing with the underflow/overflow detection feature turned off.
|
| 104 |
+
|
| 105 |
+
Let's say you want to watch the absolute min and max values for all the ingredients of each `forward` call of a
|
| 106 |
+
given batch, and only do that for batches 1 and 3. Then you instantiate this class as :
|
| 107 |
+
|
| 108 |
+
```python
|
| 109 |
+
debug_overflow = DebugUnderflowOverflow(model, trace_batch_nums=[1, 3])
|
| 110 |
+
```
|
| 111 |
+
|
| 112 |
+
And now full batches 1 and 3 will be traced using the same format as explained above. Batches are 0-indexed.
|
| 113 |
+
|
| 114 |
+
This is helpful if you know that the program starts misbehaving after a certain batch number, so you can
|
| 115 |
+
fast-forward right to that area.
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
Early stopping:
|
| 119 |
+
|
| 120 |
+
You can also specify the batch number after which to stop the training, with :
|
| 121 |
+
|
| 122 |
+
```python
|
| 123 |
+
debug_overflow = DebugUnderflowOverflow(model, trace_batch_nums=[1, 3], abort_after_batch_num=3)
|
| 124 |
+
```
|
| 125 |
+
|
| 126 |
+
This feature is mainly useful in the tracing mode, but you can use it for any mode.
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
**Performance**:
|
| 130 |
+
|
| 131 |
+
As this module measures absolute `min`/``max` of each weight of the model on every forward it'll slow the training
|
| 132 |
+
down. Therefore remember to turn it off once the debugging needs have been met.
|
| 133 |
+
|
| 134 |
+
Args:
|
| 135 |
+
model (`nn.Module`):
|
| 136 |
+
The model to debug.
|
| 137 |
+
max_frames_to_save (`int`, *optional*, defaults to 21):
|
| 138 |
+
How many frames back to record
|
| 139 |
+
trace_batch_nums(`list[int]`, *optional*, defaults to `[]`):
|
| 140 |
+
Which batch numbers to trace (turns detection off)
|
| 141 |
+
abort_after_batch_num (`int``, *optional*):
|
| 142 |
+
Whether to abort after a certain batch number has finished
|
| 143 |
+
"""
|
| 144 |
+
|
| 145 |
+
def __init__(self, model, max_frames_to_save=21, trace_batch_nums=[], abort_after_batch_num=None):
|
| 146 |
+
self.model = model
|
| 147 |
+
self.trace_batch_nums = trace_batch_nums
|
| 148 |
+
self.abort_after_batch_num = abort_after_batch_num
|
| 149 |
+
|
| 150 |
+
# keep a LIFO buffer of frames to dump as soon as inf/nan is encountered to give context to the problem emergence
|
| 151 |
+
self.frames = collections.deque([], max_frames_to_save)
|
| 152 |
+
self.frame = []
|
| 153 |
+
self.batch_number = 0
|
| 154 |
+
self.total_calls = 0
|
| 155 |
+
self.detected_overflow = False
|
| 156 |
+
self.prefix = " "
|
| 157 |
+
|
| 158 |
+
self.analyse_model()
|
| 159 |
+
|
| 160 |
+
self.register_forward_hook()
|
| 161 |
+
|
| 162 |
+
def save_frame(self, frame=None):
|
| 163 |
+
if frame is not None:
|
| 164 |
+
self.expand_frame(frame)
|
| 165 |
+
self.frames.append("\n".join(self.frame))
|
| 166 |
+
self.frame = [] # start a new frame
|
| 167 |
+
|
| 168 |
+
def expand_frame(self, line):
|
| 169 |
+
self.frame.append(line)
|
| 170 |
+
|
| 171 |
+
def trace_frames(self):
|
| 172 |
+
print("\n".join(self.frames))
|
| 173 |
+
self.frames = []
|
| 174 |
+
|
| 175 |
+
def reset_saved_frames(self):
|
| 176 |
+
self.frames = []
|
| 177 |
+
|
| 178 |
+
def dump_saved_frames(self):
|
| 179 |
+
print(f"\nDetected inf/nan during batch_number={self.batch_number}")
|
| 180 |
+
print(f"Last {len(self.frames)} forward frames:")
|
| 181 |
+
print(f"{'abs min':8} {'abs max':8} metadata")
|
| 182 |
+
print("\n".join(self.frames))
|
| 183 |
+
print("\n\n")
|
| 184 |
+
self.frames = []
|
| 185 |
+
|
| 186 |
+
def analyse_model(self):
|
| 187 |
+
# extract the fully qualified module names, to be able to report at run time. e.g.:
|
| 188 |
+
# encoder.block.2.layer.0.SelfAttention.o
|
| 189 |
+
#
|
| 190 |
+
# for shared weights only the first shared module name will be registered
|
| 191 |
+
self.module_names = {m: name for name, m in self.model.named_modules()}
|
| 192 |
+
# self.longest_module_name = max(len(v) for v in self.module_names.values())
|
| 193 |
+
|
| 194 |
+
def analyse_variable(self, var, ctx):
|
| 195 |
+
if torch.is_tensor(var):
|
| 196 |
+
self.expand_frame(get_abs_min_max(var, ctx))
|
| 197 |
+
if detect_overflow(var, ctx):
|
| 198 |
+
self.detected_overflow = True
|
| 199 |
+
elif var is None:
|
| 200 |
+
self.expand_frame(f"{'None':>17} {ctx}")
|
| 201 |
+
else:
|
| 202 |
+
self.expand_frame(f"{'not a tensor':>17} {ctx}")
|
| 203 |
+
|
| 204 |
+
def batch_start_frame(self):
|
| 205 |
+
self.expand_frame(f"\n\n{self.prefix} *** Starting batch number={self.batch_number} ***")
|
| 206 |
+
self.expand_frame(f"{'abs min':8} {'abs max':8} metadata")
|
| 207 |
+
|
| 208 |
+
def batch_end_frame(self):
|
| 209 |
+
self.expand_frame(f"{self.prefix} *** Finished batch number={self.batch_number - 1} ***\n\n")
|
| 210 |
+
|
| 211 |
+
def create_frame(self, module, input, output):
|
| 212 |
+
self.expand_frame(f"{self.prefix} {self.module_names[module]} {module.__class__.__name__}")
|
| 213 |
+
|
| 214 |
+
# params
|
| 215 |
+
for name, p in module.named_parameters(recurse=False):
|
| 216 |
+
self.analyse_variable(p, name)
|
| 217 |
+
|
| 218 |
+
# inputs
|
| 219 |
+
if isinstance(input, tuple):
|
| 220 |
+
for i, x in enumerate(input):
|
| 221 |
+
self.analyse_variable(x, f"input[{i}]")
|
| 222 |
+
else:
|
| 223 |
+
self.analyse_variable(input, "input")
|
| 224 |
+
|
| 225 |
+
# outputs
|
| 226 |
+
if isinstance(output, tuple):
|
| 227 |
+
for i, x in enumerate(output):
|
| 228 |
+
# possibly a tuple of tuples
|
| 229 |
+
if isinstance(x, tuple):
|
| 230 |
+
for j, y in enumerate(x):
|
| 231 |
+
self.analyse_variable(y, f"output[{i}][{j}]")
|
| 232 |
+
else:
|
| 233 |
+
self.analyse_variable(x, f"output[{i}]")
|
| 234 |
+
else:
|
| 235 |
+
self.analyse_variable(output, "output")
|
| 236 |
+
|
| 237 |
+
self.save_frame()
|
| 238 |
+
|
| 239 |
+
def register_forward_hook(self):
|
| 240 |
+
self.model.apply(self._register_forward_hook)
|
| 241 |
+
|
| 242 |
+
def _register_forward_hook(self, module):
|
| 243 |
+
module.register_forward_hook(self.forward_hook)
|
| 244 |
+
|
| 245 |
+
def forward_hook(self, module, input, output):
|
| 246 |
+
# - input is a tuple of packed inputs (could be non-Tensors)
|
| 247 |
+
# - output could be a Tensor or a tuple of Tensors and non-Tensors
|
| 248 |
+
|
| 249 |
+
last_frame_of_batch = False
|
| 250 |
+
|
| 251 |
+
trace_mode = self.batch_number in self.trace_batch_nums
|
| 252 |
+
if trace_mode:
|
| 253 |
+
self.reset_saved_frames()
|
| 254 |
+
|
| 255 |
+
if self.total_calls == 0:
|
| 256 |
+
self.batch_start_frame()
|
| 257 |
+
self.total_calls += 1
|
| 258 |
+
|
| 259 |
+
# count batch numbers - the very first forward hook of the batch will be called when the
|
| 260 |
+
# batch completes - i.e. it gets called very last - we know this batch has finished
|
| 261 |
+
if module == self.model:
|
| 262 |
+
self.batch_number += 1
|
| 263 |
+
last_frame_of_batch = True
|
| 264 |
+
|
| 265 |
+
self.create_frame(module, input, output)
|
| 266 |
+
|
| 267 |
+
# if last_frame_of_batch:
|
| 268 |
+
# self.batch_end_frame()
|
| 269 |
+
|
| 270 |
+
if trace_mode:
|
| 271 |
+
self.trace_frames()
|
| 272 |
+
|
| 273 |
+
if last_frame_of_batch:
|
| 274 |
+
self.batch_start_frame()
|
| 275 |
+
|
| 276 |
+
if self.detected_overflow and not trace_mode:
|
| 277 |
+
self.dump_saved_frames()
|
| 278 |
+
|
| 279 |
+
# now we can abort, as it's pointless to continue running
|
| 280 |
+
raise ValueError(
|
| 281 |
+
"DebugUnderflowOverflow: inf/nan detected, aborting as there is no point running further. "
|
| 282 |
+
"Please scroll up above this traceback to see the activation values prior to this event."
|
| 283 |
+
)
|
| 284 |
+
|
| 285 |
+
# abort after certain batch if requested to do so
|
| 286 |
+
if self.abort_after_batch_num is not None and self.batch_number > self.abort_after_batch_num:
|
| 287 |
+
raise ValueError(
|
| 288 |
+
f"DebugUnderflowOverflow: aborting after {self.batch_number} batches due to"
|
| 289 |
+
f" `abort_after_batch_num={self.abort_after_batch_num}` arg"
|
| 290 |
+
)
|
| 291 |
+
|
| 292 |
+
|
| 293 |
+
def get_abs_min_max(var, ctx):
|
| 294 |
+
abs_var = var.abs()
|
| 295 |
+
return f"{abs_var.min():8.2e} {abs_var.max():8.2e} {ctx}"
|
| 296 |
+
|
| 297 |
+
|
| 298 |
+
def detect_overflow(var, ctx):
|
| 299 |
+
"""
|
| 300 |
+
Report whether the tensor contains any `nan` or `inf` entries.
|
| 301 |
+
|
| 302 |
+
This is useful for detecting overflows/underflows and best to call right after the function that did some math that
|
| 303 |
+
modified the tensor in question.
|
| 304 |
+
|
| 305 |
+
This function contains a few other helper features that you can enable and tweak directly if you want to track
|
| 306 |
+
various other things.
|
| 307 |
+
|
| 308 |
+
Args:
|
| 309 |
+
var: the tensor variable to check
|
| 310 |
+
ctx: the message to print as a context
|
| 311 |
+
|
| 312 |
+
Return:
|
| 313 |
+
`True` if `inf` or `nan` was detected, `False` otherwise
|
| 314 |
+
"""
|
| 315 |
+
detected = False
|
| 316 |
+
if torch.isnan(var).any().item():
|
| 317 |
+
detected = True
|
| 318 |
+
print(f"{ctx} has nans")
|
| 319 |
+
if torch.isinf(var).any().item():
|
| 320 |
+
detected = True
|
| 321 |
+
print(f"{ctx} has infs")
|
| 322 |
+
|
| 323 |
+
# if needed to monitor large elements can enable the following
|
| 324 |
+
if 0: # and detected:
|
| 325 |
+
n100 = var[torch.ge(var.abs(), 100)]
|
| 326 |
+
if n100.numel() > 0:
|
| 327 |
+
print(f"{ctx}: n100={n100.numel()}")
|
| 328 |
+
n1000 = var[torch.ge(var.abs(), 1000)]
|
| 329 |
+
if n1000.numel() > 0:
|
| 330 |
+
print(f"{ctx}: n1000={n1000.numel()}")
|
| 331 |
+
n10000 = var[torch.ge(var.abs(), 10000)]
|
| 332 |
+
if n10000.numel() > 0:
|
| 333 |
+
print(f"{ctx}: n10000={n10000.numel()}")
|
| 334 |
+
|
| 335 |
+
if 0:
|
| 336 |
+
print(f"min={var.min():9.2e} max={var.max():9.2e}")
|
| 337 |
+
|
| 338 |
+
if 0:
|
| 339 |
+
print(f"min={var.min():9.2e} max={var.max():9.2e} var={var.var():9.2e} mean={var.mean():9.2e} ({ctx})")
|
| 340 |
+
|
| 341 |
+
return detected
|
| 342 |
+
|
| 343 |
+
|
| 344 |
+
class DebugOption(ExplicitEnum):
|
| 345 |
+
UNDERFLOW_OVERFLOW = "underflow_overflow"
|
| 346 |
+
TPU_METRICS_DEBUG = "tpu_metrics_debug"
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/dependency_versions_table.py
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# THIS FILE HAS BEEN AUTOGENERATED. To update:
|
| 2 |
+
# 1. modify the `_deps` dict in setup.py
|
| 3 |
+
# 2. run `make deps_table_update``
|
| 4 |
+
deps = {
|
| 5 |
+
"Pillow": "Pillow>=10.0.1,<=15.0",
|
| 6 |
+
"accelerate": "accelerate>=0.26.0",
|
| 7 |
+
"av": "av",
|
| 8 |
+
"beautifulsoup4": "beautifulsoup4",
|
| 9 |
+
"blobfile": "blobfile",
|
| 10 |
+
"codecarbon": "codecarbon>=2.8.1",
|
| 11 |
+
"cookiecutter": "cookiecutter==1.7.3",
|
| 12 |
+
"dataclasses": "dataclasses",
|
| 13 |
+
"datasets": "datasets>=2.15.0",
|
| 14 |
+
"deepspeed": "deepspeed>=0.9.3",
|
| 15 |
+
"diffusers": "diffusers",
|
| 16 |
+
"dill": "dill<0.3.5",
|
| 17 |
+
"evaluate": "evaluate>=0.2.0",
|
| 18 |
+
"faiss-cpu": "faiss-cpu",
|
| 19 |
+
"fastapi": "fastapi",
|
| 20 |
+
"filelock": "filelock",
|
| 21 |
+
"flax": "flax>=0.4.1,<=0.7.0",
|
| 22 |
+
"ftfy": "ftfy",
|
| 23 |
+
"fugashi": "fugashi>=1.0",
|
| 24 |
+
"GitPython": "GitPython<3.1.19",
|
| 25 |
+
"hf-doc-builder": "hf-doc-builder>=0.3.0",
|
| 26 |
+
"hf_xet": "hf_xet",
|
| 27 |
+
"huggingface-hub": "huggingface-hub>=0.34.0,<1.0",
|
| 28 |
+
"importlib_metadata": "importlib_metadata",
|
| 29 |
+
"ipadic": "ipadic>=1.0.0,<2.0",
|
| 30 |
+
"jax": "jax>=0.4.1,<=0.4.13",
|
| 31 |
+
"jaxlib": "jaxlib>=0.4.1,<=0.4.13",
|
| 32 |
+
"jinja2": "jinja2>=3.1.0",
|
| 33 |
+
"kenlm": "kenlm",
|
| 34 |
+
"keras": "keras>2.9,<2.16",
|
| 35 |
+
"keras-nlp": "keras-nlp>=0.3.1,<0.14.0",
|
| 36 |
+
"kernels": "kernels>=0.6.1,<=0.9",
|
| 37 |
+
"librosa": "librosa",
|
| 38 |
+
"natten": "natten>=0.14.6,<0.15.0",
|
| 39 |
+
"nltk": "nltk<=3.8.1",
|
| 40 |
+
"num2words": "num2words",
|
| 41 |
+
"numpy": "numpy>=1.17",
|
| 42 |
+
"onnxconverter-common": "onnxconverter-common",
|
| 43 |
+
"onnxruntime-tools": "onnxruntime-tools>=1.4.2",
|
| 44 |
+
"onnxruntime": "onnxruntime>=1.4.0",
|
| 45 |
+
"openai": "openai>=1.98.0",
|
| 46 |
+
"opencv-python": "opencv-python",
|
| 47 |
+
"optimum-benchmark": "optimum-benchmark>=0.3.0",
|
| 48 |
+
"optuna": "optuna",
|
| 49 |
+
"optax": "optax>=0.0.8,<=0.1.4",
|
| 50 |
+
"pandas": "pandas<2.3.0",
|
| 51 |
+
"packaging": "packaging>=20.0",
|
| 52 |
+
"parameterized": "parameterized>=0.9",
|
| 53 |
+
"phonemizer": "phonemizer",
|
| 54 |
+
"protobuf": "protobuf",
|
| 55 |
+
"psutil": "psutil",
|
| 56 |
+
"pyyaml": "pyyaml>=5.1",
|
| 57 |
+
"pydantic": "pydantic>=2",
|
| 58 |
+
"pytest": "pytest>=7.2.0",
|
| 59 |
+
"pytest-asyncio": "pytest-asyncio",
|
| 60 |
+
"pytest-rerunfailures": "pytest-rerunfailures<16.0",
|
| 61 |
+
"pytest-timeout": "pytest-timeout",
|
| 62 |
+
"pytest-xdist": "pytest-xdist",
|
| 63 |
+
"pytest-order": "pytest-order",
|
| 64 |
+
"python": "python>=3.9.0",
|
| 65 |
+
"ray[tune]": "ray[tune]>=2.7.0",
|
| 66 |
+
"regex": "regex!=2019.12.17",
|
| 67 |
+
"requests": "requests",
|
| 68 |
+
"rhoknp": "rhoknp>=1.1.0,<1.3.1",
|
| 69 |
+
"rjieba": "rjieba",
|
| 70 |
+
"rouge-score": "rouge-score!=0.0.7,!=0.0.8,!=0.1,!=0.1.1",
|
| 71 |
+
"ruff": "ruff==0.13.1",
|
| 72 |
+
"sacrebleu": "sacrebleu>=1.4.12,<2.0.0",
|
| 73 |
+
"sacremoses": "sacremoses",
|
| 74 |
+
"safetensors": "safetensors>=0.4.3",
|
| 75 |
+
"sagemaker": "sagemaker>=2.31.0",
|
| 76 |
+
"schedulefree": "schedulefree>=1.2.6",
|
| 77 |
+
"scikit-learn": "scikit-learn",
|
| 78 |
+
"scipy": "scipy<1.13.0",
|
| 79 |
+
"sentencepiece": "sentencepiece>=0.1.91,!=0.1.92",
|
| 80 |
+
"sigopt": "sigopt",
|
| 81 |
+
"starlette": "starlette",
|
| 82 |
+
"sudachipy": "sudachipy>=0.6.6",
|
| 83 |
+
"sudachidict_core": "sudachidict_core>=20220729",
|
| 84 |
+
"tensorboard": "tensorboard",
|
| 85 |
+
"tensorflow-cpu": "tensorflow-cpu>2.9,<2.16",
|
| 86 |
+
"tensorflow": "tensorflow>2.9,<2.16",
|
| 87 |
+
"tensorflow-text": "tensorflow-text<2.16",
|
| 88 |
+
"tensorflow-probability": "tensorflow-probability<0.24",
|
| 89 |
+
"tf2onnx": "tf2onnx",
|
| 90 |
+
"timeout-decorator": "timeout-decorator",
|
| 91 |
+
"tiktoken": "tiktoken",
|
| 92 |
+
"timm": "timm<=1.0.19,!=1.0.18",
|
| 93 |
+
"tokenizers": "tokenizers>=0.22.0,<=0.23.0",
|
| 94 |
+
"torch": "torch>=2.2",
|
| 95 |
+
"torchaudio": "torchaudio",
|
| 96 |
+
"torchvision": "torchvision",
|
| 97 |
+
"pyctcdecode": "pyctcdecode>=0.4.0",
|
| 98 |
+
"tqdm": "tqdm>=4.27",
|
| 99 |
+
"unidic": "unidic>=1.0.2",
|
| 100 |
+
"unidic_lite": "unidic_lite>=1.0.7",
|
| 101 |
+
"urllib3": "urllib3<2.0.0",
|
| 102 |
+
"uvicorn": "uvicorn",
|
| 103 |
+
"pytest-rich": "pytest-rich",
|
| 104 |
+
"libcst": "libcst",
|
| 105 |
+
"rich": "rich",
|
| 106 |
+
"opentelemetry-api": "opentelemetry-api",
|
| 107 |
+
"mistral-common[opencv]": "mistral-common[opencv]>=1.6.3",
|
| 108 |
+
}
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/dynamic_module_utils.py
ADDED
|
@@ -0,0 +1,843 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2021 The HuggingFace Inc. team.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""Utilities to dynamically load objects from the Hub."""
|
| 15 |
+
|
| 16 |
+
import ast
|
| 17 |
+
import filecmp
|
| 18 |
+
import hashlib
|
| 19 |
+
import importlib
|
| 20 |
+
import importlib.metadata
|
| 21 |
+
import importlib.util
|
| 22 |
+
import keyword
|
| 23 |
+
import os
|
| 24 |
+
import re
|
| 25 |
+
import shutil
|
| 26 |
+
import signal
|
| 27 |
+
import sys
|
| 28 |
+
import threading
|
| 29 |
+
import warnings
|
| 30 |
+
from pathlib import Path
|
| 31 |
+
from types import ModuleType
|
| 32 |
+
from typing import Any, Optional, Union
|
| 33 |
+
|
| 34 |
+
from huggingface_hub import try_to_load_from_cache
|
| 35 |
+
from packaging import version
|
| 36 |
+
|
| 37 |
+
from .utils import (
|
| 38 |
+
HF_MODULES_CACHE,
|
| 39 |
+
TRANSFORMERS_DYNAMIC_MODULE_NAME,
|
| 40 |
+
cached_file,
|
| 41 |
+
extract_commit_hash,
|
| 42 |
+
is_offline_mode,
|
| 43 |
+
logging,
|
| 44 |
+
)
|
| 45 |
+
from .utils.import_utils import VersionComparison, split_package_version
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
logger = logging.get_logger(__name__) # pylint: disable=invalid-name
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
def _sanitize_module_name(name: str) -> str:
|
| 52 |
+
r"""
|
| 53 |
+
Tries to sanitize a module name so that it can be used as a Python module.
|
| 54 |
+
|
| 55 |
+
The following transformations are applied:
|
| 56 |
+
|
| 57 |
+
1. Replace `.` in module names with `_dot_`.
|
| 58 |
+
2. Replace `-` in module names with `_hyphen_`.
|
| 59 |
+
3. If the module name starts with a digit, prepend it with `_`.
|
| 60 |
+
4. Warn if the sanitized name is a Python reserved keyword or not a valid identifier.
|
| 61 |
+
|
| 62 |
+
If the input name is already a valid identifier, it is returned unchanged.
|
| 63 |
+
"""
|
| 64 |
+
# We not replacing `\W` characters with `_` to avoid collisions. Because `_` is a very common
|
| 65 |
+
# separator used in module names, replacing `\W` with `_` would create too many collisions.
|
| 66 |
+
# Once a module is imported, it is cached in `sys.modules` and the second import would return
|
| 67 |
+
# the first module, which might not be the expected behavior if name collisions happen.
|
| 68 |
+
new_name = name.replace(".", "_dot_").replace("-", "_hyphen_")
|
| 69 |
+
if new_name and new_name[0].isdigit():
|
| 70 |
+
new_name = f"_{new_name}"
|
| 71 |
+
if keyword.iskeyword(new_name):
|
| 72 |
+
logger.warning(
|
| 73 |
+
f"The module name {new_name} (originally {name}) is a reserved keyword in Python. "
|
| 74 |
+
"Please rename the original module to avoid import issues."
|
| 75 |
+
)
|
| 76 |
+
elif not new_name.isidentifier():
|
| 77 |
+
logger.warning(
|
| 78 |
+
f"The module name {new_name} (originally {name}) is not a valid Python identifier. "
|
| 79 |
+
"Please rename the original module to avoid import issues."
|
| 80 |
+
)
|
| 81 |
+
return new_name
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
_HF_REMOTE_CODE_LOCK = threading.Lock()
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
def init_hf_modules():
|
| 88 |
+
"""
|
| 89 |
+
Creates the cache directory for modules with an init, and adds it to the Python path.
|
| 90 |
+
"""
|
| 91 |
+
# This function has already been executed if HF_MODULES_CACHE already is in the Python path.
|
| 92 |
+
if HF_MODULES_CACHE in sys.path:
|
| 93 |
+
return
|
| 94 |
+
|
| 95 |
+
sys.path.append(HF_MODULES_CACHE)
|
| 96 |
+
os.makedirs(HF_MODULES_CACHE, exist_ok=True)
|
| 97 |
+
init_path = Path(HF_MODULES_CACHE) / "__init__.py"
|
| 98 |
+
if not init_path.exists():
|
| 99 |
+
init_path.touch()
|
| 100 |
+
importlib.invalidate_caches()
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
def create_dynamic_module(name: Union[str, os.PathLike]) -> None:
|
| 104 |
+
"""
|
| 105 |
+
Creates a dynamic module in the cache directory for modules.
|
| 106 |
+
|
| 107 |
+
Args:
|
| 108 |
+
name (`str` or `os.PathLike`):
|
| 109 |
+
The name of the dynamic module to create.
|
| 110 |
+
"""
|
| 111 |
+
init_hf_modules()
|
| 112 |
+
dynamic_module_path = (Path(HF_MODULES_CACHE) / name).resolve()
|
| 113 |
+
# If the parent module does not exist yet, recursively create it.
|
| 114 |
+
if not dynamic_module_path.parent.exists():
|
| 115 |
+
create_dynamic_module(dynamic_module_path.parent)
|
| 116 |
+
os.makedirs(dynamic_module_path, exist_ok=True)
|
| 117 |
+
init_path = dynamic_module_path / "__init__.py"
|
| 118 |
+
if not init_path.exists():
|
| 119 |
+
init_path.touch()
|
| 120 |
+
# It is extremely important to invalidate the cache when we change stuff in those modules, or users end up
|
| 121 |
+
# with errors about module that do not exist. Same for all other `invalidate_caches` in this file.
|
| 122 |
+
importlib.invalidate_caches()
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
def get_relative_imports(module_file: Union[str, os.PathLike]) -> list[str]:
|
| 126 |
+
"""
|
| 127 |
+
Get the list of modules that are relatively imported in a module file.
|
| 128 |
+
|
| 129 |
+
Args:
|
| 130 |
+
module_file (`str` or `os.PathLike`): The module file to inspect.
|
| 131 |
+
|
| 132 |
+
Returns:
|
| 133 |
+
`list[str]`: The list of relative imports in the module.
|
| 134 |
+
"""
|
| 135 |
+
with open(module_file, encoding="utf-8") as f:
|
| 136 |
+
content = f.read()
|
| 137 |
+
|
| 138 |
+
# Imports of the form `import .xxx`
|
| 139 |
+
relative_imports = re.findall(r"^\s*import\s+\.(\S+)\s*$", content, flags=re.MULTILINE)
|
| 140 |
+
# Imports of the form `from .xxx import yyy`
|
| 141 |
+
relative_imports += re.findall(r"^\s*from\s+\.(\S+)\s+import", content, flags=re.MULTILINE)
|
| 142 |
+
# Unique-ify
|
| 143 |
+
return list(set(relative_imports))
|
| 144 |
+
|
| 145 |
+
|
| 146 |
+
def get_relative_import_files(module_file: Union[str, os.PathLike]) -> list[str]:
|
| 147 |
+
"""
|
| 148 |
+
Get the list of all files that are needed for a given module. Note that this function recurses through the relative
|
| 149 |
+
imports (if a imports b and b imports c, it will return module files for b and c).
|
| 150 |
+
|
| 151 |
+
Args:
|
| 152 |
+
module_file (`str` or `os.PathLike`): The module file to inspect.
|
| 153 |
+
|
| 154 |
+
Returns:
|
| 155 |
+
`list[str]`: The list of all relative imports a given module needs (recursively), which will give us the list
|
| 156 |
+
of module files a given module needs.
|
| 157 |
+
"""
|
| 158 |
+
no_change = False
|
| 159 |
+
files_to_check = [module_file]
|
| 160 |
+
all_relative_imports = []
|
| 161 |
+
|
| 162 |
+
# Let's recurse through all relative imports
|
| 163 |
+
while not no_change:
|
| 164 |
+
new_imports = []
|
| 165 |
+
for f in files_to_check:
|
| 166 |
+
new_imports.extend(get_relative_imports(f))
|
| 167 |
+
|
| 168 |
+
module_path = Path(module_file).parent
|
| 169 |
+
new_import_files = [f"{str(module_path / m)}.py" for m in new_imports]
|
| 170 |
+
files_to_check = [f for f in new_import_files if f not in all_relative_imports]
|
| 171 |
+
|
| 172 |
+
no_change = len(files_to_check) == 0
|
| 173 |
+
all_relative_imports.extend(files_to_check)
|
| 174 |
+
|
| 175 |
+
return all_relative_imports
|
| 176 |
+
|
| 177 |
+
|
| 178 |
+
def get_imports(filename: Union[str, os.PathLike]) -> list[str]:
|
| 179 |
+
"""
|
| 180 |
+
Extracts all the libraries (not relative imports this time) that are imported in a file.
|
| 181 |
+
|
| 182 |
+
Args:
|
| 183 |
+
filename (`str` or `os.PathLike`): The module file to inspect.
|
| 184 |
+
|
| 185 |
+
Returns:
|
| 186 |
+
`list[str]`: The list of all packages required to use the input module.
|
| 187 |
+
"""
|
| 188 |
+
with open(filename, encoding="utf-8") as f:
|
| 189 |
+
content = f.read()
|
| 190 |
+
imported_modules = set()
|
| 191 |
+
|
| 192 |
+
import transformers.utils
|
| 193 |
+
|
| 194 |
+
def recursive_look_for_imports(node):
|
| 195 |
+
if isinstance(node, ast.Try):
|
| 196 |
+
return # Don't recurse into Try blocks and ignore imports in them
|
| 197 |
+
elif isinstance(node, ast.If):
|
| 198 |
+
test = node.test
|
| 199 |
+
for condition_node in ast.walk(test):
|
| 200 |
+
if isinstance(condition_node, ast.Call):
|
| 201 |
+
check_function = getattr(condition_node.func, "id", "")
|
| 202 |
+
if (
|
| 203 |
+
check_function.endswith("available")
|
| 204 |
+
and check_function.startswith("is_flash_attn")
|
| 205 |
+
or hasattr(transformers.utils.import_utils, check_function)
|
| 206 |
+
):
|
| 207 |
+
# Don't recurse into "if flash_attn_available()" or any "if library_available" blocks
|
| 208 |
+
# that appears in `transformers.utils.import_utils` and ignore imports in them
|
| 209 |
+
return
|
| 210 |
+
elif isinstance(node, ast.Import):
|
| 211 |
+
# Handle 'import x' statements
|
| 212 |
+
for alias in node.names:
|
| 213 |
+
top_module = alias.name.split(".")[0]
|
| 214 |
+
if top_module:
|
| 215 |
+
imported_modules.add(top_module)
|
| 216 |
+
elif isinstance(node, ast.ImportFrom):
|
| 217 |
+
# Handle 'from x import y' statements, ignoring relative imports
|
| 218 |
+
if node.level == 0 and node.module:
|
| 219 |
+
top_module = node.module.split(".")[0]
|
| 220 |
+
if top_module:
|
| 221 |
+
imported_modules.add(top_module)
|
| 222 |
+
|
| 223 |
+
# Recursively visit all children
|
| 224 |
+
for child in ast.iter_child_nodes(node):
|
| 225 |
+
recursive_look_for_imports(child)
|
| 226 |
+
|
| 227 |
+
tree = ast.parse(content)
|
| 228 |
+
recursive_look_for_imports(tree)
|
| 229 |
+
|
| 230 |
+
return sorted(imported_modules)
|
| 231 |
+
|
| 232 |
+
|
| 233 |
+
def check_imports(filename: Union[str, os.PathLike]) -> list[str]:
|
| 234 |
+
"""
|
| 235 |
+
Check if the current Python environment contains all the libraries that are imported in a file. Will raise if a
|
| 236 |
+
library is missing.
|
| 237 |
+
|
| 238 |
+
Args:
|
| 239 |
+
filename (`str` or `os.PathLike`): The module file to check.
|
| 240 |
+
|
| 241 |
+
Returns:
|
| 242 |
+
`list[str]`: The list of relative imports in the file.
|
| 243 |
+
"""
|
| 244 |
+
imports = get_imports(filename)
|
| 245 |
+
missing_packages = []
|
| 246 |
+
for imp in imports:
|
| 247 |
+
try:
|
| 248 |
+
importlib.import_module(imp)
|
| 249 |
+
except ImportError as exception:
|
| 250 |
+
logger.warning(f"Encountered exception while importing {imp}: {exception}")
|
| 251 |
+
# Some packages can fail with an ImportError because of a dependency issue.
|
| 252 |
+
# This check avoids hiding such errors.
|
| 253 |
+
# See https://github.com/huggingface/transformers/issues/33604
|
| 254 |
+
if "No module named" in str(exception):
|
| 255 |
+
missing_packages.append(imp)
|
| 256 |
+
else:
|
| 257 |
+
raise
|
| 258 |
+
|
| 259 |
+
if len(missing_packages) > 0:
|
| 260 |
+
raise ImportError(
|
| 261 |
+
"This modeling file requires the following packages that were not found in your environment: "
|
| 262 |
+
f"{', '.join(missing_packages)}. Run `pip install {' '.join(missing_packages)}`"
|
| 263 |
+
)
|
| 264 |
+
|
| 265 |
+
return get_relative_imports(filename)
|
| 266 |
+
|
| 267 |
+
|
| 268 |
+
def get_class_in_module(
|
| 269 |
+
class_name: str,
|
| 270 |
+
module_path: Union[str, os.PathLike],
|
| 271 |
+
*,
|
| 272 |
+
force_reload: bool = False,
|
| 273 |
+
) -> type:
|
| 274 |
+
"""
|
| 275 |
+
Import a module on the cache directory for modules and extract a class from it.
|
| 276 |
+
|
| 277 |
+
Args:
|
| 278 |
+
class_name (`str`): The name of the class to import.
|
| 279 |
+
module_path (`str` or `os.PathLike`): The path to the module to import.
|
| 280 |
+
force_reload (`bool`, *optional*, defaults to `False`):
|
| 281 |
+
Whether to reload the dynamic module from file if it already exists in `sys.modules`.
|
| 282 |
+
Otherwise, the module is only reloaded if the file has changed.
|
| 283 |
+
|
| 284 |
+
Returns:
|
| 285 |
+
`typing.Type`: The class looked for.
|
| 286 |
+
"""
|
| 287 |
+
name = os.path.normpath(module_path)
|
| 288 |
+
name = name.removesuffix(".py")
|
| 289 |
+
name = name.replace(os.path.sep, ".")
|
| 290 |
+
module_file: Path = Path(HF_MODULES_CACHE) / module_path
|
| 291 |
+
with _HF_REMOTE_CODE_LOCK:
|
| 292 |
+
if force_reload:
|
| 293 |
+
sys.modules.pop(name, None)
|
| 294 |
+
importlib.invalidate_caches()
|
| 295 |
+
cached_module: Optional[ModuleType] = sys.modules.get(name)
|
| 296 |
+
module_spec = importlib.util.spec_from_file_location(name, location=module_file)
|
| 297 |
+
|
| 298 |
+
# Hash the module file and all its relative imports to check if we need to reload it
|
| 299 |
+
module_files: list[Path] = [module_file] + sorted(map(Path, get_relative_import_files(module_file)))
|
| 300 |
+
module_hash: str = hashlib.sha256(b"".join(bytes(f) + f.read_bytes() for f in module_files)).hexdigest()
|
| 301 |
+
|
| 302 |
+
module: ModuleType
|
| 303 |
+
if cached_module is None:
|
| 304 |
+
module = importlib.util.module_from_spec(module_spec)
|
| 305 |
+
# insert it into sys.modules before any loading begins
|
| 306 |
+
sys.modules[name] = module
|
| 307 |
+
else:
|
| 308 |
+
module = cached_module
|
| 309 |
+
# reload in both cases, unless the module is already imported and the hash hits
|
| 310 |
+
if getattr(module, "__transformers_module_hash__", "") != module_hash:
|
| 311 |
+
module_spec.loader.exec_module(module)
|
| 312 |
+
module.__transformers_module_hash__ = module_hash
|
| 313 |
+
return getattr(module, class_name)
|
| 314 |
+
|
| 315 |
+
|
| 316 |
+
def get_cached_module_file(
|
| 317 |
+
pretrained_model_name_or_path: Union[str, os.PathLike],
|
| 318 |
+
module_file: str,
|
| 319 |
+
cache_dir: Optional[Union[str, os.PathLike]] = None,
|
| 320 |
+
force_download: bool = False,
|
| 321 |
+
resume_download: Optional[bool] = None,
|
| 322 |
+
proxies: Optional[dict[str, str]] = None,
|
| 323 |
+
token: Optional[Union[bool, str]] = None,
|
| 324 |
+
revision: Optional[str] = None,
|
| 325 |
+
local_files_only: bool = False,
|
| 326 |
+
repo_type: Optional[str] = None,
|
| 327 |
+
_commit_hash: Optional[str] = None,
|
| 328 |
+
**deprecated_kwargs,
|
| 329 |
+
) -> str:
|
| 330 |
+
"""
|
| 331 |
+
Prepares Downloads a module from a local folder or a distant repo and returns its path inside the cached
|
| 332 |
+
Transformers module.
|
| 333 |
+
|
| 334 |
+
Args:
|
| 335 |
+
pretrained_model_name_or_path (`str` or `os.PathLike`):
|
| 336 |
+
This can be either:
|
| 337 |
+
|
| 338 |
+
- a string, the *model id* of a pretrained model configuration hosted inside a model repo on
|
| 339 |
+
huggingface.co.
|
| 340 |
+
- a path to a *directory* containing a configuration file saved using the
|
| 341 |
+
[`~PreTrainedTokenizer.save_pretrained`] method, e.g., `./my_model_directory/`.
|
| 342 |
+
|
| 343 |
+
module_file (`str`):
|
| 344 |
+
The name of the module file containing the class to look for.
|
| 345 |
+
cache_dir (`str` or `os.PathLike`, *optional*):
|
| 346 |
+
Path to a directory in which a downloaded pretrained model configuration should be cached if the standard
|
| 347 |
+
cache should not be used.
|
| 348 |
+
force_download (`bool`, *optional*, defaults to `False`):
|
| 349 |
+
Whether or not to force to (re-)download the configuration files and override the cached versions if they
|
| 350 |
+
exist.
|
| 351 |
+
resume_download:
|
| 352 |
+
Deprecated and ignored. All downloads are now resumed by default when possible.
|
| 353 |
+
Will be removed in v5 of Transformers.
|
| 354 |
+
proxies (`dict[str, str]`, *optional*):
|
| 355 |
+
A dictionary of proxy servers to use by protocol or endpoint, e.g., `{'http': 'foo.bar:3128',
|
| 356 |
+
'http://hostname': 'foo.bar:4012'}.` The proxies are used on each request.
|
| 357 |
+
token (`str` or *bool*, *optional*):
|
| 358 |
+
The token to use as HTTP bearer authorization for remote files. If `True`, will use the token generated
|
| 359 |
+
when running `hf auth login` (stored in `~/.huggingface`).
|
| 360 |
+
revision (`str`, *optional*, defaults to `"main"`):
|
| 361 |
+
The specific model version to use. It can be a branch name, a tag name, or a commit id, since we use a
|
| 362 |
+
git-based system for storing models and other artifacts on huggingface.co, so `revision` can be any
|
| 363 |
+
identifier allowed by git.
|
| 364 |
+
local_files_only (`bool`, *optional*, defaults to `False`):
|
| 365 |
+
If `True`, will only try to load the tokenizer configuration from local files.
|
| 366 |
+
repo_type (`str`, *optional*):
|
| 367 |
+
Specify the repo type (useful when downloading from a space for instance).
|
| 368 |
+
|
| 369 |
+
<Tip>
|
| 370 |
+
|
| 371 |
+
Passing `token=True` is required when you want to use a private model.
|
| 372 |
+
|
| 373 |
+
</Tip>
|
| 374 |
+
|
| 375 |
+
Returns:
|
| 376 |
+
`str`: The path to the module inside the cache.
|
| 377 |
+
"""
|
| 378 |
+
use_auth_token = deprecated_kwargs.pop("use_auth_token", None)
|
| 379 |
+
if use_auth_token is not None:
|
| 380 |
+
warnings.warn(
|
| 381 |
+
"The `use_auth_token` argument is deprecated and will be removed in v5 of Transformers. Please use `token` instead.",
|
| 382 |
+
FutureWarning,
|
| 383 |
+
)
|
| 384 |
+
if token is not None:
|
| 385 |
+
raise ValueError("`token` and `use_auth_token` are both specified. Please set only the argument `token`.")
|
| 386 |
+
token = use_auth_token
|
| 387 |
+
|
| 388 |
+
if is_offline_mode() and not local_files_only:
|
| 389 |
+
logger.info("Offline mode: forcing local_files_only=True")
|
| 390 |
+
local_files_only = True
|
| 391 |
+
|
| 392 |
+
# Download and cache module_file from the repo `pretrained_model_name_or_path` of grab it if it's a local file.
|
| 393 |
+
pretrained_model_name_or_path = str(pretrained_model_name_or_path)
|
| 394 |
+
is_local = os.path.isdir(pretrained_model_name_or_path)
|
| 395 |
+
if is_local:
|
| 396 |
+
submodule = _sanitize_module_name(os.path.basename(pretrained_model_name_or_path))
|
| 397 |
+
else:
|
| 398 |
+
submodule = os.path.sep.join(map(_sanitize_module_name, pretrained_model_name_or_path.split("/")))
|
| 399 |
+
cached_module = try_to_load_from_cache(
|
| 400 |
+
pretrained_model_name_or_path, module_file, cache_dir=cache_dir, revision=_commit_hash, repo_type=repo_type
|
| 401 |
+
)
|
| 402 |
+
|
| 403 |
+
new_files = []
|
| 404 |
+
try:
|
| 405 |
+
# Load from URL or cache if already cached
|
| 406 |
+
resolved_module_file = cached_file(
|
| 407 |
+
pretrained_model_name_or_path,
|
| 408 |
+
module_file,
|
| 409 |
+
cache_dir=cache_dir,
|
| 410 |
+
force_download=force_download,
|
| 411 |
+
proxies=proxies,
|
| 412 |
+
resume_download=resume_download,
|
| 413 |
+
local_files_only=local_files_only,
|
| 414 |
+
token=token,
|
| 415 |
+
revision=revision,
|
| 416 |
+
repo_type=repo_type,
|
| 417 |
+
_commit_hash=_commit_hash,
|
| 418 |
+
)
|
| 419 |
+
if not is_local and cached_module != resolved_module_file:
|
| 420 |
+
new_files.append(module_file)
|
| 421 |
+
|
| 422 |
+
except OSError:
|
| 423 |
+
logger.info(f"Could not locate the {module_file} inside {pretrained_model_name_or_path}.")
|
| 424 |
+
raise
|
| 425 |
+
|
| 426 |
+
# Check we have all the requirements in our environment
|
| 427 |
+
modules_needed = check_imports(resolved_module_file)
|
| 428 |
+
|
| 429 |
+
# Now we move the module inside our cached dynamic modules.
|
| 430 |
+
full_submodule = TRANSFORMERS_DYNAMIC_MODULE_NAME + os.path.sep + submodule
|
| 431 |
+
create_dynamic_module(full_submodule)
|
| 432 |
+
submodule_path = Path(HF_MODULES_CACHE) / full_submodule
|
| 433 |
+
if submodule == _sanitize_module_name(os.path.basename(pretrained_model_name_or_path)):
|
| 434 |
+
# We copy local files to avoid putting too many folders in sys.path. This copy is done when the file is new or
|
| 435 |
+
# has changed since last copy.
|
| 436 |
+
if not (submodule_path / module_file).exists() or not filecmp.cmp(
|
| 437 |
+
resolved_module_file, str(submodule_path / module_file)
|
| 438 |
+
):
|
| 439 |
+
(submodule_path / module_file).parent.mkdir(parents=True, exist_ok=True)
|
| 440 |
+
shutil.copy(resolved_module_file, submodule_path / module_file)
|
| 441 |
+
importlib.invalidate_caches()
|
| 442 |
+
for module_needed in modules_needed:
|
| 443 |
+
module_needed = Path(module_file).parent / f"{module_needed}.py"
|
| 444 |
+
module_needed_file = os.path.join(pretrained_model_name_or_path, module_needed)
|
| 445 |
+
if not (submodule_path / module_needed).exists() or not filecmp.cmp(
|
| 446 |
+
module_needed_file, str(submodule_path / module_needed)
|
| 447 |
+
):
|
| 448 |
+
shutil.copy(module_needed_file, submodule_path / module_needed)
|
| 449 |
+
importlib.invalidate_caches()
|
| 450 |
+
else:
|
| 451 |
+
# Get the commit hash
|
| 452 |
+
commit_hash = extract_commit_hash(resolved_module_file, _commit_hash)
|
| 453 |
+
|
| 454 |
+
# The module file will end up being placed in a subfolder with the git hash of the repo. This way we get the
|
| 455 |
+
# benefit of versioning.
|
| 456 |
+
submodule_path = submodule_path / commit_hash
|
| 457 |
+
full_submodule = full_submodule + os.path.sep + commit_hash
|
| 458 |
+
full_submodule_module_file_path = os.path.join(full_submodule, module_file)
|
| 459 |
+
create_dynamic_module(Path(full_submodule_module_file_path).parent)
|
| 460 |
+
|
| 461 |
+
if not (submodule_path / module_file).exists():
|
| 462 |
+
shutil.copy(resolved_module_file, submodule_path / module_file)
|
| 463 |
+
importlib.invalidate_caches()
|
| 464 |
+
# Make sure we also have every file with relative
|
| 465 |
+
for module_needed in modules_needed:
|
| 466 |
+
if not ((submodule_path / module_file).parent / f"{module_needed}.py").exists():
|
| 467 |
+
get_cached_module_file(
|
| 468 |
+
pretrained_model_name_or_path,
|
| 469 |
+
f"{Path(module_file).parent / module_needed}.py",
|
| 470 |
+
cache_dir=cache_dir,
|
| 471 |
+
force_download=force_download,
|
| 472 |
+
resume_download=resume_download,
|
| 473 |
+
proxies=proxies,
|
| 474 |
+
token=token,
|
| 475 |
+
revision=revision,
|
| 476 |
+
local_files_only=local_files_only,
|
| 477 |
+
_commit_hash=commit_hash,
|
| 478 |
+
)
|
| 479 |
+
new_files.append(f"{module_needed}.py")
|
| 480 |
+
|
| 481 |
+
if len(new_files) > 0 and revision is None:
|
| 482 |
+
new_files = "\n".join([f"- {f}" for f in new_files])
|
| 483 |
+
repo_type_str = "" if repo_type is None else f"{repo_type}s/"
|
| 484 |
+
url = f"https://huggingface.co/{repo_type_str}{pretrained_model_name_or_path}"
|
| 485 |
+
logger.warning(
|
| 486 |
+
f"A new version of the following files was downloaded from {url}:\n{new_files}"
|
| 487 |
+
"\n. Make sure to double-check they do not contain any added malicious code. To avoid downloading new "
|
| 488 |
+
"versions of the code file, you can pin a revision."
|
| 489 |
+
)
|
| 490 |
+
|
| 491 |
+
return os.path.join(full_submodule, module_file)
|
| 492 |
+
|
| 493 |
+
|
| 494 |
+
def get_class_from_dynamic_module(
|
| 495 |
+
class_reference: str,
|
| 496 |
+
pretrained_model_name_or_path: Union[str, os.PathLike],
|
| 497 |
+
cache_dir: Optional[Union[str, os.PathLike]] = None,
|
| 498 |
+
force_download: bool = False,
|
| 499 |
+
resume_download: Optional[bool] = None,
|
| 500 |
+
proxies: Optional[dict[str, str]] = None,
|
| 501 |
+
token: Optional[Union[bool, str]] = None,
|
| 502 |
+
revision: Optional[str] = None,
|
| 503 |
+
local_files_only: bool = False,
|
| 504 |
+
repo_type: Optional[str] = None,
|
| 505 |
+
code_revision: Optional[str] = None,
|
| 506 |
+
**kwargs,
|
| 507 |
+
) -> type:
|
| 508 |
+
"""
|
| 509 |
+
Extracts a class from a module file, present in the local folder or repository of a model.
|
| 510 |
+
|
| 511 |
+
<Tip warning={true}>
|
| 512 |
+
|
| 513 |
+
Calling this function will execute the code in the module file found locally or downloaded from the Hub. It should
|
| 514 |
+
therefore only be called on trusted repos.
|
| 515 |
+
|
| 516 |
+
</Tip>
|
| 517 |
+
|
| 518 |
+
|
| 519 |
+
|
| 520 |
+
Args:
|
| 521 |
+
class_reference (`str`):
|
| 522 |
+
The full name of the class to load, including its module and optionally its repo.
|
| 523 |
+
pretrained_model_name_or_path (`str` or `os.PathLike`):
|
| 524 |
+
This can be either:
|
| 525 |
+
|
| 526 |
+
- a string, the *model id* of a pretrained model configuration hosted inside a model repo on
|
| 527 |
+
huggingface.co.
|
| 528 |
+
- a path to a *directory* containing a configuration file saved using the
|
| 529 |
+
[`~PreTrainedTokenizer.save_pretrained`] method, e.g., `./my_model_directory/`.
|
| 530 |
+
|
| 531 |
+
This is used when `class_reference` does not specify another repo.
|
| 532 |
+
module_file (`str`):
|
| 533 |
+
The name of the module file containing the class to look for.
|
| 534 |
+
class_name (`str`):
|
| 535 |
+
The name of the class to import in the module.
|
| 536 |
+
cache_dir (`str` or `os.PathLike`, *optional*):
|
| 537 |
+
Path to a directory in which a downloaded pretrained model configuration should be cached if the standard
|
| 538 |
+
cache should not be used.
|
| 539 |
+
force_download (`bool`, *optional*, defaults to `False`):
|
| 540 |
+
Whether or not to force to (re-)download the configuration files and override the cached versions if they
|
| 541 |
+
exist.
|
| 542 |
+
resume_download:
|
| 543 |
+
Deprecated and ignored. All downloads are now resumed by default when possible.
|
| 544 |
+
Will be removed in v5 of Transformers.
|
| 545 |
+
proxies (`dict[str, str]`, *optional*):
|
| 546 |
+
A dictionary of proxy servers to use by protocol or endpoint, e.g., `{'http': 'foo.bar:3128',
|
| 547 |
+
'http://hostname': 'foo.bar:4012'}.` The proxies are used on each request.
|
| 548 |
+
token (`str` or `bool`, *optional*):
|
| 549 |
+
The token to use as HTTP bearer authorization for remote files. If `True`, will use the token generated
|
| 550 |
+
when running `hf auth login` (stored in `~/.huggingface`).
|
| 551 |
+
revision (`str`, *optional*, defaults to `"main"`):
|
| 552 |
+
The specific model version to use. It can be a branch name, a tag name, or a commit id, since we use a
|
| 553 |
+
git-based system for storing models and other artifacts on huggingface.co, so `revision` can be any
|
| 554 |
+
identifier allowed by git.
|
| 555 |
+
local_files_only (`bool`, *optional*, defaults to `False`):
|
| 556 |
+
If `True`, will only try to load the tokenizer configuration from local files.
|
| 557 |
+
repo_type (`str`, *optional*):
|
| 558 |
+
Specify the repo type (useful when downloading from a space for instance).
|
| 559 |
+
code_revision (`str`, *optional*, defaults to `"main"`):
|
| 560 |
+
The specific revision to use for the code on the Hub, if the code leaves in a different repository than the
|
| 561 |
+
rest of the model. It can be a branch name, a tag name, or a commit id, since we use a git-based system for
|
| 562 |
+
storing models and other artifacts on huggingface.co, so `revision` can be any identifier allowed by git.
|
| 563 |
+
|
| 564 |
+
<Tip>
|
| 565 |
+
|
| 566 |
+
Passing `token=True` is required when you want to use a private model.
|
| 567 |
+
|
| 568 |
+
</Tip>
|
| 569 |
+
|
| 570 |
+
Returns:
|
| 571 |
+
`typing.Type`: The class, dynamically imported from the module.
|
| 572 |
+
|
| 573 |
+
Examples:
|
| 574 |
+
|
| 575 |
+
```python
|
| 576 |
+
# Download module `modeling.py` from huggingface.co and cache then extract the class `MyBertModel` from this
|
| 577 |
+
# module.
|
| 578 |
+
cls = get_class_from_dynamic_module("modeling.MyBertModel", "sgugger/my-bert-model")
|
| 579 |
+
|
| 580 |
+
# Download module `modeling.py` from a given repo and cache then extract the class `MyBertModel` from this
|
| 581 |
+
# module.
|
| 582 |
+
cls = get_class_from_dynamic_module("sgugger/my-bert-model--modeling.MyBertModel", "sgugger/another-bert-model")
|
| 583 |
+
```"""
|
| 584 |
+
use_auth_token = kwargs.pop("use_auth_token", None)
|
| 585 |
+
if use_auth_token is not None:
|
| 586 |
+
warnings.warn(
|
| 587 |
+
"The `use_auth_token` argument is deprecated and will be removed in v5 of Transformers. Please use `token` instead.",
|
| 588 |
+
FutureWarning,
|
| 589 |
+
)
|
| 590 |
+
if token is not None:
|
| 591 |
+
raise ValueError("`token` and `use_auth_token` are both specified. Please set only the argument `token`.")
|
| 592 |
+
token = use_auth_token
|
| 593 |
+
|
| 594 |
+
# Catch the name of the repo if it's specified in `class_reference`
|
| 595 |
+
if "--" in class_reference:
|
| 596 |
+
repo_id, class_reference = class_reference.split("--")
|
| 597 |
+
else:
|
| 598 |
+
repo_id = pretrained_model_name_or_path
|
| 599 |
+
module_file, class_name = class_reference.split(".")
|
| 600 |
+
|
| 601 |
+
if code_revision is None and pretrained_model_name_or_path == repo_id:
|
| 602 |
+
code_revision = revision
|
| 603 |
+
# And lastly we get the class inside our newly created module
|
| 604 |
+
final_module = get_cached_module_file(
|
| 605 |
+
repo_id,
|
| 606 |
+
module_file + ".py",
|
| 607 |
+
cache_dir=cache_dir,
|
| 608 |
+
force_download=force_download,
|
| 609 |
+
resume_download=resume_download,
|
| 610 |
+
proxies=proxies,
|
| 611 |
+
token=token,
|
| 612 |
+
revision=code_revision,
|
| 613 |
+
local_files_only=local_files_only,
|
| 614 |
+
repo_type=repo_type,
|
| 615 |
+
)
|
| 616 |
+
return get_class_in_module(class_name, final_module, force_reload=force_download)
|
| 617 |
+
|
| 618 |
+
|
| 619 |
+
def custom_object_save(obj: Any, folder: Union[str, os.PathLike], config: Optional[dict] = None) -> list[str]:
|
| 620 |
+
"""
|
| 621 |
+
Save the modeling files corresponding to a custom model/configuration/tokenizer etc. in a given folder. Optionally
|
| 622 |
+
adds the proper fields in a config.
|
| 623 |
+
|
| 624 |
+
Args:
|
| 625 |
+
obj (`Any`): The object for which to save the module files.
|
| 626 |
+
folder (`str` or `os.PathLike`): The folder where to save.
|
| 627 |
+
config (`PretrainedConfig` or dictionary, `optional`):
|
| 628 |
+
A config in which to register the auto_map corresponding to this custom object.
|
| 629 |
+
|
| 630 |
+
Returns:
|
| 631 |
+
`list[str]`: The list of files saved.
|
| 632 |
+
"""
|
| 633 |
+
if obj.__module__ == "__main__":
|
| 634 |
+
logger.warning(
|
| 635 |
+
f"We can't save the code defining {obj} in {folder} as it's been defined in __main__. You should put "
|
| 636 |
+
"this code in a separate module so we can include it in the saved folder and make it easier to share via "
|
| 637 |
+
"the Hub."
|
| 638 |
+
)
|
| 639 |
+
return
|
| 640 |
+
|
| 641 |
+
def _set_auto_map_in_config(_config):
|
| 642 |
+
module_name = obj.__class__.__module__
|
| 643 |
+
last_module = module_name.split(".")[-1]
|
| 644 |
+
full_name = f"{last_module}.{obj.__class__.__name__}"
|
| 645 |
+
# Special handling for tokenizers
|
| 646 |
+
if "Tokenizer" in full_name:
|
| 647 |
+
slow_tokenizer_class = None
|
| 648 |
+
fast_tokenizer_class = None
|
| 649 |
+
if obj.__class__.__name__.endswith("Fast"):
|
| 650 |
+
# Fast tokenizer: we have the fast tokenizer class and we may have the slow one has an attribute.
|
| 651 |
+
fast_tokenizer_class = f"{last_module}.{obj.__class__.__name__}"
|
| 652 |
+
if getattr(obj, "slow_tokenizer_class", None) is not None:
|
| 653 |
+
slow_tokenizer = getattr(obj, "slow_tokenizer_class")
|
| 654 |
+
slow_tok_module_name = slow_tokenizer.__module__
|
| 655 |
+
last_slow_tok_module = slow_tok_module_name.split(".")[-1]
|
| 656 |
+
slow_tokenizer_class = f"{last_slow_tok_module}.{slow_tokenizer.__name__}"
|
| 657 |
+
else:
|
| 658 |
+
# Slow tokenizer: no way to have the fast class
|
| 659 |
+
slow_tokenizer_class = f"{last_module}.{obj.__class__.__name__}"
|
| 660 |
+
|
| 661 |
+
full_name = (slow_tokenizer_class, fast_tokenizer_class)
|
| 662 |
+
|
| 663 |
+
if isinstance(_config, dict):
|
| 664 |
+
auto_map = _config.get("auto_map", {})
|
| 665 |
+
auto_map[obj._auto_class] = full_name
|
| 666 |
+
_config["auto_map"] = auto_map
|
| 667 |
+
elif getattr(_config, "auto_map", None) is not None:
|
| 668 |
+
_config.auto_map[obj._auto_class] = full_name
|
| 669 |
+
else:
|
| 670 |
+
_config.auto_map = {obj._auto_class: full_name}
|
| 671 |
+
|
| 672 |
+
# Add object class to the config auto_map
|
| 673 |
+
if isinstance(config, (list, tuple)):
|
| 674 |
+
for cfg in config:
|
| 675 |
+
_set_auto_map_in_config(cfg)
|
| 676 |
+
elif config is not None:
|
| 677 |
+
_set_auto_map_in_config(config)
|
| 678 |
+
|
| 679 |
+
result = []
|
| 680 |
+
# Copy module file to the output folder.
|
| 681 |
+
object_file = sys.modules[obj.__module__].__file__
|
| 682 |
+
dest_file = Path(folder) / (Path(object_file).name)
|
| 683 |
+
shutil.copy(object_file, dest_file)
|
| 684 |
+
result.append(dest_file)
|
| 685 |
+
|
| 686 |
+
# Gather all relative imports recursively and make sure they are copied as well.
|
| 687 |
+
for needed_file in get_relative_import_files(object_file):
|
| 688 |
+
dest_file = Path(folder) / (Path(needed_file).name)
|
| 689 |
+
shutil.copy(needed_file, dest_file)
|
| 690 |
+
result.append(dest_file)
|
| 691 |
+
|
| 692 |
+
return result
|
| 693 |
+
|
| 694 |
+
|
| 695 |
+
def _raise_timeout_error(signum, frame):
|
| 696 |
+
raise ValueError(
|
| 697 |
+
"Loading this model requires you to execute custom code contained in the model repository on your local "
|
| 698 |
+
"machine. Please set the option `trust_remote_code=True` to permit loading of this model."
|
| 699 |
+
)
|
| 700 |
+
|
| 701 |
+
|
| 702 |
+
TIME_OUT_REMOTE_CODE = 15
|
| 703 |
+
|
| 704 |
+
|
| 705 |
+
def resolve_trust_remote_code(
|
| 706 |
+
trust_remote_code, model_name, has_local_code, has_remote_code, error_message=None, upstream_repo=None
|
| 707 |
+
):
|
| 708 |
+
"""
|
| 709 |
+
Resolves the `trust_remote_code` argument. If there is remote code to be loaded, the user must opt-in to loading
|
| 710 |
+
it.
|
| 711 |
+
|
| 712 |
+
Args:
|
| 713 |
+
trust_remote_code (`bool` or `None`):
|
| 714 |
+
User-defined `trust_remote_code` value.
|
| 715 |
+
model_name (`str`):
|
| 716 |
+
The name of the model repository in huggingface.co.
|
| 717 |
+
has_local_code (`bool`):
|
| 718 |
+
Whether the model has local code.
|
| 719 |
+
has_remote_code (`bool`):
|
| 720 |
+
Whether the model has remote code.
|
| 721 |
+
error_message (`str`, *optional*):
|
| 722 |
+
Custom error message to display if there is remote code to load and the user didn't opt-in. If unset, the error
|
| 723 |
+
message will be regarding loading a model with custom code.
|
| 724 |
+
|
| 725 |
+
Returns:
|
| 726 |
+
The resolved `trust_remote_code` value.
|
| 727 |
+
"""
|
| 728 |
+
if error_message is None:
|
| 729 |
+
if upstream_repo is not None:
|
| 730 |
+
error_message = (
|
| 731 |
+
f"The repository {model_name} references custom code contained in {upstream_repo} which "
|
| 732 |
+
f"must be executed to correctly load the model. You can inspect the repository "
|
| 733 |
+
f"content at https://hf.co/{upstream_repo} .\n"
|
| 734 |
+
)
|
| 735 |
+
elif os.path.isdir(model_name):
|
| 736 |
+
error_message = (
|
| 737 |
+
f"The repository {model_name} contains custom code which must be executed "
|
| 738 |
+
f"to correctly load the model. You can inspect the repository "
|
| 739 |
+
f"content at {os.path.abspath(model_name)} .\n"
|
| 740 |
+
)
|
| 741 |
+
else:
|
| 742 |
+
error_message = (
|
| 743 |
+
f"The repository {model_name} contains custom code which must be executed "
|
| 744 |
+
f"to correctly load the model. You can inspect the repository "
|
| 745 |
+
f"content at https://hf.co/{model_name} .\n"
|
| 746 |
+
)
|
| 747 |
+
|
| 748 |
+
if trust_remote_code is None:
|
| 749 |
+
if has_local_code:
|
| 750 |
+
trust_remote_code = False
|
| 751 |
+
elif has_remote_code and TIME_OUT_REMOTE_CODE > 0:
|
| 752 |
+
prev_sig_handler = None
|
| 753 |
+
try:
|
| 754 |
+
prev_sig_handler = signal.signal(signal.SIGALRM, _raise_timeout_error)
|
| 755 |
+
signal.alarm(TIME_OUT_REMOTE_CODE)
|
| 756 |
+
while trust_remote_code is None:
|
| 757 |
+
answer = input(
|
| 758 |
+
f"{error_message} You can inspect the repository content at https://hf.co/{model_name}.\n"
|
| 759 |
+
f"You can avoid this prompt in future by passing the argument `trust_remote_code=True`.\n\n"
|
| 760 |
+
f"Do you wish to run the custom code? [y/N] "
|
| 761 |
+
)
|
| 762 |
+
if answer.lower() in ["yes", "y", "1"]:
|
| 763 |
+
trust_remote_code = True
|
| 764 |
+
elif answer.lower() in ["no", "n", "0", ""]:
|
| 765 |
+
trust_remote_code = False
|
| 766 |
+
signal.alarm(0)
|
| 767 |
+
except Exception:
|
| 768 |
+
# OS which does not support signal.SIGALRM
|
| 769 |
+
raise ValueError(
|
| 770 |
+
f"{error_message} You can inspect the repository content at https://hf.co/{model_name}.\n"
|
| 771 |
+
f"Please pass the argument `trust_remote_code=True` to allow custom code to be run."
|
| 772 |
+
)
|
| 773 |
+
finally:
|
| 774 |
+
if prev_sig_handler is not None:
|
| 775 |
+
signal.signal(signal.SIGALRM, prev_sig_handler)
|
| 776 |
+
signal.alarm(0)
|
| 777 |
+
elif has_remote_code:
|
| 778 |
+
# For the CI which puts the timeout at 0
|
| 779 |
+
_raise_timeout_error(None, None)
|
| 780 |
+
|
| 781 |
+
if has_remote_code and not has_local_code and not trust_remote_code:
|
| 782 |
+
raise ValueError(
|
| 783 |
+
f"{error_message} You can inspect the repository content at https://hf.co/{model_name}.\n"
|
| 784 |
+
f"Please pass the argument `trust_remote_code=True` to allow custom code to be run."
|
| 785 |
+
)
|
| 786 |
+
|
| 787 |
+
return trust_remote_code
|
| 788 |
+
|
| 789 |
+
|
| 790 |
+
def check_python_requirements(path_or_repo_id, requirements_file="requirements.txt", **kwargs):
|
| 791 |
+
"""
|
| 792 |
+
Tries to locate `requirements_file` in a local folder or repo, and confirms that the environment has all the
|
| 793 |
+
python dependencies installed.
|
| 794 |
+
|
| 795 |
+
Args:
|
| 796 |
+
path_or_repo_id (`str` or `os.PathLike`):
|
| 797 |
+
This can be either:
|
| 798 |
+
- a string, the *model id* of a model repo on huggingface.co.
|
| 799 |
+
- a path to a *directory* potentially containing the file.
|
| 800 |
+
kwargs (`dict[str, Any]`, *optional*):
|
| 801 |
+
Additional arguments to pass to `cached_file`.
|
| 802 |
+
"""
|
| 803 |
+
failed = [] # error messages regarding requirements
|
| 804 |
+
try:
|
| 805 |
+
requirements = cached_file(path_or_repo_id=path_or_repo_id, filename=requirements_file, **kwargs)
|
| 806 |
+
with open(requirements, "r") as f:
|
| 807 |
+
requirements = f.readlines()
|
| 808 |
+
|
| 809 |
+
for requirement in requirements:
|
| 810 |
+
requirement = requirement.strip()
|
| 811 |
+
if not requirement or requirement.startswith("#"): # skip empty lines and comments
|
| 812 |
+
continue
|
| 813 |
+
|
| 814 |
+
try:
|
| 815 |
+
# e.g. "torch>2.6.0" -> "torch", ">", "2.6.0"
|
| 816 |
+
package_name, delimiter, version_number = split_package_version(requirement)
|
| 817 |
+
except ValueError: # e.g. "torch", as opposed to "torch>2.6.0"
|
| 818 |
+
package_name = requirement
|
| 819 |
+
delimiter, version_number = None, None
|
| 820 |
+
|
| 821 |
+
try:
|
| 822 |
+
local_package_version = importlib.metadata.version(package_name)
|
| 823 |
+
except importlib.metadata.PackageNotFoundError:
|
| 824 |
+
failed.append(f"{requirement} (installed: None)")
|
| 825 |
+
continue
|
| 826 |
+
|
| 827 |
+
if delimiter is not None and version_number is not None:
|
| 828 |
+
is_satisfied = VersionComparison.from_string(delimiter)(
|
| 829 |
+
version.parse(local_package_version), version.parse(version_number)
|
| 830 |
+
)
|
| 831 |
+
else:
|
| 832 |
+
is_satisfied = True
|
| 833 |
+
|
| 834 |
+
if not is_satisfied:
|
| 835 |
+
failed.append(f"{requirement} (installed: {local_package_version})")
|
| 836 |
+
|
| 837 |
+
except OSError: # no requirements.txt
|
| 838 |
+
pass
|
| 839 |
+
|
| 840 |
+
if failed:
|
| 841 |
+
raise ImportError(
|
| 842 |
+
f"Missing requirements in your local environment for `{path_or_repo_id}`:\n" + "\n".join(failed)
|
| 843 |
+
)
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/feature_extraction_sequence_utils.py
ADDED
|
@@ -0,0 +1,371 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2021 The HuggingFace Inc. team.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""
|
| 15 |
+
Sequence feature extraction class for common feature extractors to preprocess sequences.
|
| 16 |
+
"""
|
| 17 |
+
|
| 18 |
+
from typing import Optional, Union
|
| 19 |
+
|
| 20 |
+
import numpy as np
|
| 21 |
+
|
| 22 |
+
from .feature_extraction_utils import BatchFeature, FeatureExtractionMixin
|
| 23 |
+
from .utils import PaddingStrategy, TensorType, is_tf_tensor, is_torch_tensor, logging, to_numpy
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
logger = logging.get_logger(__name__)
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
class SequenceFeatureExtractor(FeatureExtractionMixin):
|
| 30 |
+
"""
|
| 31 |
+
This is a general feature extraction class for speech recognition.
|
| 32 |
+
|
| 33 |
+
Args:
|
| 34 |
+
feature_size (`int`):
|
| 35 |
+
The feature dimension of the extracted features.
|
| 36 |
+
sampling_rate (`int`):
|
| 37 |
+
The sampling rate at which the audio files should be digitalized expressed in hertz (Hz).
|
| 38 |
+
padding_value (`float`):
|
| 39 |
+
The value that is used to fill the padding values / vectors.
|
| 40 |
+
"""
|
| 41 |
+
|
| 42 |
+
def __init__(self, feature_size: int, sampling_rate: int, padding_value: float, **kwargs):
|
| 43 |
+
self.feature_size = feature_size
|
| 44 |
+
self.sampling_rate = sampling_rate
|
| 45 |
+
self.padding_value = padding_value
|
| 46 |
+
|
| 47 |
+
self.padding_side = kwargs.pop("padding_side", "right")
|
| 48 |
+
self.return_attention_mask = kwargs.pop("return_attention_mask", True)
|
| 49 |
+
|
| 50 |
+
super().__init__(**kwargs)
|
| 51 |
+
|
| 52 |
+
def pad(
|
| 53 |
+
self,
|
| 54 |
+
processed_features: Union[
|
| 55 |
+
BatchFeature,
|
| 56 |
+
list[BatchFeature],
|
| 57 |
+
dict[str, BatchFeature],
|
| 58 |
+
dict[str, list[BatchFeature]],
|
| 59 |
+
list[dict[str, BatchFeature]],
|
| 60 |
+
],
|
| 61 |
+
padding: Union[bool, str, PaddingStrategy] = True,
|
| 62 |
+
max_length: Optional[int] = None,
|
| 63 |
+
truncation: bool = False,
|
| 64 |
+
pad_to_multiple_of: Optional[int] = None,
|
| 65 |
+
return_attention_mask: Optional[bool] = None,
|
| 66 |
+
return_tensors: Optional[Union[str, TensorType]] = None,
|
| 67 |
+
) -> BatchFeature:
|
| 68 |
+
"""
|
| 69 |
+
Pad input values / input vectors or a batch of input values / input vectors up to predefined length or to the
|
| 70 |
+
max sequence length in the batch.
|
| 71 |
+
|
| 72 |
+
Padding side (left/right) padding values are defined at the feature extractor level (with `self.padding_side`,
|
| 73 |
+
`self.padding_value`)
|
| 74 |
+
|
| 75 |
+
<Tip>
|
| 76 |
+
|
| 77 |
+
If the `processed_features` passed are dictionary of numpy arrays, PyTorch tensors or TensorFlow tensors, the
|
| 78 |
+
result will use the same type unless you provide a different tensor type with `return_tensors`. In the case of
|
| 79 |
+
PyTorch tensors, you will lose the specific device of your tensors however.
|
| 80 |
+
|
| 81 |
+
</Tip>
|
| 82 |
+
|
| 83 |
+
Args:
|
| 84 |
+
processed_features ([`BatchFeature`], list of [`BatchFeature`], `dict[str, list[float]]`, `dict[str, list[list[float]]` or `list[dict[str, list[float]]]`):
|
| 85 |
+
Processed inputs. Can represent one input ([`BatchFeature`] or `dict[str, list[float]]`) or a batch of
|
| 86 |
+
input values / vectors (list of [`BatchFeature`], *dict[str, list[list[float]]]* or *list[dict[str,
|
| 87 |
+
list[float]]]*) so you can use this method during preprocessing as well as in a PyTorch Dataloader
|
| 88 |
+
collate function.
|
| 89 |
+
|
| 90 |
+
Instead of `list[float]` you can have tensors (numpy arrays, PyTorch tensors or TensorFlow tensors),
|
| 91 |
+
see the note above for the return type.
|
| 92 |
+
padding (`bool`, `str` or [`~utils.PaddingStrategy`], *optional*, defaults to `True`):
|
| 93 |
+
Select a strategy to pad the returned sequences (according to the model's padding side and padding
|
| 94 |
+
index) among:
|
| 95 |
+
|
| 96 |
+
- `True` or `'longest'`: Pad to the longest sequence in the batch (or no padding if only a single
|
| 97 |
+
sequence if provided).
|
| 98 |
+
- `'max_length'`: Pad to a maximum length specified with the argument `max_length` or to the maximum
|
| 99 |
+
acceptable input length for the model if that argument is not provided.
|
| 100 |
+
- `False` or `'do_not_pad'` (default): No padding (i.e., can output a batch with sequences of different
|
| 101 |
+
lengths).
|
| 102 |
+
max_length (`int`, *optional*):
|
| 103 |
+
Maximum length of the returned list and optionally padding length (see above).
|
| 104 |
+
truncation (`bool`):
|
| 105 |
+
Activates truncation to cut input sequences longer than `max_length` to `max_length`.
|
| 106 |
+
pad_to_multiple_of (`int`, *optional*):
|
| 107 |
+
If set will pad the sequence to a multiple of the provided value.
|
| 108 |
+
|
| 109 |
+
This is especially useful to enable the use of Tensor Cores on NVIDIA hardware with compute capability
|
| 110 |
+
`>= 7.5` (Volta), or on TPUs which benefit from having sequence lengths be a multiple of 128.
|
| 111 |
+
return_attention_mask (`bool`, *optional*):
|
| 112 |
+
Whether to return the attention mask. If left to the default, will return the attention mask according
|
| 113 |
+
to the specific feature_extractor's default.
|
| 114 |
+
|
| 115 |
+
[What are attention masks?](../glossary#attention-mask)
|
| 116 |
+
return_tensors (`str` or [`~utils.TensorType`], *optional*):
|
| 117 |
+
If set, will return tensors instead of list of python integers. Acceptable values are:
|
| 118 |
+
|
| 119 |
+
- `'tf'`: Return TensorFlow `tf.constant` objects.
|
| 120 |
+
- `'pt'`: Return PyTorch `torch.Tensor` objects.
|
| 121 |
+
- `'np'`: Return Numpy `np.ndarray` objects.
|
| 122 |
+
"""
|
| 123 |
+
# If we have a list of dicts, let's convert it in a dict of lists
|
| 124 |
+
# We do this to allow using this method as a collate_fn function in PyTorch Dataloader
|
| 125 |
+
if isinstance(processed_features, (list, tuple)) and isinstance(processed_features[0], (dict, BatchFeature)):
|
| 126 |
+
processed_features = {
|
| 127 |
+
key: [example[key] for example in processed_features] for key in processed_features[0]
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
# The model's main input name, usually `input_values`, has be passed for padding
|
| 131 |
+
if self.model_input_names[0] not in processed_features:
|
| 132 |
+
raise ValueError(
|
| 133 |
+
"You should supply an instance of `transformers.BatchFeature` or list of `transformers.BatchFeature`"
|
| 134 |
+
f" to this method that includes {self.model_input_names[0]}, but you provided"
|
| 135 |
+
f" {list(processed_features.keys())}"
|
| 136 |
+
)
|
| 137 |
+
|
| 138 |
+
required_input = processed_features[self.model_input_names[0]]
|
| 139 |
+
return_attention_mask = (
|
| 140 |
+
return_attention_mask if return_attention_mask is not None else self.return_attention_mask
|
| 141 |
+
)
|
| 142 |
+
|
| 143 |
+
if len(required_input) == 0:
|
| 144 |
+
if return_attention_mask:
|
| 145 |
+
processed_features["attention_mask"] = []
|
| 146 |
+
return processed_features
|
| 147 |
+
|
| 148 |
+
# If we have PyTorch/TF tensors or lists as inputs, we cast them as Numpy arrays
|
| 149 |
+
# and rebuild them afterwards if no return_tensors is specified
|
| 150 |
+
# Note that we lose the specific device the tensor may be on for PyTorch
|
| 151 |
+
|
| 152 |
+
first_element = required_input[0]
|
| 153 |
+
if isinstance(first_element, (list, tuple)):
|
| 154 |
+
# first_element might be an empty list/tuple in some edge cases so we grab the first non empty element.
|
| 155 |
+
index = 0
|
| 156 |
+
while len(required_input[index]) == 0:
|
| 157 |
+
index += 1
|
| 158 |
+
if index < len(required_input):
|
| 159 |
+
first_element = required_input[index][0]
|
| 160 |
+
|
| 161 |
+
if return_tensors is None:
|
| 162 |
+
if is_tf_tensor(first_element):
|
| 163 |
+
return_tensors = "tf"
|
| 164 |
+
elif is_torch_tensor(first_element):
|
| 165 |
+
return_tensors = "pt"
|
| 166 |
+
elif isinstance(first_element, (int, float, list, tuple, np.ndarray)):
|
| 167 |
+
return_tensors = "np"
|
| 168 |
+
else:
|
| 169 |
+
raise ValueError(
|
| 170 |
+
f"type of {first_element} unknown: {type(first_element)}. "
|
| 171 |
+
"Should be one of a python, numpy, pytorch or tensorflow object."
|
| 172 |
+
)
|
| 173 |
+
|
| 174 |
+
for key, value in processed_features.items():
|
| 175 |
+
if isinstance(value[0], (int, float)):
|
| 176 |
+
processed_features[key] = to_numpy(value)
|
| 177 |
+
else:
|
| 178 |
+
processed_features[key] = [to_numpy(v) for v in value]
|
| 179 |
+
|
| 180 |
+
# Convert padding_strategy in PaddingStrategy
|
| 181 |
+
padding_strategy = self._get_padding_strategies(padding=padding, max_length=max_length)
|
| 182 |
+
|
| 183 |
+
required_input = processed_features[self.model_input_names[0]]
|
| 184 |
+
|
| 185 |
+
batch_size = len(required_input)
|
| 186 |
+
if not all(len(v) == batch_size for v in processed_features.values()):
|
| 187 |
+
raise ValueError("Some items in the output dictionary have a different batch size than others.")
|
| 188 |
+
|
| 189 |
+
truncated_inputs = []
|
| 190 |
+
for i in range(batch_size):
|
| 191 |
+
inputs = {k: v[i] for k, v in processed_features.items()}
|
| 192 |
+
# truncation
|
| 193 |
+
inputs_slice = self._truncate(
|
| 194 |
+
inputs,
|
| 195 |
+
max_length=max_length,
|
| 196 |
+
pad_to_multiple_of=pad_to_multiple_of,
|
| 197 |
+
truncation=truncation,
|
| 198 |
+
)
|
| 199 |
+
truncated_inputs.append(inputs_slice)
|
| 200 |
+
|
| 201 |
+
if padding_strategy == PaddingStrategy.LONGEST:
|
| 202 |
+
# make sure that `max_length` cannot be longer than the longest truncated length
|
| 203 |
+
max_length = max(len(input_slice[self.model_input_names[0]]) for input_slice in truncated_inputs)
|
| 204 |
+
padding_strategy = PaddingStrategy.MAX_LENGTH
|
| 205 |
+
|
| 206 |
+
batch_outputs = {}
|
| 207 |
+
for i in range(batch_size):
|
| 208 |
+
# padding
|
| 209 |
+
outputs = self._pad(
|
| 210 |
+
truncated_inputs[i],
|
| 211 |
+
max_length=max_length,
|
| 212 |
+
padding_strategy=padding_strategy,
|
| 213 |
+
pad_to_multiple_of=pad_to_multiple_of,
|
| 214 |
+
return_attention_mask=return_attention_mask,
|
| 215 |
+
)
|
| 216 |
+
|
| 217 |
+
for key, value in outputs.items():
|
| 218 |
+
if key not in batch_outputs:
|
| 219 |
+
batch_outputs[key] = []
|
| 220 |
+
if value.dtype is np.dtype(np.float64):
|
| 221 |
+
value = value.astype(np.float32)
|
| 222 |
+
batch_outputs[key].append(value)
|
| 223 |
+
|
| 224 |
+
return BatchFeature(batch_outputs, tensor_type=return_tensors)
|
| 225 |
+
|
| 226 |
+
def _pad(
|
| 227 |
+
self,
|
| 228 |
+
processed_features: Union[dict[str, np.ndarray], BatchFeature],
|
| 229 |
+
max_length: Optional[int] = None,
|
| 230 |
+
padding_strategy: PaddingStrategy = PaddingStrategy.DO_NOT_PAD,
|
| 231 |
+
pad_to_multiple_of: Optional[int] = None,
|
| 232 |
+
return_attention_mask: Optional[bool] = None,
|
| 233 |
+
) -> dict:
|
| 234 |
+
"""
|
| 235 |
+
Pad inputs (on left/right and up to predefined length or max length in the batch)
|
| 236 |
+
|
| 237 |
+
Args:
|
| 238 |
+
processed_features (`Union[dict[str, np.ndarray], BatchFeature]`):
|
| 239 |
+
Dictionary of input values (`np.ndarray[float]`) / input vectors (`list[np.ndarray[float]]`) or batch
|
| 240 |
+
of inputs values (`list[np.ndarray[int]]`) / input vectors (`list[np.ndarray[int]]`)
|
| 241 |
+
max_length (`int`, *optional*):
|
| 242 |
+
Maximum length of the returned list and optionally padding length (see below)
|
| 243 |
+
padding_strategy (`PaddingStrategy`, *optional*, default to `PaddingStrategy.DO_NOT_PAD`):
|
| 244 |
+
PaddingStrategy to use for padding.
|
| 245 |
+
|
| 246 |
+
- PaddingStrategy.LONGEST Pad to the longest sequence in the batch
|
| 247 |
+
- PaddingStrategy.MAX_LENGTH: Pad to the max length (default)
|
| 248 |
+
- PaddingStrategy.DO_NOT_PAD: Do not pad
|
| 249 |
+
The feature_extractor padding sides are defined in self.padding_side:
|
| 250 |
+
|
| 251 |
+
- 'left': pads on the left of the sequences
|
| 252 |
+
- 'right': pads on the right of the sequences
|
| 253 |
+
pad_to_multiple_of (`int`, *optional*):
|
| 254 |
+
Integer if set will pad the sequence to a multiple of the provided value. This is especially useful to
|
| 255 |
+
enable the use of Tensor Core on NVIDIA hardware with compute capability `>= 7.5` (Volta), or on TPUs
|
| 256 |
+
which benefit from having sequence lengths be a multiple of 128.
|
| 257 |
+
return_attention_mask (`bool`, *optional*):
|
| 258 |
+
Set to False to avoid returning attention mask (default: set to model specifics)
|
| 259 |
+
"""
|
| 260 |
+
required_input = processed_features[self.model_input_names[0]]
|
| 261 |
+
|
| 262 |
+
if padding_strategy == PaddingStrategy.LONGEST:
|
| 263 |
+
max_length = len(required_input)
|
| 264 |
+
|
| 265 |
+
if max_length is not None and pad_to_multiple_of is not None and (max_length % pad_to_multiple_of != 0):
|
| 266 |
+
max_length = ((max_length // pad_to_multiple_of) + 1) * pad_to_multiple_of
|
| 267 |
+
|
| 268 |
+
needs_to_be_padded = padding_strategy != PaddingStrategy.DO_NOT_PAD and len(required_input) < max_length
|
| 269 |
+
|
| 270 |
+
if return_attention_mask and "attention_mask" not in processed_features:
|
| 271 |
+
processed_features["attention_mask"] = np.ones(len(required_input), dtype=np.int32)
|
| 272 |
+
|
| 273 |
+
if needs_to_be_padded:
|
| 274 |
+
difference = max_length - len(required_input)
|
| 275 |
+
if self.padding_side == "right":
|
| 276 |
+
if return_attention_mask:
|
| 277 |
+
processed_features["attention_mask"] = np.pad(
|
| 278 |
+
processed_features["attention_mask"], (0, difference)
|
| 279 |
+
)
|
| 280 |
+
padding_shape = ((0, difference), (0, 0)) if self.feature_size > 1 else (0, difference)
|
| 281 |
+
processed_features[self.model_input_names[0]] = np.pad(
|
| 282 |
+
required_input, padding_shape, "constant", constant_values=self.padding_value
|
| 283 |
+
)
|
| 284 |
+
elif self.padding_side == "left":
|
| 285 |
+
if return_attention_mask:
|
| 286 |
+
processed_features["attention_mask"] = np.pad(
|
| 287 |
+
processed_features["attention_mask"], (difference, 0)
|
| 288 |
+
)
|
| 289 |
+
padding_shape = ((difference, 0), (0, 0)) if self.feature_size > 1 else (difference, 0)
|
| 290 |
+
processed_features[self.model_input_names[0]] = np.pad(
|
| 291 |
+
required_input, padding_shape, "constant", constant_values=self.padding_value
|
| 292 |
+
)
|
| 293 |
+
else:
|
| 294 |
+
raise ValueError("Invalid padding strategy:" + str(self.padding_side))
|
| 295 |
+
|
| 296 |
+
return processed_features
|
| 297 |
+
|
| 298 |
+
def _truncate(
|
| 299 |
+
self,
|
| 300 |
+
processed_features: Union[dict[str, np.ndarray], BatchFeature],
|
| 301 |
+
max_length: Optional[int] = None,
|
| 302 |
+
pad_to_multiple_of: Optional[int] = None,
|
| 303 |
+
truncation: Optional[bool] = None,
|
| 304 |
+
):
|
| 305 |
+
"""
|
| 306 |
+
Truncate inputs to predefined length or max length in the batch
|
| 307 |
+
|
| 308 |
+
Args:
|
| 309 |
+
processed_features(`Union[dict[str, np.ndarray], BatchFeature]`):
|
| 310 |
+
Dictionary of input values (`np.ndarray[float]`) / input vectors (`list[np.ndarray[float]]`) or batch
|
| 311 |
+
of inputs values (`list[np.ndarray[int]]`) / input vectors (`list[np.ndarray[int]]`)
|
| 312 |
+
max_length (`int`, *optional*):
|
| 313 |
+
maximum length of the returned list and optionally padding length (see below)
|
| 314 |
+
pad_to_multiple_of (`int`, *optional*) :
|
| 315 |
+
Integer if set will pad the sequence to a multiple of the provided value. This is especially useful to
|
| 316 |
+
enable the use of Tensor Core on NVIDIA hardware with compute capability `>= 7.5` (Volta), or on TPUs
|
| 317 |
+
which benefit from having sequence lengths be a multiple of 128.
|
| 318 |
+
truncation (`bool`, *optional*):
|
| 319 |
+
Activates truncation to cut input sequences longer than `max_length` to `max_length`.
|
| 320 |
+
"""
|
| 321 |
+
if not truncation:
|
| 322 |
+
return processed_features
|
| 323 |
+
elif truncation and max_length is None:
|
| 324 |
+
raise ValueError("When setting ``truncation=True``, make sure that ``max_length`` is defined.")
|
| 325 |
+
|
| 326 |
+
required_input = processed_features[self.model_input_names[0]]
|
| 327 |
+
|
| 328 |
+
# find `max_length` that fits `pad_to_multiple_of`
|
| 329 |
+
if max_length is not None and pad_to_multiple_of is not None and (max_length % pad_to_multiple_of != 0):
|
| 330 |
+
max_length = ((max_length // pad_to_multiple_of) + 1) * pad_to_multiple_of
|
| 331 |
+
|
| 332 |
+
needs_to_be_truncated = len(required_input) > max_length
|
| 333 |
+
|
| 334 |
+
if needs_to_be_truncated:
|
| 335 |
+
processed_features[self.model_input_names[0]] = processed_features[self.model_input_names[0]][:max_length]
|
| 336 |
+
if "attention_mask" in processed_features:
|
| 337 |
+
processed_features["attention_mask"] = processed_features["attention_mask"][:max_length]
|
| 338 |
+
|
| 339 |
+
return processed_features
|
| 340 |
+
|
| 341 |
+
def _get_padding_strategies(self, padding=False, max_length=None):
|
| 342 |
+
"""
|
| 343 |
+
Find the correct padding strategy
|
| 344 |
+
"""
|
| 345 |
+
|
| 346 |
+
# Get padding strategy
|
| 347 |
+
if padding is not False:
|
| 348 |
+
if padding is True:
|
| 349 |
+
padding_strategy = PaddingStrategy.LONGEST # Default to pad to the longest sequence in the batch
|
| 350 |
+
elif not isinstance(padding, PaddingStrategy):
|
| 351 |
+
padding_strategy = PaddingStrategy(padding)
|
| 352 |
+
elif isinstance(padding, PaddingStrategy):
|
| 353 |
+
padding_strategy = padding
|
| 354 |
+
else:
|
| 355 |
+
padding_strategy = PaddingStrategy.DO_NOT_PAD
|
| 356 |
+
|
| 357 |
+
# Set max length if needed
|
| 358 |
+
if max_length is None:
|
| 359 |
+
if padding_strategy == PaddingStrategy.MAX_LENGTH:
|
| 360 |
+
raise ValueError(
|
| 361 |
+
f"When setting ``padding={PaddingStrategy.MAX_LENGTH}``, make sure that max_length is defined"
|
| 362 |
+
)
|
| 363 |
+
|
| 364 |
+
# Test if we have a padding value
|
| 365 |
+
if padding_strategy != PaddingStrategy.DO_NOT_PAD and (self.padding_value is None):
|
| 366 |
+
raise ValueError(
|
| 367 |
+
"Asking to pad but the feature_extractor does not have a padding value. Please select a value to use"
|
| 368 |
+
" as `padding_value`. For example: `feature_extractor.padding_value = 0.0`."
|
| 369 |
+
)
|
| 370 |
+
|
| 371 |
+
return padding_strategy
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/feature_extraction_utils.py
ADDED
|
@@ -0,0 +1,697 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2021 The HuggingFace Inc. team.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""
|
| 15 |
+
Feature extraction saving/loading class for common feature extractors.
|
| 16 |
+
"""
|
| 17 |
+
|
| 18 |
+
import copy
|
| 19 |
+
import json
|
| 20 |
+
import os
|
| 21 |
+
import warnings
|
| 22 |
+
from collections import UserDict
|
| 23 |
+
from typing import TYPE_CHECKING, Any, Optional, TypeVar, Union
|
| 24 |
+
|
| 25 |
+
import numpy as np
|
| 26 |
+
|
| 27 |
+
from .dynamic_module_utils import custom_object_save
|
| 28 |
+
from .utils import (
|
| 29 |
+
FEATURE_EXTRACTOR_NAME,
|
| 30 |
+
PROCESSOR_NAME,
|
| 31 |
+
PushToHubMixin,
|
| 32 |
+
TensorType,
|
| 33 |
+
copy_func,
|
| 34 |
+
download_url,
|
| 35 |
+
is_flax_available,
|
| 36 |
+
is_jax_tensor,
|
| 37 |
+
is_numpy_array,
|
| 38 |
+
is_offline_mode,
|
| 39 |
+
is_remote_url,
|
| 40 |
+
is_tf_available,
|
| 41 |
+
is_torch_available,
|
| 42 |
+
is_torch_device,
|
| 43 |
+
is_torch_dtype,
|
| 44 |
+
logging,
|
| 45 |
+
requires_backends,
|
| 46 |
+
)
|
| 47 |
+
from .utils.hub import cached_file
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
if TYPE_CHECKING:
|
| 51 |
+
from .feature_extraction_sequence_utils import SequenceFeatureExtractor
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
logger = logging.get_logger(__name__)
|
| 55 |
+
|
| 56 |
+
PreTrainedFeatureExtractor = Union["SequenceFeatureExtractor"]
|
| 57 |
+
|
| 58 |
+
# type hinting: specifying the type of feature extractor class that inherits from FeatureExtractionMixin
|
| 59 |
+
SpecificFeatureExtractorType = TypeVar("SpecificFeatureExtractorType", bound="FeatureExtractionMixin")
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
class BatchFeature(UserDict):
|
| 63 |
+
r"""
|
| 64 |
+
Holds the output of the [`~SequenceFeatureExtractor.pad`] and feature extractor specific `__call__` methods.
|
| 65 |
+
|
| 66 |
+
This class is derived from a python dictionary and can be used as a dictionary.
|
| 67 |
+
|
| 68 |
+
Args:
|
| 69 |
+
data (`dict`, *optional*):
|
| 70 |
+
Dictionary of lists/arrays/tensors returned by the __call__/pad methods ('input_values', 'attention_mask',
|
| 71 |
+
etc.).
|
| 72 |
+
tensor_type (`Union[None, str, TensorType]`, *optional*):
|
| 73 |
+
You can give a tensor_type here to convert the lists of integers in PyTorch/TensorFlow/Numpy Tensors at
|
| 74 |
+
initialization.
|
| 75 |
+
"""
|
| 76 |
+
|
| 77 |
+
def __init__(self, data: Optional[dict[str, Any]] = None, tensor_type: Union[None, str, TensorType] = None):
|
| 78 |
+
super().__init__(data)
|
| 79 |
+
self.convert_to_tensors(tensor_type=tensor_type)
|
| 80 |
+
|
| 81 |
+
def __getitem__(self, item: str) -> Any:
|
| 82 |
+
"""
|
| 83 |
+
If the key is a string, returns the value of the dict associated to `key` ('input_values', 'attention_mask',
|
| 84 |
+
etc.).
|
| 85 |
+
"""
|
| 86 |
+
if isinstance(item, str):
|
| 87 |
+
return self.data[item]
|
| 88 |
+
else:
|
| 89 |
+
raise KeyError("Indexing with integers is not available when using Python based feature extractors")
|
| 90 |
+
|
| 91 |
+
def __getattr__(self, item: str):
|
| 92 |
+
try:
|
| 93 |
+
return self.data[item]
|
| 94 |
+
except KeyError:
|
| 95 |
+
raise AttributeError
|
| 96 |
+
|
| 97 |
+
def __getstate__(self):
|
| 98 |
+
return {"data": self.data}
|
| 99 |
+
|
| 100 |
+
def __setstate__(self, state):
|
| 101 |
+
if "data" in state:
|
| 102 |
+
self.data = state["data"]
|
| 103 |
+
|
| 104 |
+
def _get_is_as_tensor_fns(self, tensor_type: Optional[Union[str, TensorType]] = None):
|
| 105 |
+
if tensor_type is None:
|
| 106 |
+
return None, None
|
| 107 |
+
|
| 108 |
+
# Convert to TensorType
|
| 109 |
+
if not isinstance(tensor_type, TensorType):
|
| 110 |
+
tensor_type = TensorType(tensor_type)
|
| 111 |
+
|
| 112 |
+
# Get a function reference for the correct framework
|
| 113 |
+
if tensor_type == TensorType.TENSORFLOW:
|
| 114 |
+
logger.warning_once(
|
| 115 |
+
"TensorFlow and JAX classes are deprecated and will be removed in Transformers v5. We "
|
| 116 |
+
"recommend migrating to PyTorch classes or pinning your version of Transformers."
|
| 117 |
+
)
|
| 118 |
+
if not is_tf_available():
|
| 119 |
+
raise ImportError(
|
| 120 |
+
"Unable to convert output to TensorFlow tensors format, TensorFlow is not installed."
|
| 121 |
+
)
|
| 122 |
+
import tensorflow as tf
|
| 123 |
+
|
| 124 |
+
as_tensor = tf.constant
|
| 125 |
+
is_tensor = tf.is_tensor
|
| 126 |
+
elif tensor_type == TensorType.PYTORCH:
|
| 127 |
+
if not is_torch_available():
|
| 128 |
+
raise ImportError("Unable to convert output to PyTorch tensors format, PyTorch is not installed.")
|
| 129 |
+
import torch
|
| 130 |
+
|
| 131 |
+
def as_tensor(value):
|
| 132 |
+
if isinstance(value, (list, tuple)) and len(value) > 0:
|
| 133 |
+
if isinstance(value[0], np.ndarray):
|
| 134 |
+
value = np.array(value)
|
| 135 |
+
elif (
|
| 136 |
+
isinstance(value[0], (list, tuple))
|
| 137 |
+
and len(value[0]) > 0
|
| 138 |
+
and isinstance(value[0][0], np.ndarray)
|
| 139 |
+
):
|
| 140 |
+
value = np.array(value)
|
| 141 |
+
if isinstance(value, np.ndarray):
|
| 142 |
+
return torch.from_numpy(value)
|
| 143 |
+
else:
|
| 144 |
+
return torch.tensor(value)
|
| 145 |
+
|
| 146 |
+
is_tensor = torch.is_tensor
|
| 147 |
+
elif tensor_type == TensorType.JAX:
|
| 148 |
+
logger.warning_once(
|
| 149 |
+
"TensorFlow and JAX classes are deprecated and will be removed in Transformers v5. We "
|
| 150 |
+
"recommend migrating to PyTorch classes or pinning your version of Transformers."
|
| 151 |
+
)
|
| 152 |
+
if not is_flax_available():
|
| 153 |
+
raise ImportError("Unable to convert output to JAX tensors format, JAX is not installed.")
|
| 154 |
+
import jax.numpy as jnp # noqa: F811
|
| 155 |
+
|
| 156 |
+
as_tensor = jnp.array
|
| 157 |
+
is_tensor = is_jax_tensor
|
| 158 |
+
else:
|
| 159 |
+
|
| 160 |
+
def as_tensor(value, dtype=None):
|
| 161 |
+
if isinstance(value, (list, tuple)) and isinstance(value[0], (list, tuple, np.ndarray)):
|
| 162 |
+
value_lens = [len(val) for val in value]
|
| 163 |
+
if len(set(value_lens)) > 1 and dtype is None:
|
| 164 |
+
# we have a ragged list so handle explicitly
|
| 165 |
+
value = as_tensor([np.asarray(val) for val in value], dtype=object)
|
| 166 |
+
return np.asarray(value, dtype=dtype)
|
| 167 |
+
|
| 168 |
+
is_tensor = is_numpy_array
|
| 169 |
+
return is_tensor, as_tensor
|
| 170 |
+
|
| 171 |
+
def convert_to_tensors(self, tensor_type: Optional[Union[str, TensorType]] = None):
|
| 172 |
+
"""
|
| 173 |
+
Convert the inner content to tensors.
|
| 174 |
+
|
| 175 |
+
Args:
|
| 176 |
+
tensor_type (`str` or [`~utils.TensorType`], *optional*):
|
| 177 |
+
The type of tensors to use. If `str`, should be one of the values of the enum [`~utils.TensorType`]. If
|
| 178 |
+
`None`, no modification is done.
|
| 179 |
+
"""
|
| 180 |
+
if tensor_type is None:
|
| 181 |
+
return self
|
| 182 |
+
|
| 183 |
+
is_tensor, as_tensor = self._get_is_as_tensor_fns(tensor_type)
|
| 184 |
+
|
| 185 |
+
# Do the tensor conversion in batch
|
| 186 |
+
for key, value in self.items():
|
| 187 |
+
try:
|
| 188 |
+
if not is_tensor(value):
|
| 189 |
+
tensor = as_tensor(value)
|
| 190 |
+
|
| 191 |
+
self[key] = tensor
|
| 192 |
+
except: # noqa E722
|
| 193 |
+
if key == "overflowing_values":
|
| 194 |
+
raise ValueError("Unable to create tensor returning overflowing values of different lengths. ")
|
| 195 |
+
raise ValueError(
|
| 196 |
+
"Unable to create tensor, you should probably activate padding "
|
| 197 |
+
"with 'padding=True' to have batched tensors with the same length."
|
| 198 |
+
)
|
| 199 |
+
|
| 200 |
+
return self
|
| 201 |
+
|
| 202 |
+
def to(self, *args, **kwargs) -> "BatchFeature":
|
| 203 |
+
"""
|
| 204 |
+
Send all values to device by calling `v.to(*args, **kwargs)` (PyTorch only). This should support casting in
|
| 205 |
+
different `dtypes` and sending the `BatchFeature` to a different `device`.
|
| 206 |
+
|
| 207 |
+
Args:
|
| 208 |
+
args (`Tuple`):
|
| 209 |
+
Will be passed to the `to(...)` function of the tensors.
|
| 210 |
+
kwargs (`Dict`, *optional*):
|
| 211 |
+
Will be passed to the `to(...)` function of the tensors.
|
| 212 |
+
To enable asynchronous data transfer, set the `non_blocking` flag in `kwargs` (defaults to `False`).
|
| 213 |
+
|
| 214 |
+
Returns:
|
| 215 |
+
[`BatchFeature`]: The same instance after modification.
|
| 216 |
+
"""
|
| 217 |
+
requires_backends(self, ["torch"])
|
| 218 |
+
import torch
|
| 219 |
+
|
| 220 |
+
device = kwargs.get("device")
|
| 221 |
+
non_blocking = kwargs.get("non_blocking", False)
|
| 222 |
+
# Check if the args are a device or a dtype
|
| 223 |
+
if device is None and len(args) > 0:
|
| 224 |
+
# device should be always the first argument
|
| 225 |
+
arg = args[0]
|
| 226 |
+
if is_torch_dtype(arg):
|
| 227 |
+
# The first argument is a dtype
|
| 228 |
+
pass
|
| 229 |
+
elif isinstance(arg, str) or is_torch_device(arg) or isinstance(arg, int):
|
| 230 |
+
device = arg
|
| 231 |
+
else:
|
| 232 |
+
# it's something else
|
| 233 |
+
raise ValueError(f"Attempting to cast a BatchFeature to type {str(arg)}. This is not supported.")
|
| 234 |
+
|
| 235 |
+
# We cast only floating point tensors to avoid issues with tokenizers casting `LongTensor` to `FloatTensor`
|
| 236 |
+
def maybe_to(v):
|
| 237 |
+
# check if v is a floating point
|
| 238 |
+
if isinstance(v, torch.Tensor) and torch.is_floating_point(v):
|
| 239 |
+
# cast and send to device
|
| 240 |
+
return v.to(*args, **kwargs)
|
| 241 |
+
elif isinstance(v, torch.Tensor) and device is not None:
|
| 242 |
+
return v.to(device=device, non_blocking=non_blocking)
|
| 243 |
+
else:
|
| 244 |
+
return v
|
| 245 |
+
|
| 246 |
+
self.data = {k: maybe_to(v) for k, v in self.items()}
|
| 247 |
+
return self
|
| 248 |
+
|
| 249 |
+
|
| 250 |
+
class FeatureExtractionMixin(PushToHubMixin):
|
| 251 |
+
"""
|
| 252 |
+
This is a feature extraction mixin used to provide saving/loading functionality for sequential and image feature
|
| 253 |
+
extractors.
|
| 254 |
+
"""
|
| 255 |
+
|
| 256 |
+
_auto_class = None
|
| 257 |
+
|
| 258 |
+
def __init__(self, **kwargs):
|
| 259 |
+
"""Set elements of `kwargs` as attributes."""
|
| 260 |
+
# Pop "processor_class" as it should be saved as private attribute
|
| 261 |
+
self._processor_class = kwargs.pop("processor_class", None)
|
| 262 |
+
# Additional attributes without default values
|
| 263 |
+
for key, value in kwargs.items():
|
| 264 |
+
try:
|
| 265 |
+
setattr(self, key, value)
|
| 266 |
+
except AttributeError as err:
|
| 267 |
+
logger.error(f"Can't set {key} with value {value} for {self}")
|
| 268 |
+
raise err
|
| 269 |
+
|
| 270 |
+
def _set_processor_class(self, processor_class: str):
|
| 271 |
+
"""Sets processor class as an attribute."""
|
| 272 |
+
self._processor_class = processor_class
|
| 273 |
+
|
| 274 |
+
@classmethod
|
| 275 |
+
def from_pretrained(
|
| 276 |
+
cls: type[SpecificFeatureExtractorType],
|
| 277 |
+
pretrained_model_name_or_path: Union[str, os.PathLike],
|
| 278 |
+
cache_dir: Optional[Union[str, os.PathLike]] = None,
|
| 279 |
+
force_download: bool = False,
|
| 280 |
+
local_files_only: bool = False,
|
| 281 |
+
token: Optional[Union[str, bool]] = None,
|
| 282 |
+
revision: str = "main",
|
| 283 |
+
**kwargs,
|
| 284 |
+
) -> SpecificFeatureExtractorType:
|
| 285 |
+
r"""
|
| 286 |
+
Instantiate a type of [`~feature_extraction_utils.FeatureExtractionMixin`] from a feature extractor, *e.g.* a
|
| 287 |
+
derived class of [`SequenceFeatureExtractor`].
|
| 288 |
+
|
| 289 |
+
Args:
|
| 290 |
+
pretrained_model_name_or_path (`str` or `os.PathLike`):
|
| 291 |
+
This can be either:
|
| 292 |
+
|
| 293 |
+
- a string, the *model id* of a pretrained feature_extractor hosted inside a model repo on
|
| 294 |
+
huggingface.co.
|
| 295 |
+
- a path to a *directory* containing a feature extractor file saved using the
|
| 296 |
+
[`~feature_extraction_utils.FeatureExtractionMixin.save_pretrained`] method, e.g.,
|
| 297 |
+
`./my_model_directory/`.
|
| 298 |
+
- a path or url to a saved feature extractor JSON *file*, e.g.,
|
| 299 |
+
`./my_model_directory/preprocessor_config.json`.
|
| 300 |
+
cache_dir (`str` or `os.PathLike`, *optional*):
|
| 301 |
+
Path to a directory in which a downloaded pretrained model feature extractor should be cached if the
|
| 302 |
+
standard cache should not be used.
|
| 303 |
+
force_download (`bool`, *optional*, defaults to `False`):
|
| 304 |
+
Whether or not to force to (re-)download the feature extractor files and override the cached versions
|
| 305 |
+
if they exist.
|
| 306 |
+
resume_download:
|
| 307 |
+
Deprecated and ignored. All downloads are now resumed by default when possible.
|
| 308 |
+
Will be removed in v5 of Transformers.
|
| 309 |
+
proxies (`dict[str, str]`, *optional*):
|
| 310 |
+
A dictionary of proxy servers to use by protocol or endpoint, e.g., `{'http': 'foo.bar:3128',
|
| 311 |
+
'http://hostname': 'foo.bar:4012'}.` The proxies are used on each request.
|
| 312 |
+
token (`str` or `bool`, *optional*):
|
| 313 |
+
The token to use as HTTP bearer authorization for remote files. If `True`, or not specified, will use
|
| 314 |
+
the token generated when running `hf auth login` (stored in `~/.huggingface`).
|
| 315 |
+
revision (`str`, *optional*, defaults to `"main"`):
|
| 316 |
+
The specific model version to use. It can be a branch name, a tag name, or a commit id, since we use a
|
| 317 |
+
git-based system for storing models and other artifacts on huggingface.co, so `revision` can be any
|
| 318 |
+
identifier allowed by git.
|
| 319 |
+
|
| 320 |
+
|
| 321 |
+
<Tip>
|
| 322 |
+
|
| 323 |
+
To test a pull request you made on the Hub, you can pass `revision="refs/pr/<pr_number>"`.
|
| 324 |
+
|
| 325 |
+
</Tip>
|
| 326 |
+
|
| 327 |
+
return_unused_kwargs (`bool`, *optional*, defaults to `False`):
|
| 328 |
+
If `False`, then this function returns just the final feature extractor object. If `True`, then this
|
| 329 |
+
functions returns a `Tuple(feature_extractor, unused_kwargs)` where *unused_kwargs* is a dictionary
|
| 330 |
+
consisting of the key/value pairs whose keys are not feature extractor attributes: i.e., the part of
|
| 331 |
+
`kwargs` which has not been used to update `feature_extractor` and is otherwise ignored.
|
| 332 |
+
kwargs (`dict[str, Any]`, *optional*):
|
| 333 |
+
The values in kwargs of any keys which are feature extractor attributes will be used to override the
|
| 334 |
+
loaded values. Behavior concerning key/value pairs whose keys are *not* feature extractor attributes is
|
| 335 |
+
controlled by the `return_unused_kwargs` keyword parameter.
|
| 336 |
+
|
| 337 |
+
Returns:
|
| 338 |
+
A feature extractor of type [`~feature_extraction_utils.FeatureExtractionMixin`].
|
| 339 |
+
|
| 340 |
+
Examples:
|
| 341 |
+
|
| 342 |
+
```python
|
| 343 |
+
# We can't instantiate directly the base class *FeatureExtractionMixin* nor *SequenceFeatureExtractor* so let's show the examples on a
|
| 344 |
+
# derived class: *Wav2Vec2FeatureExtractor*
|
| 345 |
+
feature_extractor = Wav2Vec2FeatureExtractor.from_pretrained(
|
| 346 |
+
"facebook/wav2vec2-base-960h"
|
| 347 |
+
) # Download feature_extraction_config from huggingface.co and cache.
|
| 348 |
+
feature_extractor = Wav2Vec2FeatureExtractor.from_pretrained(
|
| 349 |
+
"./test/saved_model/"
|
| 350 |
+
) # E.g. feature_extractor (or model) was saved using *save_pretrained('./test/saved_model/')*
|
| 351 |
+
feature_extractor = Wav2Vec2FeatureExtractor.from_pretrained("./test/saved_model/preprocessor_config.json")
|
| 352 |
+
feature_extractor = Wav2Vec2FeatureExtractor.from_pretrained(
|
| 353 |
+
"facebook/wav2vec2-base-960h", return_attention_mask=False, foo=False
|
| 354 |
+
)
|
| 355 |
+
assert feature_extractor.return_attention_mask is False
|
| 356 |
+
feature_extractor, unused_kwargs = Wav2Vec2FeatureExtractor.from_pretrained(
|
| 357 |
+
"facebook/wav2vec2-base-960h", return_attention_mask=False, foo=False, return_unused_kwargs=True
|
| 358 |
+
)
|
| 359 |
+
assert feature_extractor.return_attention_mask is False
|
| 360 |
+
assert unused_kwargs == {"foo": False}
|
| 361 |
+
```"""
|
| 362 |
+
kwargs["cache_dir"] = cache_dir
|
| 363 |
+
kwargs["force_download"] = force_download
|
| 364 |
+
kwargs["local_files_only"] = local_files_only
|
| 365 |
+
kwargs["revision"] = revision
|
| 366 |
+
|
| 367 |
+
use_auth_token = kwargs.pop("use_auth_token", None)
|
| 368 |
+
if use_auth_token is not None:
|
| 369 |
+
warnings.warn(
|
| 370 |
+
"The `use_auth_token` argument is deprecated and will be removed in v5 of Transformers. Please use `token` instead.",
|
| 371 |
+
FutureWarning,
|
| 372 |
+
)
|
| 373 |
+
if token is not None:
|
| 374 |
+
raise ValueError(
|
| 375 |
+
"`token` and `use_auth_token` are both specified. Please set only the argument `token`."
|
| 376 |
+
)
|
| 377 |
+
token = use_auth_token
|
| 378 |
+
|
| 379 |
+
if token is not None:
|
| 380 |
+
kwargs["token"] = token
|
| 381 |
+
|
| 382 |
+
feature_extractor_dict, kwargs = cls.get_feature_extractor_dict(pretrained_model_name_or_path, **kwargs)
|
| 383 |
+
|
| 384 |
+
return cls.from_dict(feature_extractor_dict, **kwargs)
|
| 385 |
+
|
| 386 |
+
def save_pretrained(self, save_directory: Union[str, os.PathLike], push_to_hub: bool = False, **kwargs):
|
| 387 |
+
"""
|
| 388 |
+
Save a feature_extractor object to the directory `save_directory`, so that it can be re-loaded using the
|
| 389 |
+
[`~feature_extraction_utils.FeatureExtractionMixin.from_pretrained`] class method.
|
| 390 |
+
|
| 391 |
+
Args:
|
| 392 |
+
save_directory (`str` or `os.PathLike`):
|
| 393 |
+
Directory where the feature extractor JSON file will be saved (will be created if it does not exist).
|
| 394 |
+
push_to_hub (`bool`, *optional*, defaults to `False`):
|
| 395 |
+
Whether or not to push your model to the Hugging Face model hub after saving it. You can specify the
|
| 396 |
+
repository you want to push to with `repo_id` (will default to the name of `save_directory` in your
|
| 397 |
+
namespace).
|
| 398 |
+
kwargs (`dict[str, Any]`, *optional*):
|
| 399 |
+
Additional key word arguments passed along to the [`~utils.PushToHubMixin.push_to_hub`] method.
|
| 400 |
+
"""
|
| 401 |
+
use_auth_token = kwargs.pop("use_auth_token", None)
|
| 402 |
+
|
| 403 |
+
if use_auth_token is not None:
|
| 404 |
+
warnings.warn(
|
| 405 |
+
"The `use_auth_token` argument is deprecated and will be removed in v5 of Transformers. Please use `token` instead.",
|
| 406 |
+
FutureWarning,
|
| 407 |
+
)
|
| 408 |
+
if kwargs.get("token") is not None:
|
| 409 |
+
raise ValueError(
|
| 410 |
+
"`token` and `use_auth_token` are both specified. Please set only the argument `token`."
|
| 411 |
+
)
|
| 412 |
+
kwargs["token"] = use_auth_token
|
| 413 |
+
|
| 414 |
+
if os.path.isfile(save_directory):
|
| 415 |
+
raise AssertionError(f"Provided path ({save_directory}) should be a directory, not a file")
|
| 416 |
+
|
| 417 |
+
os.makedirs(save_directory, exist_ok=True)
|
| 418 |
+
|
| 419 |
+
if push_to_hub:
|
| 420 |
+
commit_message = kwargs.pop("commit_message", None)
|
| 421 |
+
repo_id = kwargs.pop("repo_id", save_directory.split(os.path.sep)[-1])
|
| 422 |
+
repo_id = self._create_repo(repo_id, **kwargs)
|
| 423 |
+
files_timestamps = self._get_files_timestamps(save_directory)
|
| 424 |
+
|
| 425 |
+
# If we have a custom config, we copy the file defining it in the folder and set the attributes so it can be
|
| 426 |
+
# loaded from the Hub.
|
| 427 |
+
if self._auto_class is not None:
|
| 428 |
+
custom_object_save(self, save_directory, config=self)
|
| 429 |
+
|
| 430 |
+
# If we save using the predefined names, we can load using `from_pretrained`
|
| 431 |
+
output_feature_extractor_file = os.path.join(save_directory, FEATURE_EXTRACTOR_NAME)
|
| 432 |
+
|
| 433 |
+
self.to_json_file(output_feature_extractor_file)
|
| 434 |
+
logger.info(f"Feature extractor saved in {output_feature_extractor_file}")
|
| 435 |
+
|
| 436 |
+
if push_to_hub:
|
| 437 |
+
self._upload_modified_files(
|
| 438 |
+
save_directory,
|
| 439 |
+
repo_id,
|
| 440 |
+
files_timestamps,
|
| 441 |
+
commit_message=commit_message,
|
| 442 |
+
token=kwargs.get("token"),
|
| 443 |
+
)
|
| 444 |
+
|
| 445 |
+
return [output_feature_extractor_file]
|
| 446 |
+
|
| 447 |
+
@classmethod
|
| 448 |
+
def get_feature_extractor_dict(
|
| 449 |
+
cls, pretrained_model_name_or_path: Union[str, os.PathLike], **kwargs
|
| 450 |
+
) -> tuple[dict[str, Any], dict[str, Any]]:
|
| 451 |
+
"""
|
| 452 |
+
From a `pretrained_model_name_or_path`, resolve to a dictionary of parameters, to be used for instantiating a
|
| 453 |
+
feature extractor of type [`~feature_extraction_utils.FeatureExtractionMixin`] using `from_dict`.
|
| 454 |
+
|
| 455 |
+
Parameters:
|
| 456 |
+
pretrained_model_name_or_path (`str` or `os.PathLike`):
|
| 457 |
+
The identifier of the pre-trained checkpoint from which we want the dictionary of parameters.
|
| 458 |
+
|
| 459 |
+
Returns:
|
| 460 |
+
`tuple[Dict, Dict]`: The dictionary(ies) that will be used to instantiate the feature extractor object.
|
| 461 |
+
"""
|
| 462 |
+
cache_dir = kwargs.pop("cache_dir", None)
|
| 463 |
+
force_download = kwargs.pop("force_download", False)
|
| 464 |
+
resume_download = kwargs.pop("resume_download", None)
|
| 465 |
+
proxies = kwargs.pop("proxies", None)
|
| 466 |
+
subfolder = kwargs.pop("subfolder", None)
|
| 467 |
+
token = kwargs.pop("token", None)
|
| 468 |
+
use_auth_token = kwargs.pop("use_auth_token", None)
|
| 469 |
+
local_files_only = kwargs.pop("local_files_only", False)
|
| 470 |
+
revision = kwargs.pop("revision", None)
|
| 471 |
+
|
| 472 |
+
if use_auth_token is not None:
|
| 473 |
+
warnings.warn(
|
| 474 |
+
"The `use_auth_token` argument is deprecated and will be removed in v5 of Transformers. Please use `token` instead.",
|
| 475 |
+
FutureWarning,
|
| 476 |
+
)
|
| 477 |
+
if token is not None:
|
| 478 |
+
raise ValueError(
|
| 479 |
+
"`token` and `use_auth_token` are both specified. Please set only the argument `token`."
|
| 480 |
+
)
|
| 481 |
+
token = use_auth_token
|
| 482 |
+
|
| 483 |
+
from_pipeline = kwargs.pop("_from_pipeline", None)
|
| 484 |
+
from_auto_class = kwargs.pop("_from_auto", False)
|
| 485 |
+
|
| 486 |
+
user_agent = {"file_type": "feature extractor", "from_auto_class": from_auto_class}
|
| 487 |
+
if from_pipeline is not None:
|
| 488 |
+
user_agent["using_pipeline"] = from_pipeline
|
| 489 |
+
|
| 490 |
+
if is_offline_mode() and not local_files_only:
|
| 491 |
+
logger.info("Offline mode: forcing local_files_only=True")
|
| 492 |
+
local_files_only = True
|
| 493 |
+
|
| 494 |
+
pretrained_model_name_or_path = str(pretrained_model_name_or_path)
|
| 495 |
+
is_local = os.path.isdir(pretrained_model_name_or_path)
|
| 496 |
+
if os.path.isdir(pretrained_model_name_or_path):
|
| 497 |
+
feature_extractor_file = os.path.join(pretrained_model_name_or_path, FEATURE_EXTRACTOR_NAME)
|
| 498 |
+
if os.path.isfile(pretrained_model_name_or_path):
|
| 499 |
+
resolved_feature_extractor_file = pretrained_model_name_or_path
|
| 500 |
+
is_local = True
|
| 501 |
+
elif is_remote_url(pretrained_model_name_or_path):
|
| 502 |
+
feature_extractor_file = pretrained_model_name_or_path
|
| 503 |
+
resolved_feature_extractor_file = download_url(pretrained_model_name_or_path)
|
| 504 |
+
else:
|
| 505 |
+
feature_extractor_file = FEATURE_EXTRACTOR_NAME
|
| 506 |
+
try:
|
| 507 |
+
# Load from local folder or from cache or download from model Hub and cache
|
| 508 |
+
resolved_feature_extractor_files = [
|
| 509 |
+
resolved_file
|
| 510 |
+
for filename in [feature_extractor_file, PROCESSOR_NAME]
|
| 511 |
+
if (
|
| 512 |
+
resolved_file := cached_file(
|
| 513 |
+
pretrained_model_name_or_path,
|
| 514 |
+
filename=filename,
|
| 515 |
+
cache_dir=cache_dir,
|
| 516 |
+
force_download=force_download,
|
| 517 |
+
proxies=proxies,
|
| 518 |
+
resume_download=resume_download,
|
| 519 |
+
local_files_only=local_files_only,
|
| 520 |
+
subfolder=subfolder,
|
| 521 |
+
token=token,
|
| 522 |
+
user_agent=user_agent,
|
| 523 |
+
revision=revision,
|
| 524 |
+
_raise_exceptions_for_missing_entries=False,
|
| 525 |
+
)
|
| 526 |
+
)
|
| 527 |
+
is not None
|
| 528 |
+
]
|
| 529 |
+
resolved_feature_extractor_file = resolved_feature_extractor_files[0]
|
| 530 |
+
except OSError:
|
| 531 |
+
# Raise any environment error raise by `cached_file`. It will have a helpful error message adapted to
|
| 532 |
+
# the original exception.
|
| 533 |
+
raise
|
| 534 |
+
except Exception:
|
| 535 |
+
# For any other exception, we throw a generic error.
|
| 536 |
+
raise OSError(
|
| 537 |
+
f"Can't load feature extractor for '{pretrained_model_name_or_path}'. If you were trying to load"
|
| 538 |
+
" it from 'https://huggingface.co/models', make sure you don't have a local directory with the"
|
| 539 |
+
f" same name. Otherwise, make sure '{pretrained_model_name_or_path}' is the correct path to a"
|
| 540 |
+
f" directory containing a {FEATURE_EXTRACTOR_NAME} file"
|
| 541 |
+
)
|
| 542 |
+
|
| 543 |
+
try:
|
| 544 |
+
# Load feature_extractor dict
|
| 545 |
+
with open(resolved_feature_extractor_file, encoding="utf-8") as reader:
|
| 546 |
+
text = reader.read()
|
| 547 |
+
feature_extractor_dict = json.loads(text)
|
| 548 |
+
feature_extractor_dict = feature_extractor_dict.get("feature_extractor", feature_extractor_dict)
|
| 549 |
+
|
| 550 |
+
except json.JSONDecodeError:
|
| 551 |
+
raise OSError(
|
| 552 |
+
f"It looks like the config file at '{resolved_feature_extractor_file}' is not a valid JSON file."
|
| 553 |
+
)
|
| 554 |
+
|
| 555 |
+
if is_local:
|
| 556 |
+
logger.info(f"loading configuration file {resolved_feature_extractor_file}")
|
| 557 |
+
else:
|
| 558 |
+
logger.info(
|
| 559 |
+
f"loading configuration file {feature_extractor_file} from cache at {resolved_feature_extractor_file}"
|
| 560 |
+
)
|
| 561 |
+
|
| 562 |
+
return feature_extractor_dict, kwargs
|
| 563 |
+
|
| 564 |
+
@classmethod
|
| 565 |
+
def from_dict(
|
| 566 |
+
cls, feature_extractor_dict: dict[str, Any], **kwargs
|
| 567 |
+
) -> Union["FeatureExtractionMixin", tuple["FeatureExtractionMixin", dict[str, Any]]]:
|
| 568 |
+
"""
|
| 569 |
+
Instantiates a type of [`~feature_extraction_utils.FeatureExtractionMixin`] from a Python dictionary of
|
| 570 |
+
parameters.
|
| 571 |
+
|
| 572 |
+
Args:
|
| 573 |
+
feature_extractor_dict (`dict[str, Any]`):
|
| 574 |
+
Dictionary that will be used to instantiate the feature extractor object. Such a dictionary can be
|
| 575 |
+
retrieved from a pretrained checkpoint by leveraging the
|
| 576 |
+
[`~feature_extraction_utils.FeatureExtractionMixin.to_dict`] method.
|
| 577 |
+
kwargs (`dict[str, Any]`):
|
| 578 |
+
Additional parameters from which to initialize the feature extractor object.
|
| 579 |
+
|
| 580 |
+
Returns:
|
| 581 |
+
[`~feature_extraction_utils.FeatureExtractionMixin`]: The feature extractor object instantiated from those
|
| 582 |
+
parameters.
|
| 583 |
+
"""
|
| 584 |
+
return_unused_kwargs = kwargs.pop("return_unused_kwargs", False)
|
| 585 |
+
|
| 586 |
+
# Update feature_extractor with kwargs if needed
|
| 587 |
+
to_remove = []
|
| 588 |
+
for key, value in kwargs.items():
|
| 589 |
+
if key in feature_extractor_dict:
|
| 590 |
+
feature_extractor_dict[key] = value
|
| 591 |
+
to_remove.append(key)
|
| 592 |
+
for key in to_remove:
|
| 593 |
+
kwargs.pop(key, None)
|
| 594 |
+
|
| 595 |
+
feature_extractor = cls(**feature_extractor_dict)
|
| 596 |
+
|
| 597 |
+
logger.info(f"Feature extractor {feature_extractor}")
|
| 598 |
+
if return_unused_kwargs:
|
| 599 |
+
return feature_extractor, kwargs
|
| 600 |
+
else:
|
| 601 |
+
return feature_extractor
|
| 602 |
+
|
| 603 |
+
def to_dict(self) -> dict[str, Any]:
|
| 604 |
+
"""
|
| 605 |
+
Serializes this instance to a Python dictionary. Returns:
|
| 606 |
+
`dict[str, Any]`: Dictionary of all the attributes that make up this configuration instance.
|
| 607 |
+
"""
|
| 608 |
+
output = copy.deepcopy(self.__dict__)
|
| 609 |
+
output["feature_extractor_type"] = self.__class__.__name__
|
| 610 |
+
if "mel_filters" in output:
|
| 611 |
+
del output["mel_filters"]
|
| 612 |
+
if "window" in output:
|
| 613 |
+
del output["window"]
|
| 614 |
+
return output
|
| 615 |
+
|
| 616 |
+
@classmethod
|
| 617 |
+
def from_json_file(cls, json_file: Union[str, os.PathLike]) -> "FeatureExtractionMixin":
|
| 618 |
+
"""
|
| 619 |
+
Instantiates a feature extractor of type [`~feature_extraction_utils.FeatureExtractionMixin`] from the path to
|
| 620 |
+
a JSON file of parameters.
|
| 621 |
+
|
| 622 |
+
Args:
|
| 623 |
+
json_file (`str` or `os.PathLike`):
|
| 624 |
+
Path to the JSON file containing the parameters.
|
| 625 |
+
|
| 626 |
+
Returns:
|
| 627 |
+
A feature extractor of type [`~feature_extraction_utils.FeatureExtractionMixin`]: The feature_extractor
|
| 628 |
+
object instantiated from that JSON file.
|
| 629 |
+
"""
|
| 630 |
+
with open(json_file, encoding="utf-8") as reader:
|
| 631 |
+
text = reader.read()
|
| 632 |
+
feature_extractor_dict = json.loads(text)
|
| 633 |
+
return cls(**feature_extractor_dict)
|
| 634 |
+
|
| 635 |
+
def to_json_string(self) -> str:
|
| 636 |
+
"""
|
| 637 |
+
Serializes this instance to a JSON string.
|
| 638 |
+
|
| 639 |
+
Returns:
|
| 640 |
+
`str`: String containing all the attributes that make up this feature_extractor instance in JSON format.
|
| 641 |
+
"""
|
| 642 |
+
dictionary = self.to_dict()
|
| 643 |
+
|
| 644 |
+
for key, value in dictionary.items():
|
| 645 |
+
if isinstance(value, np.ndarray):
|
| 646 |
+
dictionary[key] = value.tolist()
|
| 647 |
+
|
| 648 |
+
# make sure private name "_processor_class" is correctly
|
| 649 |
+
# saved as "processor_class"
|
| 650 |
+
_processor_class = dictionary.pop("_processor_class", None)
|
| 651 |
+
if _processor_class is not None:
|
| 652 |
+
dictionary["processor_class"] = _processor_class
|
| 653 |
+
|
| 654 |
+
return json.dumps(dictionary, indent=2, sort_keys=True) + "\n"
|
| 655 |
+
|
| 656 |
+
def to_json_file(self, json_file_path: Union[str, os.PathLike]):
|
| 657 |
+
"""
|
| 658 |
+
Save this instance to a JSON file.
|
| 659 |
+
|
| 660 |
+
Args:
|
| 661 |
+
json_file_path (`str` or `os.PathLike`):
|
| 662 |
+
Path to the JSON file in which this feature_extractor instance's parameters will be saved.
|
| 663 |
+
"""
|
| 664 |
+
with open(json_file_path, "w", encoding="utf-8") as writer:
|
| 665 |
+
writer.write(self.to_json_string())
|
| 666 |
+
|
| 667 |
+
def __repr__(self):
|
| 668 |
+
return f"{self.__class__.__name__} {self.to_json_string()}"
|
| 669 |
+
|
| 670 |
+
@classmethod
|
| 671 |
+
def register_for_auto_class(cls, auto_class="AutoFeatureExtractor"):
|
| 672 |
+
"""
|
| 673 |
+
Register this class with a given auto class. This should only be used for custom feature extractors as the ones
|
| 674 |
+
in the library are already mapped with `AutoFeatureExtractor`.
|
| 675 |
+
|
| 676 |
+
|
| 677 |
+
|
| 678 |
+
Args:
|
| 679 |
+
auto_class (`str` or `type`, *optional*, defaults to `"AutoFeatureExtractor"`):
|
| 680 |
+
The auto class to register this new feature extractor with.
|
| 681 |
+
"""
|
| 682 |
+
if not isinstance(auto_class, str):
|
| 683 |
+
auto_class = auto_class.__name__
|
| 684 |
+
|
| 685 |
+
import transformers.models.auto as auto_module
|
| 686 |
+
|
| 687 |
+
if not hasattr(auto_module, auto_class):
|
| 688 |
+
raise ValueError(f"{auto_class} is not a valid auto class.")
|
| 689 |
+
|
| 690 |
+
cls._auto_class = auto_class
|
| 691 |
+
|
| 692 |
+
|
| 693 |
+
FeatureExtractionMixin.push_to_hub = copy_func(FeatureExtractionMixin.push_to_hub)
|
| 694 |
+
if FeatureExtractionMixin.push_to_hub.__doc__ is not None:
|
| 695 |
+
FeatureExtractionMixin.push_to_hub.__doc__ = FeatureExtractionMixin.push_to_hub.__doc__.format(
|
| 696 |
+
object="feature extractor", object_class="AutoFeatureExtractor", object_files="feature extractor file"
|
| 697 |
+
)
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/file_utils.py
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2020 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""
|
| 15 |
+
File utilities: utilities related to download and cache models
|
| 16 |
+
|
| 17 |
+
This module should not be update anymore and is only left for backward compatibility.
|
| 18 |
+
"""
|
| 19 |
+
|
| 20 |
+
from huggingface_hub import get_full_repo_name # for backward compatibility
|
| 21 |
+
from huggingface_hub.constants import HF_HUB_DISABLE_TELEMETRY as DISABLE_TELEMETRY # for backward compatibility
|
| 22 |
+
|
| 23 |
+
from . import __version__
|
| 24 |
+
|
| 25 |
+
# Backward compatibility imports, to make sure all those objects can be found in file_utils
|
| 26 |
+
from .utils import (
|
| 27 |
+
CLOUDFRONT_DISTRIB_PREFIX,
|
| 28 |
+
CONFIG_NAME,
|
| 29 |
+
DUMMY_INPUTS,
|
| 30 |
+
DUMMY_MASK,
|
| 31 |
+
ENV_VARS_TRUE_AND_AUTO_VALUES,
|
| 32 |
+
ENV_VARS_TRUE_VALUES,
|
| 33 |
+
FEATURE_EXTRACTOR_NAME,
|
| 34 |
+
FLAX_WEIGHTS_NAME,
|
| 35 |
+
HF_MODULES_CACHE,
|
| 36 |
+
HUGGINGFACE_CO_PREFIX,
|
| 37 |
+
HUGGINGFACE_CO_RESOLVE_ENDPOINT,
|
| 38 |
+
MODEL_CARD_NAME,
|
| 39 |
+
MULTIPLE_CHOICE_DUMMY_INPUTS,
|
| 40 |
+
PYTORCH_PRETRAINED_BERT_CACHE,
|
| 41 |
+
PYTORCH_TRANSFORMERS_CACHE,
|
| 42 |
+
S3_BUCKET_PREFIX,
|
| 43 |
+
SENTENCEPIECE_UNDERLINE,
|
| 44 |
+
SPIECE_UNDERLINE,
|
| 45 |
+
TF2_WEIGHTS_NAME,
|
| 46 |
+
TF_WEIGHTS_NAME,
|
| 47 |
+
TORCH_FX_REQUIRED_VERSION,
|
| 48 |
+
TRANSFORMERS_CACHE,
|
| 49 |
+
TRANSFORMERS_DYNAMIC_MODULE_NAME,
|
| 50 |
+
USE_JAX,
|
| 51 |
+
USE_TF,
|
| 52 |
+
USE_TORCH,
|
| 53 |
+
WEIGHTS_INDEX_NAME,
|
| 54 |
+
WEIGHTS_NAME,
|
| 55 |
+
ContextManagers,
|
| 56 |
+
DummyObject,
|
| 57 |
+
EntryNotFoundError,
|
| 58 |
+
ExplicitEnum,
|
| 59 |
+
ModelOutput,
|
| 60 |
+
PaddingStrategy,
|
| 61 |
+
PushToHubMixin,
|
| 62 |
+
RepositoryNotFoundError,
|
| 63 |
+
RevisionNotFoundError,
|
| 64 |
+
TensorType,
|
| 65 |
+
_LazyModule,
|
| 66 |
+
add_code_sample_docstrings,
|
| 67 |
+
add_end_docstrings,
|
| 68 |
+
add_start_docstrings,
|
| 69 |
+
add_start_docstrings_to_model_forward,
|
| 70 |
+
copy_func,
|
| 71 |
+
default_cache_path,
|
| 72 |
+
define_sagemaker_information,
|
| 73 |
+
get_torch_version,
|
| 74 |
+
has_file,
|
| 75 |
+
http_user_agent,
|
| 76 |
+
is_apex_available,
|
| 77 |
+
is_bs4_available,
|
| 78 |
+
is_coloredlogs_available,
|
| 79 |
+
is_datasets_available,
|
| 80 |
+
is_detectron2_available,
|
| 81 |
+
is_faiss_available,
|
| 82 |
+
is_flax_available,
|
| 83 |
+
is_ftfy_available,
|
| 84 |
+
is_g2p_en_available,
|
| 85 |
+
is_in_notebook,
|
| 86 |
+
is_ipex_available,
|
| 87 |
+
is_librosa_available,
|
| 88 |
+
is_offline_mode,
|
| 89 |
+
is_onnx_available,
|
| 90 |
+
is_pandas_available,
|
| 91 |
+
is_phonemizer_available,
|
| 92 |
+
is_protobuf_available,
|
| 93 |
+
is_psutil_available,
|
| 94 |
+
is_py3nvml_available,
|
| 95 |
+
is_pyctcdecode_available,
|
| 96 |
+
is_pytesseract_available,
|
| 97 |
+
is_pytorch_quantization_available,
|
| 98 |
+
is_rjieba_available,
|
| 99 |
+
is_sagemaker_dp_enabled,
|
| 100 |
+
is_sagemaker_mp_enabled,
|
| 101 |
+
is_scipy_available,
|
| 102 |
+
is_sentencepiece_available,
|
| 103 |
+
is_seqio_available,
|
| 104 |
+
is_sklearn_available,
|
| 105 |
+
is_soundfile_available,
|
| 106 |
+
is_spacy_available,
|
| 107 |
+
is_speech_available,
|
| 108 |
+
is_tensor,
|
| 109 |
+
is_tensorflow_probability_available,
|
| 110 |
+
is_tf2onnx_available,
|
| 111 |
+
is_tf_available,
|
| 112 |
+
is_timm_available,
|
| 113 |
+
is_tokenizers_available,
|
| 114 |
+
is_torch_available,
|
| 115 |
+
is_torch_bf16_available,
|
| 116 |
+
is_torch_cuda_available,
|
| 117 |
+
is_torch_fx_available,
|
| 118 |
+
is_torch_fx_proxy,
|
| 119 |
+
is_torch_mps_available,
|
| 120 |
+
is_torch_tf32_available,
|
| 121 |
+
is_torch_xla_available,
|
| 122 |
+
is_torchaudio_available,
|
| 123 |
+
is_training_run_on_sagemaker,
|
| 124 |
+
is_vision_available,
|
| 125 |
+
replace_return_docstrings,
|
| 126 |
+
requires_backends,
|
| 127 |
+
to_numpy,
|
| 128 |
+
to_py_obj,
|
| 129 |
+
torch_only_method,
|
| 130 |
+
)
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/hyperparameter_search.py
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2023-present the HuggingFace Inc. team.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
from typing import Optional
|
| 15 |
+
|
| 16 |
+
from .integrations import (
|
| 17 |
+
is_optuna_available,
|
| 18 |
+
is_ray_tune_available,
|
| 19 |
+
is_sigopt_available,
|
| 20 |
+
is_wandb_available,
|
| 21 |
+
run_hp_search_optuna,
|
| 22 |
+
run_hp_search_ray,
|
| 23 |
+
run_hp_search_sigopt,
|
| 24 |
+
run_hp_search_wandb,
|
| 25 |
+
)
|
| 26 |
+
from .trainer_utils import (
|
| 27 |
+
HPSearchBackend,
|
| 28 |
+
default_hp_space_optuna,
|
| 29 |
+
default_hp_space_ray,
|
| 30 |
+
default_hp_space_sigopt,
|
| 31 |
+
default_hp_space_wandb,
|
| 32 |
+
)
|
| 33 |
+
from .utils import logging
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
logger = logging.get_logger(__name__)
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
class HyperParamSearchBackendBase:
|
| 40 |
+
name: str
|
| 41 |
+
pip_package: Optional[str] = None
|
| 42 |
+
|
| 43 |
+
@staticmethod
|
| 44 |
+
def is_available():
|
| 45 |
+
raise NotImplementedError
|
| 46 |
+
|
| 47 |
+
def run(self, trainer, n_trials: int, direction: str, **kwargs):
|
| 48 |
+
raise NotImplementedError
|
| 49 |
+
|
| 50 |
+
def default_hp_space(self, trial):
|
| 51 |
+
raise NotImplementedError
|
| 52 |
+
|
| 53 |
+
def ensure_available(self):
|
| 54 |
+
if not self.is_available():
|
| 55 |
+
raise RuntimeError(
|
| 56 |
+
f"You picked the {self.name} backend, but it is not installed. Run {self.pip_install()}."
|
| 57 |
+
)
|
| 58 |
+
|
| 59 |
+
@classmethod
|
| 60 |
+
def pip_install(cls):
|
| 61 |
+
return f"`pip install {cls.pip_package or cls.name}`"
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
class OptunaBackend(HyperParamSearchBackendBase):
|
| 65 |
+
name = "optuna"
|
| 66 |
+
|
| 67 |
+
@staticmethod
|
| 68 |
+
def is_available():
|
| 69 |
+
return is_optuna_available()
|
| 70 |
+
|
| 71 |
+
def run(self, trainer, n_trials: int, direction: str, **kwargs):
|
| 72 |
+
return run_hp_search_optuna(trainer, n_trials, direction, **kwargs)
|
| 73 |
+
|
| 74 |
+
def default_hp_space(self, trial):
|
| 75 |
+
return default_hp_space_optuna(trial)
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
class RayTuneBackend(HyperParamSearchBackendBase):
|
| 79 |
+
name = "ray"
|
| 80 |
+
pip_package = "'ray[tune]'"
|
| 81 |
+
|
| 82 |
+
@staticmethod
|
| 83 |
+
def is_available():
|
| 84 |
+
return is_ray_tune_available()
|
| 85 |
+
|
| 86 |
+
def run(self, trainer, n_trials: int, direction: str, **kwargs):
|
| 87 |
+
return run_hp_search_ray(trainer, n_trials, direction, **kwargs)
|
| 88 |
+
|
| 89 |
+
def default_hp_space(self, trial):
|
| 90 |
+
return default_hp_space_ray(trial)
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
class SigOptBackend(HyperParamSearchBackendBase):
|
| 94 |
+
name = "sigopt"
|
| 95 |
+
|
| 96 |
+
@staticmethod
|
| 97 |
+
def is_available():
|
| 98 |
+
return is_sigopt_available()
|
| 99 |
+
|
| 100 |
+
def run(self, trainer, n_trials: int, direction: str, **kwargs):
|
| 101 |
+
return run_hp_search_sigopt(trainer, n_trials, direction, **kwargs)
|
| 102 |
+
|
| 103 |
+
def default_hp_space(self, trial):
|
| 104 |
+
return default_hp_space_sigopt(trial)
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
class WandbBackend(HyperParamSearchBackendBase):
|
| 108 |
+
name = "wandb"
|
| 109 |
+
|
| 110 |
+
@staticmethod
|
| 111 |
+
def is_available():
|
| 112 |
+
return is_wandb_available()
|
| 113 |
+
|
| 114 |
+
def run(self, trainer, n_trials: int, direction: str, **kwargs):
|
| 115 |
+
return run_hp_search_wandb(trainer, n_trials, direction, **kwargs)
|
| 116 |
+
|
| 117 |
+
def default_hp_space(self, trial):
|
| 118 |
+
return default_hp_space_wandb(trial)
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
ALL_HYPERPARAMETER_SEARCH_BACKENDS = {
|
| 122 |
+
HPSearchBackend(backend.name): backend for backend in [OptunaBackend, RayTuneBackend, SigOptBackend, WandbBackend]
|
| 123 |
+
}
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
def default_hp_search_backend() -> str:
|
| 127 |
+
available_backends = [backend for backend in ALL_HYPERPARAMETER_SEARCH_BACKENDS.values() if backend.is_available()]
|
| 128 |
+
if len(available_backends) > 0:
|
| 129 |
+
name = available_backends[0].name
|
| 130 |
+
if len(available_backends) > 1:
|
| 131 |
+
logger.info(
|
| 132 |
+
f"{len(available_backends)} hyperparameter search backends available. Using {name} as the default."
|
| 133 |
+
)
|
| 134 |
+
return name
|
| 135 |
+
raise RuntimeError(
|
| 136 |
+
"No hyperparameter search backend available.\n"
|
| 137 |
+
+ "\n".join(
|
| 138 |
+
f" - To install {backend.name} run {backend.pip_install()}"
|
| 139 |
+
for backend in ALL_HYPERPARAMETER_SEARCH_BACKENDS.values()
|
| 140 |
+
)
|
| 141 |
+
)
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/image_processing_base.py
ADDED
|
@@ -0,0 +1,543 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2020 The HuggingFace Inc. team.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
import copy
|
| 17 |
+
import json
|
| 18 |
+
import os
|
| 19 |
+
import warnings
|
| 20 |
+
from typing import Any, Optional, TypeVar, Union
|
| 21 |
+
|
| 22 |
+
import numpy as np
|
| 23 |
+
|
| 24 |
+
from .dynamic_module_utils import custom_object_save
|
| 25 |
+
from .feature_extraction_utils import BatchFeature as BaseBatchFeature
|
| 26 |
+
from .image_utils import is_valid_image, load_image
|
| 27 |
+
from .utils import (
|
| 28 |
+
IMAGE_PROCESSOR_NAME,
|
| 29 |
+
PROCESSOR_NAME,
|
| 30 |
+
PushToHubMixin,
|
| 31 |
+
copy_func,
|
| 32 |
+
download_url,
|
| 33 |
+
is_offline_mode,
|
| 34 |
+
is_remote_url,
|
| 35 |
+
logging,
|
| 36 |
+
)
|
| 37 |
+
from .utils.hub import cached_file
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
ImageProcessorType = TypeVar("ImageProcessorType", bound="ImageProcessingMixin")
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
logger = logging.get_logger(__name__)
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
# TODO: Move BatchFeature to be imported by both image_processing_utils and image_processing_utils_fast
|
| 47 |
+
# We override the class string here, but logic is the same.
|
| 48 |
+
class BatchFeature(BaseBatchFeature):
|
| 49 |
+
r"""
|
| 50 |
+
Holds the output of the image processor specific `__call__` methods.
|
| 51 |
+
|
| 52 |
+
This class is derived from a python dictionary and can be used as a dictionary.
|
| 53 |
+
|
| 54 |
+
Args:
|
| 55 |
+
data (`dict`):
|
| 56 |
+
Dictionary of lists/arrays/tensors returned by the __call__ method ('pixel_values', etc.).
|
| 57 |
+
tensor_type (`Union[None, str, TensorType]`, *optional*):
|
| 58 |
+
You can give a tensor_type here to convert the lists of integers in PyTorch/TensorFlow/Numpy Tensors at
|
| 59 |
+
initialization.
|
| 60 |
+
"""
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
# TODO: (Amy) - factor out the common parts of this and the feature extractor
|
| 64 |
+
class ImageProcessingMixin(PushToHubMixin):
|
| 65 |
+
"""
|
| 66 |
+
This is an image processor mixin used to provide saving/loading functionality for sequential and image feature
|
| 67 |
+
extractors.
|
| 68 |
+
"""
|
| 69 |
+
|
| 70 |
+
_auto_class = None
|
| 71 |
+
|
| 72 |
+
def __init__(self, **kwargs):
|
| 73 |
+
"""Set elements of `kwargs` as attributes."""
|
| 74 |
+
# This key was saved while we still used `XXXFeatureExtractor` for image processing. Now we use
|
| 75 |
+
# `XXXImageProcessor`, this attribute and its value are misleading.
|
| 76 |
+
kwargs.pop("feature_extractor_type", None)
|
| 77 |
+
# Pop "processor_class" as it should be saved as private attribute
|
| 78 |
+
self._processor_class = kwargs.pop("processor_class", None)
|
| 79 |
+
# Additional attributes without default values
|
| 80 |
+
for key, value in kwargs.items():
|
| 81 |
+
try:
|
| 82 |
+
setattr(self, key, value)
|
| 83 |
+
except AttributeError as err:
|
| 84 |
+
logger.error(f"Can't set {key} with value {value} for {self}")
|
| 85 |
+
raise err
|
| 86 |
+
|
| 87 |
+
def _set_processor_class(self, processor_class: str):
|
| 88 |
+
"""Sets processor class as an attribute."""
|
| 89 |
+
self._processor_class = processor_class
|
| 90 |
+
|
| 91 |
+
@classmethod
|
| 92 |
+
def from_pretrained(
|
| 93 |
+
cls: type[ImageProcessorType],
|
| 94 |
+
pretrained_model_name_or_path: Union[str, os.PathLike],
|
| 95 |
+
cache_dir: Optional[Union[str, os.PathLike]] = None,
|
| 96 |
+
force_download: bool = False,
|
| 97 |
+
local_files_only: bool = False,
|
| 98 |
+
token: Optional[Union[str, bool]] = None,
|
| 99 |
+
revision: str = "main",
|
| 100 |
+
**kwargs,
|
| 101 |
+
) -> ImageProcessorType:
|
| 102 |
+
r"""
|
| 103 |
+
Instantiate a type of [`~image_processing_utils.ImageProcessingMixin`] from an image processor.
|
| 104 |
+
|
| 105 |
+
Args:
|
| 106 |
+
pretrained_model_name_or_path (`str` or `os.PathLike`):
|
| 107 |
+
This can be either:
|
| 108 |
+
|
| 109 |
+
- a string, the *model id* of a pretrained image_processor hosted inside a model repo on
|
| 110 |
+
huggingface.co.
|
| 111 |
+
- a path to a *directory* containing a image processor file saved using the
|
| 112 |
+
[`~image_processing_utils.ImageProcessingMixin.save_pretrained`] method, e.g.,
|
| 113 |
+
`./my_model_directory/`.
|
| 114 |
+
- a path or url to a saved image processor JSON *file*, e.g.,
|
| 115 |
+
`./my_model_directory/preprocessor_config.json`.
|
| 116 |
+
cache_dir (`str` or `os.PathLike`, *optional*):
|
| 117 |
+
Path to a directory in which a downloaded pretrained model image processor should be cached if the
|
| 118 |
+
standard cache should not be used.
|
| 119 |
+
force_download (`bool`, *optional*, defaults to `False`):
|
| 120 |
+
Whether or not to force to (re-)download the image processor files and override the cached versions if
|
| 121 |
+
they exist.
|
| 122 |
+
resume_download:
|
| 123 |
+
Deprecated and ignored. All downloads are now resumed by default when possible.
|
| 124 |
+
Will be removed in v5 of Transformers.
|
| 125 |
+
proxies (`dict[str, str]`, *optional*):
|
| 126 |
+
A dictionary of proxy servers to use by protocol or endpoint, e.g., `{'http': 'foo.bar:3128',
|
| 127 |
+
'http://hostname': 'foo.bar:4012'}.` The proxies are used on each request.
|
| 128 |
+
token (`str` or `bool`, *optional*):
|
| 129 |
+
The token to use as HTTP bearer authorization for remote files. If `True`, or not specified, will use
|
| 130 |
+
the token generated when running `hf auth login` (stored in `~/.huggingface`).
|
| 131 |
+
revision (`str`, *optional*, defaults to `"main"`):
|
| 132 |
+
The specific model version to use. It can be a branch name, a tag name, or a commit id, since we use a
|
| 133 |
+
git-based system for storing models and other artifacts on huggingface.co, so `revision` can be any
|
| 134 |
+
identifier allowed by git.
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
<Tip>
|
| 138 |
+
|
| 139 |
+
To test a pull request you made on the Hub, you can pass `revision="refs/pr/<pr_number>"`.
|
| 140 |
+
|
| 141 |
+
</Tip>
|
| 142 |
+
|
| 143 |
+
return_unused_kwargs (`bool`, *optional*, defaults to `False`):
|
| 144 |
+
If `False`, then this function returns just the final image processor object. If `True`, then this
|
| 145 |
+
functions returns a `Tuple(image_processor, unused_kwargs)` where *unused_kwargs* is a dictionary
|
| 146 |
+
consisting of the key/value pairs whose keys are not image processor attributes: i.e., the part of
|
| 147 |
+
`kwargs` which has not been used to update `image_processor` and is otherwise ignored.
|
| 148 |
+
subfolder (`str`, *optional*, defaults to `""`):
|
| 149 |
+
In case the relevant files are located inside a subfolder of the model repo on huggingface.co, you can
|
| 150 |
+
specify the folder name here.
|
| 151 |
+
kwargs (`dict[str, Any]`, *optional*):
|
| 152 |
+
The values in kwargs of any keys which are image processor attributes will be used to override the
|
| 153 |
+
loaded values. Behavior concerning key/value pairs whose keys are *not* image processor attributes is
|
| 154 |
+
controlled by the `return_unused_kwargs` keyword parameter.
|
| 155 |
+
|
| 156 |
+
Returns:
|
| 157 |
+
A image processor of type [`~image_processing_utils.ImageProcessingMixin`].
|
| 158 |
+
|
| 159 |
+
Examples:
|
| 160 |
+
|
| 161 |
+
```python
|
| 162 |
+
# We can't instantiate directly the base class *ImageProcessingMixin* so let's show the examples on a
|
| 163 |
+
# derived class: *CLIPImageProcessor*
|
| 164 |
+
image_processor = CLIPImageProcessor.from_pretrained(
|
| 165 |
+
"openai/clip-vit-base-patch32"
|
| 166 |
+
) # Download image_processing_config from huggingface.co and cache.
|
| 167 |
+
image_processor = CLIPImageProcessor.from_pretrained(
|
| 168 |
+
"./test/saved_model/"
|
| 169 |
+
) # E.g. image processor (or model) was saved using *save_pretrained('./test/saved_model/')*
|
| 170 |
+
image_processor = CLIPImageProcessor.from_pretrained("./test/saved_model/preprocessor_config.json")
|
| 171 |
+
image_processor = CLIPImageProcessor.from_pretrained(
|
| 172 |
+
"openai/clip-vit-base-patch32", do_normalize=False, foo=False
|
| 173 |
+
)
|
| 174 |
+
assert image_processor.do_normalize is False
|
| 175 |
+
image_processor, unused_kwargs = CLIPImageProcessor.from_pretrained(
|
| 176 |
+
"openai/clip-vit-base-patch32", do_normalize=False, foo=False, return_unused_kwargs=True
|
| 177 |
+
)
|
| 178 |
+
assert image_processor.do_normalize is False
|
| 179 |
+
assert unused_kwargs == {"foo": False}
|
| 180 |
+
```"""
|
| 181 |
+
kwargs["cache_dir"] = cache_dir
|
| 182 |
+
kwargs["force_download"] = force_download
|
| 183 |
+
kwargs["local_files_only"] = local_files_only
|
| 184 |
+
kwargs["revision"] = revision
|
| 185 |
+
|
| 186 |
+
use_auth_token = kwargs.pop("use_auth_token", None)
|
| 187 |
+
if use_auth_token is not None:
|
| 188 |
+
warnings.warn(
|
| 189 |
+
"The `use_auth_token` argument is deprecated and will be removed in v5 of Transformers. Please use `token` instead.",
|
| 190 |
+
FutureWarning,
|
| 191 |
+
)
|
| 192 |
+
if token is not None:
|
| 193 |
+
raise ValueError(
|
| 194 |
+
"`token` and `use_auth_token` are both specified. Please set only the argument `token`."
|
| 195 |
+
)
|
| 196 |
+
token = use_auth_token
|
| 197 |
+
|
| 198 |
+
if token is not None:
|
| 199 |
+
kwargs["token"] = token
|
| 200 |
+
|
| 201 |
+
image_processor_dict, kwargs = cls.get_image_processor_dict(pretrained_model_name_or_path, **kwargs)
|
| 202 |
+
|
| 203 |
+
return cls.from_dict(image_processor_dict, **kwargs)
|
| 204 |
+
|
| 205 |
+
def save_pretrained(self, save_directory: Union[str, os.PathLike], push_to_hub: bool = False, **kwargs):
|
| 206 |
+
"""
|
| 207 |
+
Save an image processor object to the directory `save_directory`, so that it can be re-loaded using the
|
| 208 |
+
[`~image_processing_utils.ImageProcessingMixin.from_pretrained`] class method.
|
| 209 |
+
|
| 210 |
+
Args:
|
| 211 |
+
save_directory (`str` or `os.PathLike`):
|
| 212 |
+
Directory where the image processor JSON file will be saved (will be created if it does not exist).
|
| 213 |
+
push_to_hub (`bool`, *optional*, defaults to `False`):
|
| 214 |
+
Whether or not to push your model to the Hugging Face model hub after saving it. You can specify the
|
| 215 |
+
repository you want to push to with `repo_id` (will default to the name of `save_directory` in your
|
| 216 |
+
namespace).
|
| 217 |
+
kwargs (`dict[str, Any]`, *optional*):
|
| 218 |
+
Additional key word arguments passed along to the [`~utils.PushToHubMixin.push_to_hub`] method.
|
| 219 |
+
"""
|
| 220 |
+
use_auth_token = kwargs.pop("use_auth_token", None)
|
| 221 |
+
|
| 222 |
+
if use_auth_token is not None:
|
| 223 |
+
warnings.warn(
|
| 224 |
+
"The `use_auth_token` argument is deprecated and will be removed in v5 of Transformers. Please use `token` instead.",
|
| 225 |
+
FutureWarning,
|
| 226 |
+
)
|
| 227 |
+
if kwargs.get("token") is not None:
|
| 228 |
+
raise ValueError(
|
| 229 |
+
"`token` and `use_auth_token` are both specified. Please set only the argument `token`."
|
| 230 |
+
)
|
| 231 |
+
kwargs["token"] = use_auth_token
|
| 232 |
+
|
| 233 |
+
if os.path.isfile(save_directory):
|
| 234 |
+
raise AssertionError(f"Provided path ({save_directory}) should be a directory, not a file")
|
| 235 |
+
|
| 236 |
+
os.makedirs(save_directory, exist_ok=True)
|
| 237 |
+
|
| 238 |
+
if push_to_hub:
|
| 239 |
+
commit_message = kwargs.pop("commit_message", None)
|
| 240 |
+
repo_id = kwargs.pop("repo_id", save_directory.split(os.path.sep)[-1])
|
| 241 |
+
repo_id = self._create_repo(repo_id, **kwargs)
|
| 242 |
+
files_timestamps = self._get_files_timestamps(save_directory)
|
| 243 |
+
|
| 244 |
+
# If we have a custom config, we copy the file defining it in the folder and set the attributes so it can be
|
| 245 |
+
# loaded from the Hub.
|
| 246 |
+
if self._auto_class is not None:
|
| 247 |
+
custom_object_save(self, save_directory, config=self)
|
| 248 |
+
|
| 249 |
+
# If we save using the predefined names, we can load using `from_pretrained`
|
| 250 |
+
output_image_processor_file = os.path.join(save_directory, IMAGE_PROCESSOR_NAME)
|
| 251 |
+
|
| 252 |
+
self.to_json_file(output_image_processor_file)
|
| 253 |
+
logger.info(f"Image processor saved in {output_image_processor_file}")
|
| 254 |
+
|
| 255 |
+
if push_to_hub:
|
| 256 |
+
self._upload_modified_files(
|
| 257 |
+
save_directory,
|
| 258 |
+
repo_id,
|
| 259 |
+
files_timestamps,
|
| 260 |
+
commit_message=commit_message,
|
| 261 |
+
token=kwargs.get("token"),
|
| 262 |
+
)
|
| 263 |
+
|
| 264 |
+
return [output_image_processor_file]
|
| 265 |
+
|
| 266 |
+
@classmethod
|
| 267 |
+
def get_image_processor_dict(
|
| 268 |
+
cls, pretrained_model_name_or_path: Union[str, os.PathLike], **kwargs
|
| 269 |
+
) -> tuple[dict[str, Any], dict[str, Any]]:
|
| 270 |
+
"""
|
| 271 |
+
From a `pretrained_model_name_or_path`, resolve to a dictionary of parameters, to be used for instantiating a
|
| 272 |
+
image processor of type [`~image_processor_utils.ImageProcessingMixin`] using `from_dict`.
|
| 273 |
+
|
| 274 |
+
Parameters:
|
| 275 |
+
pretrained_model_name_or_path (`str` or `os.PathLike`):
|
| 276 |
+
The identifier of the pre-trained checkpoint from which we want the dictionary of parameters.
|
| 277 |
+
subfolder (`str`, *optional*, defaults to `""`):
|
| 278 |
+
In case the relevant files are located inside a subfolder of the model repo on huggingface.co, you can
|
| 279 |
+
specify the folder name here.
|
| 280 |
+
image_processor_filename (`str`, *optional*, defaults to `"config.json"`):
|
| 281 |
+
The name of the file in the model directory to use for the image processor config.
|
| 282 |
+
|
| 283 |
+
Returns:
|
| 284 |
+
`tuple[Dict, Dict]`: The dictionary(ies) that will be used to instantiate the image processor object.
|
| 285 |
+
"""
|
| 286 |
+
cache_dir = kwargs.pop("cache_dir", None)
|
| 287 |
+
force_download = kwargs.pop("force_download", False)
|
| 288 |
+
resume_download = kwargs.pop("resume_download", None)
|
| 289 |
+
proxies = kwargs.pop("proxies", None)
|
| 290 |
+
token = kwargs.pop("token", None)
|
| 291 |
+
use_auth_token = kwargs.pop("use_auth_token", None)
|
| 292 |
+
local_files_only = kwargs.pop("local_files_only", False)
|
| 293 |
+
revision = kwargs.pop("revision", None)
|
| 294 |
+
subfolder = kwargs.pop("subfolder", "")
|
| 295 |
+
image_processor_filename = kwargs.pop("image_processor_filename", IMAGE_PROCESSOR_NAME)
|
| 296 |
+
|
| 297 |
+
from_pipeline = kwargs.pop("_from_pipeline", None)
|
| 298 |
+
from_auto_class = kwargs.pop("_from_auto", False)
|
| 299 |
+
|
| 300 |
+
if use_auth_token is not None:
|
| 301 |
+
warnings.warn(
|
| 302 |
+
"The `use_auth_token` argument is deprecated and will be removed in v5 of Transformers. Please use `token` instead.",
|
| 303 |
+
FutureWarning,
|
| 304 |
+
)
|
| 305 |
+
if token is not None:
|
| 306 |
+
raise ValueError(
|
| 307 |
+
"`token` and `use_auth_token` are both specified. Please set only the argument `token`."
|
| 308 |
+
)
|
| 309 |
+
token = use_auth_token
|
| 310 |
+
|
| 311 |
+
user_agent = {"file_type": "image processor", "from_auto_class": from_auto_class}
|
| 312 |
+
if from_pipeline is not None:
|
| 313 |
+
user_agent["using_pipeline"] = from_pipeline
|
| 314 |
+
|
| 315 |
+
if is_offline_mode() and not local_files_only:
|
| 316 |
+
logger.info("Offline mode: forcing local_files_only=True")
|
| 317 |
+
local_files_only = True
|
| 318 |
+
|
| 319 |
+
pretrained_model_name_or_path = str(pretrained_model_name_or_path)
|
| 320 |
+
is_local = os.path.isdir(pretrained_model_name_or_path)
|
| 321 |
+
if os.path.isdir(pretrained_model_name_or_path):
|
| 322 |
+
image_processor_file = os.path.join(pretrained_model_name_or_path, image_processor_filename)
|
| 323 |
+
if os.path.isfile(pretrained_model_name_or_path):
|
| 324 |
+
resolved_image_processor_file = pretrained_model_name_or_path
|
| 325 |
+
is_local = True
|
| 326 |
+
elif is_remote_url(pretrained_model_name_or_path):
|
| 327 |
+
image_processor_file = pretrained_model_name_or_path
|
| 328 |
+
resolved_image_processor_file = download_url(pretrained_model_name_or_path)
|
| 329 |
+
else:
|
| 330 |
+
image_processor_file = image_processor_filename
|
| 331 |
+
try:
|
| 332 |
+
# Load from local folder or from cache or download from model Hub and cache
|
| 333 |
+
resolved_image_processor_files = [
|
| 334 |
+
resolved_file
|
| 335 |
+
for filename in [image_processor_file, PROCESSOR_NAME]
|
| 336 |
+
if (
|
| 337 |
+
resolved_file := cached_file(
|
| 338 |
+
pretrained_model_name_or_path,
|
| 339 |
+
filename=filename,
|
| 340 |
+
cache_dir=cache_dir,
|
| 341 |
+
force_download=force_download,
|
| 342 |
+
proxies=proxies,
|
| 343 |
+
resume_download=resume_download,
|
| 344 |
+
local_files_only=local_files_only,
|
| 345 |
+
token=token,
|
| 346 |
+
user_agent=user_agent,
|
| 347 |
+
revision=revision,
|
| 348 |
+
subfolder=subfolder,
|
| 349 |
+
_raise_exceptions_for_missing_entries=False,
|
| 350 |
+
)
|
| 351 |
+
)
|
| 352 |
+
is not None
|
| 353 |
+
]
|
| 354 |
+
resolved_image_processor_file = resolved_image_processor_files[0]
|
| 355 |
+
except OSError:
|
| 356 |
+
# Raise any environment error raise by `cached_file`. It will have a helpful error message adapted to
|
| 357 |
+
# the original exception.
|
| 358 |
+
raise
|
| 359 |
+
except Exception:
|
| 360 |
+
# For any other exception, we throw a generic error.
|
| 361 |
+
raise OSError(
|
| 362 |
+
f"Can't load image processor for '{pretrained_model_name_or_path}'. If you were trying to load"
|
| 363 |
+
" it from 'https://huggingface.co/models', make sure you don't have a local directory with the"
|
| 364 |
+
f" same name. Otherwise, make sure '{pretrained_model_name_or_path}' is the correct path to a"
|
| 365 |
+
f" directory containing a {image_processor_filename} file"
|
| 366 |
+
)
|
| 367 |
+
|
| 368 |
+
try:
|
| 369 |
+
# Load image_processor dict
|
| 370 |
+
with open(resolved_image_processor_file, encoding="utf-8") as reader:
|
| 371 |
+
text = reader.read()
|
| 372 |
+
image_processor_dict = json.loads(text)
|
| 373 |
+
image_processor_dict = image_processor_dict.get("image_processor", image_processor_dict)
|
| 374 |
+
|
| 375 |
+
except json.JSONDecodeError:
|
| 376 |
+
raise OSError(
|
| 377 |
+
f"It looks like the config file at '{resolved_image_processor_file}' is not a valid JSON file."
|
| 378 |
+
)
|
| 379 |
+
|
| 380 |
+
if is_local:
|
| 381 |
+
logger.info(f"loading configuration file {resolved_image_processor_file}")
|
| 382 |
+
else:
|
| 383 |
+
logger.info(
|
| 384 |
+
f"loading configuration file {image_processor_file} from cache at {resolved_image_processor_file}"
|
| 385 |
+
)
|
| 386 |
+
|
| 387 |
+
return image_processor_dict, kwargs
|
| 388 |
+
|
| 389 |
+
@classmethod
|
| 390 |
+
def from_dict(cls, image_processor_dict: dict[str, Any], **kwargs):
|
| 391 |
+
"""
|
| 392 |
+
Instantiates a type of [`~image_processing_utils.ImageProcessingMixin`] from a Python dictionary of parameters.
|
| 393 |
+
|
| 394 |
+
Args:
|
| 395 |
+
image_processor_dict (`dict[str, Any]`):
|
| 396 |
+
Dictionary that will be used to instantiate the image processor object. Such a dictionary can be
|
| 397 |
+
retrieved from a pretrained checkpoint by leveraging the
|
| 398 |
+
[`~image_processing_utils.ImageProcessingMixin.to_dict`] method.
|
| 399 |
+
kwargs (`dict[str, Any]`):
|
| 400 |
+
Additional parameters from which to initialize the image processor object.
|
| 401 |
+
|
| 402 |
+
Returns:
|
| 403 |
+
[`~image_processing_utils.ImageProcessingMixin`]: The image processor object instantiated from those
|
| 404 |
+
parameters.
|
| 405 |
+
"""
|
| 406 |
+
image_processor_dict = image_processor_dict.copy()
|
| 407 |
+
return_unused_kwargs = kwargs.pop("return_unused_kwargs", False)
|
| 408 |
+
|
| 409 |
+
# The `size` parameter is a dict and was previously an int or tuple in feature extractors.
|
| 410 |
+
# We set `size` here directly to the `image_processor_dict` so that it is converted to the appropriate
|
| 411 |
+
# dict within the image processor and isn't overwritten if `size` is passed in as a kwarg.
|
| 412 |
+
if "size" in kwargs and "size" in image_processor_dict:
|
| 413 |
+
image_processor_dict["size"] = kwargs.pop("size")
|
| 414 |
+
if "crop_size" in kwargs and "crop_size" in image_processor_dict:
|
| 415 |
+
image_processor_dict["crop_size"] = kwargs.pop("crop_size")
|
| 416 |
+
|
| 417 |
+
image_processor = cls(**image_processor_dict)
|
| 418 |
+
|
| 419 |
+
# Update image_processor with kwargs if needed
|
| 420 |
+
to_remove = []
|
| 421 |
+
for key, value in kwargs.items():
|
| 422 |
+
if hasattr(image_processor, key):
|
| 423 |
+
setattr(image_processor, key, value)
|
| 424 |
+
to_remove.append(key)
|
| 425 |
+
for key in to_remove:
|
| 426 |
+
kwargs.pop(key, None)
|
| 427 |
+
|
| 428 |
+
logger.info(f"Image processor {image_processor}")
|
| 429 |
+
if return_unused_kwargs:
|
| 430 |
+
return image_processor, kwargs
|
| 431 |
+
else:
|
| 432 |
+
return image_processor
|
| 433 |
+
|
| 434 |
+
def to_dict(self) -> dict[str, Any]:
|
| 435 |
+
"""
|
| 436 |
+
Serializes this instance to a Python dictionary.
|
| 437 |
+
|
| 438 |
+
Returns:
|
| 439 |
+
`dict[str, Any]`: Dictionary of all the attributes that make up this image processor instance.
|
| 440 |
+
"""
|
| 441 |
+
output = copy.deepcopy(self.__dict__)
|
| 442 |
+
output["image_processor_type"] = self.__class__.__name__
|
| 443 |
+
|
| 444 |
+
return output
|
| 445 |
+
|
| 446 |
+
@classmethod
|
| 447 |
+
def from_json_file(cls, json_file: Union[str, os.PathLike]):
|
| 448 |
+
"""
|
| 449 |
+
Instantiates a image processor of type [`~image_processing_utils.ImageProcessingMixin`] from the path to a JSON
|
| 450 |
+
file of parameters.
|
| 451 |
+
|
| 452 |
+
Args:
|
| 453 |
+
json_file (`str` or `os.PathLike`):
|
| 454 |
+
Path to the JSON file containing the parameters.
|
| 455 |
+
|
| 456 |
+
Returns:
|
| 457 |
+
A image processor of type [`~image_processing_utils.ImageProcessingMixin`]: The image_processor object
|
| 458 |
+
instantiated from that JSON file.
|
| 459 |
+
"""
|
| 460 |
+
with open(json_file, encoding="utf-8") as reader:
|
| 461 |
+
text = reader.read()
|
| 462 |
+
image_processor_dict = json.loads(text)
|
| 463 |
+
return cls(**image_processor_dict)
|
| 464 |
+
|
| 465 |
+
def to_json_string(self) -> str:
|
| 466 |
+
"""
|
| 467 |
+
Serializes this instance to a JSON string.
|
| 468 |
+
|
| 469 |
+
Returns:
|
| 470 |
+
`str`: String containing all the attributes that make up this feature_extractor instance in JSON format.
|
| 471 |
+
"""
|
| 472 |
+
dictionary = self.to_dict()
|
| 473 |
+
|
| 474 |
+
for key, value in dictionary.items():
|
| 475 |
+
if isinstance(value, np.ndarray):
|
| 476 |
+
dictionary[key] = value.tolist()
|
| 477 |
+
|
| 478 |
+
# make sure private name "_processor_class" is correctly
|
| 479 |
+
# saved as "processor_class"
|
| 480 |
+
_processor_class = dictionary.pop("_processor_class", None)
|
| 481 |
+
if _processor_class is not None:
|
| 482 |
+
dictionary["processor_class"] = _processor_class
|
| 483 |
+
|
| 484 |
+
return json.dumps(dictionary, indent=2, sort_keys=True) + "\n"
|
| 485 |
+
|
| 486 |
+
def to_json_file(self, json_file_path: Union[str, os.PathLike]):
|
| 487 |
+
"""
|
| 488 |
+
Save this instance to a JSON file.
|
| 489 |
+
|
| 490 |
+
Args:
|
| 491 |
+
json_file_path (`str` or `os.PathLike`):
|
| 492 |
+
Path to the JSON file in which this image_processor instance's parameters will be saved.
|
| 493 |
+
"""
|
| 494 |
+
with open(json_file_path, "w", encoding="utf-8") as writer:
|
| 495 |
+
writer.write(self.to_json_string())
|
| 496 |
+
|
| 497 |
+
def __repr__(self):
|
| 498 |
+
return f"{self.__class__.__name__} {self.to_json_string()}"
|
| 499 |
+
|
| 500 |
+
@classmethod
|
| 501 |
+
def register_for_auto_class(cls, auto_class="AutoImageProcessor"):
|
| 502 |
+
"""
|
| 503 |
+
Register this class with a given auto class. This should only be used for custom image processors as the ones
|
| 504 |
+
in the library are already mapped with `AutoImageProcessor `.
|
| 505 |
+
|
| 506 |
+
|
| 507 |
+
|
| 508 |
+
Args:
|
| 509 |
+
auto_class (`str` or `type`, *optional*, defaults to `"AutoImageProcessor "`):
|
| 510 |
+
The auto class to register this new image processor with.
|
| 511 |
+
"""
|
| 512 |
+
if not isinstance(auto_class, str):
|
| 513 |
+
auto_class = auto_class.__name__
|
| 514 |
+
|
| 515 |
+
import transformers.models.auto as auto_module
|
| 516 |
+
|
| 517 |
+
if not hasattr(auto_module, auto_class):
|
| 518 |
+
raise ValueError(f"{auto_class} is not a valid auto class.")
|
| 519 |
+
|
| 520 |
+
cls._auto_class = auto_class
|
| 521 |
+
|
| 522 |
+
def fetch_images(self, image_url_or_urls: Union[str, list[str], list[list[str]]]):
|
| 523 |
+
"""
|
| 524 |
+
Convert a single or a list of urls into the corresponding `PIL.Image` objects.
|
| 525 |
+
|
| 526 |
+
If a single url is passed, the return value will be a single object. If a list is passed a list of objects is
|
| 527 |
+
returned.
|
| 528 |
+
"""
|
| 529 |
+
if isinstance(image_url_or_urls, list):
|
| 530 |
+
return [self.fetch_images(x) for x in image_url_or_urls]
|
| 531 |
+
elif isinstance(image_url_or_urls, str):
|
| 532 |
+
return load_image(image_url_or_urls)
|
| 533 |
+
elif is_valid_image(image_url_or_urls):
|
| 534 |
+
return image_url_or_urls
|
| 535 |
+
else:
|
| 536 |
+
raise TypeError(f"only a single or a list of entries is supported but got type={type(image_url_or_urls)}")
|
| 537 |
+
|
| 538 |
+
|
| 539 |
+
ImageProcessingMixin.push_to_hub = copy_func(ImageProcessingMixin.push_to_hub)
|
| 540 |
+
if ImageProcessingMixin.push_to_hub.__doc__ is not None:
|
| 541 |
+
ImageProcessingMixin.push_to_hub.__doc__ = ImageProcessingMixin.push_to_hub.__doc__.format(
|
| 542 |
+
object="image processor", object_class="AutoImageProcessor", object_files="image processor file"
|
| 543 |
+
)
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/image_processing_utils.py
ADDED
|
@@ -0,0 +1,317 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2022 The HuggingFace Inc. team.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
import math
|
| 16 |
+
from collections.abc import Iterable
|
| 17 |
+
from typing import Optional, Union
|
| 18 |
+
|
| 19 |
+
import numpy as np
|
| 20 |
+
|
| 21 |
+
from .image_processing_base import BatchFeature, ImageProcessingMixin
|
| 22 |
+
from .image_transforms import center_crop, normalize, rescale
|
| 23 |
+
from .image_utils import ChannelDimension, get_image_size
|
| 24 |
+
from .utils import logging
|
| 25 |
+
from .utils.import_utils import requires
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
logger = logging.get_logger(__name__)
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
INIT_SERVICE_KWARGS = [
|
| 32 |
+
"processor_class",
|
| 33 |
+
"image_processor_type",
|
| 34 |
+
]
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
@requires(backends=("vision",))
|
| 38 |
+
class BaseImageProcessor(ImageProcessingMixin):
|
| 39 |
+
def __init__(self, **kwargs):
|
| 40 |
+
super().__init__(**kwargs)
|
| 41 |
+
|
| 42 |
+
@property
|
| 43 |
+
def is_fast(self) -> bool:
|
| 44 |
+
"""
|
| 45 |
+
`bool`: Whether or not this image processor is a fast processor (backed by PyTorch and TorchVision).
|
| 46 |
+
"""
|
| 47 |
+
return False
|
| 48 |
+
|
| 49 |
+
def __call__(self, images, **kwargs) -> BatchFeature:
|
| 50 |
+
"""Preprocess an image or a batch of images."""
|
| 51 |
+
return self.preprocess(images, **kwargs)
|
| 52 |
+
|
| 53 |
+
def preprocess(self, images, **kwargs) -> BatchFeature:
|
| 54 |
+
raise NotImplementedError("Each image processor must implement its own preprocess method")
|
| 55 |
+
|
| 56 |
+
def rescale(
|
| 57 |
+
self,
|
| 58 |
+
image: np.ndarray,
|
| 59 |
+
scale: float,
|
| 60 |
+
data_format: Optional[Union[str, ChannelDimension]] = None,
|
| 61 |
+
input_data_format: Optional[Union[str, ChannelDimension]] = None,
|
| 62 |
+
**kwargs,
|
| 63 |
+
) -> np.ndarray:
|
| 64 |
+
"""
|
| 65 |
+
Rescale an image by a scale factor. image = image * scale.
|
| 66 |
+
|
| 67 |
+
Args:
|
| 68 |
+
image (`np.ndarray`):
|
| 69 |
+
Image to rescale.
|
| 70 |
+
scale (`float`):
|
| 71 |
+
The scaling factor to rescale pixel values by.
|
| 72 |
+
data_format (`str` or `ChannelDimension`, *optional*):
|
| 73 |
+
The channel dimension format for the output image. If unset, the channel dimension format of the input
|
| 74 |
+
image is used. Can be one of:
|
| 75 |
+
- `"channels_first"` or `ChannelDimension.FIRST`: image in (num_channels, height, width) format.
|
| 76 |
+
- `"channels_last"` or `ChannelDimension.LAST`: image in (height, width, num_channels) format.
|
| 77 |
+
input_data_format (`ChannelDimension` or `str`, *optional*):
|
| 78 |
+
The channel dimension format for the input image. If unset, the channel dimension format is inferred
|
| 79 |
+
from the input image. Can be one of:
|
| 80 |
+
- `"channels_first"` or `ChannelDimension.FIRST`: image in (num_channels, height, width) format.
|
| 81 |
+
- `"channels_last"` or `ChannelDimension.LAST`: image in (height, width, num_channels) format.
|
| 82 |
+
|
| 83 |
+
Returns:
|
| 84 |
+
`np.ndarray`: The rescaled image.
|
| 85 |
+
"""
|
| 86 |
+
return rescale(image, scale=scale, data_format=data_format, input_data_format=input_data_format, **kwargs)
|
| 87 |
+
|
| 88 |
+
def normalize(
|
| 89 |
+
self,
|
| 90 |
+
image: np.ndarray,
|
| 91 |
+
mean: Union[float, Iterable[float]],
|
| 92 |
+
std: Union[float, Iterable[float]],
|
| 93 |
+
data_format: Optional[Union[str, ChannelDimension]] = None,
|
| 94 |
+
input_data_format: Optional[Union[str, ChannelDimension]] = None,
|
| 95 |
+
**kwargs,
|
| 96 |
+
) -> np.ndarray:
|
| 97 |
+
"""
|
| 98 |
+
Normalize an image. image = (image - image_mean) / image_std.
|
| 99 |
+
|
| 100 |
+
Args:
|
| 101 |
+
image (`np.ndarray`):
|
| 102 |
+
Image to normalize.
|
| 103 |
+
mean (`float` or `Iterable[float]`):
|
| 104 |
+
Image mean to use for normalization.
|
| 105 |
+
std (`float` or `Iterable[float]`):
|
| 106 |
+
Image standard deviation to use for normalization.
|
| 107 |
+
data_format (`str` or `ChannelDimension`, *optional*):
|
| 108 |
+
The channel dimension format for the output image. If unset, the channel dimension format of the input
|
| 109 |
+
image is used. Can be one of:
|
| 110 |
+
- `"channels_first"` or `ChannelDimension.FIRST`: image in (num_channels, height, width) format.
|
| 111 |
+
- `"channels_last"` or `ChannelDimension.LAST`: image in (height, width, num_channels) format.
|
| 112 |
+
input_data_format (`ChannelDimension` or `str`, *optional*):
|
| 113 |
+
The channel dimension format for the input image. If unset, the channel dimension format is inferred
|
| 114 |
+
from the input image. Can be one of:
|
| 115 |
+
- `"channels_first"` or `ChannelDimension.FIRST`: image in (num_channels, height, width) format.
|
| 116 |
+
- `"channels_last"` or `ChannelDimension.LAST`: image in (height, width, num_channels) format.
|
| 117 |
+
|
| 118 |
+
Returns:
|
| 119 |
+
`np.ndarray`: The normalized image.
|
| 120 |
+
"""
|
| 121 |
+
return normalize(
|
| 122 |
+
image, mean=mean, std=std, data_format=data_format, input_data_format=input_data_format, **kwargs
|
| 123 |
+
)
|
| 124 |
+
|
| 125 |
+
def center_crop(
|
| 126 |
+
self,
|
| 127 |
+
image: np.ndarray,
|
| 128 |
+
size: dict[str, int],
|
| 129 |
+
data_format: Optional[Union[str, ChannelDimension]] = None,
|
| 130 |
+
input_data_format: Optional[Union[str, ChannelDimension]] = None,
|
| 131 |
+
**kwargs,
|
| 132 |
+
) -> np.ndarray:
|
| 133 |
+
"""
|
| 134 |
+
Center crop an image to `(size["height"], size["width"])`. If the input size is smaller than `crop_size` along
|
| 135 |
+
any edge, the image is padded with 0's and then center cropped.
|
| 136 |
+
|
| 137 |
+
Args:
|
| 138 |
+
image (`np.ndarray`):
|
| 139 |
+
Image to center crop.
|
| 140 |
+
size (`dict[str, int]`):
|
| 141 |
+
Size of the output image.
|
| 142 |
+
data_format (`str` or `ChannelDimension`, *optional*):
|
| 143 |
+
The channel dimension format for the output image. If unset, the channel dimension format of the input
|
| 144 |
+
image is used. Can be one of:
|
| 145 |
+
- `"channels_first"` or `ChannelDimension.FIRST`: image in (num_channels, height, width) format.
|
| 146 |
+
- `"channels_last"` or `ChannelDimension.LAST`: image in (height, width, num_channels) format.
|
| 147 |
+
input_data_format (`ChannelDimension` or `str`, *optional*):
|
| 148 |
+
The channel dimension format for the input image. If unset, the channel dimension format is inferred
|
| 149 |
+
from the input image. Can be one of:
|
| 150 |
+
- `"channels_first"` or `ChannelDimension.FIRST`: image in (num_channels, height, width) format.
|
| 151 |
+
- `"channels_last"` or `ChannelDimension.LAST`: image in (height, width, num_channels) format.
|
| 152 |
+
"""
|
| 153 |
+
size = get_size_dict(size)
|
| 154 |
+
if "height" not in size or "width" not in size:
|
| 155 |
+
raise ValueError(f"The size dictionary must have keys 'height' and 'width'. Got {size.keys()}")
|
| 156 |
+
return center_crop(
|
| 157 |
+
image,
|
| 158 |
+
size=(size["height"], size["width"]),
|
| 159 |
+
data_format=data_format,
|
| 160 |
+
input_data_format=input_data_format,
|
| 161 |
+
**kwargs,
|
| 162 |
+
)
|
| 163 |
+
|
| 164 |
+
def to_dict(self):
|
| 165 |
+
encoder_dict = super().to_dict()
|
| 166 |
+
encoder_dict.pop("_valid_processor_keys", None)
|
| 167 |
+
return encoder_dict
|
| 168 |
+
|
| 169 |
+
|
| 170 |
+
VALID_SIZE_DICT_KEYS = (
|
| 171 |
+
{"height", "width"},
|
| 172 |
+
{"shortest_edge"},
|
| 173 |
+
{"shortest_edge", "longest_edge"},
|
| 174 |
+
{"longest_edge"},
|
| 175 |
+
{"max_height", "max_width"},
|
| 176 |
+
)
|
| 177 |
+
|
| 178 |
+
|
| 179 |
+
def is_valid_size_dict(size_dict):
|
| 180 |
+
if not isinstance(size_dict, dict):
|
| 181 |
+
return False
|
| 182 |
+
|
| 183 |
+
size_dict_keys = set(size_dict.keys())
|
| 184 |
+
for allowed_keys in VALID_SIZE_DICT_KEYS:
|
| 185 |
+
if size_dict_keys == allowed_keys:
|
| 186 |
+
return True
|
| 187 |
+
return False
|
| 188 |
+
|
| 189 |
+
|
| 190 |
+
def convert_to_size_dict(
|
| 191 |
+
size, max_size: Optional[int] = None, default_to_square: bool = True, height_width_order: bool = True
|
| 192 |
+
):
|
| 193 |
+
# By default, if size is an int we assume it represents a tuple of (size, size).
|
| 194 |
+
if isinstance(size, int) and default_to_square:
|
| 195 |
+
if max_size is not None:
|
| 196 |
+
raise ValueError("Cannot specify both size as an int, with default_to_square=True and max_size")
|
| 197 |
+
return {"height": size, "width": size}
|
| 198 |
+
# In other configs, if size is an int and default_to_square is False, size represents the length of
|
| 199 |
+
# the shortest edge after resizing.
|
| 200 |
+
elif isinstance(size, int) and not default_to_square:
|
| 201 |
+
size_dict = {"shortest_edge": size}
|
| 202 |
+
if max_size is not None:
|
| 203 |
+
size_dict["longest_edge"] = max_size
|
| 204 |
+
return size_dict
|
| 205 |
+
# Otherwise, if size is a tuple it's either (height, width) or (width, height)
|
| 206 |
+
elif isinstance(size, (tuple, list)) and height_width_order:
|
| 207 |
+
return {"height": size[0], "width": size[1]}
|
| 208 |
+
elif isinstance(size, (tuple, list)) and not height_width_order:
|
| 209 |
+
return {"height": size[1], "width": size[0]}
|
| 210 |
+
elif size is None and max_size is not None:
|
| 211 |
+
if default_to_square:
|
| 212 |
+
raise ValueError("Cannot specify both default_to_square=True and max_size")
|
| 213 |
+
return {"longest_edge": max_size}
|
| 214 |
+
|
| 215 |
+
raise ValueError(f"Could not convert size input to size dict: {size}")
|
| 216 |
+
|
| 217 |
+
|
| 218 |
+
def get_size_dict(
|
| 219 |
+
size: Optional[Union[int, Iterable[int], dict[str, int]]] = None,
|
| 220 |
+
max_size: Optional[int] = None,
|
| 221 |
+
height_width_order: bool = True,
|
| 222 |
+
default_to_square: bool = True,
|
| 223 |
+
param_name="size",
|
| 224 |
+
) -> dict:
|
| 225 |
+
"""
|
| 226 |
+
Converts the old size parameter in the config into the new dict expected in the config. This is to ensure backwards
|
| 227 |
+
compatibility with the old image processor configs and removes ambiguity over whether the tuple is in (height,
|
| 228 |
+
width) or (width, height) format.
|
| 229 |
+
|
| 230 |
+
- If `size` is tuple, it is converted to `{"height": size[0], "width": size[1]}` or `{"height": size[1], "width":
|
| 231 |
+
size[0]}` if `height_width_order` is `False`.
|
| 232 |
+
- If `size` is an int, and `default_to_square` is `True`, it is converted to `{"height": size, "width": size}`.
|
| 233 |
+
- If `size` is an int and `default_to_square` is False, it is converted to `{"shortest_edge": size}`. If `max_size`
|
| 234 |
+
is set, it is added to the dict as `{"longest_edge": max_size}`.
|
| 235 |
+
|
| 236 |
+
Args:
|
| 237 |
+
size (`Union[int, Iterable[int], dict[str, int]]`, *optional*):
|
| 238 |
+
The `size` parameter to be cast into a size dictionary.
|
| 239 |
+
max_size (`Optional[int]`, *optional*):
|
| 240 |
+
The `max_size` parameter to be cast into a size dictionary.
|
| 241 |
+
height_width_order (`bool`, *optional*, defaults to `True`):
|
| 242 |
+
If `size` is a tuple, whether it's in (height, width) or (width, height) order.
|
| 243 |
+
default_to_square (`bool`, *optional*, defaults to `True`):
|
| 244 |
+
If `size` is an int, whether to default to a square image or not.
|
| 245 |
+
"""
|
| 246 |
+
if not isinstance(size, dict):
|
| 247 |
+
size_dict = convert_to_size_dict(size, max_size, default_to_square, height_width_order)
|
| 248 |
+
logger.info(
|
| 249 |
+
f"{param_name} should be a dictionary on of the following set of keys: {VALID_SIZE_DICT_KEYS}, got {size}."
|
| 250 |
+
f" Converted to {size_dict}.",
|
| 251 |
+
)
|
| 252 |
+
else:
|
| 253 |
+
size_dict = size
|
| 254 |
+
|
| 255 |
+
if not is_valid_size_dict(size_dict):
|
| 256 |
+
raise ValueError(
|
| 257 |
+
f"{param_name} must have one of the following set of keys: {VALID_SIZE_DICT_KEYS}, got {size_dict.keys()}"
|
| 258 |
+
)
|
| 259 |
+
return size_dict
|
| 260 |
+
|
| 261 |
+
|
| 262 |
+
def select_best_resolution(original_size: tuple, possible_resolutions: list) -> tuple:
|
| 263 |
+
"""
|
| 264 |
+
Selects the best resolution from a list of possible resolutions based on the original size.
|
| 265 |
+
|
| 266 |
+
This is done by calculating the effective and wasted resolution for each possible resolution.
|
| 267 |
+
|
| 268 |
+
The best fit resolution is the one that maximizes the effective resolution and minimizes the wasted resolution.
|
| 269 |
+
|
| 270 |
+
Args:
|
| 271 |
+
original_size (tuple):
|
| 272 |
+
The original size of the image in the format (height, width).
|
| 273 |
+
possible_resolutions (list):
|
| 274 |
+
A list of possible resolutions in the format [(height1, width1), (height2, width2), ...].
|
| 275 |
+
|
| 276 |
+
Returns:
|
| 277 |
+
tuple: The best fit resolution in the format (height, width).
|
| 278 |
+
"""
|
| 279 |
+
original_height, original_width = original_size
|
| 280 |
+
best_fit = None
|
| 281 |
+
max_effective_resolution = 0
|
| 282 |
+
min_wasted_resolution = float("inf")
|
| 283 |
+
|
| 284 |
+
for height, width in possible_resolutions:
|
| 285 |
+
scale = min(width / original_width, height / original_height)
|
| 286 |
+
downscaled_width, downscaled_height = int(original_width * scale), int(original_height * scale)
|
| 287 |
+
effective_resolution = min(downscaled_width * downscaled_height, original_width * original_height)
|
| 288 |
+
wasted_resolution = (width * height) - effective_resolution
|
| 289 |
+
|
| 290 |
+
if effective_resolution > max_effective_resolution or (
|
| 291 |
+
effective_resolution == max_effective_resolution and wasted_resolution < min_wasted_resolution
|
| 292 |
+
):
|
| 293 |
+
max_effective_resolution = effective_resolution
|
| 294 |
+
min_wasted_resolution = wasted_resolution
|
| 295 |
+
best_fit = (height, width)
|
| 296 |
+
|
| 297 |
+
return best_fit
|
| 298 |
+
|
| 299 |
+
|
| 300 |
+
def get_patch_output_size(image, target_resolution, input_data_format):
|
| 301 |
+
"""
|
| 302 |
+
Given an image and a target resolution, calculate the output size of the image after cropping to the target
|
| 303 |
+
"""
|
| 304 |
+
original_height, original_width = get_image_size(image, channel_dim=input_data_format)
|
| 305 |
+
target_height, target_width = target_resolution
|
| 306 |
+
|
| 307 |
+
scale_w = target_width / original_width
|
| 308 |
+
scale_h = target_height / original_height
|
| 309 |
+
|
| 310 |
+
if scale_w < scale_h:
|
| 311 |
+
new_width = target_width
|
| 312 |
+
new_height = min(math.ceil(original_height * scale_w), target_height)
|
| 313 |
+
else:
|
| 314 |
+
new_height = target_height
|
| 315 |
+
new_width = min(math.ceil(original_width * scale_h), target_width)
|
| 316 |
+
|
| 317 |
+
return new_height, new_width
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/image_utils.py
ADDED
|
@@ -0,0 +1,969 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2021 The HuggingFace Inc. team.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
import base64
|
| 16 |
+
import os
|
| 17 |
+
from collections.abc import Iterable
|
| 18 |
+
from dataclasses import dataclass
|
| 19 |
+
from io import BytesIO
|
| 20 |
+
from typing import Optional, Union
|
| 21 |
+
|
| 22 |
+
import numpy as np
|
| 23 |
+
import requests
|
| 24 |
+
|
| 25 |
+
from .utils import (
|
| 26 |
+
ExplicitEnum,
|
| 27 |
+
is_jax_tensor,
|
| 28 |
+
is_numpy_array,
|
| 29 |
+
is_tf_tensor,
|
| 30 |
+
is_torch_available,
|
| 31 |
+
is_torch_tensor,
|
| 32 |
+
is_torchvision_available,
|
| 33 |
+
is_vision_available,
|
| 34 |
+
logging,
|
| 35 |
+
requires_backends,
|
| 36 |
+
to_numpy,
|
| 37 |
+
)
|
| 38 |
+
from .utils.constants import ( # noqa: F401
|
| 39 |
+
IMAGENET_DEFAULT_MEAN,
|
| 40 |
+
IMAGENET_DEFAULT_STD,
|
| 41 |
+
IMAGENET_STANDARD_MEAN,
|
| 42 |
+
IMAGENET_STANDARD_STD,
|
| 43 |
+
OPENAI_CLIP_MEAN,
|
| 44 |
+
OPENAI_CLIP_STD,
|
| 45 |
+
)
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
if is_vision_available():
|
| 49 |
+
import PIL.Image
|
| 50 |
+
import PIL.ImageOps
|
| 51 |
+
|
| 52 |
+
PILImageResampling = PIL.Image.Resampling
|
| 53 |
+
|
| 54 |
+
if is_torchvision_available():
|
| 55 |
+
from torchvision.transforms import InterpolationMode
|
| 56 |
+
|
| 57 |
+
pil_torch_interpolation_mapping = {
|
| 58 |
+
PILImageResampling.NEAREST: InterpolationMode.NEAREST_EXACT,
|
| 59 |
+
PILImageResampling.BOX: InterpolationMode.BOX,
|
| 60 |
+
PILImageResampling.BILINEAR: InterpolationMode.BILINEAR,
|
| 61 |
+
PILImageResampling.HAMMING: InterpolationMode.HAMMING,
|
| 62 |
+
PILImageResampling.BICUBIC: InterpolationMode.BICUBIC,
|
| 63 |
+
PILImageResampling.LANCZOS: InterpolationMode.LANCZOS,
|
| 64 |
+
}
|
| 65 |
+
else:
|
| 66 |
+
pil_torch_interpolation_mapping = {}
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
if is_torch_available():
|
| 70 |
+
import torch
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
logger = logging.get_logger(__name__)
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
ImageInput = Union[
|
| 77 |
+
"PIL.Image.Image", np.ndarray, "torch.Tensor", list["PIL.Image.Image"], list[np.ndarray], list["torch.Tensor"]
|
| 78 |
+
]
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
class ChannelDimension(ExplicitEnum):
|
| 82 |
+
FIRST = "channels_first"
|
| 83 |
+
LAST = "channels_last"
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
class AnnotationFormat(ExplicitEnum):
|
| 87 |
+
COCO_DETECTION = "coco_detection"
|
| 88 |
+
COCO_PANOPTIC = "coco_panoptic"
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
class AnnotionFormat(ExplicitEnum):
|
| 92 |
+
COCO_DETECTION = AnnotationFormat.COCO_DETECTION.value
|
| 93 |
+
COCO_PANOPTIC = AnnotationFormat.COCO_PANOPTIC.value
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
AnnotationType = dict[str, Union[int, str, list[dict]]]
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
def is_pil_image(img):
|
| 100 |
+
return is_vision_available() and isinstance(img, PIL.Image.Image)
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
class ImageType(ExplicitEnum):
|
| 104 |
+
PIL = "pillow"
|
| 105 |
+
TORCH = "torch"
|
| 106 |
+
NUMPY = "numpy"
|
| 107 |
+
TENSORFLOW = "tensorflow"
|
| 108 |
+
JAX = "jax"
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
def get_image_type(image):
|
| 112 |
+
if is_pil_image(image):
|
| 113 |
+
return ImageType.PIL
|
| 114 |
+
if is_torch_tensor(image):
|
| 115 |
+
return ImageType.TORCH
|
| 116 |
+
if is_numpy_array(image):
|
| 117 |
+
return ImageType.NUMPY
|
| 118 |
+
if is_tf_tensor(image):
|
| 119 |
+
return ImageType.TENSORFLOW
|
| 120 |
+
if is_jax_tensor(image):
|
| 121 |
+
return ImageType.JAX
|
| 122 |
+
raise ValueError(f"Unrecognized image type {type(image)}")
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
def is_valid_image(img):
|
| 126 |
+
return is_pil_image(img) or is_numpy_array(img) or is_torch_tensor(img) or is_tf_tensor(img) or is_jax_tensor(img)
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
def is_valid_list_of_images(images: list):
|
| 130 |
+
return images and all(is_valid_image(image) for image in images)
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
def concatenate_list(input_list):
|
| 134 |
+
if isinstance(input_list[0], list):
|
| 135 |
+
return [item for sublist in input_list for item in sublist]
|
| 136 |
+
elif isinstance(input_list[0], np.ndarray):
|
| 137 |
+
return np.concatenate(input_list, axis=0)
|
| 138 |
+
elif isinstance(input_list[0], torch.Tensor):
|
| 139 |
+
return torch.cat(input_list, dim=0)
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
def valid_images(imgs):
|
| 143 |
+
# If we have an list of images, make sure every image is valid
|
| 144 |
+
if isinstance(imgs, (list, tuple)):
|
| 145 |
+
for img in imgs:
|
| 146 |
+
if not valid_images(img):
|
| 147 |
+
return False
|
| 148 |
+
# If not a list of tuple, we have been given a single image or batched tensor of images
|
| 149 |
+
elif not is_valid_image(imgs):
|
| 150 |
+
return False
|
| 151 |
+
return True
|
| 152 |
+
|
| 153 |
+
|
| 154 |
+
def is_batched(img):
|
| 155 |
+
if isinstance(img, (list, tuple)):
|
| 156 |
+
return is_valid_image(img[0])
|
| 157 |
+
return False
|
| 158 |
+
|
| 159 |
+
|
| 160 |
+
def is_scaled_image(image: np.ndarray) -> bool:
|
| 161 |
+
"""
|
| 162 |
+
Checks to see whether the pixel values have already been rescaled to [0, 1].
|
| 163 |
+
"""
|
| 164 |
+
if image.dtype == np.uint8:
|
| 165 |
+
return False
|
| 166 |
+
|
| 167 |
+
# It's possible the image has pixel values in [0, 255] but is of floating type
|
| 168 |
+
return np.min(image) >= 0 and np.max(image) <= 1
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
def make_list_of_images(images, expected_ndims: int = 3) -> list[ImageInput]:
|
| 172 |
+
"""
|
| 173 |
+
Ensure that the output is a list of images. If the input is a single image, it is converted to a list of length 1.
|
| 174 |
+
If the input is a batch of images, it is converted to a list of images.
|
| 175 |
+
|
| 176 |
+
Args:
|
| 177 |
+
images (`ImageInput`):
|
| 178 |
+
Image of images to turn into a list of images.
|
| 179 |
+
expected_ndims (`int`, *optional*, defaults to 3):
|
| 180 |
+
Expected number of dimensions for a single input image. If the input image has a different number of
|
| 181 |
+
dimensions, an error is raised.
|
| 182 |
+
"""
|
| 183 |
+
if is_batched(images):
|
| 184 |
+
return images
|
| 185 |
+
|
| 186 |
+
# Either the input is a single image, in which case we create a list of length 1
|
| 187 |
+
if is_pil_image(images):
|
| 188 |
+
# PIL images are never batched
|
| 189 |
+
return [images]
|
| 190 |
+
|
| 191 |
+
if is_valid_image(images):
|
| 192 |
+
if images.ndim == expected_ndims + 1:
|
| 193 |
+
# Batch of images
|
| 194 |
+
images = list(images)
|
| 195 |
+
elif images.ndim == expected_ndims:
|
| 196 |
+
# Single image
|
| 197 |
+
images = [images]
|
| 198 |
+
else:
|
| 199 |
+
raise ValueError(
|
| 200 |
+
f"Invalid image shape. Expected either {expected_ndims + 1} or {expected_ndims} dimensions, but got"
|
| 201 |
+
f" {images.ndim} dimensions."
|
| 202 |
+
)
|
| 203 |
+
return images
|
| 204 |
+
raise ValueError(
|
| 205 |
+
"Invalid image type. Expected either PIL.Image.Image, numpy.ndarray, torch.Tensor, tf.Tensor or "
|
| 206 |
+
f"jax.ndarray, but got {type(images)}."
|
| 207 |
+
)
|
| 208 |
+
|
| 209 |
+
|
| 210 |
+
def make_flat_list_of_images(
|
| 211 |
+
images: Union[list[ImageInput], ImageInput],
|
| 212 |
+
expected_ndims: int = 3,
|
| 213 |
+
) -> ImageInput:
|
| 214 |
+
"""
|
| 215 |
+
Ensure that the output is a flat list of images. If the input is a single image, it is converted to a list of length 1.
|
| 216 |
+
If the input is a nested list of images, it is converted to a flat list of images.
|
| 217 |
+
Args:
|
| 218 |
+
images (`Union[list[ImageInput], ImageInput]`):
|
| 219 |
+
The input image.
|
| 220 |
+
expected_ndims (`int`, *optional*, defaults to 3):
|
| 221 |
+
The expected number of dimensions for a single input image.
|
| 222 |
+
Returns:
|
| 223 |
+
list: A list of images or a 4d array of images.
|
| 224 |
+
"""
|
| 225 |
+
# If the input is a nested list of images, we flatten it
|
| 226 |
+
if (
|
| 227 |
+
isinstance(images, (list, tuple))
|
| 228 |
+
and all(isinstance(images_i, (list, tuple)) for images_i in images)
|
| 229 |
+
and all(is_valid_list_of_images(images_i) or not images_i for images_i in images)
|
| 230 |
+
):
|
| 231 |
+
return [img for img_list in images for img in img_list]
|
| 232 |
+
|
| 233 |
+
if isinstance(images, (list, tuple)) and is_valid_list_of_images(images):
|
| 234 |
+
if is_pil_image(images[0]) or images[0].ndim == expected_ndims:
|
| 235 |
+
return images
|
| 236 |
+
if images[0].ndim == expected_ndims + 1:
|
| 237 |
+
return [img for img_list in images for img in img_list]
|
| 238 |
+
|
| 239 |
+
if is_valid_image(images):
|
| 240 |
+
if is_pil_image(images) or images.ndim == expected_ndims:
|
| 241 |
+
return [images]
|
| 242 |
+
if images.ndim == expected_ndims + 1:
|
| 243 |
+
return list(images)
|
| 244 |
+
|
| 245 |
+
raise ValueError(f"Could not make a flat list of images from {images}")
|
| 246 |
+
|
| 247 |
+
|
| 248 |
+
def make_nested_list_of_images(
|
| 249 |
+
images: Union[list[ImageInput], ImageInput],
|
| 250 |
+
expected_ndims: int = 3,
|
| 251 |
+
) -> list[ImageInput]:
|
| 252 |
+
"""
|
| 253 |
+
Ensure that the output is a nested list of images.
|
| 254 |
+
Args:
|
| 255 |
+
images (`Union[list[ImageInput], ImageInput]`):
|
| 256 |
+
The input image.
|
| 257 |
+
expected_ndims (`int`, *optional*, defaults to 3):
|
| 258 |
+
The expected number of dimensions for a single input image.
|
| 259 |
+
Returns:
|
| 260 |
+
list: A list of list of images or a list of 4d array of images.
|
| 261 |
+
"""
|
| 262 |
+
# If it's a list of batches, it's already in the right format
|
| 263 |
+
if (
|
| 264 |
+
isinstance(images, (list, tuple))
|
| 265 |
+
and all(isinstance(images_i, (list, tuple)) for images_i in images)
|
| 266 |
+
and all(is_valid_list_of_images(images_i) or not images_i for images_i in images)
|
| 267 |
+
):
|
| 268 |
+
return images
|
| 269 |
+
|
| 270 |
+
# If it's a list of images, it's a single batch, so convert it to a list of lists
|
| 271 |
+
if isinstance(images, (list, tuple)) and is_valid_list_of_images(images):
|
| 272 |
+
if is_pil_image(images[0]) or images[0].ndim == expected_ndims:
|
| 273 |
+
return [images]
|
| 274 |
+
if images[0].ndim == expected_ndims + 1:
|
| 275 |
+
return [list(image) for image in images]
|
| 276 |
+
|
| 277 |
+
# If it's a single image, convert it to a list of lists
|
| 278 |
+
if is_valid_image(images):
|
| 279 |
+
if is_pil_image(images) or images.ndim == expected_ndims:
|
| 280 |
+
return [[images]]
|
| 281 |
+
if images.ndim == expected_ndims + 1:
|
| 282 |
+
return [list(images)]
|
| 283 |
+
|
| 284 |
+
raise ValueError("Invalid input type. Must be a single image, a list of images, or a list of batches of images.")
|
| 285 |
+
|
| 286 |
+
|
| 287 |
+
def to_numpy_array(img) -> np.ndarray:
|
| 288 |
+
if not is_valid_image(img):
|
| 289 |
+
raise ValueError(f"Invalid image type: {type(img)}")
|
| 290 |
+
|
| 291 |
+
if is_vision_available() and isinstance(img, PIL.Image.Image):
|
| 292 |
+
return np.array(img)
|
| 293 |
+
return to_numpy(img)
|
| 294 |
+
|
| 295 |
+
|
| 296 |
+
def infer_channel_dimension_format(
|
| 297 |
+
image: np.ndarray, num_channels: Optional[Union[int, tuple[int, ...]]] = None
|
| 298 |
+
) -> ChannelDimension:
|
| 299 |
+
"""
|
| 300 |
+
Infers the channel dimension format of `image`.
|
| 301 |
+
|
| 302 |
+
Args:
|
| 303 |
+
image (`np.ndarray`):
|
| 304 |
+
The image to infer the channel dimension of.
|
| 305 |
+
num_channels (`int` or `tuple[int, ...]`, *optional*, defaults to `(1, 3)`):
|
| 306 |
+
The number of channels of the image.
|
| 307 |
+
|
| 308 |
+
Returns:
|
| 309 |
+
The channel dimension of the image.
|
| 310 |
+
"""
|
| 311 |
+
num_channels = num_channels if num_channels is not None else (1, 3)
|
| 312 |
+
num_channels = (num_channels,) if isinstance(num_channels, int) else num_channels
|
| 313 |
+
|
| 314 |
+
if image.ndim == 3:
|
| 315 |
+
first_dim, last_dim = 0, 2
|
| 316 |
+
elif image.ndim == 4:
|
| 317 |
+
first_dim, last_dim = 1, 3
|
| 318 |
+
elif image.ndim == 5:
|
| 319 |
+
first_dim, last_dim = 2, 4
|
| 320 |
+
else:
|
| 321 |
+
raise ValueError(f"Unsupported number of image dimensions: {image.ndim}")
|
| 322 |
+
|
| 323 |
+
if image.shape[first_dim] in num_channels and image.shape[last_dim] in num_channels:
|
| 324 |
+
logger.warning(
|
| 325 |
+
f"The channel dimension is ambiguous. Got image shape {image.shape}. Assuming channels are the first dimension. Use the [input_data_format](https://huggingface.co/docs/transformers/main/internal/image_processing_utils#transformers.image_transforms.rescale.input_data_format) parameter to assign the channel dimension."
|
| 326 |
+
)
|
| 327 |
+
return ChannelDimension.FIRST
|
| 328 |
+
elif image.shape[first_dim] in num_channels:
|
| 329 |
+
return ChannelDimension.FIRST
|
| 330 |
+
elif image.shape[last_dim] in num_channels:
|
| 331 |
+
return ChannelDimension.LAST
|
| 332 |
+
raise ValueError("Unable to infer channel dimension format")
|
| 333 |
+
|
| 334 |
+
|
| 335 |
+
def get_channel_dimension_axis(
|
| 336 |
+
image: np.ndarray, input_data_format: Optional[Union[ChannelDimension, str]] = None
|
| 337 |
+
) -> int:
|
| 338 |
+
"""
|
| 339 |
+
Returns the channel dimension axis of the image.
|
| 340 |
+
|
| 341 |
+
Args:
|
| 342 |
+
image (`np.ndarray`):
|
| 343 |
+
The image to get the channel dimension axis of.
|
| 344 |
+
input_data_format (`ChannelDimension` or `str`, *optional*):
|
| 345 |
+
The channel dimension format of the image. If `None`, will infer the channel dimension from the image.
|
| 346 |
+
|
| 347 |
+
Returns:
|
| 348 |
+
The channel dimension axis of the image.
|
| 349 |
+
"""
|
| 350 |
+
if input_data_format is None:
|
| 351 |
+
input_data_format = infer_channel_dimension_format(image)
|
| 352 |
+
if input_data_format == ChannelDimension.FIRST:
|
| 353 |
+
return image.ndim - 3
|
| 354 |
+
elif input_data_format == ChannelDimension.LAST:
|
| 355 |
+
return image.ndim - 1
|
| 356 |
+
raise ValueError(f"Unsupported data format: {input_data_format}")
|
| 357 |
+
|
| 358 |
+
|
| 359 |
+
def get_image_size(image: np.ndarray, channel_dim: Optional[ChannelDimension] = None) -> tuple[int, int]:
|
| 360 |
+
"""
|
| 361 |
+
Returns the (height, width) dimensions of the image.
|
| 362 |
+
|
| 363 |
+
Args:
|
| 364 |
+
image (`np.ndarray`):
|
| 365 |
+
The image to get the dimensions of.
|
| 366 |
+
channel_dim (`ChannelDimension`, *optional*):
|
| 367 |
+
Which dimension the channel dimension is in. If `None`, will infer the channel dimension from the image.
|
| 368 |
+
|
| 369 |
+
Returns:
|
| 370 |
+
A tuple of the image's height and width.
|
| 371 |
+
"""
|
| 372 |
+
if channel_dim is None:
|
| 373 |
+
channel_dim = infer_channel_dimension_format(image)
|
| 374 |
+
|
| 375 |
+
if channel_dim == ChannelDimension.FIRST:
|
| 376 |
+
return image.shape[-2], image.shape[-1]
|
| 377 |
+
elif channel_dim == ChannelDimension.LAST:
|
| 378 |
+
return image.shape[-3], image.shape[-2]
|
| 379 |
+
else:
|
| 380 |
+
raise ValueError(f"Unsupported data format: {channel_dim}")
|
| 381 |
+
|
| 382 |
+
|
| 383 |
+
def get_image_size_for_max_height_width(
|
| 384 |
+
image_size: tuple[int, int],
|
| 385 |
+
max_height: int,
|
| 386 |
+
max_width: int,
|
| 387 |
+
) -> tuple[int, int]:
|
| 388 |
+
"""
|
| 389 |
+
Computes the output image size given the input image and the maximum allowed height and width. Keep aspect ratio.
|
| 390 |
+
Important, even if image_height < max_height and image_width < max_width, the image will be resized
|
| 391 |
+
to at least one of the edges be equal to max_height or max_width.
|
| 392 |
+
|
| 393 |
+
For example:
|
| 394 |
+
- input_size: (100, 200), max_height: 50, max_width: 50 -> output_size: (25, 50)
|
| 395 |
+
- input_size: (100, 200), max_height: 200, max_width: 500 -> output_size: (200, 400)
|
| 396 |
+
|
| 397 |
+
Args:
|
| 398 |
+
image_size (`tuple[int, int]`):
|
| 399 |
+
The image to resize.
|
| 400 |
+
max_height (`int`):
|
| 401 |
+
The maximum allowed height.
|
| 402 |
+
max_width (`int`):
|
| 403 |
+
The maximum allowed width.
|
| 404 |
+
"""
|
| 405 |
+
height, width = image_size
|
| 406 |
+
height_scale = max_height / height
|
| 407 |
+
width_scale = max_width / width
|
| 408 |
+
min_scale = min(height_scale, width_scale)
|
| 409 |
+
new_height = int(height * min_scale)
|
| 410 |
+
new_width = int(width * min_scale)
|
| 411 |
+
return new_height, new_width
|
| 412 |
+
|
| 413 |
+
|
| 414 |
+
def is_valid_annotation_coco_detection(annotation: dict[str, Union[list, tuple]]) -> bool:
|
| 415 |
+
if (
|
| 416 |
+
isinstance(annotation, dict)
|
| 417 |
+
and "image_id" in annotation
|
| 418 |
+
and "annotations" in annotation
|
| 419 |
+
and isinstance(annotation["annotations"], (list, tuple))
|
| 420 |
+
and (
|
| 421 |
+
# an image can have no annotations
|
| 422 |
+
len(annotation["annotations"]) == 0 or isinstance(annotation["annotations"][0], dict)
|
| 423 |
+
)
|
| 424 |
+
):
|
| 425 |
+
return True
|
| 426 |
+
return False
|
| 427 |
+
|
| 428 |
+
|
| 429 |
+
def is_valid_annotation_coco_panoptic(annotation: dict[str, Union[list, tuple]]) -> bool:
|
| 430 |
+
if (
|
| 431 |
+
isinstance(annotation, dict)
|
| 432 |
+
and "image_id" in annotation
|
| 433 |
+
and "segments_info" in annotation
|
| 434 |
+
and "file_name" in annotation
|
| 435 |
+
and isinstance(annotation["segments_info"], (list, tuple))
|
| 436 |
+
and (
|
| 437 |
+
# an image can have no segments
|
| 438 |
+
len(annotation["segments_info"]) == 0 or isinstance(annotation["segments_info"][0], dict)
|
| 439 |
+
)
|
| 440 |
+
):
|
| 441 |
+
return True
|
| 442 |
+
return False
|
| 443 |
+
|
| 444 |
+
|
| 445 |
+
def valid_coco_detection_annotations(annotations: Iterable[dict[str, Union[list, tuple]]]) -> bool:
|
| 446 |
+
return all(is_valid_annotation_coco_detection(ann) for ann in annotations)
|
| 447 |
+
|
| 448 |
+
|
| 449 |
+
def valid_coco_panoptic_annotations(annotations: Iterable[dict[str, Union[list, tuple]]]) -> bool:
|
| 450 |
+
return all(is_valid_annotation_coco_panoptic(ann) for ann in annotations)
|
| 451 |
+
|
| 452 |
+
|
| 453 |
+
def load_image(image: Union[str, "PIL.Image.Image"], timeout: Optional[float] = None) -> "PIL.Image.Image":
|
| 454 |
+
"""
|
| 455 |
+
Loads `image` to a PIL Image.
|
| 456 |
+
|
| 457 |
+
Args:
|
| 458 |
+
image (`str` or `PIL.Image.Image`):
|
| 459 |
+
The image to convert to the PIL Image format.
|
| 460 |
+
timeout (`float`, *optional*):
|
| 461 |
+
The timeout value in seconds for the URL request.
|
| 462 |
+
|
| 463 |
+
Returns:
|
| 464 |
+
`PIL.Image.Image`: A PIL Image.
|
| 465 |
+
"""
|
| 466 |
+
requires_backends(load_image, ["vision"])
|
| 467 |
+
if isinstance(image, str):
|
| 468 |
+
if image.startswith("http://") or image.startswith("https://"):
|
| 469 |
+
# We need to actually check for a real protocol, otherwise it's impossible to use a local file
|
| 470 |
+
# like http_huggingface_co.png
|
| 471 |
+
image = PIL.Image.open(BytesIO(requests.get(image, timeout=timeout).content))
|
| 472 |
+
elif os.path.isfile(image):
|
| 473 |
+
image = PIL.Image.open(image)
|
| 474 |
+
else:
|
| 475 |
+
if image.startswith("data:image/"):
|
| 476 |
+
image = image.split(",")[1]
|
| 477 |
+
|
| 478 |
+
# Try to load as base64
|
| 479 |
+
try:
|
| 480 |
+
b64 = base64.decodebytes(image.encode())
|
| 481 |
+
image = PIL.Image.open(BytesIO(b64))
|
| 482 |
+
except Exception as e:
|
| 483 |
+
raise ValueError(
|
| 484 |
+
f"Incorrect image source. Must be a valid URL starting with `http://` or `https://`, a valid path to an image file, or a base64 encoded string. Got {image}. Failed with {e}"
|
| 485 |
+
)
|
| 486 |
+
elif not isinstance(image, PIL.Image.Image):
|
| 487 |
+
raise TypeError(
|
| 488 |
+
"Incorrect format used for image. Should be an url linking to an image, a base64 string, a local path, or a PIL image."
|
| 489 |
+
)
|
| 490 |
+
image = PIL.ImageOps.exif_transpose(image)
|
| 491 |
+
image = image.convert("RGB")
|
| 492 |
+
return image
|
| 493 |
+
|
| 494 |
+
|
| 495 |
+
def load_images(
|
| 496 |
+
images: Union[list, tuple, str, "PIL.Image.Image"], timeout: Optional[float] = None
|
| 497 |
+
) -> Union["PIL.Image.Image", list["PIL.Image.Image"], list[list["PIL.Image.Image"]]]:
|
| 498 |
+
"""Loads images, handling different levels of nesting.
|
| 499 |
+
|
| 500 |
+
Args:
|
| 501 |
+
images: A single image, a list of images, or a list of lists of images to load.
|
| 502 |
+
timeout: Timeout for loading images.
|
| 503 |
+
|
| 504 |
+
Returns:
|
| 505 |
+
A single image, a list of images, a list of lists of images.
|
| 506 |
+
"""
|
| 507 |
+
if isinstance(images, (list, tuple)):
|
| 508 |
+
if len(images) and isinstance(images[0], (list, tuple)):
|
| 509 |
+
return [[load_image(image, timeout=timeout) for image in image_group] for image_group in images]
|
| 510 |
+
else:
|
| 511 |
+
return [load_image(image, timeout=timeout) for image in images]
|
| 512 |
+
else:
|
| 513 |
+
return load_image(images, timeout=timeout)
|
| 514 |
+
|
| 515 |
+
|
| 516 |
+
def validate_preprocess_arguments(
|
| 517 |
+
do_rescale: Optional[bool] = None,
|
| 518 |
+
rescale_factor: Optional[float] = None,
|
| 519 |
+
do_normalize: Optional[bool] = None,
|
| 520 |
+
image_mean: Optional[Union[float, list[float]]] = None,
|
| 521 |
+
image_std: Optional[Union[float, list[float]]] = None,
|
| 522 |
+
do_pad: Optional[bool] = None,
|
| 523 |
+
pad_size: Optional[Union[dict[str, int], int]] = None,
|
| 524 |
+
do_center_crop: Optional[bool] = None,
|
| 525 |
+
crop_size: Optional[dict[str, int]] = None,
|
| 526 |
+
do_resize: Optional[bool] = None,
|
| 527 |
+
size: Optional[dict[str, int]] = None,
|
| 528 |
+
resample: Optional["PILImageResampling"] = None,
|
| 529 |
+
interpolation: Optional["InterpolationMode"] = None,
|
| 530 |
+
):
|
| 531 |
+
"""
|
| 532 |
+
Checks validity of typically used arguments in an `ImageProcessor` `preprocess` method.
|
| 533 |
+
Raises `ValueError` if arguments incompatibility is caught.
|
| 534 |
+
Many incompatibilities are model-specific. `do_pad` sometimes needs `size_divisor`,
|
| 535 |
+
sometimes `size_divisibility`, and sometimes `size`. New models and processors added should follow
|
| 536 |
+
existing arguments when possible.
|
| 537 |
+
|
| 538 |
+
"""
|
| 539 |
+
if do_rescale and rescale_factor is None:
|
| 540 |
+
raise ValueError("`rescale_factor` must be specified if `do_rescale` is `True`.")
|
| 541 |
+
|
| 542 |
+
if do_pad and pad_size is None:
|
| 543 |
+
# Processors pad images using different args depending on the model, so the below check is pointless
|
| 544 |
+
# but we keep it for BC for now. TODO: remove in v5
|
| 545 |
+
# Usually padding can be called with:
|
| 546 |
+
# - "pad_size/size" if we're padding to specific values
|
| 547 |
+
# - "size_divisor" if we're padding to any value divisible by X
|
| 548 |
+
# - "None" if we're padding to the maximum size image in batch
|
| 549 |
+
raise ValueError(
|
| 550 |
+
"Depending on the model, `size_divisor` or `pad_size` or `size` must be specified if `do_pad` is `True`."
|
| 551 |
+
)
|
| 552 |
+
|
| 553 |
+
if do_normalize and (image_mean is None or image_std is None):
|
| 554 |
+
raise ValueError("`image_mean` and `image_std` must both be specified if `do_normalize` is `True`.")
|
| 555 |
+
|
| 556 |
+
if do_center_crop and crop_size is None:
|
| 557 |
+
raise ValueError("`crop_size` must be specified if `do_center_crop` is `True`.")
|
| 558 |
+
|
| 559 |
+
if interpolation is not None and resample is not None:
|
| 560 |
+
raise ValueError(
|
| 561 |
+
"Only one of `interpolation` and `resample` should be specified, depending on image processor type."
|
| 562 |
+
)
|
| 563 |
+
|
| 564 |
+
if do_resize and not (size is not None and (resample is not None or interpolation is not None)):
|
| 565 |
+
raise ValueError("`size` and `resample/interpolation` must be specified if `do_resize` is `True`.")
|
| 566 |
+
|
| 567 |
+
|
| 568 |
+
# In the future we can add a TF implementation here when we have TF models.
|
| 569 |
+
class ImageFeatureExtractionMixin:
|
| 570 |
+
"""
|
| 571 |
+
Mixin that contain utilities for preparing image features.
|
| 572 |
+
"""
|
| 573 |
+
|
| 574 |
+
def _ensure_format_supported(self, image):
|
| 575 |
+
if not isinstance(image, (PIL.Image.Image, np.ndarray)) and not is_torch_tensor(image):
|
| 576 |
+
raise ValueError(
|
| 577 |
+
f"Got type {type(image)} which is not supported, only `PIL.Image.Image`, `np.ndarray` and "
|
| 578 |
+
"`torch.Tensor` are."
|
| 579 |
+
)
|
| 580 |
+
|
| 581 |
+
def to_pil_image(self, image, rescale=None):
|
| 582 |
+
"""
|
| 583 |
+
Converts `image` to a PIL Image. Optionally rescales it and puts the channel dimension back as the last axis if
|
| 584 |
+
needed.
|
| 585 |
+
|
| 586 |
+
Args:
|
| 587 |
+
image (`PIL.Image.Image` or `numpy.ndarray` or `torch.Tensor`):
|
| 588 |
+
The image to convert to the PIL Image format.
|
| 589 |
+
rescale (`bool`, *optional*):
|
| 590 |
+
Whether or not to apply the scaling factor (to make pixel values integers between 0 and 255). Will
|
| 591 |
+
default to `True` if the image type is a floating type, `False` otherwise.
|
| 592 |
+
"""
|
| 593 |
+
self._ensure_format_supported(image)
|
| 594 |
+
|
| 595 |
+
if is_torch_tensor(image):
|
| 596 |
+
image = image.numpy()
|
| 597 |
+
|
| 598 |
+
if isinstance(image, np.ndarray):
|
| 599 |
+
if rescale is None:
|
| 600 |
+
# rescale default to the array being of floating type.
|
| 601 |
+
rescale = isinstance(image.flat[0], np.floating)
|
| 602 |
+
# If the channel as been moved to first dim, we put it back at the end.
|
| 603 |
+
if image.ndim == 3 and image.shape[0] in [1, 3]:
|
| 604 |
+
image = image.transpose(1, 2, 0)
|
| 605 |
+
if rescale:
|
| 606 |
+
image = image * 255
|
| 607 |
+
image = image.astype(np.uint8)
|
| 608 |
+
return PIL.Image.fromarray(image)
|
| 609 |
+
return image
|
| 610 |
+
|
| 611 |
+
def convert_rgb(self, image):
|
| 612 |
+
"""
|
| 613 |
+
Converts `PIL.Image.Image` to RGB format.
|
| 614 |
+
|
| 615 |
+
Args:
|
| 616 |
+
image (`PIL.Image.Image`):
|
| 617 |
+
The image to convert.
|
| 618 |
+
"""
|
| 619 |
+
self._ensure_format_supported(image)
|
| 620 |
+
if not isinstance(image, PIL.Image.Image):
|
| 621 |
+
return image
|
| 622 |
+
|
| 623 |
+
return image.convert("RGB")
|
| 624 |
+
|
| 625 |
+
def rescale(self, image: np.ndarray, scale: Union[float, int]) -> np.ndarray:
|
| 626 |
+
"""
|
| 627 |
+
Rescale a numpy image by scale amount
|
| 628 |
+
"""
|
| 629 |
+
self._ensure_format_supported(image)
|
| 630 |
+
return image * scale
|
| 631 |
+
|
| 632 |
+
def to_numpy_array(self, image, rescale=None, channel_first=True):
|
| 633 |
+
"""
|
| 634 |
+
Converts `image` to a numpy array. Optionally rescales it and puts the channel dimension as the first
|
| 635 |
+
dimension.
|
| 636 |
+
|
| 637 |
+
Args:
|
| 638 |
+
image (`PIL.Image.Image` or `np.ndarray` or `torch.Tensor`):
|
| 639 |
+
The image to convert to a NumPy array.
|
| 640 |
+
rescale (`bool`, *optional*):
|
| 641 |
+
Whether or not to apply the scaling factor (to make pixel values floats between 0. and 1.). Will
|
| 642 |
+
default to `True` if the image is a PIL Image or an array/tensor of integers, `False` otherwise.
|
| 643 |
+
channel_first (`bool`, *optional*, defaults to `True`):
|
| 644 |
+
Whether or not to permute the dimensions of the image to put the channel dimension first.
|
| 645 |
+
"""
|
| 646 |
+
self._ensure_format_supported(image)
|
| 647 |
+
|
| 648 |
+
if isinstance(image, PIL.Image.Image):
|
| 649 |
+
image = np.array(image)
|
| 650 |
+
|
| 651 |
+
if is_torch_tensor(image):
|
| 652 |
+
image = image.numpy()
|
| 653 |
+
|
| 654 |
+
rescale = isinstance(image.flat[0], np.integer) if rescale is None else rescale
|
| 655 |
+
|
| 656 |
+
if rescale:
|
| 657 |
+
image = self.rescale(image.astype(np.float32), 1 / 255.0)
|
| 658 |
+
|
| 659 |
+
if channel_first and image.ndim == 3:
|
| 660 |
+
image = image.transpose(2, 0, 1)
|
| 661 |
+
|
| 662 |
+
return image
|
| 663 |
+
|
| 664 |
+
def expand_dims(self, image):
|
| 665 |
+
"""
|
| 666 |
+
Expands 2-dimensional `image` to 3 dimensions.
|
| 667 |
+
|
| 668 |
+
Args:
|
| 669 |
+
image (`PIL.Image.Image` or `np.ndarray` or `torch.Tensor`):
|
| 670 |
+
The image to expand.
|
| 671 |
+
"""
|
| 672 |
+
self._ensure_format_supported(image)
|
| 673 |
+
|
| 674 |
+
# Do nothing if PIL image
|
| 675 |
+
if isinstance(image, PIL.Image.Image):
|
| 676 |
+
return image
|
| 677 |
+
|
| 678 |
+
if is_torch_tensor(image):
|
| 679 |
+
image = image.unsqueeze(0)
|
| 680 |
+
else:
|
| 681 |
+
image = np.expand_dims(image, axis=0)
|
| 682 |
+
return image
|
| 683 |
+
|
| 684 |
+
def normalize(self, image, mean, std, rescale=False):
|
| 685 |
+
"""
|
| 686 |
+
Normalizes `image` with `mean` and `std`. Note that this will trigger a conversion of `image` to a NumPy array
|
| 687 |
+
if it's a PIL Image.
|
| 688 |
+
|
| 689 |
+
Args:
|
| 690 |
+
image (`PIL.Image.Image` or `np.ndarray` or `torch.Tensor`):
|
| 691 |
+
The image to normalize.
|
| 692 |
+
mean (`list[float]` or `np.ndarray` or `torch.Tensor`):
|
| 693 |
+
The mean (per channel) to use for normalization.
|
| 694 |
+
std (`list[float]` or `np.ndarray` or `torch.Tensor`):
|
| 695 |
+
The standard deviation (per channel) to use for normalization.
|
| 696 |
+
rescale (`bool`, *optional*, defaults to `False`):
|
| 697 |
+
Whether or not to rescale the image to be between 0 and 1. If a PIL image is provided, scaling will
|
| 698 |
+
happen automatically.
|
| 699 |
+
"""
|
| 700 |
+
self._ensure_format_supported(image)
|
| 701 |
+
|
| 702 |
+
if isinstance(image, PIL.Image.Image):
|
| 703 |
+
image = self.to_numpy_array(image, rescale=True)
|
| 704 |
+
# If the input image is a PIL image, it automatically gets rescaled. If it's another
|
| 705 |
+
# type it may need rescaling.
|
| 706 |
+
elif rescale:
|
| 707 |
+
if isinstance(image, np.ndarray):
|
| 708 |
+
image = self.rescale(image.astype(np.float32), 1 / 255.0)
|
| 709 |
+
elif is_torch_tensor(image):
|
| 710 |
+
image = self.rescale(image.float(), 1 / 255.0)
|
| 711 |
+
|
| 712 |
+
if isinstance(image, np.ndarray):
|
| 713 |
+
if not isinstance(mean, np.ndarray):
|
| 714 |
+
mean = np.array(mean).astype(image.dtype)
|
| 715 |
+
if not isinstance(std, np.ndarray):
|
| 716 |
+
std = np.array(std).astype(image.dtype)
|
| 717 |
+
elif is_torch_tensor(image):
|
| 718 |
+
import torch
|
| 719 |
+
|
| 720 |
+
if not isinstance(mean, torch.Tensor):
|
| 721 |
+
if isinstance(mean, np.ndarray):
|
| 722 |
+
mean = torch.from_numpy(mean)
|
| 723 |
+
else:
|
| 724 |
+
mean = torch.tensor(mean)
|
| 725 |
+
if not isinstance(std, torch.Tensor):
|
| 726 |
+
if isinstance(std, np.ndarray):
|
| 727 |
+
std = torch.from_numpy(std)
|
| 728 |
+
else:
|
| 729 |
+
std = torch.tensor(std)
|
| 730 |
+
|
| 731 |
+
if image.ndim == 3 and image.shape[0] in [1, 3]:
|
| 732 |
+
return (image - mean[:, None, None]) / std[:, None, None]
|
| 733 |
+
else:
|
| 734 |
+
return (image - mean) / std
|
| 735 |
+
|
| 736 |
+
def resize(self, image, size, resample=None, default_to_square=True, max_size=None):
|
| 737 |
+
"""
|
| 738 |
+
Resizes `image`. Enforces conversion of input to PIL.Image.
|
| 739 |
+
|
| 740 |
+
Args:
|
| 741 |
+
image (`PIL.Image.Image` or `np.ndarray` or `torch.Tensor`):
|
| 742 |
+
The image to resize.
|
| 743 |
+
size (`int` or `tuple[int, int]`):
|
| 744 |
+
The size to use for resizing the image. If `size` is a sequence like (h, w), output size will be
|
| 745 |
+
matched to this.
|
| 746 |
+
|
| 747 |
+
If `size` is an int and `default_to_square` is `True`, then image will be resized to (size, size). If
|
| 748 |
+
`size` is an int and `default_to_square` is `False`, then smaller edge of the image will be matched to
|
| 749 |
+
this number. i.e, if height > width, then image will be rescaled to (size * height / width, size).
|
| 750 |
+
resample (`int`, *optional*, defaults to `PILImageResampling.BILINEAR`):
|
| 751 |
+
The filter to user for resampling.
|
| 752 |
+
default_to_square (`bool`, *optional*, defaults to `True`):
|
| 753 |
+
How to convert `size` when it is a single int. If set to `True`, the `size` will be converted to a
|
| 754 |
+
square (`size`,`size`). If set to `False`, will replicate
|
| 755 |
+
[`torchvision.transforms.Resize`](https://pytorch.org/vision/stable/transforms.html#torchvision.transforms.Resize)
|
| 756 |
+
with support for resizing only the smallest edge and providing an optional `max_size`.
|
| 757 |
+
max_size (`int`, *optional*, defaults to `None`):
|
| 758 |
+
The maximum allowed for the longer edge of the resized image: if the longer edge of the image is
|
| 759 |
+
greater than `max_size` after being resized according to `size`, then the image is resized again so
|
| 760 |
+
that the longer edge is equal to `max_size`. As a result, `size` might be overruled, i.e the smaller
|
| 761 |
+
edge may be shorter than `size`. Only used if `default_to_square` is `False`.
|
| 762 |
+
|
| 763 |
+
Returns:
|
| 764 |
+
image: A resized `PIL.Image.Image`.
|
| 765 |
+
"""
|
| 766 |
+
resample = resample if resample is not None else PILImageResampling.BILINEAR
|
| 767 |
+
|
| 768 |
+
self._ensure_format_supported(image)
|
| 769 |
+
|
| 770 |
+
if not isinstance(image, PIL.Image.Image):
|
| 771 |
+
image = self.to_pil_image(image)
|
| 772 |
+
|
| 773 |
+
if isinstance(size, list):
|
| 774 |
+
size = tuple(size)
|
| 775 |
+
|
| 776 |
+
if isinstance(size, int) or len(size) == 1:
|
| 777 |
+
if default_to_square:
|
| 778 |
+
size = (size, size) if isinstance(size, int) else (size[0], size[0])
|
| 779 |
+
else:
|
| 780 |
+
width, height = image.size
|
| 781 |
+
# specified size only for the smallest edge
|
| 782 |
+
short, long = (width, height) if width <= height else (height, width)
|
| 783 |
+
requested_new_short = size if isinstance(size, int) else size[0]
|
| 784 |
+
|
| 785 |
+
if short == requested_new_short:
|
| 786 |
+
return image
|
| 787 |
+
|
| 788 |
+
new_short, new_long = requested_new_short, int(requested_new_short * long / short)
|
| 789 |
+
|
| 790 |
+
if max_size is not None:
|
| 791 |
+
if max_size <= requested_new_short:
|
| 792 |
+
raise ValueError(
|
| 793 |
+
f"max_size = {max_size} must be strictly greater than the requested "
|
| 794 |
+
f"size for the smaller edge size = {size}"
|
| 795 |
+
)
|
| 796 |
+
if new_long > max_size:
|
| 797 |
+
new_short, new_long = int(max_size * new_short / new_long), max_size
|
| 798 |
+
|
| 799 |
+
size = (new_short, new_long) if width <= height else (new_long, new_short)
|
| 800 |
+
|
| 801 |
+
return image.resize(size, resample=resample)
|
| 802 |
+
|
| 803 |
+
def center_crop(self, image, size):
|
| 804 |
+
"""
|
| 805 |
+
Crops `image` to the given size using a center crop. Note that if the image is too small to be cropped to the
|
| 806 |
+
size given, it will be padded (so the returned result has the size asked).
|
| 807 |
+
|
| 808 |
+
Args:
|
| 809 |
+
image (`PIL.Image.Image` or `np.ndarray` or `torch.Tensor` of shape (n_channels, height, width) or (height, width, n_channels)):
|
| 810 |
+
The image to resize.
|
| 811 |
+
size (`int` or `tuple[int, int]`):
|
| 812 |
+
The size to which crop the image.
|
| 813 |
+
|
| 814 |
+
Returns:
|
| 815 |
+
new_image: A center cropped `PIL.Image.Image` or `np.ndarray` or `torch.Tensor` of shape: (n_channels,
|
| 816 |
+
height, width).
|
| 817 |
+
"""
|
| 818 |
+
self._ensure_format_supported(image)
|
| 819 |
+
|
| 820 |
+
if not isinstance(size, tuple):
|
| 821 |
+
size = (size, size)
|
| 822 |
+
|
| 823 |
+
# PIL Image.size is (width, height) but NumPy array and torch Tensors have (height, width)
|
| 824 |
+
if is_torch_tensor(image) or isinstance(image, np.ndarray):
|
| 825 |
+
if image.ndim == 2:
|
| 826 |
+
image = self.expand_dims(image)
|
| 827 |
+
image_shape = image.shape[1:] if image.shape[0] in [1, 3] else image.shape[:2]
|
| 828 |
+
else:
|
| 829 |
+
image_shape = (image.size[1], image.size[0])
|
| 830 |
+
|
| 831 |
+
top = (image_shape[0] - size[0]) // 2
|
| 832 |
+
bottom = top + size[0] # In case size is odd, (image_shape[0] + size[0]) // 2 won't give the proper result.
|
| 833 |
+
left = (image_shape[1] - size[1]) // 2
|
| 834 |
+
right = left + size[1] # In case size is odd, (image_shape[1] + size[1]) // 2 won't give the proper result.
|
| 835 |
+
|
| 836 |
+
# For PIL Images we have a method to crop directly.
|
| 837 |
+
if isinstance(image, PIL.Image.Image):
|
| 838 |
+
return image.crop((left, top, right, bottom))
|
| 839 |
+
|
| 840 |
+
# Check if image is in (n_channels, height, width) or (height, width, n_channels) format
|
| 841 |
+
channel_first = image.shape[0] in [1, 3]
|
| 842 |
+
|
| 843 |
+
# Transpose (height, width, n_channels) format images
|
| 844 |
+
if not channel_first:
|
| 845 |
+
if isinstance(image, np.ndarray):
|
| 846 |
+
image = image.transpose(2, 0, 1)
|
| 847 |
+
if is_torch_tensor(image):
|
| 848 |
+
image = image.permute(2, 0, 1)
|
| 849 |
+
|
| 850 |
+
# Check if cropped area is within image boundaries
|
| 851 |
+
if top >= 0 and bottom <= image_shape[0] and left >= 0 and right <= image_shape[1]:
|
| 852 |
+
return image[..., top:bottom, left:right]
|
| 853 |
+
|
| 854 |
+
# Otherwise, we may need to pad if the image is too small. Oh joy...
|
| 855 |
+
new_shape = image.shape[:-2] + (max(size[0], image_shape[0]), max(size[1], image_shape[1]))
|
| 856 |
+
if isinstance(image, np.ndarray):
|
| 857 |
+
new_image = np.zeros_like(image, shape=new_shape)
|
| 858 |
+
elif is_torch_tensor(image):
|
| 859 |
+
new_image = image.new_zeros(new_shape)
|
| 860 |
+
|
| 861 |
+
top_pad = (new_shape[-2] - image_shape[0]) // 2
|
| 862 |
+
bottom_pad = top_pad + image_shape[0]
|
| 863 |
+
left_pad = (new_shape[-1] - image_shape[1]) // 2
|
| 864 |
+
right_pad = left_pad + image_shape[1]
|
| 865 |
+
new_image[..., top_pad:bottom_pad, left_pad:right_pad] = image
|
| 866 |
+
|
| 867 |
+
top += top_pad
|
| 868 |
+
bottom += top_pad
|
| 869 |
+
left += left_pad
|
| 870 |
+
right += left_pad
|
| 871 |
+
|
| 872 |
+
new_image = new_image[
|
| 873 |
+
..., max(0, top) : min(new_image.shape[-2], bottom), max(0, left) : min(new_image.shape[-1], right)
|
| 874 |
+
]
|
| 875 |
+
|
| 876 |
+
return new_image
|
| 877 |
+
|
| 878 |
+
def flip_channel_order(self, image):
|
| 879 |
+
"""
|
| 880 |
+
Flips the channel order of `image` from RGB to BGR, or vice versa. Note that this will trigger a conversion of
|
| 881 |
+
`image` to a NumPy array if it's a PIL Image.
|
| 882 |
+
|
| 883 |
+
Args:
|
| 884 |
+
image (`PIL.Image.Image` or `np.ndarray` or `torch.Tensor`):
|
| 885 |
+
The image whose color channels to flip. If `np.ndarray` or `torch.Tensor`, the channel dimension should
|
| 886 |
+
be first.
|
| 887 |
+
"""
|
| 888 |
+
self._ensure_format_supported(image)
|
| 889 |
+
|
| 890 |
+
if isinstance(image, PIL.Image.Image):
|
| 891 |
+
image = self.to_numpy_array(image)
|
| 892 |
+
|
| 893 |
+
return image[::-1, :, :]
|
| 894 |
+
|
| 895 |
+
def rotate(self, image, angle, resample=None, expand=0, center=None, translate=None, fillcolor=None):
|
| 896 |
+
"""
|
| 897 |
+
Returns a rotated copy of `image`. This method returns a copy of `image`, rotated the given number of degrees
|
| 898 |
+
counter clockwise around its centre.
|
| 899 |
+
|
| 900 |
+
Args:
|
| 901 |
+
image (`PIL.Image.Image` or `np.ndarray` or `torch.Tensor`):
|
| 902 |
+
The image to rotate. If `np.ndarray` or `torch.Tensor`, will be converted to `PIL.Image.Image` before
|
| 903 |
+
rotating.
|
| 904 |
+
|
| 905 |
+
Returns:
|
| 906 |
+
image: A rotated `PIL.Image.Image`.
|
| 907 |
+
"""
|
| 908 |
+
resample = resample if resample is not None else PIL.Image.NEAREST
|
| 909 |
+
|
| 910 |
+
self._ensure_format_supported(image)
|
| 911 |
+
|
| 912 |
+
if not isinstance(image, PIL.Image.Image):
|
| 913 |
+
image = self.to_pil_image(image)
|
| 914 |
+
|
| 915 |
+
return image.rotate(
|
| 916 |
+
angle, resample=resample, expand=expand, center=center, translate=translate, fillcolor=fillcolor
|
| 917 |
+
)
|
| 918 |
+
|
| 919 |
+
|
| 920 |
+
def validate_annotations(
|
| 921 |
+
annotation_format: AnnotationFormat,
|
| 922 |
+
supported_annotation_formats: tuple[AnnotationFormat, ...],
|
| 923 |
+
annotations: list[dict],
|
| 924 |
+
) -> None:
|
| 925 |
+
if annotation_format not in supported_annotation_formats:
|
| 926 |
+
raise ValueError(f"Unsupported annotation format: {format} must be one of {supported_annotation_formats}")
|
| 927 |
+
|
| 928 |
+
if annotation_format is AnnotationFormat.COCO_DETECTION:
|
| 929 |
+
if not valid_coco_detection_annotations(annotations):
|
| 930 |
+
raise ValueError(
|
| 931 |
+
"Invalid COCO detection annotations. Annotations must a dict (single image) or list of dicts "
|
| 932 |
+
"(batch of images) with the following keys: `image_id` and `annotations`, with the latter "
|
| 933 |
+
"being a list of annotations in the COCO format."
|
| 934 |
+
)
|
| 935 |
+
|
| 936 |
+
if annotation_format is AnnotationFormat.COCO_PANOPTIC:
|
| 937 |
+
if not valid_coco_panoptic_annotations(annotations):
|
| 938 |
+
raise ValueError(
|
| 939 |
+
"Invalid COCO panoptic annotations. Annotations must a dict (single image) or list of dicts "
|
| 940 |
+
"(batch of images) with the following keys: `image_id`, `file_name` and `segments_info`, with "
|
| 941 |
+
"the latter being a list of annotations in the COCO format."
|
| 942 |
+
)
|
| 943 |
+
|
| 944 |
+
|
| 945 |
+
def validate_kwargs(valid_processor_keys: list[str], captured_kwargs: list[str]):
|
| 946 |
+
unused_keys = set(captured_kwargs).difference(set(valid_processor_keys))
|
| 947 |
+
if unused_keys:
|
| 948 |
+
unused_key_str = ", ".join(unused_keys)
|
| 949 |
+
# TODO raise a warning here instead of simply logging?
|
| 950 |
+
logger.warning(f"Unused or unrecognized kwargs: {unused_key_str}.")
|
| 951 |
+
|
| 952 |
+
|
| 953 |
+
@dataclass(frozen=True)
|
| 954 |
+
class SizeDict:
|
| 955 |
+
"""
|
| 956 |
+
Hashable dictionary to store image size information.
|
| 957 |
+
"""
|
| 958 |
+
|
| 959 |
+
height: Optional[int] = None
|
| 960 |
+
width: Optional[int] = None
|
| 961 |
+
longest_edge: Optional[int] = None
|
| 962 |
+
shortest_edge: Optional[int] = None
|
| 963 |
+
max_height: Optional[int] = None
|
| 964 |
+
max_width: Optional[int] = None
|
| 965 |
+
|
| 966 |
+
def __getitem__(self, key):
|
| 967 |
+
if hasattr(self, key):
|
| 968 |
+
return getattr(self, key)
|
| 969 |
+
raise KeyError(f"Key {key} not found in SizeDict.")
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/keras_callbacks.py
ADDED
|
@@ -0,0 +1,413 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import logging
|
| 2 |
+
import os
|
| 3 |
+
from pathlib import Path
|
| 4 |
+
from time import sleep
|
| 5 |
+
from typing import Callable, Optional, Union
|
| 6 |
+
|
| 7 |
+
import numpy as np
|
| 8 |
+
import tensorflow as tf
|
| 9 |
+
from huggingface_hub import Repository, create_repo
|
| 10 |
+
from packaging.version import parse
|
| 11 |
+
|
| 12 |
+
from . import IntervalStrategy, PreTrainedTokenizerBase
|
| 13 |
+
from .modelcard import TrainingSummary
|
| 14 |
+
from .modeling_tf_utils import keras
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
logger = logging.getLogger(__name__)
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
class KerasMetricCallback(keras.callbacks.Callback):
|
| 21 |
+
"""
|
| 22 |
+
Callback to compute metrics at the end of every epoch. Unlike normal Keras metrics, these do not need to be
|
| 23 |
+
compilable by TF. It is particularly useful for common NLP metrics like BLEU and ROUGE that require string
|
| 24 |
+
operations or generation loops that cannot be compiled. Predictions (or generations) will be computed on the
|
| 25 |
+
`eval_dataset` before being passed to the `metric_fn` in `np.ndarray` format. The `metric_fn` should compute
|
| 26 |
+
metrics and return a dict mapping metric names to metric values.
|
| 27 |
+
|
| 28 |
+
We provide an example of a suitable metric_fn that computes ROUGE scores for a summarization model below. Note that
|
| 29 |
+
this example skips some post-processing for readability and simplicity, and should probably not be used as-is!
|
| 30 |
+
|
| 31 |
+
```py
|
| 32 |
+
from datasets import load_metric
|
| 33 |
+
|
| 34 |
+
rouge_metric = load_metric("rouge")
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
def rouge_fn(predictions, labels):
|
| 38 |
+
decoded_predictions = tokenizer.batch_decode(predictions, skip_special_tokens=True)
|
| 39 |
+
decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)
|
| 40 |
+
result = rouge_metric.compute(predictions=decoded_predictions, references=decoded_labels)
|
| 41 |
+
return {key: value.mid.fmeasure * 100 for key, value in result.items()}
|
| 42 |
+
```
|
| 43 |
+
|
| 44 |
+
The above function will return a dict containing values which will be logged like any other Keras metric:
|
| 45 |
+
|
| 46 |
+
```
|
| 47 |
+
{'rouge1': 37.4199, 'rouge2': 13.9768, 'rougeL': 34.361, 'rougeLsum': 35.0781
|
| 48 |
+
```
|
| 49 |
+
|
| 50 |
+
Args:
|
| 51 |
+
metric_fn (`Callable`):
|
| 52 |
+
Metric function provided by the user. It will be called with two arguments - `predictions` and `labels`.
|
| 53 |
+
These contain the model's outputs and matching labels from the dataset. It should return a dict mapping
|
| 54 |
+
metric names to numerical values.
|
| 55 |
+
eval_dataset (`tf.data.Dataset` or `dict` or `tuple` or `np.ndarray` or `tf.Tensor`):
|
| 56 |
+
Validation data to be used to generate predictions for the `metric_fn`.
|
| 57 |
+
output_cols (`list[str], *optional*):
|
| 58 |
+
A list of columns to be retained from the model output as the predictions. Defaults to all.
|
| 59 |
+
label_cols ('`list[str]`, *optional*'):
|
| 60 |
+
A list of columns to be retained from the input dataset as the labels. Will be autodetected if this is not
|
| 61 |
+
supplied.
|
| 62 |
+
batch_size (`int`, *optional*):
|
| 63 |
+
Batch size. Only used when the data is not a pre-batched `tf.data.Dataset`.
|
| 64 |
+
predict_with_generate (`bool`, *optional*, defaults to `False`):
|
| 65 |
+
Whether we should use `model.generate()` to get outputs for the model.
|
| 66 |
+
use_xla_generation (`bool`, *optional*, defaults to `False`):
|
| 67 |
+
If we're generating, whether to compile model generation with XLA. This can massively increase the speed of
|
| 68 |
+
generation (up to 100X speedup) but will require a new XLA compilation for each input shape. When using XLA
|
| 69 |
+
generation, it's a good idea to pad your inputs to the same size, or to use the `pad_to_multiple_of`
|
| 70 |
+
argument in your `tokenizer` or `DataCollator`, which will reduce the number of unique input shapes and
|
| 71 |
+
save a lot of compilation time. This option has no effect is `predict_with_generate` is `False`.
|
| 72 |
+
generate_kwargs (`dict`, *optional*):
|
| 73 |
+
Keyword arguments to pass to `model.generate()` when generating. Has no effect if `predict_with_generate`
|
| 74 |
+
is `False`.
|
| 75 |
+
|
| 76 |
+
"""
|
| 77 |
+
|
| 78 |
+
def __init__(
|
| 79 |
+
self,
|
| 80 |
+
metric_fn: Callable,
|
| 81 |
+
eval_dataset: Union[tf.data.Dataset, np.ndarray, tf.Tensor, tuple, dict],
|
| 82 |
+
output_cols: Optional[list[str]] = None,
|
| 83 |
+
label_cols: Optional[list[str]] = None,
|
| 84 |
+
batch_size: Optional[int] = None,
|
| 85 |
+
predict_with_generate: bool = False,
|
| 86 |
+
use_xla_generation: bool = False,
|
| 87 |
+
generate_kwargs: Optional[dict] = None,
|
| 88 |
+
):
|
| 89 |
+
super().__init__()
|
| 90 |
+
self.metric_fn = metric_fn
|
| 91 |
+
self.batch_size = batch_size
|
| 92 |
+
if not isinstance(eval_dataset, tf.data.Dataset):
|
| 93 |
+
if batch_size is None:
|
| 94 |
+
raise ValueError(
|
| 95 |
+
"When passing data to KerasMetricCallback that is not a pre-batched tf.data.Dataset "
|
| 96 |
+
"the batch_size argument must be set."
|
| 97 |
+
)
|
| 98 |
+
# Wrap a tf.data.Dataset around it
|
| 99 |
+
eval_dataset = tf.data.Dataset.from_tensor_slices(eval_dataset).batch(batch_size, drop_remainder=False)
|
| 100 |
+
self.eval_dataset = eval_dataset
|
| 101 |
+
self.predict_with_generate = predict_with_generate
|
| 102 |
+
self.output_cols = output_cols
|
| 103 |
+
|
| 104 |
+
# This next block attempts to parse out which elements of the dataset should be appended to the labels list
|
| 105 |
+
# that is passed to the metric_fn
|
| 106 |
+
if isinstance(eval_dataset.element_spec, tuple) and len(eval_dataset.element_spec) == 2:
|
| 107 |
+
input_spec, label_spec = eval_dataset.element_spec
|
| 108 |
+
else:
|
| 109 |
+
input_spec = eval_dataset.element_spec
|
| 110 |
+
label_spec = None
|
| 111 |
+
if label_cols is not None:
|
| 112 |
+
for label in label_cols:
|
| 113 |
+
if label not in input_spec:
|
| 114 |
+
raise ValueError(f"Label {label} is in label_cols but could not be found in the dataset inputs!")
|
| 115 |
+
self.label_cols = label_cols
|
| 116 |
+
self.use_keras_label = False
|
| 117 |
+
elif label_spec is not None:
|
| 118 |
+
# If the dataset inputs are split into a 2-tuple of inputs and labels,
|
| 119 |
+
# assume the second element is the labels
|
| 120 |
+
self.label_cols = None
|
| 121 |
+
self.use_keras_label = True
|
| 122 |
+
elif "labels" in input_spec:
|
| 123 |
+
self.label_cols = ["labels"]
|
| 124 |
+
self.use_keras_label = False
|
| 125 |
+
logging.warning("No label_cols specified for KerasMetricCallback, assuming you want the 'labels' key.")
|
| 126 |
+
elif "start_positions" in input_spec and "end_positions" in input_spec:
|
| 127 |
+
self.label_cols = ["start_positions", "end_positions"]
|
| 128 |
+
self.use_keras_label = False
|
| 129 |
+
logging.warning(
|
| 130 |
+
"No label_cols specified for KerasMetricCallback, assuming you want the "
|
| 131 |
+
"start_positions and end_positions keys."
|
| 132 |
+
)
|
| 133 |
+
else:
|
| 134 |
+
raise ValueError("Could not autodetect label_cols for KerasMetricCallback, please specify them!")
|
| 135 |
+
if parse(tf.__version__) < parse("2.7"):
|
| 136 |
+
logging.warning("TF versions less than 2.7 may encounter issues with KerasMetricCallback!")
|
| 137 |
+
|
| 138 |
+
self.use_xla_generation = use_xla_generation
|
| 139 |
+
self.generate_kwargs = {} if generate_kwargs is None else generate_kwargs
|
| 140 |
+
|
| 141 |
+
self.generation_function = None
|
| 142 |
+
|
| 143 |
+
@staticmethod
|
| 144 |
+
def _concatenate_batches(batches, padding_index=-100):
|
| 145 |
+
# If all batches are unidimensional or same length, do a simple concatenation
|
| 146 |
+
if batches[0].ndim == 1 or all(batch.shape[1] == batches[0].shape[1] for batch in batches):
|
| 147 |
+
return np.concatenate(batches, axis=0)
|
| 148 |
+
|
| 149 |
+
# Welp, they're not the same length. Let's do some padding
|
| 150 |
+
max_len = max([batch.shape[1] for batch in batches])
|
| 151 |
+
num_samples = sum([batch.shape[0] for batch in batches])
|
| 152 |
+
output = np.full_like(
|
| 153 |
+
batches[0], fill_value=padding_index, shape=[num_samples, max_len] + list(batches[0].shape[2:])
|
| 154 |
+
)
|
| 155 |
+
# i keeps track of which part of the concatenated array we're writing the next batch to
|
| 156 |
+
i = 0
|
| 157 |
+
for batch in batches:
|
| 158 |
+
output[i : i + len(batch), : batch.shape[1]] = batch
|
| 159 |
+
i += len(batch)
|
| 160 |
+
return output
|
| 161 |
+
|
| 162 |
+
def _postprocess_predictions_or_labels(self, inputs):
|
| 163 |
+
if isinstance(inputs[0], dict):
|
| 164 |
+
outputs = {}
|
| 165 |
+
for key in inputs[0]:
|
| 166 |
+
outputs[key] = self._concatenate_batches([batch[key] for batch in inputs])
|
| 167 |
+
# If it's a dict with only one key, just return the array
|
| 168 |
+
if len(outputs) == 1:
|
| 169 |
+
outputs = list(outputs.values())[0]
|
| 170 |
+
elif isinstance(inputs[0], (tuple, list)):
|
| 171 |
+
outputs = []
|
| 172 |
+
for input_list in zip(*inputs):
|
| 173 |
+
outputs.append(self._concatenate_batches(input_list))
|
| 174 |
+
if len(outputs) == 1:
|
| 175 |
+
outputs = outputs[0] # If it's a list with only one element, just return the array
|
| 176 |
+
elif isinstance(inputs[0], np.ndarray):
|
| 177 |
+
outputs = self._concatenate_batches(inputs)
|
| 178 |
+
elif isinstance(inputs[0], tf.Tensor):
|
| 179 |
+
outputs = self._concatenate_batches([tensor.numpy() for tensor in inputs])
|
| 180 |
+
else:
|
| 181 |
+
raise TypeError(f"Couldn't handle batch of type {type(inputs[0])}!")
|
| 182 |
+
return outputs
|
| 183 |
+
|
| 184 |
+
def on_epoch_end(self, epoch, logs=None):
|
| 185 |
+
if hasattr(self.model, "config"):
|
| 186 |
+
ignore_keys = getattr(self.model.config, "keys_to_ignore_at_inference", [])
|
| 187 |
+
else:
|
| 188 |
+
ignore_keys = []
|
| 189 |
+
|
| 190 |
+
main_input_name = None
|
| 191 |
+
if self.predict_with_generate:
|
| 192 |
+
# This dense conditional recognizes the case where we have an encoder-decoder model, but
|
| 193 |
+
# avoids getting tangled up when we just have a model with a layer called 'encoder'
|
| 194 |
+
if hasattr(self.model, "encoder") and hasattr(self.model.encoder, "main_input_name"):
|
| 195 |
+
main_input_name = self.model.encoder.main_input_name
|
| 196 |
+
else:
|
| 197 |
+
main_input_name = getattr(self.model, "main_input_name", "input_ids")
|
| 198 |
+
|
| 199 |
+
if self.use_xla_generation and self.generation_function is None:
|
| 200 |
+
|
| 201 |
+
def generation_function(inputs, attention_mask):
|
| 202 |
+
return self.model.generate(inputs, attention_mask=attention_mask, **self.generate_kwargs)
|
| 203 |
+
|
| 204 |
+
self.generation_function = tf.function(generation_function, jit_compile=True)
|
| 205 |
+
|
| 206 |
+
prediction_list = []
|
| 207 |
+
label_list = []
|
| 208 |
+
|
| 209 |
+
# The whole predict/generate loop is handled inside this method
|
| 210 |
+
for batch in self.eval_dataset:
|
| 211 |
+
if isinstance(batch, tuple):
|
| 212 |
+
batch, labels = batch
|
| 213 |
+
else:
|
| 214 |
+
labels = None
|
| 215 |
+
if self.predict_with_generate:
|
| 216 |
+
if isinstance(batch, dict):
|
| 217 |
+
generation_inputs = batch[main_input_name]
|
| 218 |
+
attention_mask = batch.get("attention_mask", None)
|
| 219 |
+
else:
|
| 220 |
+
generation_inputs = batch
|
| 221 |
+
attention_mask = None
|
| 222 |
+
if self.use_xla_generation:
|
| 223 |
+
predictions = self.generation_function(generation_inputs, attention_mask=attention_mask)
|
| 224 |
+
else:
|
| 225 |
+
predictions = self.model.generate(
|
| 226 |
+
generation_inputs, attention_mask=attention_mask, **self.generate_kwargs
|
| 227 |
+
)
|
| 228 |
+
else:
|
| 229 |
+
predictions = self.model.predict_on_batch(batch)
|
| 230 |
+
if isinstance(predictions, dict):
|
| 231 |
+
# This converts any dict-subclass to a regular dict
|
| 232 |
+
# Keras REALLY doesn't like it when we pass around a BatchEncoding or other derived class
|
| 233 |
+
predictions = dict(predictions)
|
| 234 |
+
if self.output_cols is not None:
|
| 235 |
+
predictions = {key: predictions[key] for key in self.output_cols}
|
| 236 |
+
else:
|
| 237 |
+
predictions = {
|
| 238 |
+
key: val for key, val in predictions.items() if key not in ignore_keys + ["loss"]
|
| 239 |
+
}
|
| 240 |
+
prediction_list.append(predictions)
|
| 241 |
+
if not self.use_keras_label:
|
| 242 |
+
labels = {key: batch[key].numpy() for key in self.label_cols}
|
| 243 |
+
elif isinstance(labels, dict):
|
| 244 |
+
labels = {key: array.numpy() for key, array in labels.items()}
|
| 245 |
+
elif isinstance(labels, (list, tuple)):
|
| 246 |
+
labels = [array.numpy() for array in labels]
|
| 247 |
+
elif isinstance(labels, tf.Tensor):
|
| 248 |
+
labels = labels.numpy()
|
| 249 |
+
else:
|
| 250 |
+
raise TypeError(f"Confused by labels of type {type(labels)}")
|
| 251 |
+
label_list.append(labels)
|
| 252 |
+
|
| 253 |
+
all_preds = self._postprocess_predictions_or_labels(prediction_list)
|
| 254 |
+
all_labels = self._postprocess_predictions_or_labels(label_list)
|
| 255 |
+
|
| 256 |
+
metric_output = self.metric_fn((all_preds, all_labels))
|
| 257 |
+
if not isinstance(metric_output, dict):
|
| 258 |
+
raise TypeError(
|
| 259 |
+
f"metric_fn should return a dict mapping metric names to values but instead returned {metric_output}"
|
| 260 |
+
)
|
| 261 |
+
# This is the critical bit - Keras passes a dict containing the loss and standard metric values for this epoch
|
| 262 |
+
# in the logs argument. Ordinarily, this is so the callback can read them, but in this case we write a bunch of
|
| 263 |
+
# new keys in there, which will then get read by the History callback and treated like any other metric value.
|
| 264 |
+
# I promise that I have it in writing from Chollet that this is okay.
|
| 265 |
+
logs.update(metric_output)
|
| 266 |
+
|
| 267 |
+
|
| 268 |
+
class PushToHubCallback(keras.callbacks.Callback):
|
| 269 |
+
"""
|
| 270 |
+
Callback that will save and push the model to the Hub regularly. By default, it pushes once per epoch, but this can
|
| 271 |
+
be changed with the `save_strategy` argument. Pushed models can be accessed like any other model on the hub, such
|
| 272 |
+
as with the `from_pretrained` method.
|
| 273 |
+
|
| 274 |
+
```py
|
| 275 |
+
from transformers.keras_callbacks import PushToHubCallback
|
| 276 |
+
|
| 277 |
+
push_to_hub_callback = PushToHubCallback(
|
| 278 |
+
output_dir="./model_save",
|
| 279 |
+
tokenizer=tokenizer,
|
| 280 |
+
hub_model_id="gpt5-7xlarge",
|
| 281 |
+
)
|
| 282 |
+
|
| 283 |
+
model.fit(train_dataset, callbacks=[push_to_hub_callback])
|
| 284 |
+
```
|
| 285 |
+
|
| 286 |
+
Args:
|
| 287 |
+
output_dir (`str`):
|
| 288 |
+
The output directory where the model predictions and checkpoints will be written and synced with the
|
| 289 |
+
repository on the Hub.
|
| 290 |
+
save_strategy (`str` or [`~trainer_utils.IntervalStrategy`], *optional*, defaults to `"epoch"`):
|
| 291 |
+
The checkpoint save strategy to adopt during training. Possible values are:
|
| 292 |
+
|
| 293 |
+
- `"no"`: Save is done at the end of training.
|
| 294 |
+
- `"epoch"`: Save is done at the end of each epoch.
|
| 295 |
+
- `"steps"`: Save is done every `save_steps`
|
| 296 |
+
save_steps (`int`, *optional*):
|
| 297 |
+
The number of steps between saves when using the "steps" `save_strategy`.
|
| 298 |
+
tokenizer (`PreTrainedTokenizerBase`, *optional*):
|
| 299 |
+
The tokenizer used by the model. If supplied, will be uploaded to the repo alongside the weights.
|
| 300 |
+
hub_model_id (`str`, *optional*):
|
| 301 |
+
The name of the repository to keep in sync with the local `output_dir`. It can be a simple model ID in
|
| 302 |
+
which case the model will be pushed in your namespace. Otherwise it should be the whole repository name,
|
| 303 |
+
for instance `"user_name/model"`, which allows you to push to an organization you are a member of with
|
| 304 |
+
`"organization_name/model"`.
|
| 305 |
+
|
| 306 |
+
Will default to the name of `output_dir`.
|
| 307 |
+
hub_token (`str`, *optional*):
|
| 308 |
+
The token to use to push the model to the Hub. Will default to the token in the cache folder obtained with
|
| 309 |
+
`hf auth login`.
|
| 310 |
+
checkpoint (`bool`, *optional*, defaults to `False`):
|
| 311 |
+
Whether to save full training checkpoints (including epoch and optimizer state) to allow training to be
|
| 312 |
+
resumed. Only usable when `save_strategy` is `"epoch"`.
|
| 313 |
+
"""
|
| 314 |
+
|
| 315 |
+
def __init__(
|
| 316 |
+
self,
|
| 317 |
+
output_dir: Union[str, Path],
|
| 318 |
+
save_strategy: Union[str, IntervalStrategy] = "epoch",
|
| 319 |
+
save_steps: Optional[int] = None,
|
| 320 |
+
tokenizer: Optional[PreTrainedTokenizerBase] = None,
|
| 321 |
+
hub_model_id: Optional[str] = None,
|
| 322 |
+
hub_token: Optional[str] = None,
|
| 323 |
+
checkpoint: bool = False,
|
| 324 |
+
**model_card_args,
|
| 325 |
+
):
|
| 326 |
+
super().__init__()
|
| 327 |
+
if checkpoint and save_strategy != "epoch":
|
| 328 |
+
raise ValueError("Cannot save checkpoints when save_strategy is not 'epoch'!")
|
| 329 |
+
if isinstance(save_strategy, str):
|
| 330 |
+
save_strategy = IntervalStrategy(save_strategy.lower())
|
| 331 |
+
self.save_strategy = save_strategy
|
| 332 |
+
if self.save_strategy == IntervalStrategy.STEPS and (not isinstance(save_steps, int) or save_steps <= 0):
|
| 333 |
+
raise ValueError("Please supply a positive integer argument for save_steps when save_strategy == 'steps'!")
|
| 334 |
+
self.save_steps = save_steps
|
| 335 |
+
output_dir = Path(output_dir)
|
| 336 |
+
|
| 337 |
+
# Create repo and retrieve repo_id
|
| 338 |
+
if hub_model_id is None:
|
| 339 |
+
hub_model_id = output_dir.absolute().name
|
| 340 |
+
self.hub_model_id = create_repo(repo_id=hub_model_id, exist_ok=True, token=hub_token).repo_id
|
| 341 |
+
|
| 342 |
+
self.output_dir = output_dir
|
| 343 |
+
self.repo = Repository(str(self.output_dir), clone_from=self.hub_model_id, token=hub_token)
|
| 344 |
+
|
| 345 |
+
self.tokenizer = tokenizer
|
| 346 |
+
self.last_job = None
|
| 347 |
+
self.checkpoint = checkpoint
|
| 348 |
+
self.training_history = None
|
| 349 |
+
self.model_card_args = model_card_args
|
| 350 |
+
|
| 351 |
+
def on_train_begin(self, logs=None):
|
| 352 |
+
# Although we can access model.history, we have no guarantees that the History callback will fire before this
|
| 353 |
+
# one, so we keep track of it here too
|
| 354 |
+
self.training_history = []
|
| 355 |
+
|
| 356 |
+
def on_train_batch_end(self, batch, logs=None):
|
| 357 |
+
if self.save_strategy == IntervalStrategy.STEPS and (batch + 1) % self.save_steps == 0:
|
| 358 |
+
if self.last_job is not None and not self.last_job.is_done:
|
| 359 |
+
return # The last upload is still running, don't start another
|
| 360 |
+
self.model.save_pretrained(self.output_dir)
|
| 361 |
+
if self.tokenizer is not None:
|
| 362 |
+
self.tokenizer.save_pretrained(self.output_dir)
|
| 363 |
+
_, self.last_job = self.repo.push_to_hub(
|
| 364 |
+
commit_message=f"Training in progress steps {batch}", blocking=False
|
| 365 |
+
)
|
| 366 |
+
|
| 367 |
+
def on_epoch_end(self, epoch, logs=None):
|
| 368 |
+
logs = logs.copy() # Don't accidentally write things that Keras will read later
|
| 369 |
+
if "epoch" not in logs:
|
| 370 |
+
logs["epoch"] = epoch
|
| 371 |
+
self.training_history.append(logs)
|
| 372 |
+
if self.save_strategy == IntervalStrategy.EPOCH:
|
| 373 |
+
if self.last_job is not None and not self.last_job.is_done:
|
| 374 |
+
return # The last upload is still running, don't start another
|
| 375 |
+
self.model.save_pretrained(self.output_dir)
|
| 376 |
+
if self.tokenizer is not None:
|
| 377 |
+
self.tokenizer.save_pretrained(self.output_dir)
|
| 378 |
+
if self.checkpoint:
|
| 379 |
+
checkpoint_dir = os.path.join(self.output_dir, "checkpoint")
|
| 380 |
+
self.model._save_checkpoint(checkpoint_dir, epoch)
|
| 381 |
+
train_summary = TrainingSummary.from_keras(
|
| 382 |
+
model=self.model,
|
| 383 |
+
model_name=self.hub_model_id,
|
| 384 |
+
keras_history=self.training_history,
|
| 385 |
+
**self.model_card_args,
|
| 386 |
+
)
|
| 387 |
+
model_card = train_summary.to_model_card()
|
| 388 |
+
with (self.output_dir / "README.md").open("w") as f:
|
| 389 |
+
f.write(model_card)
|
| 390 |
+
_, self.last_job = self.repo.push_to_hub(
|
| 391 |
+
commit_message=f"Training in progress epoch {epoch}", blocking=False
|
| 392 |
+
)
|
| 393 |
+
|
| 394 |
+
def on_train_end(self, logs=None):
|
| 395 |
+
# Makes sure the latest version of the model is uploaded
|
| 396 |
+
if self.last_job is not None and not self.last_job.is_done:
|
| 397 |
+
logging.info("Pushing the last epoch to the Hub, this may take a while...")
|
| 398 |
+
while not self.last_job.is_done:
|
| 399 |
+
sleep(1)
|
| 400 |
+
else:
|
| 401 |
+
self.model.save_pretrained(self.output_dir)
|
| 402 |
+
if self.tokenizer is not None:
|
| 403 |
+
self.tokenizer.save_pretrained(self.output_dir)
|
| 404 |
+
train_summary = TrainingSummary.from_keras(
|
| 405 |
+
model=self.model,
|
| 406 |
+
model_name=self.hub_model_id,
|
| 407 |
+
keras_history=self.training_history,
|
| 408 |
+
**self.model_card_args,
|
| 409 |
+
)
|
| 410 |
+
model_card = train_summary.to_model_card()
|
| 411 |
+
with (self.output_dir / "README.md").open("w") as f:
|
| 412 |
+
f.write(model_card)
|
| 413 |
+
self.repo.push_to_hub(commit_message="End of training", blocking=True)
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/model_debugging_utils.py
ADDED
|
@@ -0,0 +1,456 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2025 The HuggingFace Inc. team.
|
| 2 |
+
# All rights reserved.
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
# you may not use this file except in compliance with the License.
|
| 6 |
+
# You may obtain a copy of the License at
|
| 7 |
+
#
|
| 8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
#
|
| 10 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
# See the License for the specific language governing permissions and
|
| 14 |
+
# limitations under the License.
|
| 15 |
+
|
| 16 |
+
import functools
|
| 17 |
+
import json
|
| 18 |
+
import os
|
| 19 |
+
import re
|
| 20 |
+
from contextlib import contextmanager, redirect_stdout
|
| 21 |
+
from io import StringIO
|
| 22 |
+
from typing import Optional
|
| 23 |
+
|
| 24 |
+
from .utils import logging
|
| 25 |
+
from .utils.import_utils import is_torch_available, requires
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
if is_torch_available():
|
| 29 |
+
import torch
|
| 30 |
+
from safetensors.torch import save_file
|
| 31 |
+
|
| 32 |
+
_torch_distributed_available = False
|
| 33 |
+
# Note to code inspectors: this toolbox is intended for people who add models to `transformers`.
|
| 34 |
+
if torch.distributed.is_available():
|
| 35 |
+
import torch.distributed.tensor
|
| 36 |
+
|
| 37 |
+
_torch_distributed_available = True
|
| 38 |
+
else:
|
| 39 |
+
_torch_distributed_available = False
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
logger = logging.get_logger(__name__)
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def _is_rank_zero():
|
| 46 |
+
"""Return True if rank=0 or we aren't running distributed."""
|
| 47 |
+
if not (_torch_distributed_available and torch.distributed.is_initialized()):
|
| 48 |
+
return True
|
| 49 |
+
return torch.distributed.get_rank() == 0
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
MEMORY_ADDRESS_REGEX = re.compile(r"object at 0x[0-9A-Fa-f]+")
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
def _sanitize_repr_for_diff(x_str: str) -> str:
|
| 56 |
+
"""
|
| 57 |
+
Replace memory addresses in an object's repr with a stable placeholder
|
| 58 |
+
so that beautiful JSON diffs won't be ruined by ephemeral addresses.
|
| 59 |
+
"""
|
| 60 |
+
return MEMORY_ADDRESS_REGEX.sub("object at 0xXXXXXXXX", x_str)
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
def _dtensor_repr(x):
|
| 64 |
+
"""Return a stable string representation for a DTensor-like object."""
|
| 65 |
+
if _is_rank_zero():
|
| 66 |
+
return f"DTensor (rank0) -> {repr(x._local_tensor)}"
|
| 67 |
+
return "DTensor(non-rank0)"
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
def _serialize_tensor_like_io(
|
| 71 |
+
value, debug_path: Optional[str] = None, use_repr: bool = True, path_to_value: Optional[str] = None
|
| 72 |
+
):
|
| 73 |
+
"""
|
| 74 |
+
Converts Tensors and DTensors to a JSON-serializable dictionary representation.
|
| 75 |
+
|
| 76 |
+
Args:
|
| 77 |
+
value: Any Python object, often including torch Tensors, lists, dicts, etc.
|
| 78 |
+
debug_path (`str`, *optional*, defaults to `None`): Directory to dump debug JSON and SafeTensors files.
|
| 79 |
+
use_repr (bool, *optional*, defaults to `True`): Whether to save a `repr()`-ized version of the tensor as the
|
| 80 |
+
`value` property in the asscoiated FULL_TENSORS.json file, or to store the full tensors in separate
|
| 81 |
+
SafeTensors file and store the relative path to that file in the `value` property in the dictionary.
|
| 82 |
+
path_to_value (`str`, *optional*, defaults to `None`): The file name for the SafeTensors file holding the full
|
| 83 |
+
tensor value if `use_repr=False`.
|
| 84 |
+
|
| 85 |
+
Returns:
|
| 86 |
+
A nested Python structure (list, dict, or sanitized string) that is safe to json.dump.
|
| 87 |
+
"""
|
| 88 |
+
torch.set_printoptions(sci_mode=True)
|
| 89 |
+
|
| 90 |
+
if use_repr:
|
| 91 |
+
value_out = _repr_to_list(value)
|
| 92 |
+
elif path_to_value:
|
| 93 |
+
if not path_to_value.endswith(".safetensors"):
|
| 94 |
+
path_to_value += ".safetensors"
|
| 95 |
+
|
| 96 |
+
filepath = os.path.join(debug_path, path_to_value) if debug_path else path_to_value
|
| 97 |
+
save_file({"data": value.contiguous().detach().cpu()}, filepath)
|
| 98 |
+
value_out = f"./{path_to_value}"
|
| 99 |
+
else:
|
| 100 |
+
raise ValueError(f"{use_repr=} and {path_to_value=} cannot both be falsy.")
|
| 101 |
+
|
| 102 |
+
out = {
|
| 103 |
+
"shape": repr(value.shape),
|
| 104 |
+
"dtype": repr(value.dtype),
|
| 105 |
+
"value": value_out,
|
| 106 |
+
}
|
| 107 |
+
if value.dtype in {torch.float16, torch.float32, torch.bfloat16}:
|
| 108 |
+
out.update(
|
| 109 |
+
{
|
| 110 |
+
"mean": _sanitize_repr_for_diff(repr(value.mean())),
|
| 111 |
+
"std": _sanitize_repr_for_diff(repr(value.std())),
|
| 112 |
+
"min": _sanitize_repr_for_diff(repr(value.min())),
|
| 113 |
+
"max": _sanitize_repr_for_diff(repr(value.max())),
|
| 114 |
+
}
|
| 115 |
+
)
|
| 116 |
+
return out
|
| 117 |
+
|
| 118 |
+
|
| 119 |
+
def _serialize_io(value, debug_path: Optional[str] = None, use_repr: bool = True, path_to_value: Optional[str] = None):
|
| 120 |
+
"""
|
| 121 |
+
Recursively build a JSON-serializable Python structure from `value`.
|
| 122 |
+
Tensors and DTensors become either sanitized repr strings, or are saved to disk as SafeTensors files and their
|
| 123 |
+
relative paths are recorded in the returned Python structure.
|
| 124 |
+
Lists/tuples/dicts are recursed into.
|
| 125 |
+
All memory addresses are replaced with a stable placeholder.
|
| 126 |
+
|
| 127 |
+
Args:
|
| 128 |
+
value: Any Python object, often including torch Tensors, lists, dicts, etc.
|
| 129 |
+
debug_path (`str`, *optional*, defaults to `None`): Directory to dump debug JSON and SafeTensors files.
|
| 130 |
+
use_repr (bool, *optional*, defaults to `True`): Whether to save a `repr()`-ized version of the tensors as the
|
| 131 |
+
`value` property in the asscoiated FULL_TENSORS.json file, or to store full tensors in separate SafeTensors
|
| 132 |
+
files and store the relative path to that file in the `value` property.
|
| 133 |
+
path_to_value (`str`, *optional*, defaults to `None`): The file name for the SafeTensors file holding the full
|
| 134 |
+
tensor value if `use_repr=False`.
|
| 135 |
+
|
| 136 |
+
Returns:
|
| 137 |
+
A nested Python structure (list, dict, or sanitized string) that is safe to json.dump.
|
| 138 |
+
"""
|
| 139 |
+
if isinstance(value, (list, tuple)):
|
| 140 |
+
return [
|
| 141 |
+
_serialize_io(v, debug_path=debug_path, use_repr=use_repr, path_to_value=f"{path_to_value}_{i}")
|
| 142 |
+
for i, v in enumerate(value)
|
| 143 |
+
]
|
| 144 |
+
|
| 145 |
+
if isinstance(value, dict):
|
| 146 |
+
return {
|
| 147 |
+
k: _serialize_io(v, debug_path=debug_path, use_repr=use_repr, path_to_value=f"{path_to_value}_{k}")
|
| 148 |
+
for k, v in value.items()
|
| 149 |
+
}
|
| 150 |
+
|
| 151 |
+
if hasattr(value, "_local_tensor"):
|
| 152 |
+
return _serialize_tensor_like_io(
|
| 153 |
+
value._local_tensor, debug_path=debug_path, use_repr=use_repr, path_to_value=path_to_value
|
| 154 |
+
)
|
| 155 |
+
|
| 156 |
+
if isinstance(value, torch.Tensor):
|
| 157 |
+
return _serialize_tensor_like_io(value, debug_path=debug_path, use_repr=use_repr, path_to_value=path_to_value)
|
| 158 |
+
|
| 159 |
+
return _sanitize_repr_for_diff(repr(value))
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
def _repr_to_list(value: torch.Tensor):
|
| 163 |
+
"""
|
| 164 |
+
Converts a tensor into a sanitized multi-line string representation.
|
| 165 |
+
|
| 166 |
+
Args:
|
| 167 |
+
value (`torch.Tensor`): The tensor to represent.
|
| 168 |
+
|
| 169 |
+
Returns:
|
| 170 |
+
`list[str]`: List of string lines representing the tensor.
|
| 171 |
+
"""
|
| 172 |
+
torch.set_printoptions(sci_mode=True, linewidth=120)
|
| 173 |
+
with StringIO() as buf, redirect_stdout(buf):
|
| 174 |
+
print(value) # to redirected stdout to avoid line splits
|
| 175 |
+
raw = buf.getvalue()
|
| 176 |
+
return _sanitize_repr_for_diff(raw).splitlines()
|
| 177 |
+
|
| 178 |
+
|
| 179 |
+
def prune_outputs_if_children(node):
|
| 180 |
+
# if there are children, remove this node's "outputs"
|
| 181 |
+
# so we only see outputs at the leaf level
|
| 182 |
+
if node.get("children"):
|
| 183 |
+
node.pop("outputs", None)
|
| 184 |
+
for child in node["children"]:
|
| 185 |
+
prune_outputs_if_children(child)
|
| 186 |
+
|
| 187 |
+
|
| 188 |
+
LAYER_SUFFIX_RE = re.compile(r"(.*)\.(\d+)$") # should be generic enough, ends with a number
|
| 189 |
+
|
| 190 |
+
|
| 191 |
+
def is_layer_block(node):
|
| 192 |
+
"""
|
| 193 |
+
Checks whether a node represents a layer block with submodules.
|
| 194 |
+
|
| 195 |
+
Args:
|
| 196 |
+
node (`dict`): A node from the call tree.
|
| 197 |
+
|
| 198 |
+
Returns:
|
| 199 |
+
`bool`: Whether the node is a layer block.
|
| 200 |
+
"""
|
| 201 |
+
match = LAYER_SUFFIX_RE.match(node.get("module_path", ""))
|
| 202 |
+
if not match or not node.get("children"):
|
| 203 |
+
return False
|
| 204 |
+
number = match.group(2)
|
| 205 |
+
return any(f".{number}." in child.get("module_path", "") for child in node["children"])
|
| 206 |
+
|
| 207 |
+
|
| 208 |
+
def prune_intermediate_layers(node):
|
| 209 |
+
"""
|
| 210 |
+
Recursively removes intermediate layers from the tree to improve readability.
|
| 211 |
+
Keeps at least the first and last layers if many consecutive layers are present.
|
| 212 |
+
|
| 213 |
+
Args:
|
| 214 |
+
node (`dict`): The root or subnode to prune recursively.
|
| 215 |
+
"""
|
| 216 |
+
if not node.get("children"):
|
| 217 |
+
return
|
| 218 |
+
layer_blocks = [(i, child) for i, child in enumerate(node["children"]) if is_layer_block(child)]
|
| 219 |
+
|
| 220 |
+
if len(layer_blocks) > 2:
|
| 221 |
+
to_remove = [i for i, _ in layer_blocks[1:-1]]
|
| 222 |
+
node["children"] = [child for i, child in enumerate(node["children"]) if i not in to_remove]
|
| 223 |
+
|
| 224 |
+
for child in node["children"]:
|
| 225 |
+
prune_intermediate_layers(child)
|
| 226 |
+
|
| 227 |
+
|
| 228 |
+
def log_model_debug_trace(debug_path: Optional[str], model):
|
| 229 |
+
if debug_path:
|
| 230 |
+
try:
|
| 231 |
+
os.makedirs(debug_path, exist_ok=True)
|
| 232 |
+
base = os.path.join(debug_path, model._debugger_module_dump_name + "_debug_tree")
|
| 233 |
+
except Exception as e:
|
| 234 |
+
raise ValueError(f"Unexpected or existing debug_path={debug_path}.") from e
|
| 235 |
+
else:
|
| 236 |
+
base = model._debugger_module_dump_name + "_debug_tree"
|
| 237 |
+
|
| 238 |
+
logger.info(f"Writing model trace at {base}.json")
|
| 239 |
+
full_path = base + "_FULL_TENSORS.json"
|
| 240 |
+
summary_path = base + "_SUMMARY.json"
|
| 241 |
+
|
| 242 |
+
prune_outputs_if_children(model._call_tree)
|
| 243 |
+
|
| 244 |
+
with open(full_path, "w") as f:
|
| 245 |
+
json.dump(model._call_tree, f, indent=2)
|
| 246 |
+
|
| 247 |
+
# summary-only version for readability - traversing the tree again #TODO optimize?
|
| 248 |
+
def strip_values(node):
|
| 249 |
+
def clean(val):
|
| 250 |
+
if isinstance(val, dict):
|
| 251 |
+
val.pop("value", None)
|
| 252 |
+
for v in val.values():
|
| 253 |
+
clean(v)
|
| 254 |
+
elif isinstance(val, list):
|
| 255 |
+
for item in val:
|
| 256 |
+
clean(item)
|
| 257 |
+
|
| 258 |
+
clean(node.get("inputs", {}))
|
| 259 |
+
clean(node.get("outputs", {}))
|
| 260 |
+
|
| 261 |
+
for child in node.get("children", []):
|
| 262 |
+
strip_values(child)
|
| 263 |
+
|
| 264 |
+
tree_copy = json.loads(json.dumps(model._call_tree)) # deep copy
|
| 265 |
+
strip_values(tree_copy)
|
| 266 |
+
|
| 267 |
+
with open(summary_path, "w") as f:
|
| 268 |
+
json.dump(tree_copy, f, indent=2)
|
| 269 |
+
|
| 270 |
+
|
| 271 |
+
def _attach_debugger_logic(
|
| 272 |
+
model,
|
| 273 |
+
debug_path: str = ".",
|
| 274 |
+
do_prune_layers: bool = True,
|
| 275 |
+
use_repr: bool = True,
|
| 276 |
+
):
|
| 277 |
+
"""
|
| 278 |
+
Attaches a debugging wrapper to every module in the model.
|
| 279 |
+
|
| 280 |
+
This records structured inputs and outputs during the forward pass into a call tree.
|
| 281 |
+
|
| 282 |
+
Args:
|
| 283 |
+
model (`PreTrainedModel`, `nn.Module`): Model to wrap.
|
| 284 |
+
debug_path (`str`): Optional directory to dump debug JSON files.
|
| 285 |
+
do_prune_layers (`bool`, *optional*, defaults to `True`): Whether to prune intermediate layers.
|
| 286 |
+
use_repr (bool, *optional*, defaults to `True`): Whether to save a `repr()`-ized version of the tensors as the
|
| 287 |
+
`value` property in the associated FULL_TENSORS.json file, or to store full tensors in separate SafeTensors
|
| 288 |
+
files and store the relative path to that file in the `value` property.
|
| 289 |
+
"""
|
| 290 |
+
class_name = model.__class__.__name__
|
| 291 |
+
|
| 292 |
+
# Prepare data structures on the model object
|
| 293 |
+
model._call_tree = {"module_path": class_name, "inputs": None, "outputs": None, "children": []}
|
| 294 |
+
model._debugger_model_call_stack = []
|
| 295 |
+
model._debugger_module_dump_name = class_name # used for final JSON filename
|
| 296 |
+
|
| 297 |
+
if debug_path:
|
| 298 |
+
try:
|
| 299 |
+
os.makedirs(debug_path, exist_ok=True)
|
| 300 |
+
except Exception as e:
|
| 301 |
+
raise ValueError(f"Unexpected or existing debug_path={debug_path}.") from e
|
| 302 |
+
|
| 303 |
+
def wrap_forward(module, full_path):
|
| 304 |
+
orig_forward = module.forward
|
| 305 |
+
|
| 306 |
+
@functools.wraps(orig_forward)
|
| 307 |
+
def wrapped_forward(*inps, **kws):
|
| 308 |
+
if _is_rank_zero():
|
| 309 |
+
dict_inputs = {"args": inps, "kwargs": kws}
|
| 310 |
+
dict_inputs = {k: dict_inputs[k] for k in dict_inputs if len(dict_inputs[k]) > 0}
|
| 311 |
+
node = {
|
| 312 |
+
"module_path": full_path,
|
| 313 |
+
"inputs": _serialize_io(
|
| 314 |
+
dict_inputs,
|
| 315 |
+
debug_path=debug_path,
|
| 316 |
+
use_repr=use_repr,
|
| 317 |
+
path_to_value=f"{full_path}_inputs",
|
| 318 |
+
),
|
| 319 |
+
"outputs": None,
|
| 320 |
+
"children": [],
|
| 321 |
+
}
|
| 322 |
+
model._debugger_model_call_stack.append(node)
|
| 323 |
+
with torch.no_grad():
|
| 324 |
+
out = orig_forward(*inps, **kws)
|
| 325 |
+
|
| 326 |
+
if _is_rank_zero():
|
| 327 |
+
if sum(1 for _ in module.named_children()) > 0:
|
| 328 |
+
node["outputs"] = None
|
| 329 |
+
else:
|
| 330 |
+
node["outputs"] = _serialize_io(
|
| 331 |
+
out,
|
| 332 |
+
debug_path=debug_path,
|
| 333 |
+
use_repr=use_repr,
|
| 334 |
+
path_to_value=f"{full_path}_outputs",
|
| 335 |
+
)
|
| 336 |
+
|
| 337 |
+
finished = model._debugger_model_call_stack.pop()
|
| 338 |
+
# prune empty vertices here as well (mostly empty children nodes)
|
| 339 |
+
if not finished["children"]:
|
| 340 |
+
finished.pop("children")
|
| 341 |
+
|
| 342 |
+
if model._debugger_model_call_stack:
|
| 343 |
+
model._debugger_model_call_stack[-1]["children"].append(finished)
|
| 344 |
+
return out
|
| 345 |
+
|
| 346 |
+
module.forward = wrapped_forward
|
| 347 |
+
|
| 348 |
+
# wrap all submodules
|
| 349 |
+
for name, submodule in model.named_modules():
|
| 350 |
+
if name == "":
|
| 351 |
+
continue
|
| 352 |
+
wrap_forward(submodule, f"{class_name}.{name}")
|
| 353 |
+
|
| 354 |
+
# wrap top-level forward
|
| 355 |
+
real_top_forward = model.forward
|
| 356 |
+
|
| 357 |
+
@functools.wraps(real_top_forward)
|
| 358 |
+
def top_wrapped_forward(*inps, **kws):
|
| 359 |
+
if _is_rank_zero():
|
| 360 |
+
top_node = {
|
| 361 |
+
"module_path": f"{class_name} (top-level)",
|
| 362 |
+
"inputs": _serialize_io(
|
| 363 |
+
{"args": inps, "kwargs": kws},
|
| 364 |
+
debug_path=debug_path,
|
| 365 |
+
use_repr=use_repr,
|
| 366 |
+
path_to_value=f"{class_name}_inputs",
|
| 367 |
+
),
|
| 368 |
+
"outputs": None,
|
| 369 |
+
"children": [],
|
| 370 |
+
}
|
| 371 |
+
model._debugger_model_call_stack.append(top_node)
|
| 372 |
+
|
| 373 |
+
out = real_top_forward(*inps, **kws)
|
| 374 |
+
if _is_rank_zero() and model._debugger_model_call_stack:
|
| 375 |
+
top_node["outputs"] = _serialize_io(
|
| 376 |
+
out,
|
| 377 |
+
debug_path=debug_path,
|
| 378 |
+
use_repr=use_repr,
|
| 379 |
+
path_to_value=f"{class_name}_outputs",
|
| 380 |
+
)
|
| 381 |
+
finished = model._debugger_model_call_stack.pop()
|
| 382 |
+
model._call_tree["inputs"] = finished["inputs"]
|
| 383 |
+
model._call_tree["outputs"] = finished["outputs"]
|
| 384 |
+
model._call_tree["children"] = finished["children"]
|
| 385 |
+
# prune empty stuff for visibility
|
| 386 |
+
[model._call_tree.pop(k, None) for k in list(model._call_tree.keys()) if not model._call_tree[k]]
|
| 387 |
+
|
| 388 |
+
# prune layers that are not 0 or last
|
| 389 |
+
if do_prune_layers:
|
| 390 |
+
prune_intermediate_layers(model._call_tree)
|
| 391 |
+
# Write final JSON trace here
|
| 392 |
+
log_model_debug_trace(debug_path=debug_path, model=model)
|
| 393 |
+
return out
|
| 394 |
+
|
| 395 |
+
model.forward = top_wrapped_forward
|
| 396 |
+
|
| 397 |
+
|
| 398 |
+
@requires(backends=("torch",))
|
| 399 |
+
@contextmanager
|
| 400 |
+
def model_addition_debugger_context(
|
| 401 |
+
model,
|
| 402 |
+
debug_path: Optional[str] = None,
|
| 403 |
+
do_prune_layers: bool = True,
|
| 404 |
+
use_repr: bool = True,
|
| 405 |
+
):
|
| 406 |
+
"""
|
| 407 |
+
# Model addition debugger - context manager for model adders
|
| 408 |
+
This context manager is a power user tool intended for model adders.
|
| 409 |
+
|
| 410 |
+
It tracks all forward calls within a model forward and logs a slice of each input and output on a nested JSON file.
|
| 411 |
+
If `use_repr=True` (the default), the JSON file will record a `repr()`-ized version of the tensors as a list of
|
| 412 |
+
strings. If `use_repr=False`, the full tensors will be stored in separate SafeTensors files and the JSON file will
|
| 413 |
+
provide a relative path to that file.
|
| 414 |
+
|
| 415 |
+
To note, this context manager enforces `torch.no_grad()`.
|
| 416 |
+
|
| 417 |
+
## Usage
|
| 418 |
+
|
| 419 |
+
add the context manager to a model to debug
|
| 420 |
+
|
| 421 |
+
```python
|
| 422 |
+
import torch
|
| 423 |
+
|
| 424 |
+
from PIL import Image
|
| 425 |
+
from transformers import LlavaProcessor, LlavaForConditionalGeneration, model_addition_debugger_context
|
| 426 |
+
|
| 427 |
+
torch.random.manual_seed(673)
|
| 428 |
+
|
| 429 |
+
# load pretrained model and processor
|
| 430 |
+
model_id = "llava-hf/llava-1.5-7b-hf"
|
| 431 |
+
processor = LlavaProcessor.from_pretrained(model_id)
|
| 432 |
+
model = LlavaForConditionalGeneration.from_pretrained(model_id)
|
| 433 |
+
|
| 434 |
+
# create random image input
|
| 435 |
+
random_image = Image.fromarray(torch.randint(0, 256, (224, 224, 3), dtype=torch.uint8).numpy())
|
| 436 |
+
|
| 437 |
+
# prompt
|
| 438 |
+
prompt = "<image>Describe this image."
|
| 439 |
+
|
| 440 |
+
# process inputs
|
| 441 |
+
inputs = processor(text=prompt, images=random_image, return_tensors="pt")
|
| 442 |
+
|
| 443 |
+
# call forward method (not .generate!)
|
| 444 |
+
with model_addition_debugger_context(model, debug_path="Your_debug_path", do_prune_layers=False):
|
| 445 |
+
output = model.forward(**inputs)
|
| 446 |
+
```
|
| 447 |
+
|
| 448 |
+
"""
|
| 449 |
+
orig_forwards = {m: m.forward for _, m in model.named_modules()}
|
| 450 |
+
orig_forwards[model] = model.forward
|
| 451 |
+
_attach_debugger_logic(model, debug_path, do_prune_layers, use_repr)
|
| 452 |
+
try:
|
| 453 |
+
yield model
|
| 454 |
+
finally:
|
| 455 |
+
for module_instance, forward_method in orig_forwards.items():
|
| 456 |
+
module_instance.forward = forward_method
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/modelcard.py
ADDED
|
@@ -0,0 +1,914 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2018 The HuggingFace Inc. team.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""Configuration base class and utilities."""
|
| 15 |
+
|
| 16 |
+
import copy
|
| 17 |
+
import json
|
| 18 |
+
import os
|
| 19 |
+
import warnings
|
| 20 |
+
from dataclasses import dataclass
|
| 21 |
+
from pathlib import Path
|
| 22 |
+
from typing import Any, Optional, Union
|
| 23 |
+
|
| 24 |
+
import requests
|
| 25 |
+
import yaml
|
| 26 |
+
from huggingface_hub import model_info
|
| 27 |
+
from huggingface_hub.errors import OfflineModeIsEnabled
|
| 28 |
+
from huggingface_hub.utils import HFValidationError
|
| 29 |
+
|
| 30 |
+
from . import __version__
|
| 31 |
+
from .models.auto.modeling_auto import (
|
| 32 |
+
MODEL_FOR_AUDIO_CLASSIFICATION_MAPPING_NAMES,
|
| 33 |
+
MODEL_FOR_CAUSAL_LM_MAPPING_NAMES,
|
| 34 |
+
MODEL_FOR_CTC_MAPPING_NAMES,
|
| 35 |
+
MODEL_FOR_IMAGE_CLASSIFICATION_MAPPING_NAMES,
|
| 36 |
+
MODEL_FOR_IMAGE_SEGMENTATION_MAPPING_NAMES,
|
| 37 |
+
MODEL_FOR_IMAGE_TEXT_TO_TEXT_MAPPING_NAMES,
|
| 38 |
+
MODEL_FOR_MASKED_LM_MAPPING_NAMES,
|
| 39 |
+
MODEL_FOR_OBJECT_DETECTION_MAPPING_NAMES,
|
| 40 |
+
MODEL_FOR_QUESTION_ANSWERING_MAPPING_NAMES,
|
| 41 |
+
MODEL_FOR_SEQ_TO_SEQ_CAUSAL_LM_MAPPING_NAMES,
|
| 42 |
+
MODEL_FOR_SEQUENCE_CLASSIFICATION_MAPPING_NAMES,
|
| 43 |
+
MODEL_FOR_SPEECH_SEQ_2_SEQ_MAPPING_NAMES,
|
| 44 |
+
MODEL_FOR_TABLE_QUESTION_ANSWERING_MAPPING_NAMES,
|
| 45 |
+
MODEL_FOR_TOKEN_CLASSIFICATION_MAPPING_NAMES,
|
| 46 |
+
MODEL_FOR_ZERO_SHOT_IMAGE_CLASSIFICATION_MAPPING_NAMES,
|
| 47 |
+
)
|
| 48 |
+
from .training_args import ParallelMode
|
| 49 |
+
from .utils import (
|
| 50 |
+
MODEL_CARD_NAME,
|
| 51 |
+
cached_file,
|
| 52 |
+
is_datasets_available,
|
| 53 |
+
is_offline_mode,
|
| 54 |
+
is_tf_available,
|
| 55 |
+
is_tokenizers_available,
|
| 56 |
+
is_torch_available,
|
| 57 |
+
logging,
|
| 58 |
+
)
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
TASK_MAPPING = {
|
| 62 |
+
"text-generation": MODEL_FOR_CAUSAL_LM_MAPPING_NAMES,
|
| 63 |
+
"image-classification": MODEL_FOR_IMAGE_CLASSIFICATION_MAPPING_NAMES,
|
| 64 |
+
"image-segmentation": MODEL_FOR_IMAGE_SEGMENTATION_MAPPING_NAMES,
|
| 65 |
+
"fill-mask": MODEL_FOR_MASKED_LM_MAPPING_NAMES,
|
| 66 |
+
"object-detection": MODEL_FOR_OBJECT_DETECTION_MAPPING_NAMES,
|
| 67 |
+
"question-answering": MODEL_FOR_QUESTION_ANSWERING_MAPPING_NAMES,
|
| 68 |
+
"text2text-generation": MODEL_FOR_SEQ_TO_SEQ_CAUSAL_LM_MAPPING_NAMES,
|
| 69 |
+
"text-classification": MODEL_FOR_SEQUENCE_CLASSIFICATION_MAPPING_NAMES,
|
| 70 |
+
"table-question-answering": MODEL_FOR_TABLE_QUESTION_ANSWERING_MAPPING_NAMES,
|
| 71 |
+
"token-classification": MODEL_FOR_TOKEN_CLASSIFICATION_MAPPING_NAMES,
|
| 72 |
+
"audio-classification": MODEL_FOR_AUDIO_CLASSIFICATION_MAPPING_NAMES,
|
| 73 |
+
"automatic-speech-recognition": {**MODEL_FOR_CTC_MAPPING_NAMES, **MODEL_FOR_SPEECH_SEQ_2_SEQ_MAPPING_NAMES},
|
| 74 |
+
"zero-shot-image-classification": MODEL_FOR_ZERO_SHOT_IMAGE_CLASSIFICATION_MAPPING_NAMES,
|
| 75 |
+
"image-text-to-text": MODEL_FOR_IMAGE_TEXT_TO_TEXT_MAPPING_NAMES,
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
logger = logging.get_logger(__name__)
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
class ModelCard:
|
| 82 |
+
r"""
|
| 83 |
+
Structured Model Card class. Store model card as well as methods for loading/downloading/saving model cards.
|
| 84 |
+
|
| 85 |
+
Please read the following paper for details and explanation on the sections: "Model Cards for Model Reporting" by
|
| 86 |
+
Margaret Mitchell, Simone Wu, Andrew Zaldivar, Parker Barnes, Lucy Vasserman, Ben Hutchinson, Elena Spitzer,
|
| 87 |
+
Inioluwa Deborah Raji and Timnit Gebru for the proposal behind model cards. Link: https://huggingface.co/papers/1810.03993
|
| 88 |
+
|
| 89 |
+
Note: A model card can be loaded and saved to disk.
|
| 90 |
+
"""
|
| 91 |
+
|
| 92 |
+
def __init__(self, **kwargs):
|
| 93 |
+
warnings.warn(
|
| 94 |
+
"The class `ModelCard` is deprecated and will be removed in version 5 of Transformers", FutureWarning
|
| 95 |
+
)
|
| 96 |
+
# Recommended attributes from https://huggingface.co/papers/1810.03993 (see papers)
|
| 97 |
+
self.model_details = kwargs.pop("model_details", {})
|
| 98 |
+
self.intended_use = kwargs.pop("intended_use", {})
|
| 99 |
+
self.factors = kwargs.pop("factors", {})
|
| 100 |
+
self.metrics = kwargs.pop("metrics", {})
|
| 101 |
+
self.evaluation_data = kwargs.pop("evaluation_data", {})
|
| 102 |
+
self.training_data = kwargs.pop("training_data", {})
|
| 103 |
+
self.quantitative_analyses = kwargs.pop("quantitative_analyses", {})
|
| 104 |
+
self.ethical_considerations = kwargs.pop("ethical_considerations", {})
|
| 105 |
+
self.caveats_and_recommendations = kwargs.pop("caveats_and_recommendations", {})
|
| 106 |
+
|
| 107 |
+
# Open additional attributes
|
| 108 |
+
for key, value in kwargs.items():
|
| 109 |
+
try:
|
| 110 |
+
setattr(self, key, value)
|
| 111 |
+
except AttributeError as err:
|
| 112 |
+
logger.error(f"Can't set {key} with value {value} for {self}")
|
| 113 |
+
raise err
|
| 114 |
+
|
| 115 |
+
def save_pretrained(self, save_directory_or_file):
|
| 116 |
+
"""Save a model card object to the directory or file `save_directory_or_file`."""
|
| 117 |
+
if os.path.isdir(save_directory_or_file):
|
| 118 |
+
# If we save using the predefined names, we can load using `from_pretrained`
|
| 119 |
+
output_model_card_file = os.path.join(save_directory_or_file, MODEL_CARD_NAME)
|
| 120 |
+
else:
|
| 121 |
+
output_model_card_file = save_directory_or_file
|
| 122 |
+
|
| 123 |
+
self.to_json_file(output_model_card_file)
|
| 124 |
+
logger.info(f"Model card saved in {output_model_card_file}")
|
| 125 |
+
|
| 126 |
+
@classmethod
|
| 127 |
+
def from_pretrained(cls, pretrained_model_name_or_path, **kwargs):
|
| 128 |
+
r"""
|
| 129 |
+
Instantiate a [`ModelCard`] from a pre-trained model model card.
|
| 130 |
+
|
| 131 |
+
Parameters:
|
| 132 |
+
pretrained_model_name_or_path: either:
|
| 133 |
+
|
| 134 |
+
- a string, the *model id* of a pretrained model card hosted inside a model repo on huggingface.co.
|
| 135 |
+
- a path to a *directory* containing a model card file saved using the [`~ModelCard.save_pretrained`]
|
| 136 |
+
method, e.g.: `./my_model_directory/`.
|
| 137 |
+
- a path or url to a saved model card JSON *file*, e.g.: `./my_model_directory/modelcard.json`.
|
| 138 |
+
|
| 139 |
+
cache_dir: (*optional*) string:
|
| 140 |
+
Path to a directory in which a downloaded pre-trained model card should be cached if the standard cache
|
| 141 |
+
should not be used.
|
| 142 |
+
|
| 143 |
+
kwargs: (*optional*) dict: key/value pairs with which to update the ModelCard object after loading.
|
| 144 |
+
|
| 145 |
+
- The values in kwargs of any keys which are model card attributes will be used to override the loaded
|
| 146 |
+
values.
|
| 147 |
+
- Behavior concerning key/value pairs whose keys are *not* model card attributes is controlled by the
|
| 148 |
+
*return_unused_kwargs* keyword parameter.
|
| 149 |
+
|
| 150 |
+
proxies: (*optional*) dict, default None:
|
| 151 |
+
A dictionary of proxy servers to use by protocol or endpoint, e.g.: {'http': 'foo.bar:3128',
|
| 152 |
+
'http://hostname': 'foo.bar:4012'}. The proxies are used on each request.
|
| 153 |
+
|
| 154 |
+
return_unused_kwargs: (*optional*) bool:
|
| 155 |
+
|
| 156 |
+
- If False, then this function returns just the final model card object.
|
| 157 |
+
- If True, then this functions returns a tuple *(model card, unused_kwargs)* where *unused_kwargs* is a
|
| 158 |
+
dictionary consisting of the key/value pairs whose keys are not model card attributes: ie the part of
|
| 159 |
+
kwargs which has not been used to update *ModelCard* and is otherwise ignored.
|
| 160 |
+
|
| 161 |
+
Examples:
|
| 162 |
+
|
| 163 |
+
```python
|
| 164 |
+
# Download model card from huggingface.co and cache.
|
| 165 |
+
modelcard = ModelCard.from_pretrained("google-bert/bert-base-uncased")
|
| 166 |
+
# Model card was saved using *save_pretrained('./test/saved_model/')*
|
| 167 |
+
modelcard = ModelCard.from_pretrained("./test/saved_model/")
|
| 168 |
+
modelcard = ModelCard.from_pretrained("./test/saved_model/modelcard.json")
|
| 169 |
+
modelcard = ModelCard.from_pretrained("google-bert/bert-base-uncased", output_attentions=True, foo=False)
|
| 170 |
+
```"""
|
| 171 |
+
cache_dir = kwargs.pop("cache_dir", None)
|
| 172 |
+
proxies = kwargs.pop("proxies", None)
|
| 173 |
+
return_unused_kwargs = kwargs.pop("return_unused_kwargs", False)
|
| 174 |
+
from_pipeline = kwargs.pop("_from_pipeline", None)
|
| 175 |
+
|
| 176 |
+
user_agent = {"file_type": "model_card"}
|
| 177 |
+
if from_pipeline is not None:
|
| 178 |
+
user_agent["using_pipeline"] = from_pipeline
|
| 179 |
+
|
| 180 |
+
is_local = os.path.isdir(pretrained_model_name_or_path)
|
| 181 |
+
if os.path.isfile(pretrained_model_name_or_path):
|
| 182 |
+
resolved_model_card_file = pretrained_model_name_or_path
|
| 183 |
+
is_local = True
|
| 184 |
+
else:
|
| 185 |
+
try:
|
| 186 |
+
# Load from URL or cache if already cached
|
| 187 |
+
resolved_model_card_file = cached_file(
|
| 188 |
+
pretrained_model_name_or_path,
|
| 189 |
+
filename=MODEL_CARD_NAME,
|
| 190 |
+
cache_dir=cache_dir,
|
| 191 |
+
proxies=proxies,
|
| 192 |
+
user_agent=user_agent,
|
| 193 |
+
)
|
| 194 |
+
if is_local:
|
| 195 |
+
logger.info(f"loading model card file {resolved_model_card_file}")
|
| 196 |
+
else:
|
| 197 |
+
logger.info(f"loading model card file {MODEL_CARD_NAME} from cache at {resolved_model_card_file}")
|
| 198 |
+
# Load model card
|
| 199 |
+
modelcard = cls.from_json_file(resolved_model_card_file)
|
| 200 |
+
|
| 201 |
+
except (OSError, json.JSONDecodeError):
|
| 202 |
+
# We fall back on creating an empty model card
|
| 203 |
+
modelcard = cls()
|
| 204 |
+
|
| 205 |
+
# Update model card with kwargs if needed
|
| 206 |
+
to_remove = []
|
| 207 |
+
for key, value in kwargs.items():
|
| 208 |
+
if hasattr(modelcard, key):
|
| 209 |
+
setattr(modelcard, key, value)
|
| 210 |
+
to_remove.append(key)
|
| 211 |
+
for key in to_remove:
|
| 212 |
+
kwargs.pop(key, None)
|
| 213 |
+
|
| 214 |
+
logger.info(f"Model card: {modelcard}")
|
| 215 |
+
if return_unused_kwargs:
|
| 216 |
+
return modelcard, kwargs
|
| 217 |
+
else:
|
| 218 |
+
return modelcard
|
| 219 |
+
|
| 220 |
+
@classmethod
|
| 221 |
+
def from_dict(cls, json_object):
|
| 222 |
+
"""Constructs a `ModelCard` from a Python dictionary of parameters."""
|
| 223 |
+
return cls(**json_object)
|
| 224 |
+
|
| 225 |
+
@classmethod
|
| 226 |
+
def from_json_file(cls, json_file):
|
| 227 |
+
"""Constructs a `ModelCard` from a json file of parameters."""
|
| 228 |
+
with open(json_file, encoding="utf-8") as reader:
|
| 229 |
+
text = reader.read()
|
| 230 |
+
dict_obj = json.loads(text)
|
| 231 |
+
return cls(**dict_obj)
|
| 232 |
+
|
| 233 |
+
def __eq__(self, other):
|
| 234 |
+
return self.__dict__ == other.__dict__
|
| 235 |
+
|
| 236 |
+
def __repr__(self):
|
| 237 |
+
return str(self.to_json_string())
|
| 238 |
+
|
| 239 |
+
def to_dict(self):
|
| 240 |
+
"""Serializes this instance to a Python dictionary."""
|
| 241 |
+
output = copy.deepcopy(self.__dict__)
|
| 242 |
+
return output
|
| 243 |
+
|
| 244 |
+
def to_json_string(self):
|
| 245 |
+
"""Serializes this instance to a JSON string."""
|
| 246 |
+
return json.dumps(self.to_dict(), indent=2, sort_keys=True) + "\n"
|
| 247 |
+
|
| 248 |
+
def to_json_file(self, json_file_path):
|
| 249 |
+
"""Save this instance to a json file."""
|
| 250 |
+
with open(json_file_path, "w", encoding="utf-8") as writer:
|
| 251 |
+
writer.write(self.to_json_string())
|
| 252 |
+
|
| 253 |
+
|
| 254 |
+
AUTOGENERATED_TRAINER_COMMENT = """
|
| 255 |
+
<!-- This model card has been generated automatically according to the information the Trainer had access to. You
|
| 256 |
+
should probably proofread and complete it, then remove this comment. -->
|
| 257 |
+
"""
|
| 258 |
+
|
| 259 |
+
AUTOGENERATED_KERAS_COMMENT = """
|
| 260 |
+
<!-- This model card has been generated automatically according to the information Keras had access to. You should
|
| 261 |
+
probably proofread and complete it, then remove this comment. -->
|
| 262 |
+
"""
|
| 263 |
+
|
| 264 |
+
|
| 265 |
+
TASK_TAG_TO_NAME_MAPPING = {
|
| 266 |
+
"fill-mask": "Masked Language Modeling",
|
| 267 |
+
"image-classification": "Image Classification",
|
| 268 |
+
"image-segmentation": "Image Segmentation",
|
| 269 |
+
"multiple-choice": "Multiple Choice",
|
| 270 |
+
"object-detection": "Object Detection",
|
| 271 |
+
"question-answering": "Question Answering",
|
| 272 |
+
"summarization": "Summarization",
|
| 273 |
+
"table-question-answering": "Table Question Answering",
|
| 274 |
+
"text-classification": "Text Classification",
|
| 275 |
+
"text-generation": "Causal Language Modeling",
|
| 276 |
+
"text2text-generation": "Sequence-to-sequence Language Modeling",
|
| 277 |
+
"token-classification": "Token Classification",
|
| 278 |
+
"translation": "Translation",
|
| 279 |
+
"zero-shot-classification": "Zero Shot Classification",
|
| 280 |
+
"automatic-speech-recognition": "Automatic Speech Recognition",
|
| 281 |
+
"audio-classification": "Audio Classification",
|
| 282 |
+
}
|
| 283 |
+
|
| 284 |
+
|
| 285 |
+
METRIC_TAGS = [
|
| 286 |
+
"accuracy",
|
| 287 |
+
"bleu",
|
| 288 |
+
"f1",
|
| 289 |
+
"matthews_correlation",
|
| 290 |
+
"pearsonr",
|
| 291 |
+
"precision",
|
| 292 |
+
"recall",
|
| 293 |
+
"rouge",
|
| 294 |
+
"sacrebleu",
|
| 295 |
+
"spearmanr",
|
| 296 |
+
"wer",
|
| 297 |
+
]
|
| 298 |
+
|
| 299 |
+
|
| 300 |
+
def _listify(obj):
|
| 301 |
+
if obj is None:
|
| 302 |
+
return []
|
| 303 |
+
elif isinstance(obj, str):
|
| 304 |
+
return [obj]
|
| 305 |
+
else:
|
| 306 |
+
return obj
|
| 307 |
+
|
| 308 |
+
|
| 309 |
+
def _insert_values_as_list(metadata, name, values):
|
| 310 |
+
if values is None:
|
| 311 |
+
return metadata
|
| 312 |
+
if isinstance(values, str):
|
| 313 |
+
values = [values]
|
| 314 |
+
values = [v for v in values if v is not None]
|
| 315 |
+
if len(values) == 0:
|
| 316 |
+
return metadata
|
| 317 |
+
metadata[name] = values
|
| 318 |
+
return metadata
|
| 319 |
+
|
| 320 |
+
|
| 321 |
+
def infer_metric_tags_from_eval_results(eval_results):
|
| 322 |
+
if eval_results is None:
|
| 323 |
+
return {}
|
| 324 |
+
result = {}
|
| 325 |
+
for key in eval_results:
|
| 326 |
+
if key.lower().replace(" ", "_") in METRIC_TAGS:
|
| 327 |
+
result[key.lower().replace(" ", "_")] = key
|
| 328 |
+
elif key.lower() == "rouge1":
|
| 329 |
+
result["rouge"] = key
|
| 330 |
+
return result
|
| 331 |
+
|
| 332 |
+
|
| 333 |
+
def _insert_value(metadata, name, value):
|
| 334 |
+
if value is None:
|
| 335 |
+
return metadata
|
| 336 |
+
metadata[name] = value
|
| 337 |
+
return metadata
|
| 338 |
+
|
| 339 |
+
|
| 340 |
+
def is_hf_dataset(dataset):
|
| 341 |
+
if not is_datasets_available():
|
| 342 |
+
return False
|
| 343 |
+
|
| 344 |
+
from datasets import Dataset, IterableDataset
|
| 345 |
+
|
| 346 |
+
return isinstance(dataset, (Dataset, IterableDataset))
|
| 347 |
+
|
| 348 |
+
|
| 349 |
+
def _get_mapping_values(mapping):
|
| 350 |
+
result = []
|
| 351 |
+
for v in mapping.values():
|
| 352 |
+
if isinstance(v, (tuple, list)):
|
| 353 |
+
result += list(v)
|
| 354 |
+
else:
|
| 355 |
+
result.append(v)
|
| 356 |
+
return result
|
| 357 |
+
|
| 358 |
+
|
| 359 |
+
@dataclass
|
| 360 |
+
class TrainingSummary:
|
| 361 |
+
model_name: str
|
| 362 |
+
language: Optional[Union[str, list[str]]] = None
|
| 363 |
+
license: Optional[str] = None
|
| 364 |
+
tags: Optional[Union[str, list[str]]] = None
|
| 365 |
+
finetuned_from: Optional[str] = None
|
| 366 |
+
tasks: Optional[Union[str, list[str]]] = None
|
| 367 |
+
dataset: Optional[Union[str, list[str]]] = None
|
| 368 |
+
dataset_tags: Optional[Union[str, list[str]]] = None
|
| 369 |
+
dataset_args: Optional[Union[str, list[str]]] = None
|
| 370 |
+
dataset_metadata: Optional[dict[str, Any]] = None
|
| 371 |
+
eval_results: Optional[dict[str, float]] = None
|
| 372 |
+
eval_lines: Optional[list[str]] = None
|
| 373 |
+
hyperparameters: Optional[dict[str, Any]] = None
|
| 374 |
+
source: Optional[str] = "trainer"
|
| 375 |
+
|
| 376 |
+
def __post_init__(self):
|
| 377 |
+
# Infer default license from the checkpoint used, if possible.
|
| 378 |
+
if (
|
| 379 |
+
self.license is None
|
| 380 |
+
and not is_offline_mode()
|
| 381 |
+
and self.finetuned_from is not None
|
| 382 |
+
and len(self.finetuned_from) > 0
|
| 383 |
+
):
|
| 384 |
+
try:
|
| 385 |
+
info = model_info(self.finetuned_from)
|
| 386 |
+
for tag in info.tags:
|
| 387 |
+
if tag.startswith("license:"):
|
| 388 |
+
self.license = tag[8:]
|
| 389 |
+
except (
|
| 390 |
+
requests.exceptions.HTTPError,
|
| 391 |
+
requests.exceptions.ConnectionError,
|
| 392 |
+
HFValidationError,
|
| 393 |
+
OfflineModeIsEnabled,
|
| 394 |
+
):
|
| 395 |
+
pass
|
| 396 |
+
|
| 397 |
+
def create_model_index(self, metric_mapping):
|
| 398 |
+
model_index = {"name": self.model_name}
|
| 399 |
+
|
| 400 |
+
# Dataset mapping tag -> name
|
| 401 |
+
dataset_names = _listify(self.dataset)
|
| 402 |
+
dataset_tags = _listify(self.dataset_tags)
|
| 403 |
+
dataset_args = _listify(self.dataset_args)
|
| 404 |
+
dataset_metadata = _listify(self.dataset_metadata)
|
| 405 |
+
if len(dataset_args) < len(dataset_tags):
|
| 406 |
+
dataset_args = dataset_args + [None] * (len(dataset_tags) - len(dataset_args))
|
| 407 |
+
dataset_mapping = dict(zip(dataset_tags, dataset_names))
|
| 408 |
+
dataset_arg_mapping = dict(zip(dataset_tags, dataset_args))
|
| 409 |
+
dataset_metadata_mapping = dict(zip(dataset_tags, dataset_metadata))
|
| 410 |
+
|
| 411 |
+
task_mapping = {
|
| 412 |
+
task: TASK_TAG_TO_NAME_MAPPING[task] for task in _listify(self.tasks) if task in TASK_TAG_TO_NAME_MAPPING
|
| 413 |
+
}
|
| 414 |
+
|
| 415 |
+
model_index["results"] = []
|
| 416 |
+
|
| 417 |
+
if len(task_mapping) == 0 and len(dataset_mapping) == 0:
|
| 418 |
+
return [model_index]
|
| 419 |
+
if len(task_mapping) == 0:
|
| 420 |
+
task_mapping = {None: None}
|
| 421 |
+
if len(dataset_mapping) == 0:
|
| 422 |
+
dataset_mapping = {None: None}
|
| 423 |
+
|
| 424 |
+
# One entry per dataset and per task
|
| 425 |
+
all_possibilities = [(task_tag, ds_tag) for task_tag in task_mapping for ds_tag in dataset_mapping]
|
| 426 |
+
for task_tag, ds_tag in all_possibilities:
|
| 427 |
+
result = {}
|
| 428 |
+
if task_tag is not None:
|
| 429 |
+
result["task"] = {"name": task_mapping[task_tag], "type": task_tag}
|
| 430 |
+
|
| 431 |
+
if ds_tag is not None:
|
| 432 |
+
metadata = dataset_metadata_mapping.get(ds_tag, {})
|
| 433 |
+
result["dataset"] = {
|
| 434 |
+
"name": dataset_mapping[ds_tag],
|
| 435 |
+
"type": ds_tag,
|
| 436 |
+
**metadata,
|
| 437 |
+
}
|
| 438 |
+
if dataset_arg_mapping[ds_tag] is not None:
|
| 439 |
+
result["dataset"]["args"] = dataset_arg_mapping[ds_tag]
|
| 440 |
+
|
| 441 |
+
if len(metric_mapping) > 0:
|
| 442 |
+
result["metrics"] = []
|
| 443 |
+
for metric_tag, metric_name in metric_mapping.items():
|
| 444 |
+
result["metrics"].append(
|
| 445 |
+
{
|
| 446 |
+
"name": metric_name,
|
| 447 |
+
"type": metric_tag,
|
| 448 |
+
"value": self.eval_results[metric_name],
|
| 449 |
+
}
|
| 450 |
+
)
|
| 451 |
+
|
| 452 |
+
# Remove partial results to avoid the model card being rejected.
|
| 453 |
+
if "task" in result and "dataset" in result and "metrics" in result:
|
| 454 |
+
model_index["results"].append(result)
|
| 455 |
+
else:
|
| 456 |
+
logger.info(f"Dropping the following result as it does not have all the necessary fields:\n{result}")
|
| 457 |
+
|
| 458 |
+
return [model_index]
|
| 459 |
+
|
| 460 |
+
def create_metadata(self):
|
| 461 |
+
metric_mapping = infer_metric_tags_from_eval_results(self.eval_results)
|
| 462 |
+
|
| 463 |
+
metadata = {}
|
| 464 |
+
metadata = _insert_value(metadata, "library_name", "transformers")
|
| 465 |
+
metadata = _insert_values_as_list(metadata, "language", self.language)
|
| 466 |
+
metadata = _insert_value(metadata, "license", self.license)
|
| 467 |
+
if self.finetuned_from is not None and isinstance(self.finetuned_from, str) and len(self.finetuned_from) > 0:
|
| 468 |
+
metadata = _insert_value(metadata, "base_model", self.finetuned_from)
|
| 469 |
+
metadata = _insert_values_as_list(metadata, "tags", self.tags)
|
| 470 |
+
metadata = _insert_values_as_list(metadata, "datasets", self.dataset_tags)
|
| 471 |
+
metadata = _insert_values_as_list(metadata, "metrics", list(metric_mapping.keys()))
|
| 472 |
+
metadata["model-index"] = self.create_model_index(metric_mapping)
|
| 473 |
+
|
| 474 |
+
return metadata
|
| 475 |
+
|
| 476 |
+
def to_model_card(self):
|
| 477 |
+
model_card = ""
|
| 478 |
+
|
| 479 |
+
metadata = yaml.dump(self.create_metadata(), sort_keys=False)
|
| 480 |
+
if len(metadata) > 0:
|
| 481 |
+
model_card = f"---\n{metadata}---\n"
|
| 482 |
+
|
| 483 |
+
# Now the model card for realsies.
|
| 484 |
+
if self.source == "trainer":
|
| 485 |
+
model_card += AUTOGENERATED_TRAINER_COMMENT
|
| 486 |
+
else:
|
| 487 |
+
model_card += AUTOGENERATED_KERAS_COMMENT
|
| 488 |
+
|
| 489 |
+
model_card += f"\n# {self.model_name}\n\n"
|
| 490 |
+
|
| 491 |
+
if self.finetuned_from is None:
|
| 492 |
+
model_card += "This model was trained from scratch on "
|
| 493 |
+
else:
|
| 494 |
+
model_card += (
|
| 495 |
+
"This model is a fine-tuned version of"
|
| 496 |
+
f" [{self.finetuned_from}](https://huggingface.co/{self.finetuned_from}) on "
|
| 497 |
+
)
|
| 498 |
+
|
| 499 |
+
if self.dataset is None or (isinstance(self.dataset, list) and len(self.dataset) == 0):
|
| 500 |
+
model_card += "an unknown dataset."
|
| 501 |
+
else:
|
| 502 |
+
if isinstance(self.dataset, str):
|
| 503 |
+
model_card += f"the {self.dataset} dataset."
|
| 504 |
+
elif isinstance(self.dataset, (tuple, list)) and len(self.dataset) == 1:
|
| 505 |
+
model_card += f"the {self.dataset[0]} dataset."
|
| 506 |
+
else:
|
| 507 |
+
model_card += (
|
| 508 |
+
", ".join([f"the {ds}" for ds in self.dataset[:-1]]) + f" and the {self.dataset[-1]} datasets."
|
| 509 |
+
)
|
| 510 |
+
|
| 511 |
+
if self.eval_results is not None:
|
| 512 |
+
model_card += "\nIt achieves the following results on the evaluation set:\n"
|
| 513 |
+
model_card += "\n".join([f"- {name}: {_maybe_round(value)}" for name, value in self.eval_results.items()])
|
| 514 |
+
model_card += "\n"
|
| 515 |
+
|
| 516 |
+
model_card += "\n## Model description\n\nMore information needed\n"
|
| 517 |
+
model_card += "\n## Intended uses & limitations\n\nMore information needed\n"
|
| 518 |
+
model_card += "\n## Training and evaluation data\n\nMore information needed\n"
|
| 519 |
+
|
| 520 |
+
model_card += "\n## Training procedure\n"
|
| 521 |
+
model_card += "\n### Training hyperparameters\n"
|
| 522 |
+
if self.hyperparameters is not None:
|
| 523 |
+
model_card += "\nThe following hyperparameters were used during training:\n"
|
| 524 |
+
model_card += "\n".join([f"- {name}: {value}" for name, value in self.hyperparameters.items()])
|
| 525 |
+
model_card += "\n"
|
| 526 |
+
else:
|
| 527 |
+
model_card += "\nMore information needed\n"
|
| 528 |
+
|
| 529 |
+
if self.eval_lines is not None:
|
| 530 |
+
model_card += "\n### Training results\n\n"
|
| 531 |
+
model_card += make_markdown_table(self.eval_lines)
|
| 532 |
+
model_card += "\n"
|
| 533 |
+
|
| 534 |
+
model_card += "\n### Framework versions\n\n"
|
| 535 |
+
model_card += f"- Transformers {__version__}\n"
|
| 536 |
+
|
| 537 |
+
if self.source == "trainer" and is_torch_available():
|
| 538 |
+
import torch
|
| 539 |
+
|
| 540 |
+
model_card += f"- Pytorch {torch.__version__}\n"
|
| 541 |
+
elif self.source == "keras" and is_tf_available():
|
| 542 |
+
import tensorflow as tf
|
| 543 |
+
|
| 544 |
+
model_card += f"- TensorFlow {tf.__version__}\n"
|
| 545 |
+
if is_datasets_available():
|
| 546 |
+
import datasets
|
| 547 |
+
|
| 548 |
+
model_card += f"- Datasets {datasets.__version__}\n"
|
| 549 |
+
if is_tokenizers_available():
|
| 550 |
+
import tokenizers
|
| 551 |
+
|
| 552 |
+
model_card += f"- Tokenizers {tokenizers.__version__}\n"
|
| 553 |
+
|
| 554 |
+
return model_card
|
| 555 |
+
|
| 556 |
+
@classmethod
|
| 557 |
+
def from_trainer(
|
| 558 |
+
cls,
|
| 559 |
+
trainer,
|
| 560 |
+
language=None,
|
| 561 |
+
license=None,
|
| 562 |
+
tags=None,
|
| 563 |
+
model_name=None,
|
| 564 |
+
finetuned_from=None,
|
| 565 |
+
tasks=None,
|
| 566 |
+
dataset_tags=None,
|
| 567 |
+
dataset_metadata=None,
|
| 568 |
+
dataset=None,
|
| 569 |
+
dataset_args=None,
|
| 570 |
+
):
|
| 571 |
+
# Infer default from dataset
|
| 572 |
+
one_dataset = trainer.eval_dataset if trainer.eval_dataset is not None else trainer.train_dataset
|
| 573 |
+
if is_hf_dataset(one_dataset) and (dataset_tags is None or dataset_args is None or dataset_metadata is None):
|
| 574 |
+
default_tag = one_dataset.builder_name
|
| 575 |
+
# Those are not real datasets from the Hub so we exclude them.
|
| 576 |
+
if default_tag not in ["csv", "json", "pandas", "parquet", "text"]:
|
| 577 |
+
if dataset_metadata is None:
|
| 578 |
+
dataset_metadata = [{"config": one_dataset.config_name, "split": str(one_dataset.split)}]
|
| 579 |
+
if dataset_tags is None:
|
| 580 |
+
dataset_tags = [default_tag]
|
| 581 |
+
if dataset_args is None:
|
| 582 |
+
dataset_args = [one_dataset.config_name]
|
| 583 |
+
|
| 584 |
+
if dataset is None and dataset_tags is not None:
|
| 585 |
+
dataset = dataset_tags
|
| 586 |
+
|
| 587 |
+
# Infer default finetuned_from
|
| 588 |
+
if (
|
| 589 |
+
finetuned_from is None
|
| 590 |
+
and hasattr(trainer.model.config, "_name_or_path")
|
| 591 |
+
and not os.path.isdir(trainer.model.config._name_or_path)
|
| 592 |
+
):
|
| 593 |
+
finetuned_from = trainer.model.config._name_or_path
|
| 594 |
+
|
| 595 |
+
# Infer default task tag:
|
| 596 |
+
if tasks is None:
|
| 597 |
+
model_class_name = trainer.model.__class__.__name__
|
| 598 |
+
for task, mapping in TASK_MAPPING.items():
|
| 599 |
+
if model_class_name in _get_mapping_values(mapping):
|
| 600 |
+
tasks = task
|
| 601 |
+
|
| 602 |
+
if model_name is None:
|
| 603 |
+
model_name = Path(trainer.args.output_dir).name
|
| 604 |
+
if len(model_name) == 0:
|
| 605 |
+
model_name = finetuned_from
|
| 606 |
+
|
| 607 |
+
# Add `generated_from_trainer` to the tags
|
| 608 |
+
if tags is None:
|
| 609 |
+
tags = ["generated_from_trainer"]
|
| 610 |
+
elif isinstance(tags, str) and tags != "generated_from_trainer":
|
| 611 |
+
tags = [tags, "generated_from_trainer"]
|
| 612 |
+
elif "generated_from_trainer" not in tags:
|
| 613 |
+
tags.append("generated_from_trainer")
|
| 614 |
+
|
| 615 |
+
_, eval_lines, eval_results = parse_log_history(trainer.state.log_history)
|
| 616 |
+
hyperparameters = extract_hyperparameters_from_trainer(trainer)
|
| 617 |
+
|
| 618 |
+
return cls(
|
| 619 |
+
language=language,
|
| 620 |
+
license=license,
|
| 621 |
+
tags=tags,
|
| 622 |
+
model_name=model_name,
|
| 623 |
+
finetuned_from=finetuned_from,
|
| 624 |
+
tasks=tasks,
|
| 625 |
+
dataset=dataset,
|
| 626 |
+
dataset_tags=dataset_tags,
|
| 627 |
+
dataset_args=dataset_args,
|
| 628 |
+
dataset_metadata=dataset_metadata,
|
| 629 |
+
eval_results=eval_results,
|
| 630 |
+
eval_lines=eval_lines,
|
| 631 |
+
hyperparameters=hyperparameters,
|
| 632 |
+
)
|
| 633 |
+
|
| 634 |
+
@classmethod
|
| 635 |
+
def from_keras(
|
| 636 |
+
cls,
|
| 637 |
+
model,
|
| 638 |
+
model_name,
|
| 639 |
+
keras_history=None,
|
| 640 |
+
language=None,
|
| 641 |
+
license=None,
|
| 642 |
+
tags=None,
|
| 643 |
+
finetuned_from=None,
|
| 644 |
+
tasks=None,
|
| 645 |
+
dataset_tags=None,
|
| 646 |
+
dataset=None,
|
| 647 |
+
dataset_args=None,
|
| 648 |
+
):
|
| 649 |
+
# Infer default from dataset
|
| 650 |
+
if dataset is not None:
|
| 651 |
+
if is_hf_dataset(dataset) and (dataset_tags is None or dataset_args is None):
|
| 652 |
+
default_tag = dataset.builder_name
|
| 653 |
+
# Those are not real datasets from the Hub so we exclude them.
|
| 654 |
+
if default_tag not in ["csv", "json", "pandas", "parquet", "text"]:
|
| 655 |
+
if dataset_tags is None:
|
| 656 |
+
dataset_tags = [default_tag]
|
| 657 |
+
if dataset_args is None:
|
| 658 |
+
dataset_args = [dataset.config_name]
|
| 659 |
+
|
| 660 |
+
if dataset is None and dataset_tags is not None:
|
| 661 |
+
dataset = dataset_tags
|
| 662 |
+
|
| 663 |
+
# Infer default finetuned_from
|
| 664 |
+
if (
|
| 665 |
+
finetuned_from is None
|
| 666 |
+
and hasattr(model.config, "_name_or_path")
|
| 667 |
+
and not os.path.isdir(model.config._name_or_path)
|
| 668 |
+
):
|
| 669 |
+
finetuned_from = model.config._name_or_path
|
| 670 |
+
|
| 671 |
+
# Infer default task tag:
|
| 672 |
+
if tasks is None:
|
| 673 |
+
model_class_name = model.__class__.__name__
|
| 674 |
+
for task, mapping in TASK_MAPPING.items():
|
| 675 |
+
if model_class_name in _get_mapping_values(mapping):
|
| 676 |
+
tasks = task
|
| 677 |
+
|
| 678 |
+
# Add `generated_from_keras_callback` to the tags
|
| 679 |
+
if tags is None:
|
| 680 |
+
tags = ["generated_from_keras_callback"]
|
| 681 |
+
elif isinstance(tags, str) and tags != "generated_from_keras_callback":
|
| 682 |
+
tags = [tags, "generated_from_keras_callback"]
|
| 683 |
+
elif "generated_from_keras_callback" not in tags:
|
| 684 |
+
tags.append("generated_from_keras_callback")
|
| 685 |
+
|
| 686 |
+
if keras_history is not None:
|
| 687 |
+
_, eval_lines, eval_results = parse_keras_history(keras_history)
|
| 688 |
+
else:
|
| 689 |
+
eval_lines = []
|
| 690 |
+
eval_results = {}
|
| 691 |
+
hyperparameters = extract_hyperparameters_from_keras(model)
|
| 692 |
+
|
| 693 |
+
return cls(
|
| 694 |
+
language=language,
|
| 695 |
+
license=license,
|
| 696 |
+
tags=tags,
|
| 697 |
+
model_name=model_name,
|
| 698 |
+
finetuned_from=finetuned_from,
|
| 699 |
+
tasks=tasks,
|
| 700 |
+
dataset_tags=dataset_tags,
|
| 701 |
+
dataset=dataset,
|
| 702 |
+
dataset_args=dataset_args,
|
| 703 |
+
eval_results=eval_results,
|
| 704 |
+
eval_lines=eval_lines,
|
| 705 |
+
hyperparameters=hyperparameters,
|
| 706 |
+
source="keras",
|
| 707 |
+
)
|
| 708 |
+
|
| 709 |
+
|
| 710 |
+
def parse_keras_history(logs):
|
| 711 |
+
"""
|
| 712 |
+
Parse the `logs` of either a `keras.History` object returned by `model.fit()` or an accumulated logs `dict`
|
| 713 |
+
passed to the `PushToHubCallback`. Returns lines and logs compatible with those returned by `parse_log_history`.
|
| 714 |
+
"""
|
| 715 |
+
if hasattr(logs, "history"):
|
| 716 |
+
# This looks like a `History` object
|
| 717 |
+
if not hasattr(logs, "epoch"):
|
| 718 |
+
# This history looks empty, return empty results
|
| 719 |
+
return None, [], {}
|
| 720 |
+
logs.history["epoch"] = logs.epoch
|
| 721 |
+
logs = logs.history
|
| 722 |
+
else:
|
| 723 |
+
# Training logs is a list of dicts, let's invert it to a dict of lists to match a History object
|
| 724 |
+
logs = {log_key: [single_dict[log_key] for single_dict in logs] for log_key in logs[0]}
|
| 725 |
+
|
| 726 |
+
lines = []
|
| 727 |
+
for i in range(len(logs["epoch"])):
|
| 728 |
+
epoch_dict = {log_key: log_value_list[i] for log_key, log_value_list in logs.items()}
|
| 729 |
+
values = {}
|
| 730 |
+
for k, v in epoch_dict.items():
|
| 731 |
+
if k.startswith("val_"):
|
| 732 |
+
k = "validation_" + k[4:]
|
| 733 |
+
elif k != "epoch":
|
| 734 |
+
k = "train_" + k
|
| 735 |
+
splits = k.split("_")
|
| 736 |
+
name = " ".join([part.capitalize() for part in splits])
|
| 737 |
+
values[name] = v
|
| 738 |
+
lines.append(values)
|
| 739 |
+
|
| 740 |
+
eval_results = lines[-1]
|
| 741 |
+
|
| 742 |
+
return logs, lines, eval_results
|
| 743 |
+
|
| 744 |
+
|
| 745 |
+
def parse_log_history(log_history):
|
| 746 |
+
"""
|
| 747 |
+
Parse the `log_history` of a Trainer to get the intermediate and final evaluation results.
|
| 748 |
+
"""
|
| 749 |
+
idx = 0
|
| 750 |
+
while idx < len(log_history) and "train_runtime" not in log_history[idx]:
|
| 751 |
+
idx += 1
|
| 752 |
+
|
| 753 |
+
# If there are no training logs
|
| 754 |
+
if idx == len(log_history):
|
| 755 |
+
idx -= 1
|
| 756 |
+
while idx >= 0 and "eval_loss" not in log_history[idx]:
|
| 757 |
+
idx -= 1
|
| 758 |
+
|
| 759 |
+
if idx >= 0:
|
| 760 |
+
return None, None, log_history[idx]
|
| 761 |
+
else:
|
| 762 |
+
return None, None, None
|
| 763 |
+
|
| 764 |
+
# From now one we can assume we have training logs:
|
| 765 |
+
train_log = log_history[idx]
|
| 766 |
+
lines = []
|
| 767 |
+
training_loss = "No log"
|
| 768 |
+
for i in range(idx):
|
| 769 |
+
if "loss" in log_history[i]:
|
| 770 |
+
training_loss = log_history[i]["loss"]
|
| 771 |
+
if "eval_loss" in log_history[i]:
|
| 772 |
+
metrics = log_history[i].copy()
|
| 773 |
+
_ = metrics.pop("total_flos", None)
|
| 774 |
+
epoch = metrics.pop("epoch", None)
|
| 775 |
+
step = metrics.pop("step", None)
|
| 776 |
+
_ = metrics.pop("eval_runtime", None)
|
| 777 |
+
_ = metrics.pop("eval_samples_per_second", None)
|
| 778 |
+
_ = metrics.pop("eval_steps_per_second", None)
|
| 779 |
+
_ = metrics.pop("eval_jit_compilation_time", None)
|
| 780 |
+
values = {"Training Loss": training_loss, "Epoch": epoch, "Step": step}
|
| 781 |
+
for k, v in metrics.items():
|
| 782 |
+
if k == "eval_loss":
|
| 783 |
+
values["Validation Loss"] = v
|
| 784 |
+
else:
|
| 785 |
+
splits = k.split("_")
|
| 786 |
+
name = " ".join([part.capitalize() for part in splits[1:]])
|
| 787 |
+
values[name] = v
|
| 788 |
+
lines.append(values)
|
| 789 |
+
|
| 790 |
+
idx = len(log_history) - 1
|
| 791 |
+
while idx >= 0 and "eval_loss" not in log_history[idx]:
|
| 792 |
+
idx -= 1
|
| 793 |
+
|
| 794 |
+
if idx > 0:
|
| 795 |
+
eval_results = {}
|
| 796 |
+
for key, value in log_history[idx].items():
|
| 797 |
+
key = key.removeprefix("eval_")
|
| 798 |
+
if key not in ["runtime", "samples_per_second", "steps_per_second", "epoch", "step"]:
|
| 799 |
+
camel_cased_key = " ".join([part.capitalize() for part in key.split("_")])
|
| 800 |
+
eval_results[camel_cased_key] = value
|
| 801 |
+
return train_log, lines, eval_results
|
| 802 |
+
else:
|
| 803 |
+
return train_log, lines, None
|
| 804 |
+
|
| 805 |
+
|
| 806 |
+
def extract_hyperparameters_from_keras(model):
|
| 807 |
+
from .modeling_tf_utils import keras
|
| 808 |
+
|
| 809 |
+
hyperparameters = {}
|
| 810 |
+
if hasattr(model, "optimizer") and model.optimizer is not None:
|
| 811 |
+
hyperparameters["optimizer"] = model.optimizer.get_config()
|
| 812 |
+
else:
|
| 813 |
+
hyperparameters["optimizer"] = None
|
| 814 |
+
hyperparameters["training_precision"] = keras.mixed_precision.global_policy().name
|
| 815 |
+
|
| 816 |
+
return hyperparameters
|
| 817 |
+
|
| 818 |
+
|
| 819 |
+
def _maybe_round(v, decimals=4):
|
| 820 |
+
if isinstance(v, float) and len(str(v).split(".")) > 1 and len(str(v).split(".")[1]) > decimals:
|
| 821 |
+
return f"{v:.{decimals}f}"
|
| 822 |
+
return str(v)
|
| 823 |
+
|
| 824 |
+
|
| 825 |
+
def _regular_table_line(values, col_widths):
|
| 826 |
+
values_with_space = [f"| {v}" + " " * (w - len(v) + 1) for v, w in zip(values, col_widths)]
|
| 827 |
+
return "".join(values_with_space) + "|\n"
|
| 828 |
+
|
| 829 |
+
|
| 830 |
+
def _second_table_line(col_widths):
|
| 831 |
+
values = ["|:" + "-" * w + ":" for w in col_widths]
|
| 832 |
+
return "".join(values) + "|\n"
|
| 833 |
+
|
| 834 |
+
|
| 835 |
+
def make_markdown_table(lines):
|
| 836 |
+
"""
|
| 837 |
+
Create a nice Markdown table from the results in `lines`.
|
| 838 |
+
"""
|
| 839 |
+
if lines is None or len(lines) == 0:
|
| 840 |
+
return ""
|
| 841 |
+
col_widths = {key: len(str(key)) for key in lines[0]}
|
| 842 |
+
for line in lines:
|
| 843 |
+
for key, value in line.items():
|
| 844 |
+
if col_widths[key] < len(_maybe_round(value)):
|
| 845 |
+
col_widths[key] = len(_maybe_round(value))
|
| 846 |
+
|
| 847 |
+
table = _regular_table_line(list(lines[0].keys()), list(col_widths.values()))
|
| 848 |
+
table += _second_table_line(list(col_widths.values()))
|
| 849 |
+
for line in lines:
|
| 850 |
+
table += _regular_table_line([_maybe_round(v) for v in line.values()], list(col_widths.values()))
|
| 851 |
+
return table
|
| 852 |
+
|
| 853 |
+
|
| 854 |
+
_TRAINING_ARGS_KEYS = [
|
| 855 |
+
"learning_rate",
|
| 856 |
+
"train_batch_size",
|
| 857 |
+
"eval_batch_size",
|
| 858 |
+
"seed",
|
| 859 |
+
]
|
| 860 |
+
|
| 861 |
+
|
| 862 |
+
def extract_hyperparameters_from_trainer(trainer):
|
| 863 |
+
hyperparameters = {k: getattr(trainer.args, k) for k in _TRAINING_ARGS_KEYS}
|
| 864 |
+
|
| 865 |
+
if trainer.args.parallel_mode not in [ParallelMode.NOT_PARALLEL, ParallelMode.NOT_DISTRIBUTED]:
|
| 866 |
+
hyperparameters["distributed_type"] = (
|
| 867 |
+
"multi-GPU" if trainer.args.parallel_mode == ParallelMode.DISTRIBUTED else trainer.args.parallel_mode.value
|
| 868 |
+
)
|
| 869 |
+
if trainer.args.world_size > 1:
|
| 870 |
+
hyperparameters["num_devices"] = trainer.args.world_size
|
| 871 |
+
if trainer.args.gradient_accumulation_steps > 1:
|
| 872 |
+
hyperparameters["gradient_accumulation_steps"] = trainer.args.gradient_accumulation_steps
|
| 873 |
+
|
| 874 |
+
total_train_batch_size = (
|
| 875 |
+
trainer.args.train_batch_size * trainer.args.world_size * trainer.args.gradient_accumulation_steps
|
| 876 |
+
)
|
| 877 |
+
if total_train_batch_size != hyperparameters["train_batch_size"]:
|
| 878 |
+
hyperparameters["total_train_batch_size"] = total_train_batch_size
|
| 879 |
+
total_eval_batch_size = trainer.args.eval_batch_size * trainer.args.world_size
|
| 880 |
+
if total_eval_batch_size != hyperparameters["eval_batch_size"]:
|
| 881 |
+
hyperparameters["total_eval_batch_size"] = total_eval_batch_size
|
| 882 |
+
|
| 883 |
+
if trainer.args.optim:
|
| 884 |
+
optimizer_name = trainer.args.optim
|
| 885 |
+
optimizer_args = trainer.args.optim_args if trainer.args.optim_args else "No additional optimizer arguments"
|
| 886 |
+
|
| 887 |
+
if "adam" in optimizer_name.lower():
|
| 888 |
+
hyperparameters["optimizer"] = (
|
| 889 |
+
f"Use {optimizer_name} with betas=({trainer.args.adam_beta1},{trainer.args.adam_beta2}) and"
|
| 890 |
+
f" epsilon={trainer.args.adam_epsilon} and optimizer_args={optimizer_args}"
|
| 891 |
+
)
|
| 892 |
+
else:
|
| 893 |
+
hyperparameters["optimizer"] = f"Use {optimizer_name} and the args are:\n{optimizer_args}"
|
| 894 |
+
|
| 895 |
+
hyperparameters["lr_scheduler_type"] = trainer.args.lr_scheduler_type.value
|
| 896 |
+
if trainer.args.warmup_ratio != 0.0:
|
| 897 |
+
hyperparameters["lr_scheduler_warmup_ratio"] = trainer.args.warmup_ratio
|
| 898 |
+
if trainer.args.warmup_steps != 0.0:
|
| 899 |
+
hyperparameters["lr_scheduler_warmup_steps"] = trainer.args.warmup_steps
|
| 900 |
+
if trainer.args.max_steps != -1:
|
| 901 |
+
hyperparameters["training_steps"] = trainer.args.max_steps
|
| 902 |
+
else:
|
| 903 |
+
hyperparameters["num_epochs"] = trainer.args.num_train_epochs
|
| 904 |
+
|
| 905 |
+
if trainer.args.fp16:
|
| 906 |
+
if trainer.use_apex:
|
| 907 |
+
hyperparameters["mixed_precision_training"] = f"Apex, opt level {trainer.args.fp16_opt_level}"
|
| 908 |
+
else:
|
| 909 |
+
hyperparameters["mixed_precision_training"] = "Native AMP"
|
| 910 |
+
|
| 911 |
+
if trainer.args.label_smoothing_factor != 0.0:
|
| 912 |
+
hyperparameters["label_smoothing_factor"] = trainer.args.label_smoothing_factor
|
| 913 |
+
|
| 914 |
+
return hyperparameters
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/modeling_attn_mask_utils.py
ADDED
|
@@ -0,0 +1,487 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2023 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""
|
| 15 |
+
IMPORTANT NOTICE: Every class and function in this file is deprecated in favor of using the much more general
|
| 16 |
+
`masking_utils.py` primitives. New code should not rely on it, it is only kept for backward compatibility for now,
|
| 17 |
+
and will be removed in the future.
|
| 18 |
+
"""
|
| 19 |
+
|
| 20 |
+
from dataclasses import dataclass
|
| 21 |
+
from typing import Optional, Union
|
| 22 |
+
|
| 23 |
+
import torch
|
| 24 |
+
|
| 25 |
+
from .utils.import_utils import is_torchdynamo_compiling
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
@dataclass
|
| 29 |
+
class AttentionMaskConverter:
|
| 30 |
+
"""
|
| 31 |
+
A utility attention mask class that allows one to:
|
| 32 |
+
- Create a causal 4d mask
|
| 33 |
+
- Create a causal 4d mask with slided window
|
| 34 |
+
- Convert a 2d attention mask (batch_size, query_length) to a 4d attention mask (batch_size, 1, query_length,
|
| 35 |
+
key_value_length) that can be multiplied with attention scores
|
| 36 |
+
|
| 37 |
+
Examples:
|
| 38 |
+
|
| 39 |
+
```python
|
| 40 |
+
>>> import torch
|
| 41 |
+
>>> from transformers.modeling_attn_mask_utils import AttentionMaskConverter
|
| 42 |
+
|
| 43 |
+
>>> converter = AttentionMaskConverter(True)
|
| 44 |
+
>>> converter.to_4d(torch.tensor([[0, 0, 0, 1, 1]]), 5, key_value_length=5, dtype=torch.float32)
|
| 45 |
+
tensor([[[[-3.4028e+38, -3.4028e+38, -3.4028e+38, -3.4028e+38, -3.4028e+38],
|
| 46 |
+
[-3.4028e+38, -3.4028e+38, -3.4028e+38, -3.4028e+38, -3.4028e+38],
|
| 47 |
+
[-3.4028e+38, -3.4028e+38, -3.4028e+38, -3.4028e+38, -3.4028e+38],
|
| 48 |
+
[-3.4028e+38, -3.4028e+38, -3.4028e+38, 0.0000e+00, -3.4028e+38],
|
| 49 |
+
[-3.4028e+38, -3.4028e+38, -3.4028e+38, 0.0000e+00, 0.0000e+00]]]])
|
| 50 |
+
```
|
| 51 |
+
|
| 52 |
+
Parameters:
|
| 53 |
+
is_causal (`bool`):
|
| 54 |
+
Whether the attention mask should be a uni-directional (causal) or bi-directional mask.
|
| 55 |
+
|
| 56 |
+
sliding_window (`int`, *optional*):
|
| 57 |
+
Optionally, the sliding window masks can be created if `sliding_window` is defined to a positive integer.
|
| 58 |
+
"""
|
| 59 |
+
|
| 60 |
+
is_causal: bool
|
| 61 |
+
sliding_window: int
|
| 62 |
+
|
| 63 |
+
def __init__(self, is_causal: bool, sliding_window: Optional[int] = None):
|
| 64 |
+
self.is_causal = is_causal
|
| 65 |
+
self.sliding_window = sliding_window
|
| 66 |
+
|
| 67 |
+
if self.sliding_window is not None and self.sliding_window <= 0:
|
| 68 |
+
raise ValueError(
|
| 69 |
+
f"Make sure that when passing `sliding_window` that its value is a strictly positive integer, not `{self.sliding_window}`"
|
| 70 |
+
)
|
| 71 |
+
|
| 72 |
+
def to_causal_4d(
|
| 73 |
+
self,
|
| 74 |
+
batch_size: int,
|
| 75 |
+
query_length: int,
|
| 76 |
+
key_value_length: int,
|
| 77 |
+
dtype: torch.dtype,
|
| 78 |
+
device: Union[torch.device, "str"] = "cpu",
|
| 79 |
+
) -> Optional[torch.Tensor]:
|
| 80 |
+
"""
|
| 81 |
+
Creates a causal 4D mask of (bsz, head_dim=1, query_length, key_value_length) shape and adds large negative
|
| 82 |
+
bias to upper right hand triangular matrix (causal mask).
|
| 83 |
+
"""
|
| 84 |
+
if not self.is_causal:
|
| 85 |
+
raise ValueError(f"Please use `to_causal_4d` only if {self.__class__} has `is_causal` set to True.")
|
| 86 |
+
|
| 87 |
+
# If shape is not cached, create a new causal mask and cache it
|
| 88 |
+
input_shape = (batch_size, query_length)
|
| 89 |
+
past_key_values_length = key_value_length - query_length
|
| 90 |
+
|
| 91 |
+
# create causal mask
|
| 92 |
+
# [bsz, seq_len] -> [bsz, 1, tgt_seq_len, src_seq_len]
|
| 93 |
+
causal_4d_mask = None
|
| 94 |
+
if input_shape[-1] > 1 or self.sliding_window is not None:
|
| 95 |
+
causal_4d_mask = self._make_causal_mask(
|
| 96 |
+
input_shape,
|
| 97 |
+
dtype,
|
| 98 |
+
device=device,
|
| 99 |
+
past_key_values_length=past_key_values_length,
|
| 100 |
+
sliding_window=self.sliding_window,
|
| 101 |
+
)
|
| 102 |
+
|
| 103 |
+
return causal_4d_mask
|
| 104 |
+
|
| 105 |
+
def to_4d(
|
| 106 |
+
self,
|
| 107 |
+
attention_mask_2d: torch.Tensor,
|
| 108 |
+
query_length: int,
|
| 109 |
+
dtype: torch.dtype,
|
| 110 |
+
key_value_length: Optional[int] = None,
|
| 111 |
+
) -> torch.Tensor:
|
| 112 |
+
"""
|
| 113 |
+
Converts 2D attention mask to 4D attention mask by expanding mask to (bsz, head_dim=1, query_length,
|
| 114 |
+
key_value_length) shape and by adding a large negative bias to not-attended positions. If attention_mask is
|
| 115 |
+
causal, a causal mask will be added.
|
| 116 |
+
"""
|
| 117 |
+
input_shape = (attention_mask_2d.shape[0], query_length)
|
| 118 |
+
|
| 119 |
+
# create causal mask
|
| 120 |
+
# [bsz, seq_len] -> [bsz, 1, tgt_seq_len, src_seq_len]
|
| 121 |
+
causal_4d_mask = None
|
| 122 |
+
if (input_shape[-1] > 1 or self.sliding_window is not None) and self.is_causal:
|
| 123 |
+
if key_value_length is None:
|
| 124 |
+
raise ValueError(
|
| 125 |
+
"This attention mask converter is causal. Make sure to pass `key_value_length` to correctly create a causal mask."
|
| 126 |
+
)
|
| 127 |
+
|
| 128 |
+
past_key_values_length = key_value_length - query_length
|
| 129 |
+
causal_4d_mask = self._make_causal_mask(
|
| 130 |
+
input_shape,
|
| 131 |
+
dtype,
|
| 132 |
+
device=attention_mask_2d.device,
|
| 133 |
+
past_key_values_length=past_key_values_length,
|
| 134 |
+
sliding_window=self.sliding_window,
|
| 135 |
+
)
|
| 136 |
+
elif self.sliding_window is not None:
|
| 137 |
+
raise NotImplementedError("Sliding window is currently only implemented for causal masking")
|
| 138 |
+
|
| 139 |
+
# [bsz, seq_len] -> [bsz, 1, tgt_seq_len, src_seq_len]
|
| 140 |
+
expanded_attn_mask = self._expand_mask(attention_mask_2d, dtype, tgt_len=input_shape[-1]).to(
|
| 141 |
+
attention_mask_2d.device
|
| 142 |
+
)
|
| 143 |
+
|
| 144 |
+
if causal_4d_mask is not None:
|
| 145 |
+
expanded_attn_mask = causal_4d_mask.masked_fill(expanded_attn_mask.bool(), torch.finfo(dtype).min)
|
| 146 |
+
|
| 147 |
+
# expanded_attn_mask + causal_4d_mask can cause some overflow
|
| 148 |
+
expanded_4d_mask = expanded_attn_mask
|
| 149 |
+
|
| 150 |
+
return expanded_4d_mask
|
| 151 |
+
|
| 152 |
+
@staticmethod
|
| 153 |
+
def _make_causal_mask(
|
| 154 |
+
input_ids_shape: torch.Size,
|
| 155 |
+
dtype: torch.dtype,
|
| 156 |
+
device: torch.device,
|
| 157 |
+
past_key_values_length: int = 0,
|
| 158 |
+
sliding_window: Optional[int] = None,
|
| 159 |
+
):
|
| 160 |
+
"""
|
| 161 |
+
Make causal mask used for bi-directional self-attention.
|
| 162 |
+
"""
|
| 163 |
+
bsz, tgt_len = input_ids_shape
|
| 164 |
+
mask = torch.full((tgt_len, tgt_len), torch.finfo(dtype).min, device=device)
|
| 165 |
+
mask_cond = torch.arange(mask.size(-1), device=device)
|
| 166 |
+
mask.masked_fill_(mask_cond < (mask_cond + 1).view(mask.size(-1), 1), 0)
|
| 167 |
+
|
| 168 |
+
mask = mask.to(dtype)
|
| 169 |
+
|
| 170 |
+
if past_key_values_length > 0:
|
| 171 |
+
mask = torch.cat([torch.zeros(tgt_len, past_key_values_length, dtype=dtype, device=device), mask], dim=-1)
|
| 172 |
+
|
| 173 |
+
# add lower triangular sliding window mask if necessary
|
| 174 |
+
if sliding_window is not None:
|
| 175 |
+
diagonal = past_key_values_length - sliding_window - 1
|
| 176 |
+
|
| 177 |
+
context_mask = torch.tril(torch.ones_like(mask, dtype=torch.bool), diagonal=diagonal)
|
| 178 |
+
# Recent changes in PyTorch prevent mutations on tensors converted with aten::_to_copy
|
| 179 |
+
# See https://github.com/pytorch/pytorch/issues/127571
|
| 180 |
+
if is_torchdynamo_compiling():
|
| 181 |
+
mask = mask.clone()
|
| 182 |
+
mask.masked_fill_(context_mask, torch.finfo(dtype).min)
|
| 183 |
+
|
| 184 |
+
return mask[None, None, :, :].expand(bsz, 1, tgt_len, tgt_len + past_key_values_length)
|
| 185 |
+
|
| 186 |
+
@staticmethod
|
| 187 |
+
def _expand_mask(mask: torch.Tensor, dtype: torch.dtype, tgt_len: Optional[int] = None):
|
| 188 |
+
"""
|
| 189 |
+
Expands attention_mask from `[bsz, seq_len]` to `[bsz, 1, tgt_seq_len, src_seq_len]`.
|
| 190 |
+
"""
|
| 191 |
+
bsz, src_len = mask.size()
|
| 192 |
+
tgt_len = tgt_len if tgt_len is not None else src_len
|
| 193 |
+
|
| 194 |
+
expanded_mask = mask[:, None, None, :].expand(bsz, 1, tgt_len, src_len).to(dtype)
|
| 195 |
+
|
| 196 |
+
inverted_mask = torch.tensor(1.0, dtype=dtype) - expanded_mask
|
| 197 |
+
|
| 198 |
+
return inverted_mask.masked_fill(inverted_mask.to(torch.bool), torch.finfo(dtype).min)
|
| 199 |
+
|
| 200 |
+
@staticmethod
|
| 201 |
+
def _unmask_unattended(
|
| 202 |
+
expanded_mask: torch.FloatTensor,
|
| 203 |
+
min_dtype: float,
|
| 204 |
+
):
|
| 205 |
+
# fmt: off
|
| 206 |
+
"""
|
| 207 |
+
Attend to all tokens in masked rows from the expanded attention mask, for example the relevant first rows when
|
| 208 |
+
using left padding. This is required by F.scaled_dot_product_attention memory-efficient attention path.
|
| 209 |
+
Details: https://github.com/pytorch/pytorch/issues/110213
|
| 210 |
+
|
| 211 |
+
`expanded_mask` is [bsz, num_masks, tgt_seq_len, src_seq_len] or [bsz, tgt_seq_len, src_seq_len].
|
| 212 |
+
`attention_mask` is [bsz, src_seq_len].
|
| 213 |
+
|
| 214 |
+
The dimension num_masks of `expanded_mask` is most often 1, but it can also be the number of heads in the case of alibi attention bias.
|
| 215 |
+
|
| 216 |
+
For example, if `expanded_mask` is (e.g. here left-padding case)
|
| 217 |
+
```
|
| 218 |
+
[[[[0, 0, 0],
|
| 219 |
+
[0, 0, 0],
|
| 220 |
+
[0, 0, 1]]],
|
| 221 |
+
[[[1, 0, 0],
|
| 222 |
+
[1, 1, 0],
|
| 223 |
+
[1, 1, 1]]],
|
| 224 |
+
[[[0, 0, 0],
|
| 225 |
+
[0, 1, 0],
|
| 226 |
+
[0, 1, 1]]]]
|
| 227 |
+
```
|
| 228 |
+
then the modified `expanded_mask` will be
|
| 229 |
+
```
|
| 230 |
+
[[[[1, 1, 1], <-- modified
|
| 231 |
+
[1, 1, 1], <-- modified
|
| 232 |
+
[0, 0, 1]]],
|
| 233 |
+
[[[1, 0, 0],
|
| 234 |
+
[1, 1, 0],
|
| 235 |
+
[1, 1, 1]]],
|
| 236 |
+
[[[1, 1, 1], <-- modified
|
| 237 |
+
[0, 1, 0],
|
| 238 |
+
[0, 1, 1]]]]
|
| 239 |
+
```
|
| 240 |
+
"""
|
| 241 |
+
# fmt: on
|
| 242 |
+
if expanded_mask.dtype == torch.bool:
|
| 243 |
+
raise ValueError(
|
| 244 |
+
"AttentionMaskConverter._unmask_unattended expects a float `expanded_mask`, got a BoolTensor."
|
| 245 |
+
)
|
| 246 |
+
|
| 247 |
+
return expanded_mask.mul(~torch.all(expanded_mask == min_dtype, dim=-1, keepdim=True))
|
| 248 |
+
|
| 249 |
+
@staticmethod
|
| 250 |
+
def _ignore_causal_mask_sdpa(
|
| 251 |
+
attention_mask: Optional[torch.Tensor],
|
| 252 |
+
inputs_embeds: torch.Tensor,
|
| 253 |
+
past_key_values_length: int,
|
| 254 |
+
sliding_window: Optional[int] = None,
|
| 255 |
+
is_training: bool = False,
|
| 256 |
+
) -> bool:
|
| 257 |
+
"""
|
| 258 |
+
Detects whether the optional user-specified attention_mask & the automatically created causal mask can be
|
| 259 |
+
ignored in case PyTorch's SDPA is used, rather relying on SDPA's `is_causal` argument.
|
| 260 |
+
|
| 261 |
+
In case no token is masked in the `attention_mask` argument, if `query_length == 1` or
|
| 262 |
+
`key_value_length == query_length`, we rather rely on SDPA `is_causal` argument to use causal/non-causal masks,
|
| 263 |
+
allowing to dispatch to the flash attention kernel (that can otherwise not be used if a custom `attn_mask` is
|
| 264 |
+
passed).
|
| 265 |
+
"""
|
| 266 |
+
|
| 267 |
+
_, query_length = inputs_embeds.shape[0], inputs_embeds.shape[1]
|
| 268 |
+
key_value_length = query_length + past_key_values_length
|
| 269 |
+
|
| 270 |
+
is_tracing = torch.jit.is_tracing() or isinstance(inputs_embeds, torch.fx.Proxy) or is_torchdynamo_compiling()
|
| 271 |
+
|
| 272 |
+
ignore_causal_mask = False
|
| 273 |
+
|
| 274 |
+
if attention_mask is None:
|
| 275 |
+
# TODO: When tracing with TorchDynamo with fullgraph=True, the model is recompiled depending on the input
|
| 276 |
+
# shape, thus SDPA's `is_causal` argument is rightfully updated
|
| 277 |
+
# (see https://gist.github.com/fxmarty/1313f39037fc1c112508989628c57363). However, when using
|
| 278 |
+
# `torch.export` or `torch.onnx.dynamo_export`, we must pass an example input, and `is_causal` behavior is
|
| 279 |
+
# hard-coded. If a user exports a model with q_len > 1, the exported model will hard-code `is_causal=True`
|
| 280 |
+
# which is in general wrong (see https://github.com/pytorch/pytorch/issues/108108).
|
| 281 |
+
# Thus, we only set `ignore_causal_mask = True` if the model is set to training.
|
| 282 |
+
#
|
| 283 |
+
# Besides, jit.trace can not handle the `q_len > 1` condition for `is_causal`
|
| 284 |
+
# ("TypeError: scaled_dot_product_attention(): argument 'is_causal' must be bool, not Tensor").
|
| 285 |
+
if (
|
| 286 |
+
(is_training or not is_tracing)
|
| 287 |
+
and (query_length == 1 or key_value_length == query_length)
|
| 288 |
+
and (sliding_window is None or key_value_length < sliding_window)
|
| 289 |
+
):
|
| 290 |
+
ignore_causal_mask = True
|
| 291 |
+
elif sliding_window is None or key_value_length < sliding_window:
|
| 292 |
+
if len(attention_mask.shape) == 4:
|
| 293 |
+
return False
|
| 294 |
+
elif not is_tracing and torch.all(attention_mask == 1):
|
| 295 |
+
if query_length == 1 or key_value_length == query_length:
|
| 296 |
+
# For query_length == 1, causal attention and bi-directional attention are the same.
|
| 297 |
+
ignore_causal_mask = True
|
| 298 |
+
|
| 299 |
+
# Unfortunately, for query_length > 1 and key_value_length != query_length, we cannot generally ignore
|
| 300 |
+
# the attention mask, as SDPA causal mask generation may be wrong. We will set `is_causal=False` in
|
| 301 |
+
# SDPA and rely on Transformers attention_mask instead, hence not setting it to None here.
|
| 302 |
+
# Reference: https://github.com/pytorch/pytorch/issues/108108
|
| 303 |
+
# TODO: maybe revisit this with https://github.com/pytorch/pytorch/pull/114823 in PyTorch 2.3.
|
| 304 |
+
|
| 305 |
+
return ignore_causal_mask
|
| 306 |
+
|
| 307 |
+
|
| 308 |
+
def _prepare_4d_causal_attention_mask(
|
| 309 |
+
attention_mask: Optional[torch.Tensor],
|
| 310 |
+
input_shape: Union[torch.Size, tuple, list],
|
| 311 |
+
inputs_embeds: torch.Tensor,
|
| 312 |
+
past_key_values_length: int,
|
| 313 |
+
sliding_window: Optional[int] = None,
|
| 314 |
+
):
|
| 315 |
+
"""
|
| 316 |
+
Creates a causal 4D mask of shape `(batch_size, 1, query_length, key_value_length)` from a 2D mask of shape
|
| 317 |
+
`(batch_size, key_value_length)`
|
| 318 |
+
|
| 319 |
+
Args:
|
| 320 |
+
attention_mask (`torch.Tensor` or `None`):
|
| 321 |
+
A 2D attention mask of shape `(batch_size, key_value_length)`
|
| 322 |
+
input_shape (`tuple(int)` or `list(int)` or `torch.Size`):
|
| 323 |
+
The input shape should be a tuple that defines `(batch_size, query_length)`.
|
| 324 |
+
inputs_embeds (`torch.Tensor`):
|
| 325 |
+
The embedded inputs as a torch Tensor.
|
| 326 |
+
past_key_values_length (`int`):
|
| 327 |
+
The length of the key value cache.
|
| 328 |
+
sliding_window (`int`, *optional*):
|
| 329 |
+
If the model uses windowed attention, a sliding window should be passed.
|
| 330 |
+
"""
|
| 331 |
+
attn_mask_converter = AttentionMaskConverter(is_causal=True, sliding_window=sliding_window)
|
| 332 |
+
|
| 333 |
+
key_value_length = input_shape[-1] + past_key_values_length
|
| 334 |
+
|
| 335 |
+
# 4d mask is passed through the layers
|
| 336 |
+
if attention_mask is not None and len(attention_mask.shape) == 2:
|
| 337 |
+
attention_mask = attn_mask_converter.to_4d(
|
| 338 |
+
attention_mask, input_shape[-1], key_value_length=key_value_length, dtype=inputs_embeds.dtype
|
| 339 |
+
)
|
| 340 |
+
elif attention_mask is not None and len(attention_mask.shape) == 4:
|
| 341 |
+
expected_shape = (input_shape[0], 1, input_shape[1], key_value_length)
|
| 342 |
+
if tuple(attention_mask.shape) != expected_shape:
|
| 343 |
+
raise ValueError(
|
| 344 |
+
f"Incorrect 4D attention_mask shape: {tuple(attention_mask.shape)}; expected: {expected_shape}."
|
| 345 |
+
)
|
| 346 |
+
else:
|
| 347 |
+
# if the 4D mask has correct shape - invert it and fill with negative infinity
|
| 348 |
+
inverted_mask = 1.0 - attention_mask
|
| 349 |
+
attention_mask = inverted_mask.masked_fill(
|
| 350 |
+
inverted_mask.to(torch.bool), torch.finfo(inputs_embeds.dtype).min
|
| 351 |
+
)
|
| 352 |
+
else:
|
| 353 |
+
attention_mask = attn_mask_converter.to_causal_4d(
|
| 354 |
+
input_shape[0], input_shape[-1], key_value_length, dtype=inputs_embeds.dtype, device=inputs_embeds.device
|
| 355 |
+
)
|
| 356 |
+
|
| 357 |
+
return attention_mask
|
| 358 |
+
|
| 359 |
+
|
| 360 |
+
# Adapted from _prepare_4d_causal_attention_mask
|
| 361 |
+
def _prepare_4d_causal_attention_mask_for_sdpa(
|
| 362 |
+
attention_mask: Optional[torch.Tensor],
|
| 363 |
+
input_shape: Union[torch.Size, tuple, list],
|
| 364 |
+
inputs_embeds: torch.Tensor,
|
| 365 |
+
past_key_values_length: int,
|
| 366 |
+
sliding_window: Optional[int] = None,
|
| 367 |
+
):
|
| 368 |
+
"""
|
| 369 |
+
Prepares the correct `attn_mask` argument to be used by `torch.nn.functional.scaled_dot_product_attention`.
|
| 370 |
+
|
| 371 |
+
In case no token is masked in the `attention_mask` argument, we simply set it to `None` for the cases `query_length == 1` and
|
| 372 |
+
`key_value_length == query_length`, and rely instead on SDPA `is_causal` argument to use causal/non-causal masks,
|
| 373 |
+
allowing to dispatch to the flash attention kernel (that can otherwise not be used if a custom `attn_mask` is passed).
|
| 374 |
+
"""
|
| 375 |
+
attn_mask_converter = AttentionMaskConverter(is_causal=True, sliding_window=sliding_window)
|
| 376 |
+
|
| 377 |
+
key_value_length = input_shape[-1] + past_key_values_length
|
| 378 |
+
|
| 379 |
+
# torch.jit.trace, symbolic_trace and torchdynamo with fullgraph=True are unable to capture the controlflow `is_causal=attention_mask is None and q_len > 1`
|
| 380 |
+
# used as an SDPA argument. We keep compatibility with these tracing tools by always using SDPA's `attn_mask` argument in case we are tracing.
|
| 381 |
+
# TODO: For dynamo, rather use a check on fullgraph=True once this is possible (https://github.com/pytorch/pytorch/pull/120400).
|
| 382 |
+
is_tracing = torch.jit.is_tracing() or isinstance(inputs_embeds, torch.fx.Proxy) or is_torchdynamo_compiling()
|
| 383 |
+
|
| 384 |
+
ignore_causal_mask = AttentionMaskConverter._ignore_causal_mask_sdpa(
|
| 385 |
+
attention_mask=attention_mask,
|
| 386 |
+
inputs_embeds=inputs_embeds,
|
| 387 |
+
past_key_values_length=past_key_values_length,
|
| 388 |
+
sliding_window=sliding_window,
|
| 389 |
+
)
|
| 390 |
+
|
| 391 |
+
if ignore_causal_mask:
|
| 392 |
+
expanded_4d_mask = None
|
| 393 |
+
elif attention_mask is None:
|
| 394 |
+
expanded_4d_mask = attn_mask_converter.to_causal_4d(
|
| 395 |
+
input_shape[0], input_shape[-1], key_value_length, dtype=inputs_embeds.dtype, device=inputs_embeds.device
|
| 396 |
+
)
|
| 397 |
+
else:
|
| 398 |
+
if attention_mask.dim() == 4:
|
| 399 |
+
expanded_4d_mask = attention_mask
|
| 400 |
+
else:
|
| 401 |
+
expanded_4d_mask = attn_mask_converter.to_4d(
|
| 402 |
+
attention_mask,
|
| 403 |
+
input_shape[-1],
|
| 404 |
+
dtype=inputs_embeds.dtype,
|
| 405 |
+
key_value_length=key_value_length,
|
| 406 |
+
)
|
| 407 |
+
|
| 408 |
+
# Attend to all tokens in masked rows from the causal_mask, for example the relevant first rows when
|
| 409 |
+
# using left padding. This is required by F.scaled_dot_product_attention memory-efficient attention path.
|
| 410 |
+
# Details: https://github.com/pytorch/pytorch/issues/110213
|
| 411 |
+
if not is_tracing and expanded_4d_mask.device.type == "cuda":
|
| 412 |
+
expanded_4d_mask = AttentionMaskConverter._unmask_unattended(
|
| 413 |
+
expanded_4d_mask, min_dtype=torch.finfo(inputs_embeds.dtype).min
|
| 414 |
+
)
|
| 415 |
+
|
| 416 |
+
return expanded_4d_mask
|
| 417 |
+
|
| 418 |
+
|
| 419 |
+
def _prepare_4d_attention_mask(mask: torch.Tensor, dtype: torch.dtype, tgt_len: Optional[int] = None):
|
| 420 |
+
"""
|
| 421 |
+
Creates a non-causal 4D mask of shape `(batch_size, 1, query_length, key_value_length)` from a 2D mask of shape
|
| 422 |
+
`(batch_size, key_value_length)`
|
| 423 |
+
|
| 424 |
+
Args:
|
| 425 |
+
mask (`torch.Tensor`):
|
| 426 |
+
A 2D attention mask of shape `(batch_size, key_value_length)`
|
| 427 |
+
dtype (`torch.dtype`):
|
| 428 |
+
The torch dtype the created mask shall have.
|
| 429 |
+
tgt_len (`int`):
|
| 430 |
+
The target length or query length the created mask shall have.
|
| 431 |
+
"""
|
| 432 |
+
return AttentionMaskConverter._expand_mask(mask=mask, dtype=dtype, tgt_len=tgt_len)
|
| 433 |
+
|
| 434 |
+
|
| 435 |
+
def _prepare_4d_attention_mask_for_sdpa(mask: torch.Tensor, dtype: torch.dtype, tgt_len: Optional[int] = None):
|
| 436 |
+
"""
|
| 437 |
+
Creates a non-causal 4D mask of shape `(batch_size, 1, query_length, key_value_length)` from a 2D mask of shape
|
| 438 |
+
`(batch_size, key_value_length)`
|
| 439 |
+
|
| 440 |
+
Args:
|
| 441 |
+
mask (`torch.Tensor`):
|
| 442 |
+
A 2D attention mask of shape `(batch_size, key_value_length)`
|
| 443 |
+
dtype (`torch.dtype`):
|
| 444 |
+
The torch dtype the created mask shall have.
|
| 445 |
+
tgt_len (`int`):
|
| 446 |
+
The target length or query length the created mask shall have.
|
| 447 |
+
"""
|
| 448 |
+
_, key_value_length = mask.shape
|
| 449 |
+
tgt_len = tgt_len if tgt_len is not None else key_value_length
|
| 450 |
+
|
| 451 |
+
is_tracing = torch.jit.is_tracing() or isinstance(mask, torch.fx.Proxy) or is_torchdynamo_compiling()
|
| 452 |
+
|
| 453 |
+
# torch.jit.trace, symbolic_trace and torchdynamo with fullgraph=True are unable to capture data-dependent controlflows.
|
| 454 |
+
if not is_tracing and torch.all(mask == 1):
|
| 455 |
+
return None
|
| 456 |
+
else:
|
| 457 |
+
return AttentionMaskConverter._expand_mask(mask=mask, dtype=dtype, tgt_len=tgt_len)
|
| 458 |
+
|
| 459 |
+
|
| 460 |
+
def _create_4d_causal_attention_mask(
|
| 461 |
+
input_shape: Union[torch.Size, tuple, list],
|
| 462 |
+
dtype: torch.dtype,
|
| 463 |
+
device: torch.device,
|
| 464 |
+
past_key_values_length: int = 0,
|
| 465 |
+
sliding_window: Optional[int] = None,
|
| 466 |
+
) -> Optional[torch.Tensor]:
|
| 467 |
+
"""
|
| 468 |
+
Creates a causal 4D mask of shape `(batch_size, 1, query_length, key_value_length)`
|
| 469 |
+
|
| 470 |
+
Args:
|
| 471 |
+
input_shape (`tuple(int)` or `list(int)` or `torch.Size`):
|
| 472 |
+
The input shape should be a tuple that defines `(batch_size, query_length)`.
|
| 473 |
+
dtype (`torch.dtype`):
|
| 474 |
+
The torch dtype the created mask shall have.
|
| 475 |
+
device (`int`):
|
| 476 |
+
The torch device the created mask shall have.
|
| 477 |
+
sliding_window (`int`, *optional*):
|
| 478 |
+
If the model uses windowed attention, a sliding window should be passed.
|
| 479 |
+
"""
|
| 480 |
+
attn_mask_converter = AttentionMaskConverter(is_causal=True, sliding_window=sliding_window)
|
| 481 |
+
|
| 482 |
+
key_value_length = past_key_values_length + input_shape[-1]
|
| 483 |
+
attention_mask = attn_mask_converter.to_causal_4d(
|
| 484 |
+
input_shape[0], input_shape[-1], key_value_length, dtype=dtype, device=device
|
| 485 |
+
)
|
| 486 |
+
|
| 487 |
+
return attention_mask
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/modeling_flash_attention_utils.py
ADDED
|
@@ -0,0 +1,668 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2025 The Fairseq Authors and the HuggingFace Inc. team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
import inspect
|
| 15 |
+
import os
|
| 16 |
+
from functools import partial
|
| 17 |
+
from typing import Optional, TypedDict
|
| 18 |
+
|
| 19 |
+
import torch
|
| 20 |
+
import torch.nn.functional as F
|
| 21 |
+
|
| 22 |
+
from .utils import (
|
| 23 |
+
is_flash_attn_2_available,
|
| 24 |
+
is_flash_attn_3_available,
|
| 25 |
+
is_flash_attn_greater_or_equal_2_10,
|
| 26 |
+
is_torch_npu_available,
|
| 27 |
+
logging,
|
| 28 |
+
)
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
logger = logging.get_logger(__name__)
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
# TODO Deprecate when all models have the attention interface
|
| 35 |
+
def flash_attn_supports_top_left_mask():
|
| 36 |
+
if is_flash_attn_3_available():
|
| 37 |
+
return False
|
| 38 |
+
if is_flash_attn_2_available():
|
| 39 |
+
return not is_flash_attn_greater_or_equal_2_10()
|
| 40 |
+
|
| 41 |
+
from .integrations.npu_flash_attention import is_npu_fa2_top_left_aligned_causal_mask
|
| 42 |
+
|
| 43 |
+
return is_npu_fa2_top_left_aligned_causal_mask()
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
# TODO Deprecate when all models have the attention interface
|
| 47 |
+
def is_flash_attn_available():
|
| 48 |
+
return is_flash_attn_3_available() or is_flash_attn_2_available() or is_torch_npu_available()
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
# `globals()` is not compatible with dynamo, hence we have do define them in global scope ourselves
|
| 52 |
+
_flash_fn = None
|
| 53 |
+
_flash_varlen_fn = None
|
| 54 |
+
_pad_fn = None
|
| 55 |
+
_unpad_fn = None
|
| 56 |
+
|
| 57 |
+
# function that processes kwargs, generalized to handle any supported kwarg within the function
|
| 58 |
+
_process_flash_kwargs_fn = None
|
| 59 |
+
# exceptions where hf API doesn't match the original flash attention API
|
| 60 |
+
_hf_api_to_flash_mapping = {
|
| 61 |
+
"dropout": "dropout_p",
|
| 62 |
+
"sliding_window": "window_size",
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
def _lazy_imports(implementation: Optional[str]):
|
| 67 |
+
"""
|
| 68 |
+
Lazy loads the respective flash attention implementations.
|
| 69 |
+
|
| 70 |
+
Return:
|
| 71 |
+
flash_attn_func: The base flash attention function.
|
| 72 |
+
flash_attn_varlen_func: The flash attention function supporting variable sequence lengths,
|
| 73 |
+
e.g. for padding-free training.
|
| 74 |
+
pad_input: The function to pad inputs into one sequence and returning the respective kwargs.
|
| 75 |
+
unpad_input: The function to unpad outputs based on the kwargs (from pad_input).
|
| 76 |
+
"""
|
| 77 |
+
is_fa2 = is_flash_attn_2_available()
|
| 78 |
+
is_fa3 = is_flash_attn_3_available()
|
| 79 |
+
|
| 80 |
+
pad_input, unpad_input = _pad_input, _unpad_input
|
| 81 |
+
|
| 82 |
+
if (implementation == "flash_attention_2" and is_fa2) or (implementation is None and is_fa2 and not is_fa3):
|
| 83 |
+
from flash_attn import flash_attn_func, flash_attn_varlen_func
|
| 84 |
+
from flash_attn.bert_padding import pad_input, unpad_input
|
| 85 |
+
elif is_torch_npu_available():
|
| 86 |
+
# Package `flash-attn` is unavailable on Ascend NPU, which will cause ImportError
|
| 87 |
+
# Flash-Attention2 related apis for Ascend NPU must be imported from `.integrations.npu_flash_attention` module
|
| 88 |
+
from .integrations.npu_flash_attention import npu_flash_attn_func as flash_attn_func
|
| 89 |
+
from .integrations.npu_flash_attention import npu_flash_attn_varlen_func as flash_attn_varlen_func
|
| 90 |
+
else:
|
| 91 |
+
if implementation == "flash_attention_3" or (implementation is None and is_fa3):
|
| 92 |
+
from flash_attn_interface import flash_attn_func, flash_attn_varlen_func
|
| 93 |
+
# Kernels fallback
|
| 94 |
+
else:
|
| 95 |
+
flash_attn_func = getattr(implementation, "flash_attn_func", None)
|
| 96 |
+
flash_attn_varlen_func = getattr(implementation, "flash_attn_varlen_func", None)
|
| 97 |
+
if flash_attn_varlen_func is None or flash_attn_func is None:
|
| 98 |
+
raise ValueError(
|
| 99 |
+
f"Could not find the currently requested flash attention implementation at `{implementation}`."
|
| 100 |
+
f"Make sure that you request a valid kernel from the hub, e.g. `kernels-community/flash-attn`."
|
| 101 |
+
)
|
| 102 |
+
|
| 103 |
+
return flash_attn_func, flash_attn_varlen_func, pad_input, unpad_input
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
def _lazy_define_process_function(flash_function):
|
| 107 |
+
"""
|
| 108 |
+
Depending on the version and kernel some features are not supported. Due to limitations in
|
| 109 |
+
`torch.compile`, we opt to statically type which (optional) kwarg parameters are supported
|
| 110 |
+
within `_process_flash_attention_kwargs`.
|
| 111 |
+
|
| 112 |
+
NOTE: While all supported kwargs are marked as `True`, everything else is marked as `False`.
|
| 113 |
+
This might be confusing for kwargs that we use in any case, e.g. `is_causal`.
|
| 114 |
+
"""
|
| 115 |
+
|
| 116 |
+
flash_parameters = inspect.signature(flash_function).parameters
|
| 117 |
+
process_parameters = inspect.signature(_process_flash_attention_kwargs).parameters
|
| 118 |
+
|
| 119 |
+
supports_mapping = {}
|
| 120 |
+
for param in process_parameters:
|
| 121 |
+
fa_param = _hf_api_to_flash_mapping.get(param, param)
|
| 122 |
+
supports_mapping[fa_param] = fa_param in flash_parameters
|
| 123 |
+
|
| 124 |
+
return partial(_process_flash_attention_kwargs, supports_mapping=supports_mapping)
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
def lazy_import_flash_attention(implementation: Optional[str], force_import: Optional[bool] = False):
|
| 128 |
+
"""
|
| 129 |
+
Lazily import flash attention and return the respective functions + flags.
|
| 130 |
+
|
| 131 |
+
NOTE: For fullgraph, this needs to be called before compile, while no fullgraph can
|
| 132 |
+
work without preloading. See `load_and_register_kernel` in `integrations.hub_kernels`.
|
| 133 |
+
"""
|
| 134 |
+
global _flash_fn, _flash_varlen_fn, _pad_fn, _unpad_fn
|
| 135 |
+
if force_import or any(k is None for k in [_flash_fn, _flash_varlen_fn, _pad_fn, _unpad_fn]):
|
| 136 |
+
_flash_fn, _flash_varlen_fn, _pad_fn, _unpad_fn = _lazy_imports(implementation)
|
| 137 |
+
|
| 138 |
+
global _process_flash_kwargs_fn
|
| 139 |
+
if force_import or _process_flash_kwargs_fn is None:
|
| 140 |
+
_process_flash_kwargs_fn = _lazy_define_process_function(_flash_varlen_fn)
|
| 141 |
+
|
| 142 |
+
return (_flash_fn, _flash_varlen_fn, _pad_fn, _unpad_fn), _process_flash_kwargs_fn
|
| 143 |
+
|
| 144 |
+
|
| 145 |
+
def _index_first_axis(tensor, indices):
|
| 146 |
+
"""
|
| 147 |
+
A local implementation of the PyTorch indexing operation `tensor[indices]` on the first axis,
|
| 148 |
+
after flattening the first two dimensions of the tensor. This is functionally equivalent to
|
| 149 |
+
FA2's `index_first_axis` and replaces the need to import it.
|
| 150 |
+
"""
|
| 151 |
+
# The input tensor is expected to be of shape (batch, seq_len, ...). We flatten the first
|
| 152 |
+
# two dimensions to get (total_tokens, ...) before indexing.
|
| 153 |
+
reshaped_tensor = tensor.reshape(-1, *tensor.shape[2:])
|
| 154 |
+
return reshaped_tensor[indices]
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
def _unpad_input(hidden_states, attention_mask, unused_mask=None):
|
| 158 |
+
"""
|
| 159 |
+
unpad_input function for flash attention variants that do not have them within their pkg themselves, e.g. fa3.
|
| 160 |
+
|
| 161 |
+
Arguments:
|
| 162 |
+
hidden_states: (batch, seqlen, ...)
|
| 163 |
+
attention_mask: (batch, seqlen), bool / int, 1 means valid and 0 means not valid.
|
| 164 |
+
unused_mask: (batch, seqlen), bool / int, 1 means the element is allocated but unused.
|
| 165 |
+
|
| 166 |
+
Return:
|
| 167 |
+
hidden_states: (total_nnz, ...), where total_nnz = number of tokens selected in attention_mask + unused_mask.
|
| 168 |
+
indices: (total_nnz), the indices of masked tokens from the flattened input sequence.
|
| 169 |
+
cu_seqlens: (batch + 1), the cumulative sequence lengths, used to index into hidden_states.
|
| 170 |
+
max_seqlen_in_batch: int
|
| 171 |
+
seqused: (batch), returns the number of tokens selected in attention_mask + unused_mask.
|
| 172 |
+
"""
|
| 173 |
+
all_masks = (attention_mask + unused_mask) if unused_mask is not None else attention_mask
|
| 174 |
+
seqlens_in_batch = all_masks.sum(dim=-1, dtype=torch.int32)
|
| 175 |
+
used_seqlens_in_batch = attention_mask.sum(dim=-1, dtype=torch.int32)
|
| 176 |
+
indices = torch.nonzero(all_masks.flatten(), as_tuple=False).flatten()
|
| 177 |
+
max_seqlen_in_batch = seqlens_in_batch.max().item()
|
| 178 |
+
cu_seqlens = F.pad(torch.cumsum(seqlens_in_batch, dim=0, dtype=torch.int32), (1, 0))
|
| 179 |
+
|
| 180 |
+
return (
|
| 181 |
+
_index_first_axis(hidden_states, indices),
|
| 182 |
+
indices,
|
| 183 |
+
cu_seqlens,
|
| 184 |
+
max_seqlen_in_batch,
|
| 185 |
+
used_seqlens_in_batch,
|
| 186 |
+
)
|
| 187 |
+
|
| 188 |
+
|
| 189 |
+
def _pad_input(hidden_states, indices, batch, seqlen):
|
| 190 |
+
"""
|
| 191 |
+
pad_input function for flash attention variants that do not have them within their pkg themselves, e.g. fa3.
|
| 192 |
+
|
| 193 |
+
Arguments:
|
| 194 |
+
hidden_states: (total_nnz, ...), where total_nnz = number of tokens in selected in attention_mask.
|
| 195 |
+
indices: (total_nnz), the indices that represent the non-masked tokens of the original padded input sequence.
|
| 196 |
+
batch: int, batch size for the padded sequence.
|
| 197 |
+
seqlen: int, maximum sequence length for the padded sequence.
|
| 198 |
+
|
| 199 |
+
Return:
|
| 200 |
+
hidden_states: (batch, seqlen, ...)
|
| 201 |
+
"""
|
| 202 |
+
dim = hidden_states.shape[1:]
|
| 203 |
+
output = torch.zeros((batch * seqlen), *dim, device=hidden_states.device, dtype=hidden_states.dtype)
|
| 204 |
+
output[indices] = hidden_states
|
| 205 |
+
return output.view(batch, seqlen, *dim)
|
| 206 |
+
|
| 207 |
+
|
| 208 |
+
def _get_unpad_data(attention_mask: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor, int]:
|
| 209 |
+
"""
|
| 210 |
+
Retrieves indexing data required to repad unpadded (ragged) tensors.
|
| 211 |
+
|
| 212 |
+
Arguments:
|
| 213 |
+
attention_mask (`torch.Tensor`):
|
| 214 |
+
Boolean or int tensor of shape (batch_size, sequence_length), 1 means valid and 0 means not valid.
|
| 215 |
+
|
| 216 |
+
Return:
|
| 217 |
+
indices (`torch.Tensor`):
|
| 218 |
+
The indices of non-masked tokens from the flattened input sequence.
|
| 219 |
+
cu_seqlens (`torch.Tensor`):
|
| 220 |
+
The cumulative sequence lengths, used to index into ragged (unpadded) tensors. `cu_seqlens` shape is (batch_size + 1,).
|
| 221 |
+
max_seqlen_in_batch (`int`):
|
| 222 |
+
Maximum sequence length in batch.
|
| 223 |
+
"""
|
| 224 |
+
seqlens_in_batch = attention_mask.sum(dim=-1, dtype=torch.int32)
|
| 225 |
+
indices = torch.nonzero(attention_mask.flatten(), as_tuple=False).flatten()
|
| 226 |
+
# NOTE: Similar to the `.item()` in prepare_fa2_from_position_ids, with torch compile,
|
| 227 |
+
# this might cause a graph break
|
| 228 |
+
max_seqlen_in_batch = seqlens_in_batch.max().item()
|
| 229 |
+
cu_seqlens = F.pad(torch.cumsum(seqlens_in_batch, dim=0, dtype=torch.int32), (1, 0))
|
| 230 |
+
return (
|
| 231 |
+
indices,
|
| 232 |
+
cu_seqlens,
|
| 233 |
+
max_seqlen_in_batch,
|
| 234 |
+
)
|
| 235 |
+
|
| 236 |
+
|
| 237 |
+
def _upad_input(
|
| 238 |
+
query_layer: torch.Tensor,
|
| 239 |
+
key_layer: torch.Tensor,
|
| 240 |
+
value_layer: torch.Tensor,
|
| 241 |
+
attention_mask: torch.Tensor,
|
| 242 |
+
query_length: int,
|
| 243 |
+
unpad_input_func,
|
| 244 |
+
):
|
| 245 |
+
"""
|
| 246 |
+
Unpads query, key, and values tensors, using a single dimension for all tokens even though they belong to different batches.
|
| 247 |
+
This function is used instead of `flash_attn.bert_padding.unpad_input` in order to avoid the recomputation of the same intermediary
|
| 248 |
+
tensors for query, key, value tensors.
|
| 249 |
+
|
| 250 |
+
Arguments:
|
| 251 |
+
query_layer (`torch.Tensor`):
|
| 252 |
+
Query state with padding. Shape: (batch_size, query_length, num_heads, head_dim).
|
| 253 |
+
key_layer (`torch.Tensor`):
|
| 254 |
+
Key state with padding. Shape: (batch_size, kv_seq_len, num_key_value_heads, head_dim).
|
| 255 |
+
value_layer (`torch.Tensor`):
|
| 256 |
+
Value state with padding. Shape: (batch_size, kv_seq_len, num_key_value_heads, head_dim).
|
| 257 |
+
attention_mask (`torch.Tensor`):
|
| 258 |
+
Boolean or int tensor of shape (batch_size, sequence_length), 1 means valid and 0 means not valid.
|
| 259 |
+
query_length (`int`):
|
| 260 |
+
Target length.
|
| 261 |
+
unpad_input_func:
|
| 262 |
+
The function to use for unpadding the input tensors.
|
| 263 |
+
|
| 264 |
+
Return:
|
| 265 |
+
query_layer (`torch.Tensor`):
|
| 266 |
+
Query state without padding. Shape: (total_target_length, num_heads, head_dim).
|
| 267 |
+
key_layer (`torch.Tensor`):
|
| 268 |
+
Key state with padding. Shape: (total_source_length, num_key_value_heads, head_dim).
|
| 269 |
+
value_layer (`torch.Tensor`):
|
| 270 |
+
Value state with padding. Shape: (total_source_length, num_key_value_heads, head_dim).
|
| 271 |
+
indices_q (`torch.Tensor`):
|
| 272 |
+
The indices of non-masked tokens from the flattened input target sequence.
|
| 273 |
+
(cu_seqlens_q, cu_seqlens_k) (`tuple[int]`):
|
| 274 |
+
The cumulative sequence lengths for the target (query) and source (key, value), used to index into ragged (unpadded) tensors. `cu_seqlens` shape is (batch_size + 1,).
|
| 275 |
+
(max_seqlen_in_batch_q, max_seqlen_in_batch_k) (`tuple[int]`):
|
| 276 |
+
Maximum sequence length in batch (`max_seqlen_in_batch_q` for the target sequence i.e. query, `max_seqlen_in_batch_k` for the source sequence i.e. key/value).
|
| 277 |
+
"""
|
| 278 |
+
indices_k, cu_seqlens_k, max_seqlen_in_batch_k = _get_unpad_data(attention_mask)
|
| 279 |
+
|
| 280 |
+
# With static caches, the k/v states may be larger than the mask -> we need to slice them to avoid generating garbage
|
| 281 |
+
# It's a bit of an anti-pattern, but otherwise we silently compute wrong attentions scores
|
| 282 |
+
if key_layer.shape[1] > (seq_len := attention_mask.shape[-1]):
|
| 283 |
+
key_layer, value_layer = key_layer[:, :seq_len, :, :], value_layer[:, :seq_len, :, :]
|
| 284 |
+
|
| 285 |
+
batch_size, kv_seq_len, num_key_value_heads, head_dim = key_layer.shape
|
| 286 |
+
|
| 287 |
+
key_layer = _index_first_axis(key_layer, indices_k)
|
| 288 |
+
value_layer = _index_first_axis(value_layer, indices_k)
|
| 289 |
+
if query_length == kv_seq_len:
|
| 290 |
+
query_layer = _index_first_axis(query_layer, indices_k)
|
| 291 |
+
cu_seqlens_q = cu_seqlens_k
|
| 292 |
+
max_seqlen_in_batch_q = max_seqlen_in_batch_k
|
| 293 |
+
indices_q = indices_k
|
| 294 |
+
elif query_length == 1:
|
| 295 |
+
max_seqlen_in_batch_q = 1
|
| 296 |
+
cu_seqlens_q = torch.arange(
|
| 297 |
+
batch_size + 1, dtype=torch.int32, device=query_layer.device
|
| 298 |
+
) # There is a memcpy here, that is very bad.
|
| 299 |
+
indices_q = cu_seqlens_q[:-1]
|
| 300 |
+
query_layer = query_layer.squeeze(1)
|
| 301 |
+
else:
|
| 302 |
+
# The -q_len: slice assumes left padding.
|
| 303 |
+
attention_mask = attention_mask[:, -query_length:]
|
| 304 |
+
query_layer, indices_q, cu_seqlens_q, max_seqlen_in_batch_q, *_ = unpad_input_func(query_layer, attention_mask)
|
| 305 |
+
|
| 306 |
+
return (
|
| 307 |
+
query_layer,
|
| 308 |
+
key_layer,
|
| 309 |
+
value_layer,
|
| 310 |
+
indices_q,
|
| 311 |
+
(cu_seqlens_q, cu_seqlens_k),
|
| 312 |
+
(max_seqlen_in_batch_q, max_seqlen_in_batch_k),
|
| 313 |
+
)
|
| 314 |
+
|
| 315 |
+
|
| 316 |
+
def prepare_fa_kwargs_from_position_ids(position_ids):
|
| 317 |
+
"""
|
| 318 |
+
This function returns all the necessary kwargs to call `flash_attn_varlen_func` extracted from position_ids.
|
| 319 |
+
|
| 320 |
+
Arguments:
|
| 321 |
+
position_ids (`torch.Tensor`):
|
| 322 |
+
Boolean or int tensor of shape (batch_size, sequence_length), 1 means valid and 0 means not valid.
|
| 323 |
+
|
| 324 |
+
Return:
|
| 325 |
+
(cu_seqlens_q, cu_seqlens_k) (`tuple[int]`):
|
| 326 |
+
The cumulative sequence lengths for the target (query) and source (key, value), used to index into
|
| 327 |
+
ragged (unpadded) tensors. `cu_seqlens` shape is (batch_size + 1,).
|
| 328 |
+
(max_seqlen_in_batch_q, max_seqlen_in_batch_k) (`tuple[int]`):
|
| 329 |
+
Maximum sequence length in batch (`max_seqlen_in_batch_q` for the target sequence i.e. query,
|
| 330 |
+
`max_seqlen_in_batch_k` for the source sequence i.e. key/value).
|
| 331 |
+
"""
|
| 332 |
+
tensor_kwargs = {"dtype": torch.int32, "device": position_ids.device}
|
| 333 |
+
|
| 334 |
+
position_ids = position_ids.view(-1)
|
| 335 |
+
indices_q = (position_ids == 0).nonzero().view(-1)
|
| 336 |
+
|
| 337 |
+
cu_seq_lens_q = torch.cat(
|
| 338 |
+
(
|
| 339 |
+
indices_q.to(**tensor_kwargs),
|
| 340 |
+
torch.tensor(position_ids.size(), **tensor_kwargs),
|
| 341 |
+
)
|
| 342 |
+
)
|
| 343 |
+
cu_seq_lens_k = cu_seq_lens_q
|
| 344 |
+
|
| 345 |
+
# https://github.com/Dao-AILab/flash-attention/blob/2dd8078adc1d9b74e315ee99718c0dea0de8eeb6/flash_attn/flash_attn_interface.py#L1423-L1424
|
| 346 |
+
# We should use cu_seq_lens instead of position_ids to get the max length since position_ids is not always increasing
|
| 347 |
+
# for some models (e.g. qwen2-vl).
|
| 348 |
+
max_length_q = cu_seq_lens_q.diff().max()
|
| 349 |
+
# NOTE: With torch compile, this will cause a graph break if you don't set
|
| 350 |
+
# `TORCHDYNAMO_CAPTURE_SCALAR_OUTPUTS=1` in the environment or call
|
| 351 |
+
# `torch._dynamo.config.capture_scalar_outputs = True` before doing the forward pass.
|
| 352 |
+
# This is a limitation of flash attention API, as the function `flash_attn_varlen_func`
|
| 353 |
+
# requires `max_length_q`, `max_length_k` to be passed as `int` and not `torch.Tensor`.
|
| 354 |
+
max_length_q = max_length_q.item()
|
| 355 |
+
max_length_k = max_length_q
|
| 356 |
+
|
| 357 |
+
return (cu_seq_lens_q, cu_seq_lens_k), (max_length_q, max_length_k)
|
| 358 |
+
|
| 359 |
+
|
| 360 |
+
def _prepare_from_posids(query, key, value, position_ids):
|
| 361 |
+
"""
|
| 362 |
+
This function returns necessary arguments to call `flash_attn_varlen_func`.
|
| 363 |
+
All three query, key, value states will be flattened.
|
| 364 |
+
Cumulative lengths of each examples in the batch will be extracted from position_ids.
|
| 365 |
+
NOTE: ideally cumulative lengths should be prepared at the data collator stage
|
| 366 |
+
|
| 367 |
+
Arguments:
|
| 368 |
+
query (`torch.Tensor`):
|
| 369 |
+
Query state with padding. Shape: (batch_size, query_length, num_heads, head_dim).
|
| 370 |
+
key (`torch.Tensor`):
|
| 371 |
+
Key state with padding. Shape: (batch_size, kv_seq_len, num_key_value_heads, head_dim).
|
| 372 |
+
value (`torch.Tensor`):
|
| 373 |
+
Value state with padding. Shape: (batch_size, kv_seq_len, num_key_value_heads, head_dim).
|
| 374 |
+
position_ids (`torch.Tensor`):
|
| 375 |
+
Boolean or int tensor of shape (batch_size, sequence_length), 1 means valid and 0 means not valid.
|
| 376 |
+
|
| 377 |
+
Return:
|
| 378 |
+
query (`torch.Tensor`):
|
| 379 |
+
Query state without padding. Shape: (total_target_length, num_heads, head_dim).
|
| 380 |
+
key (`torch.Tensor`):
|
| 381 |
+
Key state with padding. Shape: (total_source_length, num_key_value_heads, head_dim).
|
| 382 |
+
value (`torch.Tensor`):
|
| 383 |
+
Value state with padding. Shape: (total_source_length, num_key_value_heads, head_dim).
|
| 384 |
+
(cu_seqlens_q, cu_seqlens_k) (`tuple[int]`):
|
| 385 |
+
The cumulative sequence lengths for the target (query) and source (key, value), used to index into ragged (unpadded) tensors. `cu_seqlens` shape is (batch_size + 1,).
|
| 386 |
+
(max_seqlen_in_batch_q, max_seqlen_in_batch_k) (`tuple[int]`):
|
| 387 |
+
Maximum sequence length in batch (`max_seqlen_in_batch_q` for the target sequence i.e. query, `max_seqlen_in_batch_k` for the source sequence i.e. key/value).
|
| 388 |
+
"""
|
| 389 |
+
query = query.contiguous().view(-1, query.size(-2), query.size(-1))
|
| 390 |
+
key = key.contiguous().view(-1, key.size(-2), key.size(-1))
|
| 391 |
+
value = value.contiguous().view(-1, value.size(-2), value.size(-1))
|
| 392 |
+
|
| 393 |
+
(cu_seq_lens_q, cu_seq_lens_k), (max_length_q, max_length_k) = prepare_fa_kwargs_from_position_ids(position_ids)
|
| 394 |
+
|
| 395 |
+
return (query, key, value, (cu_seq_lens_q, cu_seq_lens_k), (max_length_q, max_length_k))
|
| 396 |
+
|
| 397 |
+
|
| 398 |
+
def _is_packed_sequence(position_ids, batch_size):
|
| 399 |
+
"""
|
| 400 |
+
Check the position ids whether packed sequences are indicated or not
|
| 401 |
+
1. Position ids exist
|
| 402 |
+
2. Flattened sequences only are supported
|
| 403 |
+
3. Compile-friendly `not (torch.diff(position_ids, dim=-1) >= 0).all()`, i.e. we have multiple increasing sequences
|
| 404 |
+
"""
|
| 405 |
+
if position_ids is None:
|
| 406 |
+
return False
|
| 407 |
+
|
| 408 |
+
increasing_position_sequences = (
|
| 409 |
+
torch.arange(position_ids.shape[1], device=position_ids.device) + position_ids.min()
|
| 410 |
+
)
|
| 411 |
+
return batch_size == 1 and (increasing_position_sequences - position_ids).abs().sum().bool()
|
| 412 |
+
|
| 413 |
+
|
| 414 |
+
def fa_peft_integration_check(
|
| 415 |
+
q: torch.Tensor,
|
| 416 |
+
k: torch.Tensor,
|
| 417 |
+
v: torch.Tensor,
|
| 418 |
+
target_dtype: Optional[torch.dtype] = None,
|
| 419 |
+
):
|
| 420 |
+
"""
|
| 421 |
+
PEFT usually casts the layer norms in float32 for training stability reasons
|
| 422 |
+
therefore the input hidden states gets silently casted in float32. Hence, we need
|
| 423 |
+
cast them back in float16 / bfloat16 just to be sure everything works as expected.
|
| 424 |
+
This might slowdown training & inference so it is recommended to not cast the LayerNorms!
|
| 425 |
+
"""
|
| 426 |
+
if target_dtype and q.dtype == torch.float32:
|
| 427 |
+
logger.warning_once(f"Casting fp32 inputs back to {target_dtype} for flash-attn compatibility.")
|
| 428 |
+
q, k, v = q.to(target_dtype), k.to(target_dtype), v.to(target_dtype)
|
| 429 |
+
return q, k, v
|
| 430 |
+
|
| 431 |
+
|
| 432 |
+
class FlashAttentionKwargs(TypedDict, total=False):
|
| 433 |
+
"""
|
| 434 |
+
Keyword arguments for Flash Attention with Compile.
|
| 435 |
+
|
| 436 |
+
Attributes:
|
| 437 |
+
cu_seq_lens_q (`torch.LongTensor`, *optional*)
|
| 438 |
+
Gets cumulative sequence length for query state.
|
| 439 |
+
cu_seq_lens_k (`torch.LongTensor`, *optional*)
|
| 440 |
+
Gets cumulative sequence length for key state.
|
| 441 |
+
max_length_q (`int`, *optional*):
|
| 442 |
+
Maximum sequence length for query state.
|
| 443 |
+
max_length_k (`int`, *optional*):
|
| 444 |
+
Maximum sequence length for key state.
|
| 445 |
+
"""
|
| 446 |
+
|
| 447 |
+
cu_seq_lens_q: Optional[torch.LongTensor]
|
| 448 |
+
cu_seq_lens_k: Optional[torch.LongTensor]
|
| 449 |
+
max_length_q: Optional[int]
|
| 450 |
+
max_length_k: Optional[int]
|
| 451 |
+
|
| 452 |
+
|
| 453 |
+
def _process_flash_attention_kwargs(
|
| 454 |
+
query_length: int,
|
| 455 |
+
key_length: int,
|
| 456 |
+
is_causal: bool,
|
| 457 |
+
dropout: float = 0.0,
|
| 458 |
+
softmax_scale: Optional[float] = None,
|
| 459 |
+
sliding_window: Optional[int] = None,
|
| 460 |
+
use_top_left_mask: bool = False,
|
| 461 |
+
softcap: Optional[float] = None,
|
| 462 |
+
deterministic: Optional[bool] = None,
|
| 463 |
+
s_aux: Optional[torch.Tensor] = None,
|
| 464 |
+
supports_mapping: Optional[dict[str, bool]] = None,
|
| 465 |
+
**kwargs,
|
| 466 |
+
):
|
| 467 |
+
"""
|
| 468 |
+
Returns a set of kwargs that are passed down to the according flash attention function based on
|
| 469 |
+
requested features and whether it is supported - depends on the version and kernel implementation
|
| 470 |
+
which is dynamically configured at `lazy_import_flash_attention`. The (un)supported features can be
|
| 471 |
+
inspected in `supports_mapping`, see `_lazy_define_process_function` for more details.
|
| 472 |
+
|
| 473 |
+
Args:
|
| 474 |
+
query_length (`int`):
|
| 475 |
+
Length of the query states
|
| 476 |
+
key_length (`int`):
|
| 477 |
+
Length of the key states
|
| 478 |
+
is_causal (`bool`):
|
| 479 |
+
Whether we perform causal (decoder) attention or full attention.
|
| 480 |
+
dropout (`float`):
|
| 481 |
+
Attention dropout.
|
| 482 |
+
softmax_scale (`float`, *optional*):
|
| 483 |
+
The scaling of QK^T before applying softmax. Default to `1 / sqrt(head_dim)`.
|
| 484 |
+
sliding_window (`int`, *optional*):
|
| 485 |
+
The size of the sliding window, i.e. we look at a max of `sliding_window` tokens back.
|
| 486 |
+
use_top_left_mask (`bool`):
|
| 487 |
+
Deprecated behavior of older versions of flash attention requiring different masking.
|
| 488 |
+
softcap (`float`, *optional*):
|
| 489 |
+
Softcap for the attention logits, used e.g. in gemma2.
|
| 490 |
+
deterministic (`bool`, *optional*):
|
| 491 |
+
Determines if the deterministic option introduced in flash_attn>=2.4.1 is enabled.
|
| 492 |
+
s_aux (`torch.Tensor`, *optional*):
|
| 493 |
+
Attention sink auxiliary that adds a `bias` to the attention calculation via an additional head.
|
| 494 |
+
Return:
|
| 495 |
+
flash_kwargs (`dict`):
|
| 496 |
+
A dict of kwargs that are requested and supported.
|
| 497 |
+
"""
|
| 498 |
+
flash_kwargs = {
|
| 499 |
+
"causal": is_causal and not (use_top_left_mask and query_length == 1),
|
| 500 |
+
"softmax_scale": softmax_scale,
|
| 501 |
+
}
|
| 502 |
+
|
| 503 |
+
if supports_mapping["dropout_p"]:
|
| 504 |
+
flash_kwargs["dropout_p"] = dropout
|
| 505 |
+
|
| 506 |
+
if supports_mapping["window_size"] and sliding_window is not None and key_length > sliding_window:
|
| 507 |
+
# The flash attention API sets inclusive boundaries, i.e. (4, 0) would take 4 tokens to the left
|
| 508 |
+
# and the current token for a total size of 5. However, we usually define our window sizes by
|
| 509 |
+
# their total window size (when causal). Encoder models as of now seldom use SWA and when they
|
| 510 |
+
# do, they have a custom workaround (e.g. ModernBERT) which would align with this symmetric logic, i.e.
|
| 511 |
+
# for a total of `2*sliding_window + 1`.
|
| 512 |
+
flash_kwargs["window_size"] = (sliding_window - 1, sliding_window - 1)
|
| 513 |
+
|
| 514 |
+
if supports_mapping["deterministic"]:
|
| 515 |
+
flash_kwargs["deterministic"] = (
|
| 516 |
+
deterministic if deterministic is not None else os.getenv("FLASH_ATTENTION_DETERMINISTIC", "0") == "1"
|
| 517 |
+
)
|
| 518 |
+
|
| 519 |
+
if supports_mapping["softcap"] and softcap is not None:
|
| 520 |
+
flash_kwargs["softcap"] = softcap
|
| 521 |
+
|
| 522 |
+
# Only within kernel implementation atm
|
| 523 |
+
if supports_mapping["s_aux"] and s_aux is not None:
|
| 524 |
+
flash_kwargs["s_aux"] = s_aux
|
| 525 |
+
|
| 526 |
+
return flash_kwargs
|
| 527 |
+
|
| 528 |
+
|
| 529 |
+
def _flash_attention_forward(
|
| 530 |
+
query_states: torch.Tensor,
|
| 531 |
+
key_states: torch.Tensor,
|
| 532 |
+
value_states: torch.Tensor,
|
| 533 |
+
attention_mask: Optional[torch.Tensor],
|
| 534 |
+
query_length: int,
|
| 535 |
+
is_causal: bool,
|
| 536 |
+
dropout: float = 0.0,
|
| 537 |
+
position_ids: Optional[torch.Tensor] = None,
|
| 538 |
+
softmax_scale: Optional[float] = None,
|
| 539 |
+
sliding_window: Optional[int] = None,
|
| 540 |
+
use_top_left_mask: bool = False,
|
| 541 |
+
softcap: Optional[float] = None,
|
| 542 |
+
deterministic: Optional[bool] = None,
|
| 543 |
+
cu_seq_lens_q: Optional[torch.LongTensor] = None,
|
| 544 |
+
cu_seq_lens_k: Optional[torch.LongTensor] = None,
|
| 545 |
+
max_length_q: Optional[int] = None,
|
| 546 |
+
max_length_k: Optional[int] = None,
|
| 547 |
+
target_dtype: Optional[torch.dtype] = None,
|
| 548 |
+
implementation: Optional[str] = None,
|
| 549 |
+
**kwargs,
|
| 550 |
+
):
|
| 551 |
+
"""
|
| 552 |
+
Calls the forward method of Flash Attention - if the input hidden states contain at least one padding token
|
| 553 |
+
first unpad the input, then computes the attention scores and pad the final attention scores.
|
| 554 |
+
|
| 555 |
+
(Optional) kwargs are described further in `_process_flash_attention_kwargs` and `FlashAttentionKwargs`.
|
| 556 |
+
|
| 557 |
+
Args:
|
| 558 |
+
query_states (`torch.Tensor`):
|
| 559 |
+
Input query states to be passed to Flash Attention API
|
| 560 |
+
key_states (`torch.Tensor`):
|
| 561 |
+
Input key states to be passed to Flash Attention API
|
| 562 |
+
value_states (`torch.Tensor`):
|
| 563 |
+
Input value states to be passed to Flash Attention API
|
| 564 |
+
attention_mask (`torch.Tensor`, *optional*):
|
| 565 |
+
The padding mask - corresponds to a tensor of size `(batch_size, seq_len)` where 0 stands for the
|
| 566 |
+
position of padding tokens and 1 for the position of non-padding tokens.
|
| 567 |
+
implementation (`str`, *optional*):
|
| 568 |
+
The attention implementation to use. If None, will default to the one based on the environment.
|
| 569 |
+
"""
|
| 570 |
+
(flash_fn, flash_varlen_fn, pad_fn, unpad_fn), process_flash_kwargs_fn = lazy_import_flash_attention(
|
| 571 |
+
implementation
|
| 572 |
+
)
|
| 573 |
+
|
| 574 |
+
# PEFT possibly silently casts tensors to fp32, this potentially reconverts to correct dtype or is a no op
|
| 575 |
+
query_states, key_states, value_states = fa_peft_integration_check(
|
| 576 |
+
query_states, key_states, value_states, target_dtype
|
| 577 |
+
)
|
| 578 |
+
|
| 579 |
+
# Extract the flash attention kwargs that have been requested (and are supported by the implementation)
|
| 580 |
+
flash_kwargs = process_flash_kwargs_fn(
|
| 581 |
+
query_length=query_length,
|
| 582 |
+
key_length=key_states.size(1),
|
| 583 |
+
is_causal=is_causal,
|
| 584 |
+
dropout=dropout,
|
| 585 |
+
softmax_scale=softmax_scale,
|
| 586 |
+
sliding_window=sliding_window,
|
| 587 |
+
use_top_left_mask=use_top_left_mask,
|
| 588 |
+
softcap=softcap,
|
| 589 |
+
deterministic=deterministic,
|
| 590 |
+
**kwargs,
|
| 591 |
+
)
|
| 592 |
+
|
| 593 |
+
# We will use `flash_varlen_fn` to prevent cross-example attention and also allow padding free approach under two cases:
|
| 594 |
+
# Case 1. If position ids is provided and the position ids indicate packed sequences, see `_is_packed_sequence`.
|
| 595 |
+
# Case 2. Some models pass directly pre-computed `cu_seqlens` so we don't need to infer it from position ids. It is safe to
|
| 596 |
+
# use `flash_varlen_fn` knowing we already have all necessary the kwargs.
|
| 597 |
+
#
|
| 598 |
+
# NOTE: it is user's responsibility to take care of flattening `position_ids` if that's needed by the model.
|
| 599 |
+
# See #39121 for more information.
|
| 600 |
+
is_fa_with_position_ids = _is_packed_sequence(position_ids, batch_size=query_states.size(0))
|
| 601 |
+
is_fa_with_varlen_kwargs = all(
|
| 602 |
+
kwarg is not None for kwarg in (cu_seq_lens_q, cu_seq_lens_k, max_length_q, max_length_k)
|
| 603 |
+
)
|
| 604 |
+
|
| 605 |
+
# Contains at least one padding token in the sequence
|
| 606 |
+
if attention_mask is not None:
|
| 607 |
+
q, k, v, indices_q, (cu_seq_lens_q, cu_seq_lens_k), (max_length_q, max_length_k) = _upad_input(
|
| 608 |
+
query_states, key_states, value_states, attention_mask, query_length, unpad_fn
|
| 609 |
+
)
|
| 610 |
+
|
| 611 |
+
# TODO for now this is required to work with
|
| 612 |
+
# https://huggingface.co/kernels-community/metal-flash-sdpa/blob/main/torch-ext/metal_flash_sdpa/__init__.py
|
| 613 |
+
if "mps" in str(q.device):
|
| 614 |
+
cu_seq_lens_k = cu_seq_lens_k.clone()
|
| 615 |
+
|
| 616 |
+
out_unpad = flash_varlen_fn(
|
| 617 |
+
q,
|
| 618 |
+
k,
|
| 619 |
+
v,
|
| 620 |
+
cu_seqlens_q=cu_seq_lens_q,
|
| 621 |
+
cu_seqlens_k=cu_seq_lens_k,
|
| 622 |
+
max_seqlen_q=max_length_q,
|
| 623 |
+
max_seqlen_k=max_length_k,
|
| 624 |
+
**flash_kwargs,
|
| 625 |
+
)
|
| 626 |
+
if isinstance(out_unpad, tuple):
|
| 627 |
+
out_unpad = out_unpad[0]
|
| 628 |
+
|
| 629 |
+
out = pad_fn(out_unpad, indices_q, query_states.size(0), query_length)
|
| 630 |
+
|
| 631 |
+
# Padding free, i.e. sequences flattened into one total sequence
|
| 632 |
+
elif is_fa_with_varlen_kwargs or is_fa_with_position_ids:
|
| 633 |
+
if cu_seq_lens_q is None or cu_seq_lens_k is None:
|
| 634 |
+
q, k, v, (cu_seq_lens_q, cu_seq_lens_k), (max_length_q, max_length_k) = _prepare_from_posids(
|
| 635 |
+
query_states, key_states, value_states, position_ids
|
| 636 |
+
)
|
| 637 |
+
else:
|
| 638 |
+
q = query_states.reshape(-1, query_states.size(-2), query_states.size(-1))
|
| 639 |
+
k = key_states.reshape(-1, key_states.size(-2), key_states.size(-1))
|
| 640 |
+
v = value_states.reshape(-1, value_states.size(-2), value_states.size(-1))
|
| 641 |
+
|
| 642 |
+
# TODO for now this is required to work with
|
| 643 |
+
# https://huggingface.co/kernels-community/metal-flash-sdpa/blob/main/torch-ext/metal_flash_sdpa/__init__.py
|
| 644 |
+
if "mps" in str(q.device):
|
| 645 |
+
cu_seq_lens_k = cu_seq_lens_k.clone()
|
| 646 |
+
|
| 647 |
+
out = flash_varlen_fn(
|
| 648 |
+
q,
|
| 649 |
+
k,
|
| 650 |
+
v,
|
| 651 |
+
cu_seqlens_q=cu_seq_lens_q,
|
| 652 |
+
cu_seqlens_k=cu_seq_lens_k,
|
| 653 |
+
max_seqlen_q=max_length_q,
|
| 654 |
+
max_seqlen_k=max_length_k,
|
| 655 |
+
**flash_kwargs,
|
| 656 |
+
)
|
| 657 |
+
if isinstance(out, tuple):
|
| 658 |
+
out = out[0]
|
| 659 |
+
|
| 660 |
+
out = out.view(query_states.size(0), -1, out.size(-2), out.size(-1))
|
| 661 |
+
|
| 662 |
+
# No padding
|
| 663 |
+
else:
|
| 664 |
+
out = flash_fn(query_states, key_states, value_states, **flash_kwargs)
|
| 665 |
+
if isinstance(out, tuple):
|
| 666 |
+
out = out[0]
|
| 667 |
+
|
| 668 |
+
return out
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/modeling_flax_outputs.py
ADDED
|
@@ -0,0 +1,700 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2021 The HuggingFace Team. All rights reserved.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
from typing import Optional
|
| 15 |
+
|
| 16 |
+
import flax
|
| 17 |
+
import jax.numpy as jnp
|
| 18 |
+
|
| 19 |
+
from .utils import ModelOutput
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
@flax.struct.dataclass
|
| 23 |
+
class FlaxBaseModelOutput(ModelOutput):
|
| 24 |
+
"""
|
| 25 |
+
Base class for model's outputs, with potential hidden states and attentions.
|
| 26 |
+
|
| 27 |
+
Args:
|
| 28 |
+
last_hidden_state (`jnp.ndarray` of shape `(batch_size, sequence_length, hidden_size)`):
|
| 29 |
+
Sequence of hidden-states at the output of the last layer of the model.
|
| 30 |
+
hidden_states (`tuple(jnp.ndarray)`, *optional*, returned when `output_hidden_states=True` is passed or when `config.output_hidden_states=True`):
|
| 31 |
+
Tuple of `jnp.ndarray` (one for the output of the embeddings + one for the output of each layer) of shape
|
| 32 |
+
`(batch_size, sequence_length, hidden_size)`.
|
| 33 |
+
|
| 34 |
+
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
| 35 |
+
attentions (`tuple(jnp.ndarray)`, *optional*, returned when `output_attentions=True` is passed or when `config.output_attentions=True`):
|
| 36 |
+
Tuple of `jnp.ndarray` (one for each layer) of shape `(batch_size, num_heads, sequence_length,
|
| 37 |
+
sequence_length)`.
|
| 38 |
+
|
| 39 |
+
Attentions weights after the attention softmax, used to compute the weighted average in the self-attention
|
| 40 |
+
heads.
|
| 41 |
+
"""
|
| 42 |
+
|
| 43 |
+
last_hidden_state: Optional[jnp.ndarray] = None
|
| 44 |
+
hidden_states: Optional[tuple[jnp.ndarray]] = None
|
| 45 |
+
attentions: Optional[tuple[jnp.ndarray]] = None
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
@flax.struct.dataclass
|
| 49 |
+
class FlaxBaseModelOutputWithNoAttention(ModelOutput):
|
| 50 |
+
"""
|
| 51 |
+
Base class for model's outputs, with potential hidden states.
|
| 52 |
+
|
| 53 |
+
Args:
|
| 54 |
+
last_hidden_state (`jnp.ndarray` of shape `(batch_size, num_channels, height, width)`):
|
| 55 |
+
Sequence of hidden-states at the output of the last layer of the model.
|
| 56 |
+
hidden_states (`tuple(jnp.ndarray)`, *optional*, returned when `output_hidden_states=True` is passed or when `config.output_hidden_states=True`):
|
| 57 |
+
Tuple of `jnp.ndarray` (one for the output of the embeddings, if the model has an embedding layer, + one
|
| 58 |
+
for the output of each layer) of shape `(batch_size, num_channels, height, width)`. Hidden-states of the
|
| 59 |
+
model at the output of each layer plus the optional initial embedding outputs.
|
| 60 |
+
"""
|
| 61 |
+
|
| 62 |
+
last_hidden_state: Optional[jnp.ndarray] = None
|
| 63 |
+
hidden_states: Optional[tuple[jnp.ndarray]] = None
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
@flax.struct.dataclass
|
| 67 |
+
class FlaxBaseModelOutputWithPoolingAndNoAttention(ModelOutput):
|
| 68 |
+
"""
|
| 69 |
+
Base class for model's outputs that also contains a pooling of the last hidden states.
|
| 70 |
+
|
| 71 |
+
Args:
|
| 72 |
+
last_hidden_state (`jnp.ndarray` of shape `(batch_size, num_channels, height, width)`):
|
| 73 |
+
Sequence of hidden-states at the output of the last layer of the model.
|
| 74 |
+
pooler_output (`jnp.ndarray` of shape `(batch_size, hidden_size)`):
|
| 75 |
+
Last layer hidden-state after a pooling operation on the spatial dimensions.
|
| 76 |
+
hidden_states (`tuple(jnp.ndarray)`, *optional*, returned when `output_hidden_states=True` is passed or when `config.output_hidden_states=True`):
|
| 77 |
+
Tuple of `jnp.ndarray` (one for the output of the embeddings, if the model has an embedding layer, + one
|
| 78 |
+
for the output of each layer) of shape `(batch_size, num_channels, height, width)`. Hidden-states of the
|
| 79 |
+
model at the output of each layer plus the optional initial embedding outputs.
|
| 80 |
+
"""
|
| 81 |
+
|
| 82 |
+
last_hidden_state: Optional[jnp.ndarray] = None
|
| 83 |
+
pooler_output: Optional[jnp.ndarray] = None
|
| 84 |
+
hidden_states: Optional[tuple[jnp.ndarray]] = None
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
@flax.struct.dataclass
|
| 88 |
+
class FlaxImageClassifierOutputWithNoAttention(ModelOutput):
|
| 89 |
+
"""
|
| 90 |
+
Base class for outputs of image classification models.
|
| 91 |
+
|
| 92 |
+
Args:
|
| 93 |
+
logits (`jnp.ndarray` of shape `(batch_size, config.num_labels)`):
|
| 94 |
+
Classification (or regression if config.num_labels==1) scores (before SoftMax).
|
| 95 |
+
hidden_states (`tuple(jnp.ndarray)`, *optional*, returned when `output_hidden_states=True` is passed or when
|
| 96 |
+
`config.output_hidden_states=True`):
|
| 97 |
+
Tuple of `jnp.ndarray` (one for the output of the embeddings, if the model has an embedding layer, + one
|
| 98 |
+
for the output of each stage) of shape `(batch_size, num_channels, height, width)`. Hidden-states (also
|
| 99 |
+
called feature maps) of the model at the output of each stage.
|
| 100 |
+
"""
|
| 101 |
+
|
| 102 |
+
logits: Optional[jnp.ndarray] = None
|
| 103 |
+
hidden_states: Optional[tuple[jnp.ndarray]] = None
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
@flax.struct.dataclass
|
| 107 |
+
class FlaxBaseModelOutputWithPast(ModelOutput):
|
| 108 |
+
"""
|
| 109 |
+
Base class for model's outputs, with potential hidden states and attentions.
|
| 110 |
+
|
| 111 |
+
Args:
|
| 112 |
+
last_hidden_state (`jnp.ndarray` of shape `(batch_size, sequence_length, hidden_size)`):
|
| 113 |
+
Sequence of hidden-states at the output of the last layer of the model.
|
| 114 |
+
past_key_values (`dict[str, jnp.ndarray]`):
|
| 115 |
+
Dictionary of pre-computed hidden-states (key and values in the attention blocks) that can be used for fast
|
| 116 |
+
auto-regressive decoding. Pre-computed key and value hidden-states are of shape *[batch_size, max_length]*.
|
| 117 |
+
hidden_states (`tuple(jnp.ndarray)`, *optional*, returned when `output_hidden_states=True` is passed or when `config.output_hidden_states=True`):
|
| 118 |
+
Tuple of `jnp.ndarray` (one for the output of the embeddings + one for the output of each layer) of shape
|
| 119 |
+
`(batch_size, sequence_length, hidden_size)`.
|
| 120 |
+
|
| 121 |
+
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
| 122 |
+
attentions (`tuple(jnp.ndarray)`, *optional*, returned when `output_attentions=True` is passed or when `config.output_attentions=True`):
|
| 123 |
+
Tuple of `jnp.ndarray` (one for each layer) of shape `(batch_size, num_heads, sequence_length,
|
| 124 |
+
sequence_length)`.
|
| 125 |
+
|
| 126 |
+
Attentions weights after the attention softmax, used to compute the weighted average in the self-attention
|
| 127 |
+
heads.
|
| 128 |
+
"""
|
| 129 |
+
|
| 130 |
+
last_hidden_state: Optional[jnp.ndarray] = None
|
| 131 |
+
past_key_values: Optional[dict[str, jnp.ndarray]] = None
|
| 132 |
+
hidden_states: Optional[tuple[jnp.ndarray]] = None
|
| 133 |
+
attentions: Optional[tuple[jnp.ndarray]] = None
|
| 134 |
+
|
| 135 |
+
|
| 136 |
+
@flax.struct.dataclass
|
| 137 |
+
class FlaxBaseModelOutputWithPooling(ModelOutput):
|
| 138 |
+
"""
|
| 139 |
+
Base class for model's outputs that also contains a pooling of the last hidden states.
|
| 140 |
+
|
| 141 |
+
Args:
|
| 142 |
+
last_hidden_state (`jnp.ndarray` of shape `(batch_size, sequence_length, hidden_size)`):
|
| 143 |
+
Sequence of hidden-states at the output of the last layer of the model.
|
| 144 |
+
pooler_output (`jnp.ndarray` of shape `(batch_size, hidden_size)`):
|
| 145 |
+
Last layer hidden-state of the first token of the sequence (classification token) further processed by a
|
| 146 |
+
Linear layer and a Tanh activation function. The Linear layer weights are trained from the next sentence
|
| 147 |
+
prediction (classification) objective during pretraining.
|
| 148 |
+
hidden_states (`tuple(jnp.ndarray)`, *optional*, returned when `output_hidden_states=True` is passed or when `config.output_hidden_states=True`):
|
| 149 |
+
Tuple of `jnp.ndarray` (one for the output of the embeddings + one for the output of each layer) of shape
|
| 150 |
+
`(batch_size, sequence_length, hidden_size)`.
|
| 151 |
+
|
| 152 |
+
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
| 153 |
+
attentions (`tuple(jnp.ndarray)`, *optional*, returned when `output_attentions=True` is passed or when `config.output_attentions=True`):
|
| 154 |
+
Tuple of `jnp.ndarray` (one for each layer) of shape `(batch_size, num_heads, sequence_length,
|
| 155 |
+
sequence_length)`.
|
| 156 |
+
|
| 157 |
+
Attentions weights after the attention softmax, used to compute the weighted average in the self-attention
|
| 158 |
+
heads.
|
| 159 |
+
"""
|
| 160 |
+
|
| 161 |
+
last_hidden_state: Optional[jnp.ndarray] = None
|
| 162 |
+
pooler_output: Optional[jnp.ndarray] = None
|
| 163 |
+
hidden_states: Optional[tuple[jnp.ndarray]] = None
|
| 164 |
+
attentions: Optional[tuple[jnp.ndarray]] = None
|
| 165 |
+
|
| 166 |
+
|
| 167 |
+
@flax.struct.dataclass
|
| 168 |
+
class FlaxBaseModelOutputWithPoolingAndCrossAttentions(ModelOutput):
|
| 169 |
+
"""
|
| 170 |
+
Base class for model's outputs that also contains a pooling of the last hidden states.
|
| 171 |
+
|
| 172 |
+
Args:
|
| 173 |
+
last_hidden_state (`jnp.ndarray` of shape `(batch_size, sequence_length, hidden_size)`):
|
| 174 |
+
Sequence of hidden-states at the output of the last layer of the model.
|
| 175 |
+
pooler_output (`jnp.ndarray` of shape `(batch_size, hidden_size)`):
|
| 176 |
+
Last layer hidden-state of the first token of the sequence (classification token) after further processing
|
| 177 |
+
through the layers used for the auxiliary pretraining task. E.g. for BERT-family of models, this returns
|
| 178 |
+
the classification token after processing through a linear layer and a tanh activation function. The linear
|
| 179 |
+
layer weights are trained from the next sentence prediction (classification) objective during pretraining.
|
| 180 |
+
hidden_states (`tuple(jnp.ndarray)`, *optional*, returned when `output_hidden_states=True` is passed or when `config.output_hidden_states=True`):
|
| 181 |
+
Tuple of `jnp.ndarray` (one for the output of the embeddings, if the model has an embedding layer, + one
|
| 182 |
+
for the output of each layer) of shape `(batch_size, sequence_length, hidden_size)`.
|
| 183 |
+
|
| 184 |
+
Hidden-states of the model at the output of each layer plus the optional initial embedding outputs.
|
| 185 |
+
attentions (`tuple(jnp.ndarray)`, *optional*, returned when `output_attentions=True` is passed or when `config.output_attentions=True`):
|
| 186 |
+
Tuple of `jnp.ndarray` (one for each layer) of shape `(batch_size, num_heads, sequence_length,
|
| 187 |
+
sequence_length)`.
|
| 188 |
+
|
| 189 |
+
Attentions weights after the attention softmax, used to compute the weighted average in the self-attention
|
| 190 |
+
heads.
|
| 191 |
+
cross_attentions (`tuple(jnp.ndarray)`, *optional*, returned when `output_attentions=True` and `config.add_cross_attention=True` is passed or when `config.output_attentions=True`):
|
| 192 |
+
Tuple of `jnp.ndarray` (one for each layer) of shape `(batch_size, num_heads, sequence_length,
|
| 193 |
+
sequence_length)`.
|
| 194 |
+
|
| 195 |
+
Attentions weights of the decoder's cross-attention layer, after the attention softmax, used to compute the
|
| 196 |
+
weighted average in the cross-attention heads.
|
| 197 |
+
past_key_values (`tuple(tuple(jnp.ndarray))`, *optional*, returned when `use_cache=True` is passed or when `config.use_cache=True`):
|
| 198 |
+
Tuple of `tuple(jnp.ndarray)` of length `config.n_layers`, with each tuple having 2 tensors of shape
|
| 199 |
+
`(batch_size, num_heads, sequence_length, embed_size_per_head)`) and optionally if
|
| 200 |
+
`config.is_encoder_decoder=True` 2 additional tensors of shape `(batch_size, num_heads,
|
| 201 |
+
encoder_sequence_length, embed_size_per_head)`.
|
| 202 |
+
|
| 203 |
+
Contains pre-computed hidden-states (key and values in the self-attention blocks and optionally if
|
| 204 |
+
`config.is_encoder_decoder=True` in the cross-attention blocks) that can be used (see `past_key_values`
|
| 205 |
+
input) to speed up sequential decoding.
|
| 206 |
+
"""
|
| 207 |
+
|
| 208 |
+
last_hidden_state: Optional[jnp.ndarray] = None
|
| 209 |
+
pooler_output: Optional[jnp.ndarray] = None
|
| 210 |
+
hidden_states: Optional[tuple[jnp.ndarray]] = None
|
| 211 |
+
past_key_values: Optional[tuple[tuple[jnp.ndarray]]] = None
|
| 212 |
+
attentions: Optional[tuple[jnp.ndarray]] = None
|
| 213 |
+
cross_attentions: Optional[tuple[jnp.ndarray]] = None
|
| 214 |
+
|
| 215 |
+
|
| 216 |
+
@flax.struct.dataclass
|
| 217 |
+
class FlaxBaseModelOutputWithPastAndCrossAttentions(ModelOutput):
|
| 218 |
+
"""
|
| 219 |
+
Base class for model's outputs that may also contain a past key/values (to speed up sequential decoding).
|
| 220 |
+
|
| 221 |
+
Args:
|
| 222 |
+
last_hidden_state (`jnp.ndarray` of shape `(batch_size, sequence_length, hidden_size)`):
|
| 223 |
+
Sequence of hidden-states at the output of the last layer of the model.
|
| 224 |
+
|
| 225 |
+
If `past_key_values` is used only the last hidden-state of the sequences of shape `(batch_size, 1,
|
| 226 |
+
hidden_size)` is output.
|
| 227 |
+
past_key_values (`tuple(tuple(jnp.ndarray))`, *optional*, returned when `use_cache=True` is passed or when `config.use_cache=True`):
|
| 228 |
+
Tuple of `tuple(jnp.ndarray)` of length `config.n_layers`, with each tuple having 2 tensors of shape
|
| 229 |
+
`(batch_size, num_heads, sequence_length, embed_size_per_head)`) and optionally if
|
| 230 |
+
`config.is_encoder_decoder=True` 2 additional tensors of shape `(batch_size, num_heads,
|
| 231 |
+
encoder_sequence_length, embed_size_per_head)`.
|
| 232 |
+
|
| 233 |
+
Contains pre-computed hidden-states (key and values in the self-attention blocks and optionally if
|
| 234 |
+
`config.is_encoder_decoder=True` in the cross-attention blocks) that can be used (see `past_key_values`
|
| 235 |
+
input) to speed up sequential decoding.
|
| 236 |
+
hidden_states (`tuple(jnp.ndarray)`, *optional*, returned when `output_hidden_states=True` is passed or when `config.output_hidden_states=True`):
|
| 237 |
+
Tuple of `jnp.ndarray` (one for the output of the embeddings + one for the output of each layer) of shape
|
| 238 |
+
`(batch_size, sequence_length, hidden_size)`.
|
| 239 |
+
|
| 240 |
+
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
| 241 |
+
attentions (`tuple(jnp.ndarray)`, *optional*, returned when `output_attentions=True` is passed or when `config.output_attentions=True`):
|
| 242 |
+
Tuple of `jnp.ndarray` (one for each layer) of shape `(batch_size, num_heads, sequence_length,
|
| 243 |
+
sequence_length)`.
|
| 244 |
+
|
| 245 |
+
Attentions weights after the attention softmax, used to compute the weighted average in the self-attention
|
| 246 |
+
heads.
|
| 247 |
+
cross_attentions (`tuple(jnp.ndarray)`, *optional*, returned when `output_attentions=True` and `config.add_cross_attention=True` is passed or when `config.output_attentions=True`):
|
| 248 |
+
Tuple of `jnp.ndarray` (one for each layer) of shape `(batch_size, num_heads, sequence_length,
|
| 249 |
+
sequence_length)`.
|
| 250 |
+
|
| 251 |
+
Attentions weights of the decoder's cross-attention layer, after the attention softmax, used to compute the
|
| 252 |
+
weighted average in the cross-attention heads.
|
| 253 |
+
"""
|
| 254 |
+
|
| 255 |
+
last_hidden_state: Optional[jnp.ndarray] = None
|
| 256 |
+
past_key_values: Optional[tuple[tuple[jnp.ndarray]]] = None
|
| 257 |
+
hidden_states: Optional[tuple[jnp.ndarray]] = None
|
| 258 |
+
attentions: Optional[tuple[jnp.ndarray]] = None
|
| 259 |
+
cross_attentions: Optional[tuple[jnp.ndarray]] = None
|
| 260 |
+
|
| 261 |
+
|
| 262 |
+
@flax.struct.dataclass
|
| 263 |
+
class FlaxSeq2SeqModelOutput(ModelOutput):
|
| 264 |
+
"""
|
| 265 |
+
Base class for model encoder's outputs that also contains : pre-computed hidden states that can speed up sequential
|
| 266 |
+
decoding.
|
| 267 |
+
|
| 268 |
+
Args:
|
| 269 |
+
last_hidden_state (`jnp.ndarray` of shape `(batch_size, sequence_length, hidden_size)`):
|
| 270 |
+
Sequence of hidden-states at the output of the last layer of the decoder of the model.
|
| 271 |
+
|
| 272 |
+
If `past_key_values` is used only the last hidden-state of the sequences of shape `(batch_size, 1,
|
| 273 |
+
hidden_size)` is output.
|
| 274 |
+
past_key_values (`tuple(tuple(jnp.ndarray))`, *optional*, returned when `use_cache=True` is passed or when `config.use_cache=True`):
|
| 275 |
+
Tuple of `tuple(jnp.ndarray)` of length `config.n_layers`, with each tuple having 2 tensors of shape
|
| 276 |
+
`(batch_size, num_heads, sequence_length, embed_size_per_head)`) and 2 additional tensors of shape
|
| 277 |
+
`(batch_size, num_heads, encoder_sequence_length, embed_size_per_head)`.
|
| 278 |
+
|
| 279 |
+
Contains pre-computed hidden-states (key and values in the self-attention blocks and in the cross-attention
|
| 280 |
+
blocks) that can be used (see `past_key_values` input) to speed up sequential decoding.
|
| 281 |
+
decoder_hidden_states (`tuple(jnp.ndarray)`, *optional*, returned when `output_hidden_states=True` is passed or when `config.output_hidden_states=True`):
|
| 282 |
+
Tuple of `jnp.ndarray` (one for the output of the embeddings + one for the output of each layer) of shape
|
| 283 |
+
`(batch_size, sequence_length, hidden_size)`.
|
| 284 |
+
|
| 285 |
+
Hidden-states of the decoder at the output of each layer plus the initial embedding outputs.
|
| 286 |
+
decoder_attentions (`tuple(jnp.ndarray)`, *optional*, returned when `output_attentions=True` is passed or when `config.output_attentions=True`):
|
| 287 |
+
Tuple of `jnp.ndarray` (one for each layer) of shape `(batch_size, num_heads, sequence_length,
|
| 288 |
+
sequence_length)`.
|
| 289 |
+
|
| 290 |
+
Attentions weights of the decoder, after the attention softmax, used to compute the weighted average in the
|
| 291 |
+
self-attention heads.
|
| 292 |
+
cross_attentions (`tuple(jnp.ndarray)`, *optional*, returned when `output_attentions=True` is passed or when `config.output_attentions=True`):
|
| 293 |
+
Tuple of `jnp.ndarray` (one for each layer) of shape `(batch_size, num_heads, sequence_length,
|
| 294 |
+
sequence_length)`.
|
| 295 |
+
|
| 296 |
+
Attentions weights of the decoder's cross-attention layer, after the attention softmax, used to compute the
|
| 297 |
+
weighted average in the cross-attention heads.
|
| 298 |
+
encoder_last_hidden_state (`jnp.ndarray` of shape `(batch_size, sequence_length, hidden_size)`, *optional*):
|
| 299 |
+
Sequence of hidden-states at the output of the last layer of the encoder of the model.
|
| 300 |
+
encoder_hidden_states (`tuple(jnp.ndarray)`, *optional*, returned when `output_hidden_states=True` is passed or when `config.output_hidden_states=True`):
|
| 301 |
+
Tuple of `jnp.ndarray` (one for the output of the embeddings + one for the output of each layer) of shape
|
| 302 |
+
`(batch_size, sequence_length, hidden_size)`.
|
| 303 |
+
|
| 304 |
+
Hidden-states of the encoder at the output of each layer plus the initial embedding outputs.
|
| 305 |
+
encoder_attentions (`tuple(jnp.ndarray)`, *optional*, returned when `output_attentions=True` is passed or when `config.output_attentions=True`):
|
| 306 |
+
Tuple of `jnp.ndarray` (one for each layer) of shape `(batch_size, num_heads, sequence_length,
|
| 307 |
+
sequence_length)`.
|
| 308 |
+
|
| 309 |
+
Attentions weights of the encoder, after the attention softmax, used to compute the weighted average in the
|
| 310 |
+
self-attention heads.
|
| 311 |
+
"""
|
| 312 |
+
|
| 313 |
+
last_hidden_state: Optional[jnp.ndarray] = None
|
| 314 |
+
past_key_values: Optional[tuple[tuple[jnp.ndarray]]] = None
|
| 315 |
+
decoder_hidden_states: Optional[tuple[jnp.ndarray]] = None
|
| 316 |
+
decoder_attentions: Optional[tuple[jnp.ndarray]] = None
|
| 317 |
+
cross_attentions: Optional[tuple[jnp.ndarray]] = None
|
| 318 |
+
encoder_last_hidden_state: Optional[jnp.ndarray] = None
|
| 319 |
+
encoder_hidden_states: Optional[tuple[jnp.ndarray]] = None
|
| 320 |
+
encoder_attentions: Optional[tuple[jnp.ndarray]] = None
|
| 321 |
+
|
| 322 |
+
|
| 323 |
+
@flax.struct.dataclass
|
| 324 |
+
class FlaxCausalLMOutputWithCrossAttentions(ModelOutput):
|
| 325 |
+
"""
|
| 326 |
+
Base class for causal language model (or autoregressive) outputs.
|
| 327 |
+
|
| 328 |
+
Args:
|
| 329 |
+
logits (`jnp.ndarray` of shape `(batch_size, sequence_length, config.vocab_size)`):
|
| 330 |
+
Prediction scores of the language modeling head (scores for each vocabulary token before SoftMax).
|
| 331 |
+
hidden_states (`tuple(jnp.ndarray)`, *optional*, returned when `output_hidden_states=True` is passed or when `config.output_hidden_states=True`):
|
| 332 |
+
Tuple of `jnp.ndarray` (one for the output of the embeddings + one for the output of each layer) of shape
|
| 333 |
+
`(batch_size, sequence_length, hidden_size)`.
|
| 334 |
+
|
| 335 |
+
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
| 336 |
+
attentions (`tuple(jnp.ndarray)`, *optional*, returned when `output_attentions=True` is passed or when `config.output_attentions=True`):
|
| 337 |
+
Tuple of `jnp.ndarray` (one for each layer) of shape `(batch_size, num_heads, sequence_length,
|
| 338 |
+
sequence_length)`.
|
| 339 |
+
|
| 340 |
+
Attentions weights after the attention softmax, used to compute the weighted average in the self-attention
|
| 341 |
+
heads.
|
| 342 |
+
cross_attentions (`tuple(jnp.ndarray)`, *optional*, returned when `output_attentions=True` is passed or when `config.output_attentions=True`):
|
| 343 |
+
Tuple of `jnp.ndarray` (one for each layer) of shape `(batch_size, num_heads, sequence_length,
|
| 344 |
+
sequence_length)`.
|
| 345 |
+
|
| 346 |
+
Cross attentions weights after the attention softmax, used to compute the weighted average in the
|
| 347 |
+
cross-attention heads.
|
| 348 |
+
past_key_values (`tuple(tuple(jnp.ndarray))`, *optional*, returned when `use_cache=True` is passed or when `config.use_cache=True`):
|
| 349 |
+
Tuple of `jnp.ndarray` tuples of length `config.n_layers`, with each tuple containing the cached key, value
|
| 350 |
+
states of the self-attention and the cross-attention layers if model is used in encoder-decoder setting.
|
| 351 |
+
Only relevant if `config.is_decoder = True`.
|
| 352 |
+
|
| 353 |
+
Contains pre-computed hidden-states (key and values in the attention blocks) that can be used (see
|
| 354 |
+
`past_key_values` input) to speed up sequential decoding.
|
| 355 |
+
"""
|
| 356 |
+
|
| 357 |
+
logits: Optional[jnp.ndarray] = None
|
| 358 |
+
past_key_values: Optional[tuple[tuple[jnp.ndarray]]] = None
|
| 359 |
+
hidden_states: Optional[tuple[jnp.ndarray]] = None
|
| 360 |
+
attentions: Optional[tuple[jnp.ndarray]] = None
|
| 361 |
+
cross_attentions: Optional[tuple[jnp.ndarray]] = None
|
| 362 |
+
|
| 363 |
+
|
| 364 |
+
@flax.struct.dataclass
|
| 365 |
+
class FlaxMaskedLMOutput(ModelOutput):
|
| 366 |
+
"""
|
| 367 |
+
Base class for masked language models outputs.
|
| 368 |
+
|
| 369 |
+
Args:
|
| 370 |
+
logits (`jnp.ndarray` of shape `(batch_size, sequence_length, config.vocab_size)`):
|
| 371 |
+
Prediction scores of the language modeling head (scores for each vocabulary token before SoftMax).
|
| 372 |
+
hidden_states (`tuple(jnp.ndarray)`, *optional*, returned when `output_hidden_states=True` is passed or when `config.output_hidden_states=True`):
|
| 373 |
+
Tuple of `jnp.ndarray` (one for the output of the embeddings + one for the output of each layer) of shape
|
| 374 |
+
`(batch_size, sequence_length, hidden_size)`.
|
| 375 |
+
|
| 376 |
+
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
| 377 |
+
attentions (`tuple(jnp.ndarray)`, *optional*, returned when `output_attentions=True` is passed or when `config.output_attentions=True`):
|
| 378 |
+
Tuple of `jnp.ndarray` (one for each layer) of shape `(batch_size, num_heads, sequence_length,
|
| 379 |
+
sequence_length)`.
|
| 380 |
+
|
| 381 |
+
Attentions weights after the attention softmax, used to compute the weighted average in the self-attention
|
| 382 |
+
heads.
|
| 383 |
+
"""
|
| 384 |
+
|
| 385 |
+
logits: Optional[jnp.ndarray] = None
|
| 386 |
+
hidden_states: Optional[tuple[jnp.ndarray]] = None
|
| 387 |
+
attentions: Optional[tuple[jnp.ndarray]] = None
|
| 388 |
+
|
| 389 |
+
|
| 390 |
+
FlaxCausalLMOutput = FlaxMaskedLMOutput
|
| 391 |
+
|
| 392 |
+
|
| 393 |
+
@flax.struct.dataclass
|
| 394 |
+
class FlaxSeq2SeqLMOutput(ModelOutput):
|
| 395 |
+
"""
|
| 396 |
+
Base class for sequence-to-sequence language models outputs.
|
| 397 |
+
|
| 398 |
+
Args:
|
| 399 |
+
logits (`jnp.ndarray` of shape `(batch_size, sequence_length, config.vocab_size)`):
|
| 400 |
+
Prediction scores of the language modeling head (scores for each vocabulary token before SoftMax).
|
| 401 |
+
past_key_values (`tuple(tuple(jnp.ndarray))`, *optional*, returned when `use_cache=True` is passed or when `config.use_cache=True`):
|
| 402 |
+
Tuple of `tuple(jnp.ndarray)` of length `config.n_layers`, with each tuple having 2 tensors of shape
|
| 403 |
+
`(batch_size, num_heads, sequence_length, embed_size_per_head)`) and 2 additional tensors of shape
|
| 404 |
+
`(batch_size, num_heads, encoder_sequence_length, embed_size_per_head)`.
|
| 405 |
+
|
| 406 |
+
Contains pre-computed hidden-states (key and values in the self-attention blocks and in the cross-attention
|
| 407 |
+
blocks) that can be used (see `past_key_values` input) to speed up sequential decoding.
|
| 408 |
+
decoder_hidden_states (`tuple(jnp.ndarray)`, *optional*, returned when `output_hidden_states=True` is passed or when `config.output_hidden_states=True`):
|
| 409 |
+
Tuple of `jnp.ndarray` (one for the output of the embeddings + one for the output of each layer) of shape
|
| 410 |
+
`(batch_size, sequence_length, hidden_size)`.
|
| 411 |
+
|
| 412 |
+
Hidden-states of the decoder at the output of each layer plus the initial embedding outputs.
|
| 413 |
+
decoder_attentions (`tuple(jnp.ndarray)`, *optional*, returned when `output_attentions=True` is passed or when `config.output_attentions=True`):
|
| 414 |
+
Tuple of `jnp.ndarray` (one for each layer) of shape `(batch_size, num_heads, sequence_length,
|
| 415 |
+
sequence_length)`.
|
| 416 |
+
|
| 417 |
+
Attentions weights of the decoder, after the attention softmax, used to compute the weighted average in the
|
| 418 |
+
self-attention heads.
|
| 419 |
+
cross_attentions (`tuple(jnp.ndarray)`, *optional*, returned when `output_attentions=True` is passed or when `config.output_attentions=True`):
|
| 420 |
+
Tuple of `jnp.ndarray` (one for each layer) of shape `(batch_size, num_heads, sequence_length,
|
| 421 |
+
sequence_length)`.
|
| 422 |
+
|
| 423 |
+
Attentions weights of the decoder's cross-attention layer, after the attention softmax, used to compute the
|
| 424 |
+
weighted average in the cross-attention heads.
|
| 425 |
+
encoder_last_hidden_state (`jnp.ndarray` of shape `(batch_size, sequence_length, hidden_size)`, *optional*):
|
| 426 |
+
Sequence of hidden-states at the output of the last layer of the encoder of the model.
|
| 427 |
+
encoder_hidden_states (`tuple(jnp.ndarray)`, *optional*, returned when `output_hidden_states=True` is passed or when `config.output_hidden_states=True`):
|
| 428 |
+
Tuple of `jnp.ndarray` (one for the output of the embeddings + one for the output of each layer) of shape
|
| 429 |
+
`(batch_size, sequence_length, hidden_size)`.
|
| 430 |
+
|
| 431 |
+
Hidden-states of the encoder at the output of each layer plus the initial embedding outputs.
|
| 432 |
+
encoder_attentions (`tuple(jnp.ndarray)`, *optional*, returned when `output_attentions=True` is passed or when `config.output_attentions=True`):
|
| 433 |
+
Tuple of `jnp.ndarray` (one for each layer) of shape `(batch_size, num_heads, sequence_length,
|
| 434 |
+
sequence_length)`.
|
| 435 |
+
|
| 436 |
+
Attentions weights of the encoder, after the attention softmax, used to compute the weighted average in the
|
| 437 |
+
self-attention heads.
|
| 438 |
+
"""
|
| 439 |
+
|
| 440 |
+
logits: Optional[jnp.ndarray] = None
|
| 441 |
+
past_key_values: Optional[tuple[tuple[jnp.ndarray]]] = None
|
| 442 |
+
decoder_hidden_states: Optional[tuple[jnp.ndarray]] = None
|
| 443 |
+
decoder_attentions: Optional[tuple[jnp.ndarray]] = None
|
| 444 |
+
cross_attentions: Optional[tuple[jnp.ndarray]] = None
|
| 445 |
+
encoder_last_hidden_state: Optional[jnp.ndarray] = None
|
| 446 |
+
encoder_hidden_states: Optional[tuple[jnp.ndarray]] = None
|
| 447 |
+
encoder_attentions: Optional[tuple[jnp.ndarray]] = None
|
| 448 |
+
|
| 449 |
+
|
| 450 |
+
@flax.struct.dataclass
|
| 451 |
+
class FlaxNextSentencePredictorOutput(ModelOutput):
|
| 452 |
+
"""
|
| 453 |
+
Base class for outputs of models predicting if two sentences are consecutive or not.
|
| 454 |
+
|
| 455 |
+
Args:
|
| 456 |
+
logits (`jnp.ndarray` of shape `(batch_size, 2)`):
|
| 457 |
+
Prediction scores of the next sequence prediction (classification) head (scores of True/False continuation
|
| 458 |
+
before SoftMax).
|
| 459 |
+
hidden_states (`tuple(jnp.ndarray)`, *optional*, returned when `output_hidden_states=True` is passed or when `config.output_hidden_states=True`):
|
| 460 |
+
Tuple of `jnp.ndarray` (one for the output of the embeddings + one for the output of each layer) of shape
|
| 461 |
+
`(batch_size, sequence_length, hidden_size)`.
|
| 462 |
+
|
| 463 |
+
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
| 464 |
+
attentions (`tuple(jnp.ndarray)`, *optional*, returned when `output_attentions=True` is passed or when `config.output_attentions=True`):
|
| 465 |
+
Tuple of `jnp.ndarray` (one for each layer) of shape `(batch_size, num_heads, sequence_length,
|
| 466 |
+
sequence_length)`.
|
| 467 |
+
|
| 468 |
+
Attentions weights after the attention softmax, used to compute the weighted average in the self-attention
|
| 469 |
+
heads.
|
| 470 |
+
"""
|
| 471 |
+
|
| 472 |
+
logits: Optional[jnp.ndarray] = None
|
| 473 |
+
hidden_states: Optional[tuple[jnp.ndarray]] = None
|
| 474 |
+
attentions: Optional[tuple[jnp.ndarray]] = None
|
| 475 |
+
|
| 476 |
+
|
| 477 |
+
@flax.struct.dataclass
|
| 478 |
+
class FlaxSequenceClassifierOutput(ModelOutput):
|
| 479 |
+
"""
|
| 480 |
+
Base class for outputs of sentence classification models.
|
| 481 |
+
|
| 482 |
+
Args:
|
| 483 |
+
logits (`jnp.ndarray` of shape `(batch_size, config.num_labels)`):
|
| 484 |
+
Classification (or regression if config.num_labels==1) scores (before SoftMax).
|
| 485 |
+
hidden_states (`tuple(jnp.ndarray)`, *optional*, returned when `output_hidden_states=True` is passed or when `config.output_hidden_states=True`):
|
| 486 |
+
Tuple of `jnp.ndarray` (one for the output of the embeddings + one for the output of each layer) of shape
|
| 487 |
+
`(batch_size, sequence_length, hidden_size)`.
|
| 488 |
+
|
| 489 |
+
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
| 490 |
+
attentions (`tuple(jnp.ndarray)`, *optional*, returned when `output_attentions=True` is passed or when `config.output_attentions=True`):
|
| 491 |
+
Tuple of `jnp.ndarray` (one for each layer) of shape `(batch_size, num_heads, sequence_length,
|
| 492 |
+
sequence_length)`.
|
| 493 |
+
|
| 494 |
+
Attentions weights after the attention softmax, used to compute the weighted average in the self-attention
|
| 495 |
+
heads.
|
| 496 |
+
"""
|
| 497 |
+
|
| 498 |
+
logits: Optional[jnp.ndarray] = None
|
| 499 |
+
hidden_states: Optional[tuple[jnp.ndarray]] = None
|
| 500 |
+
attentions: Optional[tuple[jnp.ndarray]] = None
|
| 501 |
+
|
| 502 |
+
|
| 503 |
+
@flax.struct.dataclass
|
| 504 |
+
class FlaxSeq2SeqSequenceClassifierOutput(ModelOutput):
|
| 505 |
+
"""
|
| 506 |
+
Base class for outputs of sequence-to-sequence sentence classification models.
|
| 507 |
+
|
| 508 |
+
Args:
|
| 509 |
+
logits (`jnp.ndarray` of shape `(batch_size, config.num_labels)`):
|
| 510 |
+
Classification (or regression if config.num_labels==1) scores (before SoftMax).
|
| 511 |
+
past_key_values (`tuple(tuple(jnp.ndarray))`, *optional*, returned when `use_cache=True` is passed or when `config.use_cache=True`):
|
| 512 |
+
Tuple of `tuple(jnp.ndarray)` of length `config.n_layers`, with each tuple having 2 tensors of shape
|
| 513 |
+
`(batch_size, num_heads, sequence_length, embed_size_per_head)`) and 2 additional tensors of shape
|
| 514 |
+
`(batch_size, num_heads, encoder_sequence_length, embed_size_per_head)`.
|
| 515 |
+
|
| 516 |
+
Contains pre-computed hidden-states (key and values in the self-attention blocks and in the cross-attention
|
| 517 |
+
blocks) that can be used (see `past_key_values` input) to speed up sequential decoding.
|
| 518 |
+
decoder_hidden_states (`tuple(jnp.ndarray)`, *optional*, returned when `output_hidden_states=True` is passed or when `config.output_hidden_states=True`):
|
| 519 |
+
Tuple of `jnp.ndarray` (one for the output of the embeddings + one for the output of each layer) of shape
|
| 520 |
+
`(batch_size, sequence_length, hidden_size)`.
|
| 521 |
+
|
| 522 |
+
Hidden-states of the decoder at the output of each layer plus the initial embedding outputs.
|
| 523 |
+
decoder_attentions (`tuple(jnp.ndarray)`, *optional*, returned when `output_attentions=True` is passed or when `config.output_attentions=True`):
|
| 524 |
+
Tuple of `jnp.ndarray` (one for each layer) of shape `(batch_size, num_heads, sequence_length,
|
| 525 |
+
sequence_length)`.
|
| 526 |
+
|
| 527 |
+
Attentions weights of the decoder, after the attention softmax, used to compute the weighted average in the
|
| 528 |
+
self-attention heads.
|
| 529 |
+
cross_attentions (`tuple(jnp.ndarray)`, *optional*, returned when `output_attentions=True` is passed or when `config.output_attentions=True`):
|
| 530 |
+
Tuple of `jnp.ndarray` (one for each layer) of shape `(batch_size, num_heads, sequence_length,
|
| 531 |
+
sequence_length)`.
|
| 532 |
+
|
| 533 |
+
Attentions weights of the decoder's cross-attention layer, after the attention softmax, used to compute the
|
| 534 |
+
weighted average in the cross-attention heads.
|
| 535 |
+
encoder_last_hidden_state (`jnp.ndarray` of shape `(batch_size, sequence_length, hidden_size)`, *optional*):
|
| 536 |
+
Sequence of hidden-states at the output of the last layer of the encoder of the model.
|
| 537 |
+
encoder_hidden_states (`tuple(jnp.ndarray)`, *optional*, returned when `output_hidden_states=True` is passed or when `config.output_hidden_states=True`):
|
| 538 |
+
Tuple of `jnp.ndarray` (one for the output of the embeddings + one for the output of each layer) of shape
|
| 539 |
+
`(batch_size, sequence_length, hidden_size)`.
|
| 540 |
+
|
| 541 |
+
Hidden-states of the encoder at the output of each layer plus the initial embedding outputs.
|
| 542 |
+
encoder_attentions (`tuple(jnp.ndarray)`, *optional*, returned when `output_attentions=True` is passed or when `config.output_attentions=True`):
|
| 543 |
+
Tuple of `jnp.ndarray` (one for each layer) of shape `(batch_size, num_heads, sequence_length,
|
| 544 |
+
sequence_length)`.
|
| 545 |
+
|
| 546 |
+
Attentions weights of the encoder, after the attention softmax, used to compute the weighted average in the
|
| 547 |
+
self-attention heads.
|
| 548 |
+
"""
|
| 549 |
+
|
| 550 |
+
logits: Optional[jnp.ndarray] = None
|
| 551 |
+
past_key_values: Optional[tuple[tuple[jnp.ndarray]]] = None
|
| 552 |
+
decoder_hidden_states: Optional[tuple[jnp.ndarray]] = None
|
| 553 |
+
decoder_attentions: Optional[tuple[jnp.ndarray]] = None
|
| 554 |
+
cross_attentions: Optional[tuple[jnp.ndarray]] = None
|
| 555 |
+
encoder_last_hidden_state: Optional[jnp.ndarray] = None
|
| 556 |
+
encoder_hidden_states: Optional[tuple[jnp.ndarray]] = None
|
| 557 |
+
encoder_attentions: Optional[tuple[jnp.ndarray]] = None
|
| 558 |
+
|
| 559 |
+
|
| 560 |
+
@flax.struct.dataclass
|
| 561 |
+
class FlaxMultipleChoiceModelOutput(ModelOutput):
|
| 562 |
+
"""
|
| 563 |
+
Base class for outputs of multiple choice models.
|
| 564 |
+
|
| 565 |
+
Args:
|
| 566 |
+
logits (`jnp.ndarray` of shape `(batch_size, num_choices)`):
|
| 567 |
+
*num_choices* is the second dimension of the input tensors. (see *input_ids* above).
|
| 568 |
+
|
| 569 |
+
Classification scores (before SoftMax).
|
| 570 |
+
hidden_states (`tuple(jnp.ndarray)`, *optional*, returned when `output_hidden_states=True` is passed or when `config.output_hidden_states=True`):
|
| 571 |
+
Tuple of `jnp.ndarray` (one for the output of the embeddings + one for the output of each layer) of shape
|
| 572 |
+
`(batch_size, sequence_length, hidden_size)`.
|
| 573 |
+
|
| 574 |
+
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
| 575 |
+
attentions (`tuple(jnp.ndarray)`, *optional*, returned when `output_attentions=True` is passed or when `config.output_attentions=True`):
|
| 576 |
+
Tuple of `jnp.ndarray` (one for each layer) of shape `(batch_size, num_heads, sequence_length,
|
| 577 |
+
sequence_length)`.
|
| 578 |
+
|
| 579 |
+
Attentions weights after the attention softmax, used to compute the weighted average in the self-attention
|
| 580 |
+
heads.
|
| 581 |
+
"""
|
| 582 |
+
|
| 583 |
+
logits: Optional[jnp.ndarray] = None
|
| 584 |
+
hidden_states: Optional[tuple[jnp.ndarray]] = None
|
| 585 |
+
attentions: Optional[tuple[jnp.ndarray]] = None
|
| 586 |
+
|
| 587 |
+
|
| 588 |
+
@flax.struct.dataclass
|
| 589 |
+
class FlaxTokenClassifierOutput(ModelOutput):
|
| 590 |
+
"""
|
| 591 |
+
Base class for outputs of token classification models.
|
| 592 |
+
|
| 593 |
+
Args:
|
| 594 |
+
logits (`jnp.ndarray` of shape `(batch_size, sequence_length, config.num_labels)`):
|
| 595 |
+
Classification scores (before SoftMax).
|
| 596 |
+
hidden_states (`tuple(jnp.ndarray)`, *optional*, returned when `output_hidden_states=True` is passed or when `config.output_hidden_states=True`):
|
| 597 |
+
Tuple of `jnp.ndarray` (one for the output of the embeddings + one for the output of each layer) of shape
|
| 598 |
+
`(batch_size, sequence_length, hidden_size)`.
|
| 599 |
+
|
| 600 |
+
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
| 601 |
+
attentions (`tuple(jnp.ndarray)`, *optional*, returned when `output_attentions=True` is passed or when `config.output_attentions=True`):
|
| 602 |
+
Tuple of `jnp.ndarray` (one for each layer) of shape `(batch_size, num_heads, sequence_length,
|
| 603 |
+
sequence_length)`.
|
| 604 |
+
|
| 605 |
+
Attentions weights after the attention softmax, used to compute the weighted average in the self-attention
|
| 606 |
+
heads.
|
| 607 |
+
"""
|
| 608 |
+
|
| 609 |
+
logits: Optional[jnp.ndarray] = None
|
| 610 |
+
hidden_states: Optional[tuple[jnp.ndarray]] = None
|
| 611 |
+
attentions: Optional[tuple[jnp.ndarray]] = None
|
| 612 |
+
|
| 613 |
+
|
| 614 |
+
@flax.struct.dataclass
|
| 615 |
+
class FlaxQuestionAnsweringModelOutput(ModelOutput):
|
| 616 |
+
"""
|
| 617 |
+
Base class for outputs of question answering models.
|
| 618 |
+
|
| 619 |
+
Args:
|
| 620 |
+
start_logits (`jnp.ndarray` of shape `(batch_size, sequence_length)`):
|
| 621 |
+
Span-start scores (before SoftMax).
|
| 622 |
+
end_logits (`jnp.ndarray` of shape `(batch_size, sequence_length)`):
|
| 623 |
+
Span-end scores (before SoftMax).
|
| 624 |
+
hidden_states (`tuple(jnp.ndarray)`, *optional*, returned when `output_hidden_states=True` is passed or when `config.output_hidden_states=True`):
|
| 625 |
+
Tuple of `jnp.ndarray` (one for the output of the embeddings + one for the output of each layer) of shape
|
| 626 |
+
`(batch_size, sequence_length, hidden_size)`.
|
| 627 |
+
|
| 628 |
+
Hidden-states of the model at the output of each layer plus the initial embedding outputs.
|
| 629 |
+
attentions (`tuple(jnp.ndarray)`, *optional*, returned when `output_attentions=True` is passed or when `config.output_attentions=True`):
|
| 630 |
+
Tuple of `jnp.ndarray` (one for each layer) of shape `(batch_size, num_heads, sequence_length,
|
| 631 |
+
sequence_length)`.
|
| 632 |
+
|
| 633 |
+
Attentions weights after the attention softmax, used to compute the weighted average in the self-attention
|
| 634 |
+
heads.
|
| 635 |
+
"""
|
| 636 |
+
|
| 637 |
+
start_logits: Optional[jnp.ndarray] = None
|
| 638 |
+
end_logits: Optional[jnp.ndarray] = None
|
| 639 |
+
hidden_states: Optional[tuple[jnp.ndarray]] = None
|
| 640 |
+
attentions: Optional[tuple[jnp.ndarray]] = None
|
| 641 |
+
|
| 642 |
+
|
| 643 |
+
@flax.struct.dataclass
|
| 644 |
+
class FlaxSeq2SeqQuestionAnsweringModelOutput(ModelOutput):
|
| 645 |
+
"""
|
| 646 |
+
Base class for outputs of sequence-to-sequence question answering models.
|
| 647 |
+
|
| 648 |
+
Args:
|
| 649 |
+
start_logits (`jnp.ndarray` of shape `(batch_size, sequence_length)`):
|
| 650 |
+
Span-start scores (before SoftMax).
|
| 651 |
+
end_logits (`jnp.ndarray` of shape `(batch_size, sequence_length)`):
|
| 652 |
+
Span-end scores (before SoftMax).
|
| 653 |
+
past_key_values (`tuple(tuple(jnp.ndarray))`, *optional*, returned when `use_cache=True` is passed or when `config.use_cache=True`):
|
| 654 |
+
Tuple of `tuple(jnp.ndarray)` of length `config.n_layers`, with each tuple having 2 tensors of shape
|
| 655 |
+
`(batch_size, num_heads, sequence_length, embed_size_per_head)`) and 2 additional tensors of shape
|
| 656 |
+
`(batch_size, num_heads, encoder_sequence_length, embed_size_per_head)`.
|
| 657 |
+
|
| 658 |
+
Contains pre-computed hidden-states (key and values in the self-attention blocks and in the cross-attention
|
| 659 |
+
blocks) that can be used (see `past_key_values` input) to speed up sequential decoding.
|
| 660 |
+
decoder_hidden_states (`tuple(jnp.ndarray)`, *optional*, returned when `output_hidden_states=True` is passed or when `config.output_hidden_states=True`):
|
| 661 |
+
Tuple of `jnp.ndarray` (one for the output of the embeddings + one for the output of each layer) of shape
|
| 662 |
+
`(batch_size, sequence_length, hidden_size)`.
|
| 663 |
+
|
| 664 |
+
Hidden-states of the decoder at the output of each layer plus the initial embedding outputs.
|
| 665 |
+
decoder_attentions (`tuple(jnp.ndarray)`, *optional*, returned when `output_attentions=True` is passed or when `config.output_attentions=True`):
|
| 666 |
+
Tuple of `jnp.ndarray` (one for each layer) of shape `(batch_size, num_heads, sequence_length,
|
| 667 |
+
sequence_length)`.
|
| 668 |
+
|
| 669 |
+
Attentions weights of the decoder, after the attention softmax, used to compute the weighted average in the
|
| 670 |
+
self-attention heads.
|
| 671 |
+
cross_attentions (`tuple(jnp.ndarray)`, *optional*, returned when `output_attentions=True` is passed or when `config.output_attentions=True`):
|
| 672 |
+
Tuple of `jnp.ndarray` (one for each layer) of shape `(batch_size, num_heads, sequence_length,
|
| 673 |
+
sequence_length)`.
|
| 674 |
+
|
| 675 |
+
Attentions weights of the decoder's cross-attention layer, after the attention softmax, used to compute the
|
| 676 |
+
weighted average in the cross-attention heads.
|
| 677 |
+
encoder_last_hidden_state (`jnp.ndarray` of shape `(batch_size, sequence_length, hidden_size)`, *optional*):
|
| 678 |
+
Sequence of hidden-states at the output of the last layer of the encoder of the model.
|
| 679 |
+
encoder_hidden_states (`tuple(jnp.ndarray)`, *optional*, returned when `output_hidden_states=True` is passed or when `config.output_hidden_states=True`):
|
| 680 |
+
Tuple of `jnp.ndarray` (one for the output of the embeddings + one for the output of each layer) of shape
|
| 681 |
+
`(batch_size, sequence_length, hidden_size)`.
|
| 682 |
+
|
| 683 |
+
Hidden-states of the encoder at the output of each layer plus the initial embedding outputs.
|
| 684 |
+
encoder_attentions (`tuple(jnp.ndarray)`, *optional*, returned when `output_attentions=True` is passed or when `config.output_attentions=True`):
|
| 685 |
+
Tuple of `jnp.ndarray` (one for each layer) of shape `(batch_size, num_heads, sequence_length,
|
| 686 |
+
sequence_length)`.
|
| 687 |
+
|
| 688 |
+
Attentions weights of the encoder, after the attention softmax, used to compute the weighted average in the
|
| 689 |
+
self-attention heads.
|
| 690 |
+
"""
|
| 691 |
+
|
| 692 |
+
start_logits: Optional[jnp.ndarray] = None
|
| 693 |
+
end_logits: Optional[jnp.ndarray] = None
|
| 694 |
+
past_key_values: Optional[tuple[tuple[jnp.ndarray]]] = None
|
| 695 |
+
decoder_hidden_states: Optional[tuple[jnp.ndarray]] = None
|
| 696 |
+
decoder_attentions: Optional[tuple[jnp.ndarray]] = None
|
| 697 |
+
cross_attentions: Optional[tuple[jnp.ndarray]] = None
|
| 698 |
+
encoder_last_hidden_state: Optional[jnp.ndarray] = None
|
| 699 |
+
encoder_hidden_states: Optional[tuple[jnp.ndarray]] = None
|
| 700 |
+
encoder_attentions: Optional[tuple[jnp.ndarray]] = None
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/modeling_flax_utils.py
ADDED
|
@@ -0,0 +1,1274 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# coding=utf-8
|
| 2 |
+
# Copyright 2021 The Google Flax Team Authors and The HuggingFace Inc. team.
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
# you may not use this file except in compliance with the License.
|
| 6 |
+
# You may obtain a copy of the License at
|
| 7 |
+
#
|
| 8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
#
|
| 10 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
# See the License for the specific language governing permissions and
|
| 14 |
+
# limitations under the License.
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
import gc
|
| 18 |
+
import json
|
| 19 |
+
import os
|
| 20 |
+
import warnings
|
| 21 |
+
from functools import partial
|
| 22 |
+
from pickle import UnpicklingError
|
| 23 |
+
from typing import Any, Optional, Union
|
| 24 |
+
|
| 25 |
+
import flax.linen as nn
|
| 26 |
+
import jax
|
| 27 |
+
import jax.numpy as jnp
|
| 28 |
+
import msgpack.exceptions
|
| 29 |
+
from flax.core.frozen_dict import FrozenDict, unfreeze
|
| 30 |
+
from flax.serialization import from_bytes, to_bytes
|
| 31 |
+
from flax.traverse_util import flatten_dict, unflatten_dict
|
| 32 |
+
from jax.random import PRNGKey
|
| 33 |
+
|
| 34 |
+
from .configuration_utils import PretrainedConfig
|
| 35 |
+
from .dynamic_module_utils import custom_object_save
|
| 36 |
+
from .generation import FlaxGenerationMixin, GenerationConfig
|
| 37 |
+
from .modeling_flax_pytorch_utils import load_pytorch_checkpoint_in_flax_state_dict
|
| 38 |
+
from .utils import (
|
| 39 |
+
FLAX_WEIGHTS_INDEX_NAME,
|
| 40 |
+
FLAX_WEIGHTS_NAME,
|
| 41 |
+
SAFE_WEIGHTS_INDEX_NAME,
|
| 42 |
+
SAFE_WEIGHTS_NAME,
|
| 43 |
+
WEIGHTS_INDEX_NAME,
|
| 44 |
+
WEIGHTS_NAME,
|
| 45 |
+
PushToHubMixin,
|
| 46 |
+
add_code_sample_docstrings,
|
| 47 |
+
add_start_docstrings_to_model_forward,
|
| 48 |
+
cached_file,
|
| 49 |
+
copy_func,
|
| 50 |
+
download_url,
|
| 51 |
+
has_file,
|
| 52 |
+
is_offline_mode,
|
| 53 |
+
is_remote_url,
|
| 54 |
+
logging,
|
| 55 |
+
replace_return_docstrings,
|
| 56 |
+
)
|
| 57 |
+
from .utils.hub import convert_file_size_to_int, get_checkpoint_shard_files
|
| 58 |
+
from .utils.import_utils import is_safetensors_available
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
if is_safetensors_available():
|
| 62 |
+
from safetensors import safe_open
|
| 63 |
+
from safetensors.flax import load_file as safe_load_file
|
| 64 |
+
from safetensors.flax import save_file as safe_save_file
|
| 65 |
+
|
| 66 |
+
logger = logging.get_logger(__name__)
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
def quick_gelu(x):
|
| 70 |
+
return x * jax.nn.sigmoid(1.702 * x)
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
ACT2FN = {
|
| 74 |
+
"gelu": partial(nn.gelu, approximate=False),
|
| 75 |
+
"relu": nn.relu,
|
| 76 |
+
"silu": nn.swish,
|
| 77 |
+
"swish": nn.swish,
|
| 78 |
+
"gelu_new": partial(nn.gelu, approximate=True),
|
| 79 |
+
"quick_gelu": quick_gelu,
|
| 80 |
+
"gelu_pytorch_tanh": partial(nn.gelu, approximate=True),
|
| 81 |
+
"tanh": nn.tanh,
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
def flax_shard_checkpoint(params, max_shard_size="10GB"):
|
| 86 |
+
"""
|
| 87 |
+
Splits a model state dictionary in sub-checkpoints so that the final size of each sub-checkpoint does not exceed a
|
| 88 |
+
given size. The sub-checkpoints are determined by iterating through the `state_dict` in the order of its keys, so
|
| 89 |
+
there is no optimization made to make each sub-checkpoint as close as possible to the maximum size passed. For
|
| 90 |
+
example, if the limit is 10GB and we have weights of sizes [6GB, 6GB, 2GB, 6GB, 2GB, 2GB] they will get sharded as
|
| 91 |
+
[6GB], [6+2GB], [6+2+2GB] and not [6+2+2GB], [6+2GB], [6GB].
|
| 92 |
+
|
| 93 |
+
<Tip warning={true}>
|
| 94 |
+
|
| 95 |
+
If one of the model's weight is bigger that `max_shard_size`, it will end up in its own sub-checkpoint which will
|
| 96 |
+
have a size greater than `max_shard_size`.
|
| 97 |
+
|
| 98 |
+
</Tip>
|
| 99 |
+
|
| 100 |
+
Args:
|
| 101 |
+
params (`Union[Dict, FrozenDict]`): A `PyTree` of model parameters.
|
| 102 |
+
max_shard_size (`int` or `str`, *optional*, defaults to `"10GB"`):
|
| 103 |
+
The maximum size of each sub-checkpoint. If expressed as a string, needs to be digits followed by a unit
|
| 104 |
+
(like `"5MB"`).
|
| 105 |
+
"""
|
| 106 |
+
max_shard_size = convert_file_size_to_int(max_shard_size)
|
| 107 |
+
|
| 108 |
+
sharded_state_dicts = []
|
| 109 |
+
current_block = {}
|
| 110 |
+
current_block_size = 0
|
| 111 |
+
total_size = 0
|
| 112 |
+
|
| 113 |
+
# flatten the weights to chunk
|
| 114 |
+
weights = flatten_dict(params, sep="/")
|
| 115 |
+
for item in weights:
|
| 116 |
+
weight_size = weights[item].size * weights[item].dtype.itemsize
|
| 117 |
+
|
| 118 |
+
# If this weight is going to tip up over the maximal size, we split.
|
| 119 |
+
if current_block_size + weight_size > max_shard_size:
|
| 120 |
+
sharded_state_dicts.append(current_block)
|
| 121 |
+
current_block = {}
|
| 122 |
+
current_block_size = 0
|
| 123 |
+
|
| 124 |
+
current_block[item] = weights[item]
|
| 125 |
+
current_block_size += weight_size
|
| 126 |
+
total_size += weight_size
|
| 127 |
+
|
| 128 |
+
# Add the last block
|
| 129 |
+
sharded_state_dicts.append(current_block)
|
| 130 |
+
|
| 131 |
+
# If we only have one shard, we return it
|
| 132 |
+
if len(sharded_state_dicts) == 1:
|
| 133 |
+
return {FLAX_WEIGHTS_NAME: sharded_state_dicts[0]}, None
|
| 134 |
+
|
| 135 |
+
# Otherwise, let's build the index
|
| 136 |
+
weight_map = {}
|
| 137 |
+
shards = {}
|
| 138 |
+
for idx, shard in enumerate(sharded_state_dicts):
|
| 139 |
+
shard_file = FLAX_WEIGHTS_NAME.replace(".msgpack", f"-{idx + 1:05d}-of-{len(sharded_state_dicts):05d}.msgpack")
|
| 140 |
+
shards[shard_file] = shard
|
| 141 |
+
for weight_name in shard:
|
| 142 |
+
weight_map[weight_name] = shard_file
|
| 143 |
+
|
| 144 |
+
# Add the metadata
|
| 145 |
+
metadata = {"total_size": total_size}
|
| 146 |
+
index = {"metadata": metadata, "weight_map": weight_map}
|
| 147 |
+
return shards, index
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
class FlaxPreTrainedModel(PushToHubMixin, FlaxGenerationMixin):
|
| 151 |
+
r"""
|
| 152 |
+
Base class for all models.
|
| 153 |
+
|
| 154 |
+
[`FlaxPreTrainedModel`] takes care of storing the configuration of the models and handles methods for loading,
|
| 155 |
+
downloading and saving models.
|
| 156 |
+
|
| 157 |
+
Class attributes (overridden by derived classes):
|
| 158 |
+
|
| 159 |
+
- **config_class** ([`PretrainedConfig`]) -- A subclass of [`PretrainedConfig`] to use as configuration class
|
| 160 |
+
for this model architecture.
|
| 161 |
+
- **base_model_prefix** (`str`) -- A string indicating the attribute associated to the base model in derived
|
| 162 |
+
classes of the same architecture adding modules on top of the base model.
|
| 163 |
+
- **main_input_name** (`str`) -- The name of the principal input to the model (often `input_ids` for NLP
|
| 164 |
+
models, `pixel_values` for vision models and `input_values` for speech models).
|
| 165 |
+
"""
|
| 166 |
+
|
| 167 |
+
config_class = None
|
| 168 |
+
base_model_prefix = ""
|
| 169 |
+
main_input_name = "input_ids"
|
| 170 |
+
_auto_class = None
|
| 171 |
+
_missing_keys = set()
|
| 172 |
+
|
| 173 |
+
def __init__(
|
| 174 |
+
self,
|
| 175 |
+
config: PretrainedConfig,
|
| 176 |
+
module: nn.Module,
|
| 177 |
+
input_shape: tuple = (1, 1),
|
| 178 |
+
seed: int = 0,
|
| 179 |
+
dtype: jnp.dtype = jnp.float32,
|
| 180 |
+
_do_init: bool = True,
|
| 181 |
+
):
|
| 182 |
+
logger.warning_once(
|
| 183 |
+
"TensorFlow and JAX classes are deprecated and will be removed in Transformers v5. We "
|
| 184 |
+
"recommend migrating to PyTorch classes or pinning your version of Transformers."
|
| 185 |
+
)
|
| 186 |
+
if config is None:
|
| 187 |
+
raise ValueError("config cannot be None")
|
| 188 |
+
|
| 189 |
+
if module is None:
|
| 190 |
+
raise ValueError("module cannot be None")
|
| 191 |
+
|
| 192 |
+
# Those are private to be exposed as typed property on derived classes.
|
| 193 |
+
self._config = config
|
| 194 |
+
self._module = module
|
| 195 |
+
|
| 196 |
+
# Those are public as their type is generic to every derived classes.
|
| 197 |
+
self.key = PRNGKey(seed)
|
| 198 |
+
self.dtype = dtype
|
| 199 |
+
self.input_shape = input_shape
|
| 200 |
+
self.generation_config = GenerationConfig.from_model_config(config) if self.can_generate() else None
|
| 201 |
+
|
| 202 |
+
# To check if the model was initialized automatically.
|
| 203 |
+
self._is_initialized = _do_init
|
| 204 |
+
|
| 205 |
+
if _do_init:
|
| 206 |
+
# randomly initialized parameters
|
| 207 |
+
random_params = self.init_weights(self.key, input_shape)
|
| 208 |
+
params_shape_tree = jax.eval_shape(lambda params: params, random_params)
|
| 209 |
+
else:
|
| 210 |
+
init_fn = partial(self.init_weights, input_shape=input_shape)
|
| 211 |
+
params_shape_tree = jax.eval_shape(init_fn, self.key)
|
| 212 |
+
|
| 213 |
+
logger.info(
|
| 214 |
+
"Model weights are not initialized as `_do_init` is set to `False`. "
|
| 215 |
+
f"Make sure to call `{self.__class__.__name__}.init_weights` manually to initialize the weights."
|
| 216 |
+
)
|
| 217 |
+
|
| 218 |
+
# get the shape of the parameters
|
| 219 |
+
self._params_shape_tree = params_shape_tree
|
| 220 |
+
|
| 221 |
+
# save required_params as set
|
| 222 |
+
self._required_params = set(flatten_dict(unfreeze(params_shape_tree)).keys())
|
| 223 |
+
|
| 224 |
+
# initialize the parameters
|
| 225 |
+
if _do_init:
|
| 226 |
+
self.params = random_params
|
| 227 |
+
|
| 228 |
+
def init_weights(self, rng: jax.random.PRNGKey, input_shape: tuple, params: FrozenDict = None) -> dict:
|
| 229 |
+
raise NotImplementedError(f"init method has to be implemented for {self}")
|
| 230 |
+
|
| 231 |
+
def enable_gradient_checkpointing(self):
|
| 232 |
+
raise NotImplementedError(f"gradient checkpointing method has to be implemented for {self}")
|
| 233 |
+
|
| 234 |
+
@classmethod
|
| 235 |
+
def _from_config(cls, config, **kwargs):
|
| 236 |
+
"""
|
| 237 |
+
All context managers that the model should be initialized under go here.
|
| 238 |
+
"""
|
| 239 |
+
return cls(config, **kwargs)
|
| 240 |
+
|
| 241 |
+
@property
|
| 242 |
+
def framework(self) -> str:
|
| 243 |
+
"""
|
| 244 |
+
:str: Identifies that this is a Flax model.
|
| 245 |
+
"""
|
| 246 |
+
return "flax"
|
| 247 |
+
|
| 248 |
+
@property
|
| 249 |
+
def config(self) -> PretrainedConfig:
|
| 250 |
+
return self._config
|
| 251 |
+
|
| 252 |
+
@property
|
| 253 |
+
def module(self) -> nn.Module:
|
| 254 |
+
return self._module
|
| 255 |
+
|
| 256 |
+
@property
|
| 257 |
+
def params(self) -> Union[dict, FrozenDict]:
|
| 258 |
+
if not self._is_initialized:
|
| 259 |
+
raise ValueError(
|
| 260 |
+
"`params` cannot be accessed from model when the model is created with `_do_init=False`. "
|
| 261 |
+
"You must call `init_weights` manually and store the params outside of the model and "
|
| 262 |
+
"pass it explicitly where needed."
|
| 263 |
+
)
|
| 264 |
+
return self._params
|
| 265 |
+
|
| 266 |
+
@property
|
| 267 |
+
def required_params(self) -> set:
|
| 268 |
+
return self._required_params
|
| 269 |
+
|
| 270 |
+
@property
|
| 271 |
+
def params_shape_tree(self) -> dict:
|
| 272 |
+
return self._params_shape_tree
|
| 273 |
+
|
| 274 |
+
@params.setter
|
| 275 |
+
def params(self, params: Union[dict, FrozenDict]):
|
| 276 |
+
# don't set params if the model is not initialized
|
| 277 |
+
if not self._is_initialized:
|
| 278 |
+
raise ValueError(
|
| 279 |
+
"`params` cannot be set from model when the model is created with `_do_init=False`. "
|
| 280 |
+
"You store the params outside of the model."
|
| 281 |
+
)
|
| 282 |
+
|
| 283 |
+
if isinstance(params, FrozenDict):
|
| 284 |
+
params = unfreeze(params)
|
| 285 |
+
param_keys = set(flatten_dict(params).keys())
|
| 286 |
+
if len(self.required_params - param_keys) > 0:
|
| 287 |
+
raise ValueError(
|
| 288 |
+
"Some parameters are missing. Make sure that `params` include the following "
|
| 289 |
+
f"parameters {self.required_params - param_keys}"
|
| 290 |
+
)
|
| 291 |
+
self._params = params
|
| 292 |
+
|
| 293 |
+
def _cast_floating_to(self, params: Union[dict, FrozenDict], dtype: jnp.dtype, mask: Any = None) -> Any:
|
| 294 |
+
"""
|
| 295 |
+
Helper method to cast floating-point values of given parameter `PyTree` to given `dtype`.
|
| 296 |
+
"""
|
| 297 |
+
|
| 298 |
+
# taken from https://github.com/deepmind/jmp/blob/3a8318abc3292be38582794dbf7b094e6583b192/jmp/_src/policy.py#L27
|
| 299 |
+
def conditional_cast(param):
|
| 300 |
+
if isinstance(param, jnp.ndarray) and jnp.issubdtype(param.dtype, jnp.floating):
|
| 301 |
+
param = param.astype(dtype)
|
| 302 |
+
return param
|
| 303 |
+
|
| 304 |
+
if mask is None:
|
| 305 |
+
return jax.tree_util.tree_map(conditional_cast, params)
|
| 306 |
+
|
| 307 |
+
flat_params = flatten_dict(params)
|
| 308 |
+
flat_mask, _ = jax.tree_util.tree_flatten(mask)
|
| 309 |
+
|
| 310 |
+
for masked, key in zip(flat_mask, sorted(flat_params.keys())):
|
| 311 |
+
if masked:
|
| 312 |
+
flat_params[key] = conditional_cast(flat_params[key])
|
| 313 |
+
|
| 314 |
+
return unflatten_dict(flat_params)
|
| 315 |
+
|
| 316 |
+
def to_bf16(self, params: Union[dict, FrozenDict], mask: Any = None):
|
| 317 |
+
r"""
|
| 318 |
+
Cast the floating-point `params` to `jax.numpy.bfloat16`. This returns a new `params` tree and does not cast
|
| 319 |
+
the `params` in place.
|
| 320 |
+
|
| 321 |
+
This method can be used on TPU to explicitly convert the model parameters to bfloat16 precision to do full
|
| 322 |
+
half-precision training or to save weights in bfloat16 for inference in order to save memory and improve speed.
|
| 323 |
+
|
| 324 |
+
Arguments:
|
| 325 |
+
params (`Union[Dict, FrozenDict]`):
|
| 326 |
+
A `PyTree` of model parameters.
|
| 327 |
+
mask (`Union[Dict, FrozenDict]`):
|
| 328 |
+
A `PyTree` with same structure as the `params` tree. The leaves should be booleans, `True` for params
|
| 329 |
+
you want to cast, and should be `False` for those you want to skip.
|
| 330 |
+
|
| 331 |
+
Examples:
|
| 332 |
+
|
| 333 |
+
```python
|
| 334 |
+
>>> from transformers import FlaxBertModel
|
| 335 |
+
|
| 336 |
+
>>> # load model
|
| 337 |
+
>>> model = FlaxBertModel.from_pretrained("google-bert/bert-base-cased")
|
| 338 |
+
>>> # By default, the model parameters will be in fp32 precision, to cast these to bfloat16 precision
|
| 339 |
+
>>> model.params = model.to_bf16(model.params)
|
| 340 |
+
>>> # If you want don't want to cast certain parameters (for example layer norm bias and scale)
|
| 341 |
+
>>> # then pass the mask as follows
|
| 342 |
+
>>> from flax import traverse_util
|
| 343 |
+
|
| 344 |
+
>>> model = FlaxBertModel.from_pretrained("google-bert/bert-base-cased")
|
| 345 |
+
>>> flat_params = traverse_util.flatten_dict(model.params)
|
| 346 |
+
>>> mask = {
|
| 347 |
+
... path: (path[-2] != ("LayerNorm", "bias") and path[-2:] != ("LayerNorm", "scale"))
|
| 348 |
+
... for path in flat_params
|
| 349 |
+
... }
|
| 350 |
+
>>> mask = traverse_util.unflatten_dict(mask)
|
| 351 |
+
>>> model.params = model.to_bf16(model.params, mask)
|
| 352 |
+
```"""
|
| 353 |
+
return self._cast_floating_to(params, jnp.bfloat16, mask)
|
| 354 |
+
|
| 355 |
+
def to_fp32(self, params: Union[dict, FrozenDict], mask: Any = None):
|
| 356 |
+
r"""
|
| 357 |
+
Cast the floating-point `params` to `jax.numpy.float32`. This method can be used to explicitly convert the
|
| 358 |
+
model parameters to fp32 precision. This returns a new `params` tree and does not cast the `params` in place.
|
| 359 |
+
|
| 360 |
+
Arguments:
|
| 361 |
+
params (`Union[Dict, FrozenDict]`):
|
| 362 |
+
A `PyTree` of model parameters.
|
| 363 |
+
mask (`Union[Dict, FrozenDict]`):
|
| 364 |
+
A `PyTree` with same structure as the `params` tree. The leaves should be booleans, `True` for params
|
| 365 |
+
you want to cast, and should be `False` for those you want to skip
|
| 366 |
+
|
| 367 |
+
Examples:
|
| 368 |
+
|
| 369 |
+
```python
|
| 370 |
+
>>> from transformers import FlaxBertModel
|
| 371 |
+
|
| 372 |
+
>>> # Download model and configuration from huggingface.co
|
| 373 |
+
>>> model = FlaxBertModel.from_pretrained("google-bert/bert-base-cased")
|
| 374 |
+
>>> # By default, the model params will be in fp32, to illustrate the use of this method,
|
| 375 |
+
>>> # we'll first cast to fp16 and back to fp32
|
| 376 |
+
>>> model.params = model.to_f16(model.params)
|
| 377 |
+
>>> # now cast back to fp32
|
| 378 |
+
>>> model.params = model.to_fp32(model.params)
|
| 379 |
+
```"""
|
| 380 |
+
return self._cast_floating_to(params, jnp.float32, mask)
|
| 381 |
+
|
| 382 |
+
def to_fp16(self, params: Union[dict, FrozenDict], mask: Any = None):
|
| 383 |
+
r"""
|
| 384 |
+
Cast the floating-point `params` to `jax.numpy.float16`. This returns a new `params` tree and does not cast the
|
| 385 |
+
`params` in place.
|
| 386 |
+
|
| 387 |
+
This method can be used on GPU to explicitly convert the model parameters to float16 precision to do full
|
| 388 |
+
half-precision training or to save weights in float16 for inference in order to save memory and improve speed.
|
| 389 |
+
|
| 390 |
+
Arguments:
|
| 391 |
+
params (`Union[Dict, FrozenDict]`):
|
| 392 |
+
A `PyTree` of model parameters.
|
| 393 |
+
mask (`Union[Dict, FrozenDict]`):
|
| 394 |
+
A `PyTree` with same structure as the `params` tree. The leaves should be booleans, `True` for params
|
| 395 |
+
you want to cast, and should be `False` for those you want to skip
|
| 396 |
+
|
| 397 |
+
Examples:
|
| 398 |
+
|
| 399 |
+
```python
|
| 400 |
+
>>> from transformers import FlaxBertModel
|
| 401 |
+
|
| 402 |
+
>>> # load model
|
| 403 |
+
>>> model = FlaxBertModel.from_pretrained("google-bert/bert-base-cased")
|
| 404 |
+
>>> # By default, the model params will be in fp32, to cast these to float16
|
| 405 |
+
>>> model.params = model.to_fp16(model.params)
|
| 406 |
+
>>> # If you want don't want to cast certain parameters (for example layer norm bias and scale)
|
| 407 |
+
>>> # then pass the mask as follows
|
| 408 |
+
>>> from flax import traverse_util
|
| 409 |
+
|
| 410 |
+
>>> model = FlaxBertModel.from_pretrained("google-bert/bert-base-cased")
|
| 411 |
+
>>> flat_params = traverse_util.flatten_dict(model.params)
|
| 412 |
+
>>> mask = {
|
| 413 |
+
... path: (path[-2] != ("LayerNorm", "bias") and path[-2:] != ("LayerNorm", "scale"))
|
| 414 |
+
... for path in flat_params
|
| 415 |
+
... }
|
| 416 |
+
>>> mask = traverse_util.unflatten_dict(mask)
|
| 417 |
+
>>> model.params = model.to_fp16(model.params, mask)
|
| 418 |
+
```"""
|
| 419 |
+
return self._cast_floating_to(params, jnp.float16, mask)
|
| 420 |
+
|
| 421 |
+
@classmethod
|
| 422 |
+
def load_flax_weights(cls, resolved_archive_file):
|
| 423 |
+
try:
|
| 424 |
+
if resolved_archive_file.endswith(".safetensors"):
|
| 425 |
+
state = safe_load_file(resolved_archive_file)
|
| 426 |
+
state = unflatten_dict(state, sep=".")
|
| 427 |
+
else:
|
| 428 |
+
with open(resolved_archive_file, "rb") as state_f:
|
| 429 |
+
state = from_bytes(cls, state_f.read())
|
| 430 |
+
except (UnpicklingError, msgpack.exceptions.ExtraData) as e:
|
| 431 |
+
try:
|
| 432 |
+
with open(resolved_archive_file) as f:
|
| 433 |
+
if f.read().startswith("version"):
|
| 434 |
+
raise OSError(
|
| 435 |
+
"You seem to have cloned a repository without having git-lfs installed. Please"
|
| 436 |
+
" install git-lfs and run `git lfs install` followed by `git lfs pull` in the"
|
| 437 |
+
" folder you cloned."
|
| 438 |
+
)
|
| 439 |
+
else:
|
| 440 |
+
raise ValueError from e
|
| 441 |
+
except (UnicodeDecodeError, ValueError):
|
| 442 |
+
raise OSError(f"Unable to convert {resolved_archive_file} to Flax deserializable object. ")
|
| 443 |
+
|
| 444 |
+
return state
|
| 445 |
+
|
| 446 |
+
@classmethod
|
| 447 |
+
def load_flax_sharded_weights(cls, shard_files):
|
| 448 |
+
"""
|
| 449 |
+
This is the same as [`flax.serialization.from_bytes`]
|
| 450 |
+
(https:lax.readthedocs.io/en/latest/_modules/flax/serialization.html#from_bytes) but for a sharded checkpoint.
|
| 451 |
+
|
| 452 |
+
This load is performed efficiently: each checkpoint shard is loaded one by one in RAM and deleted after being
|
| 453 |
+
loaded in the model.
|
| 454 |
+
|
| 455 |
+
Args:
|
| 456 |
+
shard_files (`list[str]`:
|
| 457 |
+
The list of shard files to load.
|
| 458 |
+
|
| 459 |
+
Returns:
|
| 460 |
+
`Dict`: A nested dictionary of the model parameters, in the expected format for flax models : `{'model':
|
| 461 |
+
{'params': {'...'}}}`.
|
| 462 |
+
"""
|
| 463 |
+
|
| 464 |
+
# Load the index
|
| 465 |
+
state_sharded_dict = {}
|
| 466 |
+
|
| 467 |
+
for shard_file in shard_files:
|
| 468 |
+
# load using msgpack utils
|
| 469 |
+
try:
|
| 470 |
+
with open(shard_file, "rb") as state_f:
|
| 471 |
+
state = from_bytes(cls, state_f.read())
|
| 472 |
+
except (UnpicklingError, msgpack.exceptions.ExtraData) as e:
|
| 473 |
+
with open(shard_file) as f:
|
| 474 |
+
if f.read().startswith("version"):
|
| 475 |
+
raise OSError(
|
| 476 |
+
"You seem to have cloned a repository without having git-lfs installed. Please"
|
| 477 |
+
" install git-lfs and run `git lfs install` followed by `git lfs pull` in the"
|
| 478 |
+
" folder you cloned."
|
| 479 |
+
)
|
| 480 |
+
else:
|
| 481 |
+
raise ValueError from e
|
| 482 |
+
except (UnicodeDecodeError, ValueError):
|
| 483 |
+
raise OSError(f"Unable to convert {shard_file} to Flax deserializable object. ")
|
| 484 |
+
|
| 485 |
+
state = flatten_dict(state, sep="/")
|
| 486 |
+
state_sharded_dict.update(state)
|
| 487 |
+
del state
|
| 488 |
+
gc.collect()
|
| 489 |
+
|
| 490 |
+
# the state dict is unflattened to the match the format of model.params
|
| 491 |
+
return unflatten_dict(state_sharded_dict, sep="/")
|
| 492 |
+
|
| 493 |
+
@classmethod
|
| 494 |
+
def can_generate(cls) -> bool:
|
| 495 |
+
"""
|
| 496 |
+
Returns whether this model can generate sequences with `.generate()`. Returns:
|
| 497 |
+
`bool`: Whether this model can generate sequences with `.generate()`.
|
| 498 |
+
"""
|
| 499 |
+
# Detects whether `prepare_inputs_for_generation` has been overwritten, which is a requirement for generation.
|
| 500 |
+
# Alternatively, the model can also have a custom `generate` function.
|
| 501 |
+
if "GenerationMixin" in str(cls.prepare_inputs_for_generation) and "GenerationMixin" in str(cls.generate):
|
| 502 |
+
return False
|
| 503 |
+
return True
|
| 504 |
+
|
| 505 |
+
@classmethod
|
| 506 |
+
def from_pretrained(
|
| 507 |
+
cls,
|
| 508 |
+
pretrained_model_name_or_path: Union[str, os.PathLike],
|
| 509 |
+
dtype: jnp.dtype = jnp.float32,
|
| 510 |
+
*model_args,
|
| 511 |
+
config: Optional[Union[PretrainedConfig, str, os.PathLike]] = None,
|
| 512 |
+
cache_dir: Optional[Union[str, os.PathLike]] = None,
|
| 513 |
+
ignore_mismatched_sizes: bool = False,
|
| 514 |
+
force_download: bool = False,
|
| 515 |
+
local_files_only: bool = False,
|
| 516 |
+
token: Optional[Union[str, bool]] = None,
|
| 517 |
+
revision: str = "main",
|
| 518 |
+
**kwargs,
|
| 519 |
+
):
|
| 520 |
+
r"""
|
| 521 |
+
Instantiate a pretrained flax model from a pre-trained model configuration.
|
| 522 |
+
|
| 523 |
+
The warning *Weights from XXX not initialized from pretrained model* means that the weights of XXX do not come
|
| 524 |
+
pretrained with the rest of the model. It is up to you to train those weights with a downstream fine-tuning
|
| 525 |
+
task.
|
| 526 |
+
|
| 527 |
+
The warning *Weights from XXX not used in YYY* means that the layer XXX is not used by YYY, therefore those
|
| 528 |
+
weights are discarded.
|
| 529 |
+
|
| 530 |
+
Parameters:
|
| 531 |
+
pretrained_model_name_or_path (`str` or `os.PathLike`):
|
| 532 |
+
Can be either:
|
| 533 |
+
|
| 534 |
+
- A string, the *model id* of a pretrained model hosted inside a model repo on huggingface.co.
|
| 535 |
+
- A path to a *directory* containing model weights saved using
|
| 536 |
+
[`~FlaxPreTrainedModel.save_pretrained`], e.g., `./my_model_directory/`.
|
| 537 |
+
- A path or url to a *pt index checkpoint file* (e.g, `./tf_model/model.ckpt.index`). In this case,
|
| 538 |
+
`from_pt` should be set to `True`.
|
| 539 |
+
dtype (`jax.numpy.dtype`, *optional*, defaults to `jax.numpy.float32`):
|
| 540 |
+
The data type of the computation. Can be one of `jax.numpy.float32`, `jax.numpy.float16` (on GPUs) and
|
| 541 |
+
`jax.numpy.bfloat16` (on TPUs).
|
| 542 |
+
|
| 543 |
+
This can be used to enable mixed-precision training or half-precision inference on GPUs or TPUs. If
|
| 544 |
+
specified all the computation will be performed with the given `dtype`.
|
| 545 |
+
|
| 546 |
+
**Note that this only specifies the dtype of the computation and does not influence the dtype of model
|
| 547 |
+
parameters.**
|
| 548 |
+
|
| 549 |
+
If you wish to change the dtype of the model parameters, see [`~FlaxPreTrainedModel.to_fp16`] and
|
| 550 |
+
[`~FlaxPreTrainedModel.to_bf16`].
|
| 551 |
+
model_args (sequence of positional arguments, *optional*):
|
| 552 |
+
All remaining positional arguments will be passed to the underlying model's `__init__` method.
|
| 553 |
+
config (`Union[PretrainedConfig, str, os.PathLike]`, *optional*):
|
| 554 |
+
Can be either:
|
| 555 |
+
|
| 556 |
+
- an instance of a class derived from [`PretrainedConfig`],
|
| 557 |
+
- a string or path valid as input to [`~PretrainedConfig.from_pretrained`].
|
| 558 |
+
|
| 559 |
+
Configuration for the model to use instead of an automatically loaded configuration. Configuration can
|
| 560 |
+
be automatically loaded when:
|
| 561 |
+
|
| 562 |
+
- The model is a model provided by the library (loaded with the *model id* string of a pretrained
|
| 563 |
+
model).
|
| 564 |
+
- The model was saved using [`~PreTrainedModel.save_pretrained`] and is reloaded by supplying the
|
| 565 |
+
save directory.
|
| 566 |
+
- The model is loaded by supplying a local directory as `pretrained_model_name_or_path` and a
|
| 567 |
+
configuration JSON file named *config.json* is found in the directory.
|
| 568 |
+
cache_dir (`Union[str, os.PathLike]`, *optional*):
|
| 569 |
+
Path to a directory in which a downloaded pretrained model configuration should be cached if the
|
| 570 |
+
standard cache should not be used.
|
| 571 |
+
from_pt (`bool`, *optional*, defaults to `False`):
|
| 572 |
+
Load the model weights from a PyTorch checkpoint save file (see docstring of
|
| 573 |
+
`pretrained_model_name_or_path` argument).
|
| 574 |
+
ignore_mismatched_sizes (`bool`, *optional*, defaults to `False`):
|
| 575 |
+
Whether or not to raise an error if some of the weights from the checkpoint do not have the same size
|
| 576 |
+
as the weights of the model (if for instance, you are instantiating a model with 10 labels from a
|
| 577 |
+
checkpoint with 3 labels).
|
| 578 |
+
force_download (`bool`, *optional*, defaults to `False`):
|
| 579 |
+
Whether or not to force the (re-)download of the model weights and configuration files, overriding the
|
| 580 |
+
cached versions if they exist.
|
| 581 |
+
resume_download:
|
| 582 |
+
Deprecated and ignored. All downloads are now resumed by default when possible.
|
| 583 |
+
Will be removed in v5 of Transformers.
|
| 584 |
+
proxies (`dict[str, str]`, *optional*):
|
| 585 |
+
A dictionary of proxy servers to use by protocol or endpoint, e.g., `{'http': 'foo.bar:3128',
|
| 586 |
+
'http://hostname': 'foo.bar:4012'}`. The proxies are used on each request.
|
| 587 |
+
local_files_only(`bool`, *optional*, defaults to `False`):
|
| 588 |
+
Whether or not to only look at local files (i.e., do not try to download the model).
|
| 589 |
+
token (`str` or `bool`, *optional*):
|
| 590 |
+
The token to use as HTTP bearer authorization for remote files. If `True`, or not specified, will use
|
| 591 |
+
the token generated when running `hf auth login` (stored in `~/.huggingface`).
|
| 592 |
+
revision (`str`, *optional*, defaults to `"main"`):
|
| 593 |
+
The specific model version to use. It can be a branch name, a tag name, or a commit id, since we use a
|
| 594 |
+
git-based system for storing models and other artifacts on huggingface.co, so `revision` can be any
|
| 595 |
+
identifier allowed by git.
|
| 596 |
+
|
| 597 |
+
|
| 598 |
+
<Tip>
|
| 599 |
+
|
| 600 |
+
To test a pull request you made on the Hub, you can pass `revision="refs/pr/<pr_number>"`.
|
| 601 |
+
|
| 602 |
+
</Tip>
|
| 603 |
+
|
| 604 |
+
subfolder (`str`, *optional*, defaults to `""`):
|
| 605 |
+
In case the relevant files are located inside a subfolder of the model repo on huggingface.co, you can
|
| 606 |
+
specify the folder name here.
|
| 607 |
+
kwargs (remaining dictionary of keyword arguments, *optional*):
|
| 608 |
+
Can be used to update the configuration object (after it being loaded) and initiate the model (e.g.,
|
| 609 |
+
`output_attentions=True`). Behaves differently depending on whether a `config` is provided or
|
| 610 |
+
automatically loaded:
|
| 611 |
+
|
| 612 |
+
- If a configuration is provided with `config`, `**kwargs` will be directly passed to the
|
| 613 |
+
underlying model's `__init__` method (we assume all relevant updates to the configuration have
|
| 614 |
+
already been done)
|
| 615 |
+
- If a configuration is not provided, `kwargs` will be first passed to the configuration class
|
| 616 |
+
initialization function ([`~PretrainedConfig.from_pretrained`]). Each key of `kwargs` that
|
| 617 |
+
corresponds to a configuration attribute will be used to override said attribute with the
|
| 618 |
+
supplied `kwargs` value. Remaining keys that do not correspond to any configuration attribute
|
| 619 |
+
will be passed to the underlying model's `__init__` function.
|
| 620 |
+
|
| 621 |
+
Examples:
|
| 622 |
+
|
| 623 |
+
```python
|
| 624 |
+
>>> from transformers import BertConfig, FlaxBertModel
|
| 625 |
+
|
| 626 |
+
>>> # Download model and configuration from huggingface.co and cache.
|
| 627 |
+
>>> model = FlaxBertModel.from_pretrained("google-bert/bert-base-cased")
|
| 628 |
+
>>> # Model was saved using *save_pretrained('./test/saved_model/')* (for example purposes, not runnable).
|
| 629 |
+
>>> model = FlaxBertModel.from_pretrained("./test/saved_model/")
|
| 630 |
+
>>> # Loading from a PyTorch checkpoint file instead of a PyTorch model (slower, for example purposes, not runnable).
|
| 631 |
+
>>> config = BertConfig.from_json_file("./pt_model/config.json")
|
| 632 |
+
>>> model = FlaxBertModel.from_pretrained("./pt_model/pytorch_model.bin", from_pt=True, config=config)
|
| 633 |
+
```"""
|
| 634 |
+
from_pt = kwargs.pop("from_pt", False)
|
| 635 |
+
resume_download = kwargs.pop("resume_download", None)
|
| 636 |
+
proxies = kwargs.pop("proxies", None)
|
| 637 |
+
use_auth_token = kwargs.pop("use_auth_token", None)
|
| 638 |
+
trust_remote_code = kwargs.pop("trust_remote_code", None)
|
| 639 |
+
from_pipeline = kwargs.pop("_from_pipeline", None)
|
| 640 |
+
from_auto_class = kwargs.pop("_from_auto", False)
|
| 641 |
+
_do_init = kwargs.pop("_do_init", True)
|
| 642 |
+
subfolder = kwargs.pop("subfolder", "")
|
| 643 |
+
commit_hash = kwargs.pop("_commit_hash", None)
|
| 644 |
+
|
| 645 |
+
# Not relevant for Flax Models
|
| 646 |
+
_ = kwargs.pop("adapter_kwargs", None)
|
| 647 |
+
|
| 648 |
+
if use_auth_token is not None:
|
| 649 |
+
warnings.warn(
|
| 650 |
+
"The `use_auth_token` argument is deprecated and will be removed in v5 of Transformers. Please use `token` instead.",
|
| 651 |
+
FutureWarning,
|
| 652 |
+
)
|
| 653 |
+
if token is not None:
|
| 654 |
+
raise ValueError(
|
| 655 |
+
"`token` and `use_auth_token` are both specified. Please set only the argument `token`."
|
| 656 |
+
)
|
| 657 |
+
token = use_auth_token
|
| 658 |
+
|
| 659 |
+
if trust_remote_code is True:
|
| 660 |
+
logger.warning(
|
| 661 |
+
"The argument `trust_remote_code` is to be used with Auto classes. It has no effect here and is"
|
| 662 |
+
" ignored."
|
| 663 |
+
)
|
| 664 |
+
|
| 665 |
+
user_agent = {"file_type": "model", "framework": "flax", "from_auto_class": from_auto_class}
|
| 666 |
+
if from_pipeline is not None:
|
| 667 |
+
user_agent["using_pipeline"] = from_pipeline
|
| 668 |
+
|
| 669 |
+
if is_offline_mode() and not local_files_only:
|
| 670 |
+
logger.info("Offline mode: forcing local_files_only=True")
|
| 671 |
+
local_files_only = True
|
| 672 |
+
|
| 673 |
+
# Load config if we don't provide a configuration
|
| 674 |
+
if not isinstance(config, PretrainedConfig):
|
| 675 |
+
config_path = config if config is not None else pretrained_model_name_or_path
|
| 676 |
+
config, model_kwargs = cls.config_class.from_pretrained(
|
| 677 |
+
config_path,
|
| 678 |
+
cache_dir=cache_dir,
|
| 679 |
+
return_unused_kwargs=True,
|
| 680 |
+
force_download=force_download,
|
| 681 |
+
resume_download=resume_download,
|
| 682 |
+
proxies=proxies,
|
| 683 |
+
local_files_only=local_files_only,
|
| 684 |
+
token=token,
|
| 685 |
+
revision=revision,
|
| 686 |
+
subfolder=subfolder,
|
| 687 |
+
_from_auto=from_auto_class,
|
| 688 |
+
_from_pipeline=from_pipeline,
|
| 689 |
+
_commit_hash=commit_hash,
|
| 690 |
+
**kwargs,
|
| 691 |
+
)
|
| 692 |
+
else:
|
| 693 |
+
model_kwargs = kwargs.copy()
|
| 694 |
+
|
| 695 |
+
if commit_hash is None:
|
| 696 |
+
commit_hash = getattr(config, "_commit_hash", None)
|
| 697 |
+
|
| 698 |
+
# Add the dtype to model_kwargs
|
| 699 |
+
model_kwargs["dtype"] = dtype
|
| 700 |
+
|
| 701 |
+
# This variable will flag if we're loading a sharded checkpoint. In this case the archive file is just the
|
| 702 |
+
# index of the files.
|
| 703 |
+
is_sharded = False
|
| 704 |
+
|
| 705 |
+
# Load model
|
| 706 |
+
if pretrained_model_name_or_path is not None:
|
| 707 |
+
pretrained_model_name_or_path = str(pretrained_model_name_or_path)
|
| 708 |
+
is_local = os.path.isdir(pretrained_model_name_or_path)
|
| 709 |
+
if is_local:
|
| 710 |
+
if os.path.isfile(os.path.join(pretrained_model_name_or_path, subfolder, FLAX_WEIGHTS_NAME)):
|
| 711 |
+
# Load from a Flax checkpoint
|
| 712 |
+
archive_file = os.path.join(pretrained_model_name_or_path, subfolder, FLAX_WEIGHTS_NAME)
|
| 713 |
+
elif os.path.isfile(os.path.join(pretrained_model_name_or_path, subfolder, FLAX_WEIGHTS_INDEX_NAME)):
|
| 714 |
+
# Load from a sharded Flax checkpoint
|
| 715 |
+
archive_file = os.path.join(pretrained_model_name_or_path, subfolder, FLAX_WEIGHTS_INDEX_NAME)
|
| 716 |
+
is_sharded = True
|
| 717 |
+
elif is_safetensors_available() and os.path.isfile(
|
| 718 |
+
os.path.join(pretrained_model_name_or_path, subfolder, SAFE_WEIGHTS_NAME)
|
| 719 |
+
):
|
| 720 |
+
# Load from a safetensors checkpoint
|
| 721 |
+
archive_file = os.path.join(pretrained_model_name_or_path, subfolder, SAFE_WEIGHTS_NAME)
|
| 722 |
+
elif is_safetensors_available() and os.path.isfile(
|
| 723 |
+
os.path.join(pretrained_model_name_or_path, SAFE_WEIGHTS_NAME)
|
| 724 |
+
):
|
| 725 |
+
# Load from a safetensors checkpoint
|
| 726 |
+
archive_file = os.path.join(pretrained_model_name_or_path, SAFE_WEIGHTS_NAME)
|
| 727 |
+
elif from_pt and os.path.isfile(os.path.join(pretrained_model_name_or_path, subfolder, WEIGHTS_NAME)):
|
| 728 |
+
# Load from a PyTorch checkpoint
|
| 729 |
+
archive_file = os.path.join(pretrained_model_name_or_path, subfolder, WEIGHTS_NAME)
|
| 730 |
+
elif from_pt and os.path.isfile(
|
| 731 |
+
os.path.join(pretrained_model_name_or_path, subfolder, WEIGHTS_INDEX_NAME)
|
| 732 |
+
):
|
| 733 |
+
# Load from a sharded pytorch checkpoint
|
| 734 |
+
archive_file = os.path.join(pretrained_model_name_or_path, subfolder, WEIGHTS_INDEX_NAME)
|
| 735 |
+
is_sharded = True
|
| 736 |
+
# At this stage we don't have a weight file so we will raise an error.
|
| 737 |
+
elif is_safetensors_available() and os.path.isfile(
|
| 738 |
+
os.path.join(pretrained_model_name_or_path, SAFE_WEIGHTS_INDEX_NAME)
|
| 739 |
+
):
|
| 740 |
+
# Load from a sharded safetensors checkpoint
|
| 741 |
+
archive_file = os.path.join(pretrained_model_name_or_path, SAFE_WEIGHTS_INDEX_NAME)
|
| 742 |
+
is_sharded = True
|
| 743 |
+
raise NotImplementedError("Support for sharded checkpoints using safetensors is coming soon!")
|
| 744 |
+
elif os.path.isfile(os.path.join(pretrained_model_name_or_path, subfolder, WEIGHTS_NAME)):
|
| 745 |
+
raise OSError(
|
| 746 |
+
f"Error no file named {FLAX_WEIGHTS_NAME} found in directory {pretrained_model_name_or_path} "
|
| 747 |
+
"but there is a file for PyTorch weights. Use `from_pt=True` to load this model from those "
|
| 748 |
+
"weights."
|
| 749 |
+
)
|
| 750 |
+
else:
|
| 751 |
+
raise OSError(
|
| 752 |
+
f"Error no file named {FLAX_WEIGHTS_NAME} or {WEIGHTS_NAME} found in directory "
|
| 753 |
+
f"{pretrained_model_name_or_path}."
|
| 754 |
+
)
|
| 755 |
+
elif os.path.isfile(os.path.join(subfolder, pretrained_model_name_or_path)):
|
| 756 |
+
archive_file = pretrained_model_name_or_path
|
| 757 |
+
is_local = True
|
| 758 |
+
elif is_remote_url(pretrained_model_name_or_path):
|
| 759 |
+
filename = pretrained_model_name_or_path
|
| 760 |
+
resolved_archive_file = download_url(pretrained_model_name_or_path)
|
| 761 |
+
else:
|
| 762 |
+
if from_pt:
|
| 763 |
+
filename = WEIGHTS_NAME
|
| 764 |
+
else:
|
| 765 |
+
filename = FLAX_WEIGHTS_NAME
|
| 766 |
+
|
| 767 |
+
try:
|
| 768 |
+
# Load from URL or cache if already cached
|
| 769 |
+
cached_file_kwargs = {
|
| 770 |
+
"cache_dir": cache_dir,
|
| 771 |
+
"force_download": force_download,
|
| 772 |
+
"proxies": proxies,
|
| 773 |
+
"resume_download": resume_download,
|
| 774 |
+
"local_files_only": local_files_only,
|
| 775 |
+
"token": token,
|
| 776 |
+
"user_agent": user_agent,
|
| 777 |
+
"revision": revision,
|
| 778 |
+
"subfolder": subfolder,
|
| 779 |
+
"_raise_exceptions_for_gated_repo": False,
|
| 780 |
+
"_raise_exceptions_for_missing_entries": False,
|
| 781 |
+
"_commit_hash": commit_hash,
|
| 782 |
+
}
|
| 783 |
+
resolved_archive_file = cached_file(pretrained_model_name_or_path, filename, **cached_file_kwargs)
|
| 784 |
+
|
| 785 |
+
# Maybe the checkpoint is sharded, we try to grab the index name in this case.
|
| 786 |
+
if resolved_archive_file is None and filename == FLAX_WEIGHTS_NAME:
|
| 787 |
+
resolved_archive_file = cached_file(
|
| 788 |
+
pretrained_model_name_or_path, FLAX_WEIGHTS_INDEX_NAME, **cached_file_kwargs
|
| 789 |
+
)
|
| 790 |
+
if resolved_archive_file is not None:
|
| 791 |
+
is_sharded = True
|
| 792 |
+
|
| 793 |
+
# Maybe the checkpoint is pytorch sharded, we try to grab the pytorch index name in this case.
|
| 794 |
+
if resolved_archive_file is None and from_pt:
|
| 795 |
+
resolved_archive_file = cached_file(
|
| 796 |
+
pretrained_model_name_or_path, WEIGHTS_INDEX_NAME, **cached_file_kwargs
|
| 797 |
+
)
|
| 798 |
+
if resolved_archive_file is not None:
|
| 799 |
+
is_sharded = True
|
| 800 |
+
|
| 801 |
+
# If we still haven't found anything, look for `safetensors`.
|
| 802 |
+
if resolved_archive_file is None:
|
| 803 |
+
# No support for sharded safetensors yet, so we'll raise an error if that's all we find.
|
| 804 |
+
filename = SAFE_WEIGHTS_NAME
|
| 805 |
+
resolved_archive_file = cached_file(
|
| 806 |
+
pretrained_model_name_or_path, SAFE_WEIGHTS_NAME, **cached_file_kwargs
|
| 807 |
+
)
|
| 808 |
+
|
| 809 |
+
# Since we set _raise_exceptions_for_missing_entries=False, we don't get an exception but a None
|
| 810 |
+
# result when internet is up, the repo and revision exist, but the file does not.
|
| 811 |
+
if resolved_archive_file is None:
|
| 812 |
+
# Otherwise, maybe there is a TF or Torch model file. We try those to give a helpful error
|
| 813 |
+
# message.
|
| 814 |
+
has_file_kwargs = {
|
| 815 |
+
"revision": revision,
|
| 816 |
+
"proxies": proxies,
|
| 817 |
+
"token": token,
|
| 818 |
+
"cache_dir": cache_dir,
|
| 819 |
+
"local_files_only": local_files_only,
|
| 820 |
+
}
|
| 821 |
+
if has_file(pretrained_model_name_or_path, SAFE_WEIGHTS_INDEX_NAME, **has_file_kwargs):
|
| 822 |
+
is_sharded = True
|
| 823 |
+
raise NotImplementedError(
|
| 824 |
+
"Support for sharded checkpoints using safetensors is coming soon!"
|
| 825 |
+
)
|
| 826 |
+
elif has_file(pretrained_model_name_or_path, WEIGHTS_NAME, **has_file_kwargs):
|
| 827 |
+
raise OSError(
|
| 828 |
+
f"{pretrained_model_name_or_path} does not appear to have a file named"
|
| 829 |
+
f" {FLAX_WEIGHTS_NAME} but there is a file for PyTorch weights. Use `from_pt=True` to"
|
| 830 |
+
" load this model from those weights."
|
| 831 |
+
)
|
| 832 |
+
elif has_file(pretrained_model_name_or_path, WEIGHTS_INDEX_NAME, **has_file_kwargs):
|
| 833 |
+
raise OSError(
|
| 834 |
+
f"{pretrained_model_name_or_path} does not appear to have a file named"
|
| 835 |
+
f" {FLAX_WEIGHTS_INDEX_NAME} but there is a sharded file for PyTorch weights. Use"
|
| 836 |
+
" `from_pt=True` to load this model from those weights."
|
| 837 |
+
)
|
| 838 |
+
else:
|
| 839 |
+
raise OSError(
|
| 840 |
+
f"{pretrained_model_name_or_path} does not appear to have a file named"
|
| 841 |
+
f" {FLAX_WEIGHTS_NAME} or {WEIGHTS_NAME}."
|
| 842 |
+
)
|
| 843 |
+
except OSError:
|
| 844 |
+
# Raise any environment error raise by `cached_file`. It will have a helpful error message adapted
|
| 845 |
+
# to the original exception.
|
| 846 |
+
raise
|
| 847 |
+
except Exception:
|
| 848 |
+
# For any other exception, we throw a generic error.
|
| 849 |
+
raise OSError(
|
| 850 |
+
f"Can't load the model for '{pretrained_model_name_or_path}'. If you were trying to load it"
|
| 851 |
+
" from 'https://huggingface.co/models', make sure you don't have a local directory with the"
|
| 852 |
+
f" same name. Otherwise, make sure '{pretrained_model_name_or_path}' is the correct path to a"
|
| 853 |
+
f" directory containing a file named {FLAX_WEIGHTS_NAME} or {WEIGHTS_NAME}."
|
| 854 |
+
)
|
| 855 |
+
|
| 856 |
+
if is_local:
|
| 857 |
+
logger.info(f"loading weights file {archive_file}")
|
| 858 |
+
resolved_archive_file = archive_file
|
| 859 |
+
filename = resolved_archive_file.split(os.path.sep)[-1]
|
| 860 |
+
else:
|
| 861 |
+
logger.info(f"loading weights file {filename} from cache at {resolved_archive_file}")
|
| 862 |
+
else:
|
| 863 |
+
resolved_archive_file = None
|
| 864 |
+
|
| 865 |
+
# We'll need to download and cache each checkpoint shard if the checkpoint is sharded.
|
| 866 |
+
if is_sharded:
|
| 867 |
+
# resolved_archive_file becomes a list of files that point to the different checkpoint shards in this case.
|
| 868 |
+
resolved_archive_file, _ = get_checkpoint_shard_files(
|
| 869 |
+
pretrained_model_name_or_path,
|
| 870 |
+
resolved_archive_file,
|
| 871 |
+
cache_dir=cache_dir,
|
| 872 |
+
force_download=force_download,
|
| 873 |
+
proxies=proxies,
|
| 874 |
+
resume_download=resume_download,
|
| 875 |
+
local_files_only=local_files_only,
|
| 876 |
+
token=token,
|
| 877 |
+
user_agent=user_agent,
|
| 878 |
+
revision=revision,
|
| 879 |
+
subfolder=subfolder,
|
| 880 |
+
_commit_hash=commit_hash,
|
| 881 |
+
)
|
| 882 |
+
|
| 883 |
+
safetensors_from_pt = False
|
| 884 |
+
if filename == SAFE_WEIGHTS_NAME:
|
| 885 |
+
with safe_open(resolved_archive_file, framework="flax") as f:
|
| 886 |
+
safetensors_metadata = f.metadata()
|
| 887 |
+
if safetensors_metadata is None or safetensors_metadata.get("format") not in ["pt", "tf", "flax"]:
|
| 888 |
+
raise OSError(
|
| 889 |
+
f"The safetensors archive passed at {resolved_archive_file} does not contain the valid metadata."
|
| 890 |
+
" Make sure you save your model with the `save_pretrained` method."
|
| 891 |
+
)
|
| 892 |
+
safetensors_from_pt = safetensors_metadata.get("format") == "pt"
|
| 893 |
+
|
| 894 |
+
# init random models
|
| 895 |
+
model = cls(config, *model_args, _do_init=_do_init, **model_kwargs)
|
| 896 |
+
|
| 897 |
+
if from_pt or safetensors_from_pt:
|
| 898 |
+
state = load_pytorch_checkpoint_in_flax_state_dict(model, resolved_archive_file, is_sharded)
|
| 899 |
+
else:
|
| 900 |
+
if is_sharded:
|
| 901 |
+
state = cls.load_flax_sharded_weights(resolved_archive_file)
|
| 902 |
+
else:
|
| 903 |
+
state = cls.load_flax_weights(resolved_archive_file)
|
| 904 |
+
# make sure all arrays are stored as jnp.arrays
|
| 905 |
+
# NOTE: This is to prevent a bug this will be fixed in Flax >= v0.3.4:
|
| 906 |
+
# https://github.com/google/flax/issues/1261
|
| 907 |
+
if _do_init:
|
| 908 |
+
state = jax.tree_util.tree_map(jnp.array, state)
|
| 909 |
+
else:
|
| 910 |
+
# keep the params on CPU if we don't want to initialize
|
| 911 |
+
state = jax.tree_util.tree_map(lambda x: jax.device_put(x, jax.local_devices(backend="cpu")[0]), state)
|
| 912 |
+
|
| 913 |
+
if "batch_stats" in state: # if flax model contains batch norm layers
|
| 914 |
+
# if model is base model only use model_prefix key
|
| 915 |
+
if (
|
| 916 |
+
cls.base_model_prefix not in dict(model.params_shape_tree["params"])
|
| 917 |
+
and cls.base_model_prefix in state["params"]
|
| 918 |
+
):
|
| 919 |
+
state["params"] = state["params"][cls.base_model_prefix]
|
| 920 |
+
state["batch_stats"] = state["batch_stats"][cls.base_model_prefix]
|
| 921 |
+
|
| 922 |
+
# if model is head model and we are loading weights from base model
|
| 923 |
+
# we initialize new params dict with base_model_prefix
|
| 924 |
+
if (
|
| 925 |
+
cls.base_model_prefix in dict(model.params_shape_tree["params"])
|
| 926 |
+
and cls.base_model_prefix not in state["params"]
|
| 927 |
+
):
|
| 928 |
+
state = {
|
| 929 |
+
"params": {cls.base_model_prefix: state["params"]},
|
| 930 |
+
"batch_stats": {cls.base_model_prefix: state["batch_stats"]},
|
| 931 |
+
}
|
| 932 |
+
|
| 933 |
+
else:
|
| 934 |
+
# if model is base model only use model_prefix key
|
| 935 |
+
if cls.base_model_prefix not in dict(model.params_shape_tree) and cls.base_model_prefix in state:
|
| 936 |
+
state = state[cls.base_model_prefix]
|
| 937 |
+
|
| 938 |
+
# if model is head model and we are loading weights from base model
|
| 939 |
+
# we initialize new params dict with base_model_prefix
|
| 940 |
+
if cls.base_model_prefix in dict(model.params_shape_tree) and cls.base_model_prefix not in state:
|
| 941 |
+
state = {cls.base_model_prefix: state}
|
| 942 |
+
|
| 943 |
+
# flatten dicts
|
| 944 |
+
state = flatten_dict(state)
|
| 945 |
+
|
| 946 |
+
random_state = flatten_dict(unfreeze(model.params if _do_init else model.params_shape_tree))
|
| 947 |
+
|
| 948 |
+
missing_keys = model.required_params - set(state.keys())
|
| 949 |
+
unexpected_keys = set(state.keys()) - model.required_params
|
| 950 |
+
|
| 951 |
+
# Disabling warning when porting pytorch weights to flax, flax does not uses num_batches_tracked
|
| 952 |
+
for unexpected_key in unexpected_keys.copy():
|
| 953 |
+
if "num_batches_tracked" in unexpected_key[-1]:
|
| 954 |
+
unexpected_keys.remove(unexpected_key)
|
| 955 |
+
|
| 956 |
+
if missing_keys and not _do_init:
|
| 957 |
+
logger.warning(
|
| 958 |
+
f"The checkpoint {pretrained_model_name_or_path} is missing required keys: {missing_keys}. "
|
| 959 |
+
"Make sure to call model.init_weights to initialize the missing weights."
|
| 960 |
+
)
|
| 961 |
+
cls._missing_keys = missing_keys
|
| 962 |
+
|
| 963 |
+
# Mismatched keys contains tuples key/shape1/shape2 of weights in the checkpoint that have a shape not
|
| 964 |
+
# matching the weights in the model.
|
| 965 |
+
mismatched_keys = []
|
| 966 |
+
for key in state:
|
| 967 |
+
if key in random_state and state[key].shape != random_state[key].shape:
|
| 968 |
+
if ignore_mismatched_sizes:
|
| 969 |
+
mismatched_keys.append((key, state[key].shape, random_state[key].shape))
|
| 970 |
+
state[key] = random_state[key]
|
| 971 |
+
else:
|
| 972 |
+
raise ValueError(
|
| 973 |
+
f"Trying to load the pretrained weight for {key} failed: checkpoint has shape "
|
| 974 |
+
f"{state[key].shape} which is incompatible with the model shape {random_state[key].shape}. "
|
| 975 |
+
"Using `ignore_mismatched_sizes=True` if you really want to load this checkpoint inside this "
|
| 976 |
+
"model."
|
| 977 |
+
)
|
| 978 |
+
|
| 979 |
+
# add missing keys as random parameters if we are initializing
|
| 980 |
+
if missing_keys and _do_init:
|
| 981 |
+
for missing_key in missing_keys:
|
| 982 |
+
state[missing_key] = random_state[missing_key]
|
| 983 |
+
|
| 984 |
+
# remove unexpected keys to not be saved again
|
| 985 |
+
for unexpected_key in unexpected_keys:
|
| 986 |
+
del state[unexpected_key]
|
| 987 |
+
|
| 988 |
+
if len(unexpected_keys) > 0:
|
| 989 |
+
logger.warning(
|
| 990 |
+
f"Some weights of the model checkpoint at {pretrained_model_name_or_path} were not used when"
|
| 991 |
+
f" initializing {model.__class__.__name__}: {unexpected_keys}\n- This IS expected if you are"
|
| 992 |
+
f" initializing {model.__class__.__name__} from the checkpoint of a model trained on another task or"
|
| 993 |
+
" with another architecture (e.g. initializing a BertForSequenceClassification model from a"
|
| 994 |
+
" BertForPreTraining model).\n- This IS NOT expected if you are initializing"
|
| 995 |
+
f" {model.__class__.__name__} from the checkpoint of a model that you expect to be exactly identical"
|
| 996 |
+
" (initializing a BertForSequenceClassification model from a BertForSequenceClassification model)."
|
| 997 |
+
)
|
| 998 |
+
else:
|
| 999 |
+
logger.info(f"All model checkpoint weights were used when initializing {model.__class__.__name__}.\n")
|
| 1000 |
+
|
| 1001 |
+
if len(missing_keys) > 0:
|
| 1002 |
+
logger.warning(
|
| 1003 |
+
f"Some weights of {model.__class__.__name__} were not initialized from the model checkpoint at"
|
| 1004 |
+
f" {pretrained_model_name_or_path} and are newly initialized: {missing_keys}\nYou should probably"
|
| 1005 |
+
" TRAIN this model on a down-stream task to be able to use it for predictions and inference."
|
| 1006 |
+
)
|
| 1007 |
+
elif len(mismatched_keys) == 0:
|
| 1008 |
+
logger.info(
|
| 1009 |
+
f"All the weights of {model.__class__.__name__} were initialized from the model checkpoint at"
|
| 1010 |
+
f" {pretrained_model_name_or_path}.\nIf your task is similar to the task the model of the checkpoint"
|
| 1011 |
+
f" was trained on, you can already use {model.__class__.__name__} for predictions without further"
|
| 1012 |
+
" training."
|
| 1013 |
+
)
|
| 1014 |
+
if len(mismatched_keys) > 0:
|
| 1015 |
+
mismatched_warning = "\n".join(
|
| 1016 |
+
[
|
| 1017 |
+
f"- {key}: found shape {shape1} in the checkpoint and {shape2} in the model instantiated"
|
| 1018 |
+
for key, shape1, shape2 in mismatched_keys
|
| 1019 |
+
]
|
| 1020 |
+
)
|
| 1021 |
+
logger.warning(
|
| 1022 |
+
f"Some weights of {model.__class__.__name__} were not initialized from the model checkpoint at"
|
| 1023 |
+
f" {pretrained_model_name_or_path} and are newly initialized because the shapes did not"
|
| 1024 |
+
f" match:\n{mismatched_warning}\nYou should probably TRAIN this model on a down-stream task to be able"
|
| 1025 |
+
" to use it for predictions and inference."
|
| 1026 |
+
)
|
| 1027 |
+
|
| 1028 |
+
# dictionary of key: dtypes for the model params
|
| 1029 |
+
param_dtypes = jax.tree_util.tree_map(lambda x: x.dtype, state)
|
| 1030 |
+
# extract keys of parameters not in jnp.float32
|
| 1031 |
+
fp16_params = [k for k in param_dtypes if param_dtypes[k] == jnp.float16]
|
| 1032 |
+
bf16_params = [k for k in param_dtypes if param_dtypes[k] == jnp.bfloat16]
|
| 1033 |
+
|
| 1034 |
+
# raise a warning if any of the parameters are not in jnp.float32
|
| 1035 |
+
if len(fp16_params) > 0:
|
| 1036 |
+
logger.warning(
|
| 1037 |
+
f"Some of the weights of {model.__class__.__name__} were initialized in float16 precision from "
|
| 1038 |
+
f"the model checkpoint at {pretrained_model_name_or_path}:\n{fp16_params}\n"
|
| 1039 |
+
"You should probably UPCAST the model weights to float32 if this was not intended. "
|
| 1040 |
+
"See [`~FlaxPreTrainedModel.to_fp32`] for further information on how to do this."
|
| 1041 |
+
)
|
| 1042 |
+
|
| 1043 |
+
if len(bf16_params) > 0:
|
| 1044 |
+
logger.warning(
|
| 1045 |
+
f"Some of the weights of {model.__class__.__name__} were initialized in bfloat16 precision from "
|
| 1046 |
+
f"the model checkpoint at {pretrained_model_name_or_path}:\n{bf16_params}\n"
|
| 1047 |
+
"You should probably UPCAST the model weights to float32 if this was not intended. "
|
| 1048 |
+
"See [`~FlaxPreTrainedModel.to_fp32`] for further information on how to do this."
|
| 1049 |
+
)
|
| 1050 |
+
|
| 1051 |
+
# If it is a model with generation capabilities, attempt to load the generation config
|
| 1052 |
+
if model.can_generate():
|
| 1053 |
+
try:
|
| 1054 |
+
model.generation_config = GenerationConfig.from_pretrained(
|
| 1055 |
+
pretrained_model_name_or_path,
|
| 1056 |
+
cache_dir=cache_dir,
|
| 1057 |
+
force_download=force_download,
|
| 1058 |
+
resume_download=resume_download,
|
| 1059 |
+
proxies=proxies,
|
| 1060 |
+
local_files_only=local_files_only,
|
| 1061 |
+
token=token,
|
| 1062 |
+
revision=revision,
|
| 1063 |
+
subfolder=subfolder,
|
| 1064 |
+
_from_auto=from_auto_class,
|
| 1065 |
+
_from_pipeline=from_pipeline,
|
| 1066 |
+
**kwargs,
|
| 1067 |
+
)
|
| 1068 |
+
except OSError:
|
| 1069 |
+
logger.info(
|
| 1070 |
+
"Generation config file not found, using a generation config created from the model config."
|
| 1071 |
+
)
|
| 1072 |
+
pass
|
| 1073 |
+
|
| 1074 |
+
if _do_init:
|
| 1075 |
+
# set correct parameters
|
| 1076 |
+
model.params = unflatten_dict(state)
|
| 1077 |
+
return model
|
| 1078 |
+
else:
|
| 1079 |
+
return model, unflatten_dict(state)
|
| 1080 |
+
|
| 1081 |
+
def save_pretrained(
|
| 1082 |
+
self,
|
| 1083 |
+
save_directory: Union[str, os.PathLike],
|
| 1084 |
+
params=None,
|
| 1085 |
+
push_to_hub=False,
|
| 1086 |
+
max_shard_size="10GB",
|
| 1087 |
+
token: Optional[Union[str, bool]] = None,
|
| 1088 |
+
safe_serialization: bool = False,
|
| 1089 |
+
**kwargs,
|
| 1090 |
+
):
|
| 1091 |
+
"""
|
| 1092 |
+
Save a model and its configuration file to a directory, so that it can be re-loaded using the
|
| 1093 |
+
`[`~FlaxPreTrainedModel.from_pretrained`]` class method
|
| 1094 |
+
|
| 1095 |
+
Arguments:
|
| 1096 |
+
save_directory (`str` or `os.PathLike`):
|
| 1097 |
+
Directory to which to save. Will be created if it doesn't exist.
|
| 1098 |
+
push_to_hub (`bool`, *optional*, defaults to `False`):
|
| 1099 |
+
Whether or not to push your model to the Hugging Face model hub after saving it. You can specify the
|
| 1100 |
+
repository you want to push to with `repo_id` (will default to the name of `save_directory` in your
|
| 1101 |
+
namespace).
|
| 1102 |
+
max_shard_size (`int` or `str`, *optional*, defaults to `"10GB"`):
|
| 1103 |
+
The maximum size for a checkpoint before being sharded. Checkpoints shard will then be each of size
|
| 1104 |
+
lower than this size. If expressed as a string, needs to be digits followed by a unit (like `"5MB"`).
|
| 1105 |
+
|
| 1106 |
+
<Tip warning={true}>
|
| 1107 |
+
|
| 1108 |
+
If a single weight of the model is bigger than `max_shard_size`, it will be in its own checkpoint shard
|
| 1109 |
+
which will be bigger than `max_shard_size`.
|
| 1110 |
+
|
| 1111 |
+
</Tip>
|
| 1112 |
+
|
| 1113 |
+
token (`str` or `bool`, *optional*):
|
| 1114 |
+
The token to use as HTTP bearer authorization for remote files. If `True`, or not specified, will use
|
| 1115 |
+
the token generated when running `hf auth login` (stored in `~/.huggingface`).
|
| 1116 |
+
kwargs (`dict[str, Any]`, *optional*):
|
| 1117 |
+
Additional key word arguments passed along to the [`~utils.PushToHubMixin.push_to_hub`] method.
|
| 1118 |
+
safe_serialization (`bool`, *optional*, defaults to `False`):
|
| 1119 |
+
Whether to save the model using `safetensors` or through msgpack.
|
| 1120 |
+
"""
|
| 1121 |
+
use_auth_token = kwargs.pop("use_auth_token", None)
|
| 1122 |
+
|
| 1123 |
+
if use_auth_token is not None:
|
| 1124 |
+
warnings.warn(
|
| 1125 |
+
"The `use_auth_token` argument is deprecated and will be removed in v5 of Transformers. Please use `token` instead.",
|
| 1126 |
+
FutureWarning,
|
| 1127 |
+
)
|
| 1128 |
+
if token is not None:
|
| 1129 |
+
raise ValueError(
|
| 1130 |
+
"`token` and `use_auth_token` are both specified. Please set only the argument `token`."
|
| 1131 |
+
)
|
| 1132 |
+
token = use_auth_token
|
| 1133 |
+
|
| 1134 |
+
if token is not None:
|
| 1135 |
+
kwargs["token"] = token
|
| 1136 |
+
|
| 1137 |
+
if os.path.isfile(save_directory):
|
| 1138 |
+
logger.error(f"Provided path ({save_directory}) should be a directory, not a file")
|
| 1139 |
+
return
|
| 1140 |
+
|
| 1141 |
+
os.makedirs(save_directory, exist_ok=True)
|
| 1142 |
+
|
| 1143 |
+
if push_to_hub:
|
| 1144 |
+
commit_message = kwargs.pop("commit_message", None)
|
| 1145 |
+
repo_id = kwargs.pop("repo_id", save_directory.split(os.path.sep)[-1])
|
| 1146 |
+
repo_id = self._create_repo(repo_id, **kwargs)
|
| 1147 |
+
files_timestamps = self._get_files_timestamps(save_directory)
|
| 1148 |
+
|
| 1149 |
+
# get abs dir
|
| 1150 |
+
save_directory = os.path.abspath(save_directory)
|
| 1151 |
+
# save config as well
|
| 1152 |
+
self.config.architectures = [self.__class__.__name__[4:]]
|
| 1153 |
+
|
| 1154 |
+
# If we have a custom model, we copy the file defining it in the folder and set the attributes so it can be
|
| 1155 |
+
# loaded from the Hub.
|
| 1156 |
+
if self._auto_class is not None:
|
| 1157 |
+
custom_object_save(self, save_directory, config=self.config)
|
| 1158 |
+
|
| 1159 |
+
self.config.save_pretrained(save_directory)
|
| 1160 |
+
if self.can_generate():
|
| 1161 |
+
self.generation_config.save_pretrained(save_directory)
|
| 1162 |
+
|
| 1163 |
+
# save model
|
| 1164 |
+
weights_name = SAFE_WEIGHTS_NAME if safe_serialization else FLAX_WEIGHTS_NAME
|
| 1165 |
+
output_model_file = os.path.join(save_directory, weights_name)
|
| 1166 |
+
|
| 1167 |
+
shards, index = flax_shard_checkpoint(params if params is not None else self.params, max_shard_size)
|
| 1168 |
+
# Clean the folder from a previous save
|
| 1169 |
+
for filename in os.listdir(save_directory):
|
| 1170 |
+
full_filename = os.path.join(save_directory, filename)
|
| 1171 |
+
weights_no_suffix = weights_name.replace(".bin", "").replace(".safetensors", "")
|
| 1172 |
+
if filename.startswith(weights_no_suffix) and os.path.isfile(full_filename) and filename not in shards:
|
| 1173 |
+
os.remove(full_filename)
|
| 1174 |
+
|
| 1175 |
+
if index is None:
|
| 1176 |
+
if safe_serialization:
|
| 1177 |
+
params = params if params is not None else self.params
|
| 1178 |
+
flat_dict = flatten_dict(params, sep=".")
|
| 1179 |
+
safe_save_file(flat_dict, output_model_file, metadata={"format": "flax"})
|
| 1180 |
+
else:
|
| 1181 |
+
with open(output_model_file, "wb") as f:
|
| 1182 |
+
params = params if params is not None else self.params
|
| 1183 |
+
model_bytes = to_bytes(params)
|
| 1184 |
+
f.write(model_bytes)
|
| 1185 |
+
|
| 1186 |
+
else:
|
| 1187 |
+
save_index_file = os.path.join(save_directory, FLAX_WEIGHTS_INDEX_NAME)
|
| 1188 |
+
# Save the index as well
|
| 1189 |
+
with open(save_index_file, "w", encoding="utf-8") as f:
|
| 1190 |
+
content = json.dumps(index, indent=2, sort_keys=True) + "\n"
|
| 1191 |
+
f.write(content)
|
| 1192 |
+
logger.info(
|
| 1193 |
+
f"The model is bigger than the maximum size per checkpoint ({max_shard_size}) and is going to be "
|
| 1194 |
+
f"split in {len(shards)} checkpoint shards. You can find where each parameters has been saved in the "
|
| 1195 |
+
f"index located at {save_index_file}."
|
| 1196 |
+
)
|
| 1197 |
+
for shard_file, shard in shards.items():
|
| 1198 |
+
# the shard item are unflattened, to save them we need to flatten them again
|
| 1199 |
+
with open(os.path.join(save_directory, shard_file), mode="wb") as f:
|
| 1200 |
+
params = unflatten_dict(shard, sep="/")
|
| 1201 |
+
shard_bytes = to_bytes(params)
|
| 1202 |
+
f.write(shard_bytes)
|
| 1203 |
+
|
| 1204 |
+
logger.info(f"Model weights saved in {output_model_file}")
|
| 1205 |
+
|
| 1206 |
+
if push_to_hub:
|
| 1207 |
+
self._upload_modified_files(
|
| 1208 |
+
save_directory,
|
| 1209 |
+
repo_id,
|
| 1210 |
+
files_timestamps,
|
| 1211 |
+
commit_message=commit_message,
|
| 1212 |
+
token=token,
|
| 1213 |
+
)
|
| 1214 |
+
|
| 1215 |
+
@classmethod
|
| 1216 |
+
def register_for_auto_class(cls, auto_class="FlaxAutoModel"):
|
| 1217 |
+
"""
|
| 1218 |
+
Register this class with a given auto class. This should only be used for custom models as the ones in the
|
| 1219 |
+
library are already mapped with an auto class.
|
| 1220 |
+
|
| 1221 |
+
|
| 1222 |
+
|
| 1223 |
+
Args:
|
| 1224 |
+
auto_class (`str` or `type`, *optional*, defaults to `"FlaxAutoModel"`):
|
| 1225 |
+
The auto class to register this new model with.
|
| 1226 |
+
"""
|
| 1227 |
+
if not isinstance(auto_class, str):
|
| 1228 |
+
auto_class = auto_class.__name__
|
| 1229 |
+
|
| 1230 |
+
import transformers.models.auto as auto_module
|
| 1231 |
+
|
| 1232 |
+
if not hasattr(auto_module, auto_class):
|
| 1233 |
+
raise ValueError(f"{auto_class} is not a valid auto class.")
|
| 1234 |
+
|
| 1235 |
+
cls._auto_class = auto_class
|
| 1236 |
+
|
| 1237 |
+
|
| 1238 |
+
# To update the docstring, we need to copy the method, otherwise we change the original docstring.
|
| 1239 |
+
FlaxPreTrainedModel.push_to_hub = copy_func(FlaxPreTrainedModel.push_to_hub)
|
| 1240 |
+
if FlaxPreTrainedModel.push_to_hub.__doc__ is not None:
|
| 1241 |
+
FlaxPreTrainedModel.push_to_hub.__doc__ = FlaxPreTrainedModel.push_to_hub.__doc__.format(
|
| 1242 |
+
object="model", object_class="FlaxAutoModel", object_files="model checkpoint"
|
| 1243 |
+
)
|
| 1244 |
+
|
| 1245 |
+
|
| 1246 |
+
def overwrite_call_docstring(model_class, docstring):
|
| 1247 |
+
# copy __call__ function to be sure docstring is changed only for this function
|
| 1248 |
+
model_class.__call__ = copy_func(model_class.__call__)
|
| 1249 |
+
# delete existing docstring
|
| 1250 |
+
model_class.__call__.__doc__ = None
|
| 1251 |
+
# set correct docstring
|
| 1252 |
+
model_class.__call__ = add_start_docstrings_to_model_forward(docstring)(model_class.__call__)
|
| 1253 |
+
|
| 1254 |
+
|
| 1255 |
+
def append_call_sample_docstring(
|
| 1256 |
+
model_class, checkpoint, output_type, config_class, mask=None, revision=None, real_checkpoint=None
|
| 1257 |
+
):
|
| 1258 |
+
model_class.__call__ = copy_func(model_class.__call__)
|
| 1259 |
+
model_class.__call__ = add_code_sample_docstrings(
|
| 1260 |
+
checkpoint=checkpoint,
|
| 1261 |
+
output_type=output_type,
|
| 1262 |
+
config_class=config_class,
|
| 1263 |
+
model_cls=model_class.__name__,
|
| 1264 |
+
revision=revision,
|
| 1265 |
+
real_checkpoint=real_checkpoint,
|
| 1266 |
+
)(model_class.__call__)
|
| 1267 |
+
|
| 1268 |
+
|
| 1269 |
+
def append_replace_return_docstrings(model_class, output_type, config_class):
|
| 1270 |
+
model_class.__call__ = copy_func(model_class.__call__)
|
| 1271 |
+
model_class.__call__ = replace_return_docstrings(
|
| 1272 |
+
output_type=output_type,
|
| 1273 |
+
config_class=config_class,
|
| 1274 |
+
)(model_class.__call__)
|
Prism/LLaDA/LLaDA_Prism/.venv/lib/python3.12/site-packages/transformers/modeling_gguf_pytorch_utils.py
ADDED
|
@@ -0,0 +1,532 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2024 The ggml.ai team and The HuggingFace Inc. team. and pygguf author (github.com/99991)
|
| 2 |
+
# https://github.com/99991/pygguf
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
# you may not use this file except in compliance with the License.
|
| 6 |
+
# You may obtain a copy of the License at
|
| 7 |
+
#
|
| 8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
#
|
| 10 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
# See the License for the specific language governing permissions and
|
| 14 |
+
# limitations under the License.
|
| 15 |
+
|
| 16 |
+
import re
|
| 17 |
+
from typing import NamedTuple, Optional
|
| 18 |
+
|
| 19 |
+
import numpy as np
|
| 20 |
+
from tqdm.auto import tqdm
|
| 21 |
+
|
| 22 |
+
from .integrations import (
|
| 23 |
+
GGUF_CONFIG_MAPPING,
|
| 24 |
+
GGUF_TOKENIZER_MAPPING,
|
| 25 |
+
_gguf_parse_value,
|
| 26 |
+
)
|
| 27 |
+
from .utils import is_torch_available
|
| 28 |
+
from .utils.import_utils import is_gguf_available
|
| 29 |
+
from .utils.logging import get_logger
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
if is_torch_available():
|
| 33 |
+
import torch
|
| 34 |
+
|
| 35 |
+
logger = get_logger(__name__)
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
GGUF_TO_TRANSFORMERS_MAPPING = {
|
| 39 |
+
"ignore": {
|
| 40 |
+
"GGUF": {
|
| 41 |
+
"version": "version",
|
| 42 |
+
"tensor_count": "tensor_count",
|
| 43 |
+
"kv_count": "kv_count",
|
| 44 |
+
},
|
| 45 |
+
"general": {"file_type": "file_type", "quantization_version": "quantization_version"},
|
| 46 |
+
},
|
| 47 |
+
"config": GGUF_CONFIG_MAPPING,
|
| 48 |
+
"tokenizer": {"tokenizer": GGUF_TOKENIZER_MAPPING["tokenizer"]},
|
| 49 |
+
"tokenizer_config": {"tokenizer": GGUF_TOKENIZER_MAPPING["tokenizer_config"]},
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
GGUF_SUPPORTED_ARCHITECTURES = list(GGUF_TO_TRANSFORMERS_MAPPING["config"].keys())
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
class GGUFTensor(NamedTuple):
|
| 56 |
+
weights: np.ndarray
|
| 57 |
+
name: str
|
| 58 |
+
metadata: dict
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
class TensorProcessor:
|
| 62 |
+
def __init__(self, config=None):
|
| 63 |
+
self.config = config or {}
|
| 64 |
+
|
| 65 |
+
def process(self, weights, name, **kwargs):
|
| 66 |
+
return GGUFTensor(weights, name, {})
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
class LlamaTensorProcessor(TensorProcessor):
|
| 70 |
+
def __init__(self, config=None):
|
| 71 |
+
super().__init__(config=config)
|
| 72 |
+
|
| 73 |
+
def process(self, weights, name, **kwargs):
|
| 74 |
+
if ".attn_k." in name or ".attn_q." in name:
|
| 75 |
+
num_heads = self.config.get("num_attention_heads")
|
| 76 |
+
num_kv_heads = self.config.get("num_key_value_heads")
|
| 77 |
+
|
| 78 |
+
if None in (num_heads, num_kv_heads):
|
| 79 |
+
return GGUFTensor(weights, name, {})
|
| 80 |
+
if ".attn_q." in name:
|
| 81 |
+
weights = self._reverse_permute_weights(weights, num_heads, num_heads)
|
| 82 |
+
elif ".attn_k." in name:
|
| 83 |
+
weights = self._reverse_permute_weights(weights, num_heads, num_kv_heads)
|
| 84 |
+
return GGUFTensor(weights, name, {})
|
| 85 |
+
|
| 86 |
+
def _reverse_permute_weights(
|
| 87 |
+
self, weights: np.ndarray, n_head: int, num_kv_heads: Optional[int] = None
|
| 88 |
+
) -> np.ndarray:
|
| 89 |
+
# Original permutation implementation
|
| 90 |
+
# https://github.com/ggerganov/llama.cpp/blob/a38b884c6c4b0c256583acfaaabdf556c62fabea/convert_hf_to_gguf.py#L1402-L1408
|
| 91 |
+
if num_kv_heads is not None and n_head != num_kv_heads:
|
| 92 |
+
n_head = num_kv_heads
|
| 93 |
+
|
| 94 |
+
dim = weights.shape[0] // n_head // 2
|
| 95 |
+
w = weights.reshape(n_head, dim, 2, *weights.shape[1:])
|
| 96 |
+
return w.swapaxes(2, 1).reshape(weights.shape)
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
class Qwen2MoeTensorProcessor(TensorProcessor):
|
| 100 |
+
def __init__(self, config=None):
|
| 101 |
+
super().__init__(config=config)
|
| 102 |
+
|
| 103 |
+
def process(self, weights, name, **kwargs):
|
| 104 |
+
if "_exp" in name:
|
| 105 |
+
tensor_key_mapping = kwargs.get("tensor_key_mapping")
|
| 106 |
+
parsed_parameters = kwargs.get("parsed_parameters")
|
| 107 |
+
if tensor_key_mapping:
|
| 108 |
+
self._split_moe_expert_tensor(weights, parsed_parameters, name, tensor_key_mapping)
|
| 109 |
+
return GGUFTensor(weights, None, {})
|
| 110 |
+
if "ffn_gate_inp_shexp" in name:
|
| 111 |
+
# for compatibility tensor shared_expert_gate must be (1, 2048) dim,
|
| 112 |
+
# quantized one is (2048)
|
| 113 |
+
weights = np.expand_dims(weights, axis=0)
|
| 114 |
+
return GGUFTensor(weights, name, {})
|
| 115 |
+
|
| 116 |
+
def _split_moe_expert_tensor(
|
| 117 |
+
self, weights: np.ndarray, parsed_parameters: dict[str, dict], name: str, tensor_key_mapping: dict
|
| 118 |
+
):
|
| 119 |
+
# Original merge implementation
|
| 120 |
+
# https://github.com/ggerganov/llama.cpp/blob/master/convert_hf_to_gguf.py#L1994-L2022
|
| 121 |
+
name = tensor_key_mapping[name]
|
| 122 |
+
w_counter = self.config.get("num_experts", 60)
|
| 123 |
+
for i in range(0, w_counter):
|
| 124 |
+
temp_name = name.replace("mlp.experts.", f"mlp.experts.{i}.")
|
| 125 |
+
exp_weight = weights[i]
|
| 126 |
+
parsed_parameters["tensors"][temp_name] = torch.from_numpy(np.copy(exp_weight))
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
class BloomTensorProcessor(TensorProcessor):
|
| 130 |
+
def __init__(self, config=None):
|
| 131 |
+
super().__init__(config=config)
|
| 132 |
+
|
| 133 |
+
def process(self, weights, name, **kwargs):
|
| 134 |
+
if "attn_qkv" in name:
|
| 135 |
+
num_heads = self.config["n_head"]
|
| 136 |
+
n_embed = self.config["hidden_size"]
|
| 137 |
+
if "weight" in name:
|
| 138 |
+
weights = self._reverse_reshape_weights(weights, num_heads, n_embed)
|
| 139 |
+
else:
|
| 140 |
+
weights = self._reverse_reshape_bias(weights, num_heads, n_embed)
|
| 141 |
+
return GGUFTensor(weights, name, {})
|
| 142 |
+
|
| 143 |
+
def _reverse_reshape_weights(self, weights: np.ndarray, n_head: int, n_embed: int):
|
| 144 |
+
# Original reshape implementation
|
| 145 |
+
# https://github.com/ggerganov/llama.cpp/blob/master/convert_hf_to_gguf.py#L972-L985
|
| 146 |
+
q, k, v = np.array_split(weights, 3, axis=0)
|
| 147 |
+
|
| 148 |
+
q = q.reshape(n_head, n_embed // n_head, n_embed)
|
| 149 |
+
k = k.reshape(n_head, n_embed // n_head, n_embed)
|
| 150 |
+
v = v.reshape(n_head, n_embed // n_head, n_embed)
|
| 151 |
+
qkv_weights = np.stack([q, k, v], axis=1)
|
| 152 |
+
|
| 153 |
+
return qkv_weights.reshape(n_head * 3 * (n_embed // n_head), n_embed)
|
| 154 |
+
|
| 155 |
+
def _reverse_reshape_bias(self, weights: np.ndarray, n_head: int, n_embed: int):
|
| 156 |
+
# Original reshape implementation
|
| 157 |
+
# https://github.com/ggerganov/llama.cpp/blob/master/convert_hf_to_gguf.py#L986-L998
|
| 158 |
+
q_bias, k_bias, v_bias = np.array_split(weights, 3)
|
| 159 |
+
|
| 160 |
+
q_bias = q_bias.reshape(n_head, n_embed // n_head)
|
| 161 |
+
k_bias = k_bias.reshape(n_head, n_embed // n_head)
|
| 162 |
+
v_bias = v_bias.reshape(n_head, n_embed // n_head)
|
| 163 |
+
|
| 164 |
+
qkv_bias = np.stack([q_bias, k_bias, v_bias], axis=1).flatten()
|
| 165 |
+
return qkv_bias
|
| 166 |
+
|
| 167 |
+
|
| 168 |
+
class T5TensorProcessor(TensorProcessor):
|
| 169 |
+
def __init__(self, config=None):
|
| 170 |
+
super().__init__(config=config)
|
| 171 |
+
|
| 172 |
+
def process(self, weights, name, **kwargs):
|
| 173 |
+
bid = None
|
| 174 |
+
for chunk in name.split("."):
|
| 175 |
+
if chunk.isdigit():
|
| 176 |
+
bid = int(chunk)
|
| 177 |
+
break
|
| 178 |
+
return GGUFTensor(weights, name, {"bid": bid})
|
| 179 |
+
|
| 180 |
+
|
| 181 |
+
class GPT2TensorProcessor(TensorProcessor):
|
| 182 |
+
def __init__(self, config=None):
|
| 183 |
+
super().__init__(config=config)
|
| 184 |
+
|
| 185 |
+
def process(self, weights, name, **kwargs):
|
| 186 |
+
# Original transpose implementation
|
| 187 |
+
# https://github.com/ggerganov/llama.cpp/blob/a38b884c6c4b0c256583acfaaabdf556c62fabea/convert_hf_to_gguf.py#L2060-L2061
|
| 188 |
+
if (
|
| 189 |
+
"attn_qkv.weight" in name
|
| 190 |
+
or "ffn_down.weight" in name
|
| 191 |
+
or "ffn_up.weight" in name
|
| 192 |
+
or "attn_output.weight" in name
|
| 193 |
+
):
|
| 194 |
+
weights = weights.T
|
| 195 |
+
|
| 196 |
+
# Handle special case for output.weight
|
| 197 |
+
if name == "output.weight":
|
| 198 |
+
# output.weight has conflicts with attn_output.weight in name checking
|
| 199 |
+
# Store the tensor directly and signal to skip further processing
|
| 200 |
+
name = "lm_head.weight"
|
| 201 |
+
parsed_parameters = kwargs.get("parsed_parameters", {})
|
| 202 |
+
parsed_parameters["tensors"][name] = torch.from_numpy(np.copy(weights))
|
| 203 |
+
name = None # Signal to skip further processing
|
| 204 |
+
return GGUFTensor(weights, name, {})
|
| 205 |
+
|
| 206 |
+
|
| 207 |
+
class MambaTensorProcessor(TensorProcessor):
|
| 208 |
+
def __init__(self, config=None):
|
| 209 |
+
super().__init__(config=config)
|
| 210 |
+
|
| 211 |
+
def process(self, weights, name, **kwargs):
|
| 212 |
+
if "ssm_conv1d.weight" in name:
|
| 213 |
+
# for compatibility tensor ssm_conv1d must be (5120, 1, 4]) dim,
|
| 214 |
+
# quantized one is (5120, 4)
|
| 215 |
+
weights = np.expand_dims(weights, axis=1)
|
| 216 |
+
if "ssm_a" in name:
|
| 217 |
+
# Original exponential implementation
|
| 218 |
+
# https://github.com/ggerganov/llama.cpp/blob/master/convert_hf_to_gguf.py#L2975-L2977
|
| 219 |
+
weights = np.log(-weights)
|
| 220 |
+
return GGUFTensor(weights, name, {})
|
| 221 |
+
|
| 222 |
+
|
| 223 |
+
class NemotronTensorProcessor(TensorProcessor):
|
| 224 |
+
def __init__(self, config=None):
|
| 225 |
+
super().__init__(config=config)
|
| 226 |
+
|
| 227 |
+
# ref : https://github.com/ggerganov/llama.cpp/blob/master/convert_hf_to_gguf.py#L4666
|
| 228 |
+
def process(self, weights, name, **kwargs):
|
| 229 |
+
if "norm.weight" in name:
|
| 230 |
+
weights = weights - 1
|
| 231 |
+
return GGUFTensor(weights, name, {})
|
| 232 |
+
|
| 233 |
+
|
| 234 |
+
class Gemma2TensorProcessor(TensorProcessor):
|
| 235 |
+
def __init__(self, config=None):
|
| 236 |
+
super().__init__(config=config)
|
| 237 |
+
|
| 238 |
+
# ref: https://github.com/ggerganov/llama.cpp/blob/d79d8f39b4da6deca4aea8bf130c6034c482b320/convert_hf_to_gguf.py#L3191
|
| 239 |
+
# ref: https://github.com/huggingface/transformers/blob/fc37f38915372c15992b540dfcbbe00a916d4fc6/src/transformers/models/gemma/modeling_gemma.py#L89
|
| 240 |
+
def process(self, weights, name, **kwargs):
|
| 241 |
+
if "norm.weight" in name:
|
| 242 |
+
weights = weights - 1
|
| 243 |
+
return GGUFTensor(weights, name, {})
|
| 244 |
+
|
| 245 |
+
|
| 246 |
+
class Lfm2TensorProcessor(TensorProcessor):
|
| 247 |
+
def __init__(self, config=None):
|
| 248 |
+
super().__init__(config=config)
|
| 249 |
+
|
| 250 |
+
def process(self, weights, name, **kwargs):
|
| 251 |
+
if "shortconv.conv.weight" in name:
|
| 252 |
+
## GGUF shape is [hidden_dim, L_cache], HF expects [hidden_dim, 1, L_cache]
|
| 253 |
+
weights = np.expand_dims(weights, axis=1) ## equivalent to unsqueeze(1)
|
| 254 |
+
return GGUFTensor(weights, name, {})
|
| 255 |
+
|
| 256 |
+
|
| 257 |
+
TENSOR_PROCESSORS = {
|
| 258 |
+
"llama": LlamaTensorProcessor,
|
| 259 |
+
"qwen2moe": Qwen2MoeTensorProcessor,
|
| 260 |
+
"qwen3moe": Qwen2MoeTensorProcessor,
|
| 261 |
+
"bloom": BloomTensorProcessor,
|
| 262 |
+
"t5": T5TensorProcessor,
|
| 263 |
+
"t5encoder": T5TensorProcessor,
|
| 264 |
+
"gpt2": GPT2TensorProcessor,
|
| 265 |
+
"mamba": MambaTensorProcessor,
|
| 266 |
+
"nemotron": NemotronTensorProcessor,
|
| 267 |
+
"gemma2": Gemma2TensorProcessor,
|
| 268 |
+
"gemma3": Gemma2TensorProcessor,
|
| 269 |
+
"lfm2": Lfm2TensorProcessor,
|
| 270 |
+
}
|
| 271 |
+
|
| 272 |
+
|
| 273 |
+
def read_field(reader, field):
|
| 274 |
+
if field not in reader.fields:
|
| 275 |
+
return []
|
| 276 |
+
value = reader.fields[field]
|
| 277 |
+
return [_gguf_parse_value(value.parts[_data_index], value.types) for _data_index in value.data]
|
| 278 |
+
|
| 279 |
+
|
| 280 |
+
# modified from https://github.com/vllm-project/vllm/blob/v0.6.4.post1/vllm/model_executor/model_loader/loader.py#L1115-L1147
|
| 281 |
+
def get_gguf_hf_weights_map(
|
| 282 |
+
hf_model,
|
| 283 |
+
model_type: Optional[str] = None,
|
| 284 |
+
num_layers: Optional[int] = None,
|
| 285 |
+
qual_name: str = "",
|
| 286 |
+
):
|
| 287 |
+
"""
|
| 288 |
+
GGUF uses this naming convention for their tensors from HF checkpoint:
|
| 289 |
+
`blk.N.BB.weight` and `blk.N.BB.bias`
|
| 290 |
+
where N signifies the block number of a layer, and BB signifies the
|
| 291 |
+
attention/mlp layer components.
|
| 292 |
+
See "Standardized tensor names" in
|
| 293 |
+
https://github.com/ggerganov/ggml/blob/master/docs/gguf.md for details.
|
| 294 |
+
"""
|
| 295 |
+
if is_gguf_available() and is_torch_available():
|
| 296 |
+
from gguf import MODEL_ARCH_NAMES, get_tensor_name_map
|
| 297 |
+
else:
|
| 298 |
+
logger.error(
|
| 299 |
+
"Loading a GGUF checkpoint in PyTorch, requires both PyTorch and GGUF>=0.10.0 to be installed. Please see "
|
| 300 |
+
"https://pytorch.org/ and https://github.com/ggerganov/llama.cpp/tree/master/gguf-py for installation instructions."
|
| 301 |
+
)
|
| 302 |
+
raise ImportError("Please install torch and gguf>=0.10.0 to load a GGUF checkpoint in PyTorch.")
|
| 303 |
+
|
| 304 |
+
model_type = hf_model.config.model_type if model_type is None else model_type
|
| 305 |
+
num_layers = hf_model.config.num_hidden_layers if num_layers is None else num_layers
|
| 306 |
+
# hack: ggufs have a different name for cohere
|
| 307 |
+
if model_type == "cohere":
|
| 308 |
+
model_type = "command-r"
|
| 309 |
+
elif model_type == "qwen2_moe":
|
| 310 |
+
model_type = "qwen2moe"
|
| 311 |
+
elif model_type == "qwen3_moe":
|
| 312 |
+
model_type = "qwen3moe"
|
| 313 |
+
elif model_type == "gemma3_text":
|
| 314 |
+
model_type = "gemma3"
|
| 315 |
+
elif model_type == "umt5":
|
| 316 |
+
model_type = "t5"
|
| 317 |
+
arch = None
|
| 318 |
+
for key, value in MODEL_ARCH_NAMES.items():
|
| 319 |
+
if value == model_type:
|
| 320 |
+
arch = key
|
| 321 |
+
break
|
| 322 |
+
if arch is None:
|
| 323 |
+
raise NotImplementedError(
|
| 324 |
+
f"Unknown gguf model_type: {model_type} in gguf-py. "
|
| 325 |
+
"This might because you're using an outdated version of gguf-py package, "
|
| 326 |
+
"you can install `gguf` package from source refer to "
|
| 327 |
+
"https://github.com/ggerganov/llama.cpp/tree/master/gguf-py#development"
|
| 328 |
+
)
|
| 329 |
+
name_map = get_tensor_name_map(arch, num_layers)
|
| 330 |
+
|
| 331 |
+
# Use a dummy conversion to get the mapping, because
|
| 332 |
+
# hf => gguf and gguf => hf mappings are reversed
|
| 333 |
+
gguf_to_hf_name_map = {}
|
| 334 |
+
state_dict = hf_model.state_dict()
|
| 335 |
+
for hf_name in state_dict:
|
| 336 |
+
# An exception for qwen2moe/qwen3moe model, where the expert layers are packed
|
| 337 |
+
if model_type in ("qwen2moe", "qwen3moe") and "mlp.experts." in hf_name:
|
| 338 |
+
hf_name = re.sub(r"mlp.experts.\d+.", "mlp.experts.", hf_name)
|
| 339 |
+
|
| 340 |
+
name, suffix = hf_name, ""
|
| 341 |
+
if hf_name.endswith(".weight") or hf_name.endswith(".bias"):
|
| 342 |
+
name, suffix = hf_name.rsplit(".", 1)
|
| 343 |
+
suffix = "." + suffix
|
| 344 |
+
|
| 345 |
+
gguf_name = name_map.get_name(name)
|
| 346 |
+
if gguf_name is None:
|
| 347 |
+
continue
|
| 348 |
+
|
| 349 |
+
gguf_to_hf_name_map[gguf_name + suffix] = qual_name + hf_name
|
| 350 |
+
|
| 351 |
+
# Some model like Bloom converted from BloomModel instead of BloomForCausalLM
|
| 352 |
+
# Therefore, we need to check submodule as well to get a correct mapping
|
| 353 |
+
if named_children := hf_model.named_children():
|
| 354 |
+
for name, child in named_children:
|
| 355 |
+
sub_map = get_gguf_hf_weights_map(child, model_type, num_layers, qual_name=f"{qual_name}{name}.")
|
| 356 |
+
# Ignore the keys that are already in the main map to avoid overwriting
|
| 357 |
+
sub_map = {k: v for k, v in sub_map.items() if k not in gguf_to_hf_name_map}
|
| 358 |
+
gguf_to_hf_name_map.update(sub_map)
|
| 359 |
+
|
| 360 |
+
return gguf_to_hf_name_map
|
| 361 |
+
|
| 362 |
+
|
| 363 |
+
def load_gguf_checkpoint(gguf_checkpoint_path, return_tensors=False, model_to_load=None):
|
| 364 |
+
"""
|
| 365 |
+
Load a GGUF file and return a dictionary of parsed parameters containing tensors, the parsed
|
| 366 |
+
tokenizer and config attributes.
|
| 367 |
+
|
| 368 |
+
Args:
|
| 369 |
+
gguf_checkpoint_path (`str`):
|
| 370 |
+
The path the to GGUF file to load
|
| 371 |
+
return_tensors (`bool`, defaults to `False`):
|
| 372 |
+
Whether to read the tensors from the file and return them. Not doing so is faster
|
| 373 |
+
and only loads the metadata in memory.
|
| 374 |
+
"""
|
| 375 |
+
if is_gguf_available() and is_torch_available():
|
| 376 |
+
from gguf import GGUFReader, dequantize
|
| 377 |
+
else:
|
| 378 |
+
logger.error(
|
| 379 |
+
"Loading a GGUF checkpoint in PyTorch, requires both PyTorch and GGUF>=0.10.0 to be installed. Please see "
|
| 380 |
+
"https://pytorch.org/ and https://github.com/ggerganov/llama.cpp/tree/master/gguf-py for installation instructions."
|
| 381 |
+
)
|
| 382 |
+
raise ImportError("Please install torch and gguf>=0.10.0 to load a GGUF checkpoint in PyTorch.")
|
| 383 |
+
|
| 384 |
+
reader = GGUFReader(gguf_checkpoint_path)
|
| 385 |
+
fields = reader.fields
|
| 386 |
+
reader_keys = list(fields.keys())
|
| 387 |
+
|
| 388 |
+
parsed_parameters = {k: {} for k in GGUF_TO_TRANSFORMERS_MAPPING}
|
| 389 |
+
|
| 390 |
+
architecture = read_field(reader, "general.architecture")[0]
|
| 391 |
+
# NOTE: Some GGUF checkpoints may miss `general.name` field in metadata
|
| 392 |
+
model_name = read_field(reader, "general.name")
|
| 393 |
+
|
| 394 |
+
updated_architecture = None
|
| 395 |
+
# in llama.cpp mistral models use the same architecture as llama. We need
|
| 396 |
+
# to add this patch to ensure things work correctly on our side.
|
| 397 |
+
if "llama" in architecture and "mistral" in model_name:
|
| 398 |
+
updated_architecture = "mistral"
|
| 399 |
+
# FIXME: Currently this implementation is only for flan-t5 architecture.
|
| 400 |
+
# It needs to be developed for supporting legacy t5.
|
| 401 |
+
elif "t5" in architecture or "t5encoder" in architecture:
|
| 402 |
+
parsed_parameters["config"]["is_gated_act"] = True
|
| 403 |
+
if model_name and "umt5" in model_name[0].lower():
|
| 404 |
+
updated_architecture = "umt5"
|
| 405 |
+
if "t5encoder" in architecture:
|
| 406 |
+
parsed_parameters["config"]["architectures"] = ["UMT5EncoderModel"]
|
| 407 |
+
else:
|
| 408 |
+
if "t5encoder" in architecture:
|
| 409 |
+
parsed_parameters["config"]["architectures"] = ["T5EncoderModel"]
|
| 410 |
+
updated_architecture = "t5"
|
| 411 |
+
else:
|
| 412 |
+
updated_architecture = architecture
|
| 413 |
+
|
| 414 |
+
if "qwen2moe" in architecture:
|
| 415 |
+
updated_architecture = "qwen2_moe"
|
| 416 |
+
elif "qwen3moe" in architecture:
|
| 417 |
+
updated_architecture = "qwen3_moe"
|
| 418 |
+
|
| 419 |
+
# For stablelm architecture, we need to set qkv_bias and use_parallel_residual from tensors
|
| 420 |
+
# If `qkv_bias=True`, qkv_proj with bias will be present in the tensors
|
| 421 |
+
# If `use_parallel_residual=False`, ffn_norm will be present in the tensors
|
| 422 |
+
if "stablelm" in architecture:
|
| 423 |
+
attn_bias_name = {"attn_q.bias", "attn_k.bias", "attn_v.bias"}
|
| 424 |
+
ffn_norm_name = "ffn_norm"
|
| 425 |
+
qkv_bias = any(bias_name in tensor.name for tensor in reader.tensors for bias_name in attn_bias_name)
|
| 426 |
+
use_parallel_residual = any(ffn_norm_name in tensor.name for tensor in reader.tensors)
|
| 427 |
+
parsed_parameters["config"]["use_qkv_bias"] = qkv_bias
|
| 428 |
+
parsed_parameters["config"]["use_parallel_residual"] = not use_parallel_residual
|
| 429 |
+
|
| 430 |
+
if architecture not in GGUF_SUPPORTED_ARCHITECTURES and updated_architecture not in GGUF_SUPPORTED_ARCHITECTURES:
|
| 431 |
+
raise ValueError(f"GGUF model with architecture {architecture} is not supported yet.")
|
| 432 |
+
|
| 433 |
+
# Handle tie_word_embeddings, if lm_head.weight is not present in tensors,
|
| 434 |
+
# tie_word_embeddings is true otherwise false
|
| 435 |
+
exceptions = ["falcon", "bloom"]
|
| 436 |
+
parsed_parameters["config"]["tie_word_embeddings"] = (
|
| 437 |
+
all("output.weight" != tensor.name for tensor in reader.tensors) or architecture in exceptions
|
| 438 |
+
)
|
| 439 |
+
|
| 440 |
+
# List all key-value pairs in a columnized format
|
| 441 |
+
for gguf_key, field in reader.fields.items():
|
| 442 |
+
gguf_key = gguf_key.replace(architecture, updated_architecture)
|
| 443 |
+
split = gguf_key.split(".")
|
| 444 |
+
prefix = split[0]
|
| 445 |
+
config_key = ".".join(split[1:])
|
| 446 |
+
|
| 447 |
+
value = [_gguf_parse_value(field.parts[_data_index], field.types) for _data_index in field.data]
|
| 448 |
+
|
| 449 |
+
if len(value) == 1:
|
| 450 |
+
value = value[0]
|
| 451 |
+
|
| 452 |
+
if isinstance(value, str) and architecture in value:
|
| 453 |
+
value = value.replace(architecture, updated_architecture)
|
| 454 |
+
|
| 455 |
+
for parameter, parameter_renames in GGUF_TO_TRANSFORMERS_MAPPING.items():
|
| 456 |
+
if prefix in parameter_renames and config_key in parameter_renames[prefix]:
|
| 457 |
+
renamed_config_key = parameter_renames[prefix][config_key]
|
| 458 |
+
if renamed_config_key == -1:
|
| 459 |
+
continue
|
| 460 |
+
|
| 461 |
+
if renamed_config_key is not None:
|
| 462 |
+
parsed_parameters[parameter][renamed_config_key] = value
|
| 463 |
+
|
| 464 |
+
if gguf_key in reader_keys:
|
| 465 |
+
reader_keys.remove(gguf_key)
|
| 466 |
+
|
| 467 |
+
if gguf_key in reader_keys:
|
| 468 |
+
logger.info(f"Some keys were not parsed and added into account {gguf_key} | {value}")
|
| 469 |
+
|
| 470 |
+
# Gemma3 GGUF checkpoint only contains weights of text backbone
|
| 471 |
+
if parsed_parameters["config"]["model_type"] == "gemma3":
|
| 472 |
+
parsed_parameters["config"]["model_type"] = "gemma3_text"
|
| 473 |
+
|
| 474 |
+
if parsed_parameters["config"]["model_type"] == "lfm2":
|
| 475 |
+
gguf_num_key_value_heads = parsed_parameters["config"]["num_key_value_heads"]
|
| 476 |
+
# LFM2 GGUF checkpoint defines num_key_value_heads as a list of integers .e.g [0, 0, 8, 0, 0, 8, 0, 0, 8, 0, 8, 0, 8, 0, 8, 0] but we need to set it to the max value for HF
|
| 477 |
+
parsed_parameters["config"]["num_key_value_heads"] = max(gguf_num_key_value_heads)
|
| 478 |
+
## we already read the correct intermediate_size from the GGUF checkpoint so we need to set block_auto_adjust_ff_dim to False
|
| 479 |
+
parsed_parameters["config"]["block_auto_adjust_ff_dim"] = False
|
| 480 |
+
|
| 481 |
+
## llama.cpp defines the layers that are full-attention by looking at num_key_value_heads
|
| 482 |
+
## we need to set the full_attn_idxs to the layers that are full-attention
|
| 483 |
+
parsed_parameters["config"]["full_attn_idxs"] = [
|
| 484 |
+
i for i, num_kv_heads in enumerate(gguf_num_key_value_heads) if num_kv_heads > 0
|
| 485 |
+
]
|
| 486 |
+
|
| 487 |
+
# retrieve config vocab_size from tokenizer
|
| 488 |
+
# Please refer to https://github.com/huggingface/transformers/issues/32526 for more details
|
| 489 |
+
if "vocab_size" not in parsed_parameters["config"]:
|
| 490 |
+
tokenizer_parameters = parsed_parameters["tokenizer"]
|
| 491 |
+
if "tokens" in tokenizer_parameters:
|
| 492 |
+
parsed_parameters["config"]["vocab_size"] = len(tokenizer_parameters["tokens"])
|
| 493 |
+
else:
|
| 494 |
+
logger.warning(
|
| 495 |
+
"Can't find a way to retrieve missing config vocab_size from tokenizer parameters. "
|
| 496 |
+
"This will use default value from model config class and cause unexpected behavior."
|
| 497 |
+
)
|
| 498 |
+
|
| 499 |
+
if return_tensors:
|
| 500 |
+
parsed_parameters["tensors"] = {}
|
| 501 |
+
|
| 502 |
+
tensor_key_mapping = get_gguf_hf_weights_map(model_to_load)
|
| 503 |
+
config = parsed_parameters.get("config", {})
|
| 504 |
+
|
| 505 |
+
ProcessorClass = TENSOR_PROCESSORS.get(architecture, TensorProcessor)
|
| 506 |
+
processor = ProcessorClass(config=config)
|
| 507 |
+
|
| 508 |
+
for tensor in tqdm(reader.tensors, desc="Converting and de-quantizing GGUF tensors..."):
|
| 509 |
+
name = tensor.name
|
| 510 |
+
weights = dequantize(tensor.data, tensor.tensor_type)
|
| 511 |
+
|
| 512 |
+
result = processor.process(
|
| 513 |
+
weights=weights,
|
| 514 |
+
name=name,
|
| 515 |
+
tensor_key_mapping=tensor_key_mapping,
|
| 516 |
+
parsed_parameters=parsed_parameters,
|
| 517 |
+
)
|
| 518 |
+
|
| 519 |
+
weights = result.weights
|
| 520 |
+
name = result.name
|
| 521 |
+
|
| 522 |
+
if name not in tensor_key_mapping:
|
| 523 |
+
continue
|
| 524 |
+
|
| 525 |
+
name = tensor_key_mapping[name]
|
| 526 |
+
|
| 527 |
+
parsed_parameters["tensors"][name] = torch.from_numpy(np.copy(weights))
|
| 528 |
+
|
| 529 |
+
if len(reader_keys) > 0:
|
| 530 |
+
logger.info(f"Some keys of the GGUF file were not considered: {reader_keys}")
|
| 531 |
+
|
| 532 |
+
return parsed_parameters
|