about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/lxml
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/lxml')
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/ElementInclude.py244
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/__init__.py22
-rwxr-xr-x.venv/lib/python3.12/site-packages/lxml/_elementpath.cpython-312-x86_64-linux-gnu.sobin0 -> 204992 bytes
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/_elementpath.py341
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/apihelpers.pxi1793
-rwxr-xr-x.venv/lib/python3.12/site-packages/lxml/builder.cpython-312-x86_64-linux-gnu.sobin0 -> 116536 bytes
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/builder.py232
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/classlookup.pxi580
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/cleanup.pxi215
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/cssselect.py101
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/debug.pxi90
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/docloader.pxi178
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/doctestcompare.py488
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/dtd.pxi479
-rwxr-xr-x.venv/lib/python3.12/site-packages/lxml/etree.cpython-312-x86_64-linux-gnu.sobin0 -> 5353704 bytes
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/etree.h248
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/etree.pyx3732
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/etree_api.h195
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/extensions.pxi833
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/html/ElementSoup.py10
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/html/__init__.py1923
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/html/_diffcommand.py86
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/html/_html5builder.py100
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/html/_setmixin.py56
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/html/builder.py133
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/html/clean.py21
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/html/defs.py135
-rwxr-xr-x.venv/lib/python3.12/site-packages/lxml/html/diff.cpython-312-x86_64-linux-gnu.sobin0 -> 360632 bytes
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/html/diff.py878
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/html/formfill.py299
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/html/html5parser.py260
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/html/soupparser.py314
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/html/usedoctest.py13
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/__init__.pxd0
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/__init__.py0
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/c14n.pxd25
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/config.pxd3
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/dtdvalid.pxd18
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/etree_defs.h379
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/etreepublic.pxd237
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/extlibs/__init__.py0
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/extlibs/libcharset.h45
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/extlibs/localcharset.h137
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/extlibs/zconf.h543
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/extlibs/zlib.h1938
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/htmlparser.pxd56
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libexslt/__init__.py0
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libexslt/exslt.h108
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libexslt/exsltconfig.h70
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libexslt/exsltexports.h63
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/HTMLparser.h343
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/HTMLtree.h147
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/SAX.h202
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/SAX2.h171
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/__init__.py0
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/c14n.h126
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/catalog.h182
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/chvalid.h230
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/debugXML.h217
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/dict.h82
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/encoding.h235
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/entities.h155
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/globals.h41
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/hash.h232
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/list.h137
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/nanoftp.h186
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/nanohttp.h81
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/parser.h1384
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/parserInternals.h663
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/relaxng.h219
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/schemasInternals.h959
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/schematron.h143
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/threads.h87
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/tree.h1362
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/uri.h95
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/valid.h450
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/xinclude.h129
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/xlink.h189
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlIO.h421
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlautomata.h146
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlerror.h948
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlexports.h50
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlmemory.h225
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlmodule.h57
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlreader.h434
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlregexp.h215
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlsave.h97
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlschemas.h249
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlschemastypes.h152
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlstring.h140
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlunicode.h202
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlversion.h511
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlwriter.h488
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/xpath.h575
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/xpathInternals.h633
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxml/xpointer.h138
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxslt/__init__.py0
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxslt/attributes.h39
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxslt/documents.h93
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxslt/extensions.h262
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxslt/extra.h72
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxslt/functions.h78
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxslt/imports.h75
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxslt/keys.h53
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxslt/namespaces.h68
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxslt/numbersInternals.h73
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxslt/pattern.h84
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxslt/preproc.h43
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxslt/security.h104
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxslt/templates.h77
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxslt/transform.h207
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxslt/variables.h118
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxslt/xslt.h110
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxslt/xsltInternals.h1995
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxslt/xsltconfig.h146
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxslt/xsltexports.h64
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxslt/xsltlocale.h44
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/libxslt/xsltutils.h343
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/lxml-version.h3
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/relaxng.pxd64
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/schematron.pxd34
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/tree.pxd494
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/uri.pxd5
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/xinclude.pxd22
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/xmlerror.pxd852
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/xmlparser.pxd265
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/xmlschema.pxd35
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/xpath.pxd136
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/includes/xslt.pxd190
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/isoschematron/__init__.py348
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/isoschematron/resources/rng/iso-schematron.rng709
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/isoschematron/resources/xsl/RNG2Schtrn.xsl75
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/isoschematron/resources/xsl/XSD2Schtrn.xsl77
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_abstract_expand.xsl313
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_dsdl_include.xsl1160
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_schematron_message.xsl55
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_schematron_skeleton_for_xslt1.xsl1796
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_svrl_for_xslt1.xsl588
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/readme.txt84
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/iterparse.pxi438
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/lxml.etree.h248
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/lxml.etree_api.h195
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/nsclasses.pxi281
-rwxr-xr-x.venv/lib/python3.12/site-packages/lxml/objectify.cpython-312-x86_64-linux-gnu.sobin0 -> 3077608 bytes
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/objectify.pyx2145
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/objectpath.pxi332
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/parser.pxi2000
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/parsertarget.pxi180
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/proxy.pxi619
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/public-api.pxi178
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/pyclasslookup.py3
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/readonlytree.pxi565
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/relaxng.pxi165
-rwxr-xr-x.venv/lib/python3.12/site-packages/lxml/sax.cpython-312-x86_64-linux-gnu.sobin0 -> 186040 bytes
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/sax.py275
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/saxparser.pxi875
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/schematron.pxi168
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/serializer.pxi1781
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/usedoctest.py13
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/xinclude.pxi67
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/xmlerror.pxi1654
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/xmlid.pxi179
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/xmlschema.pxi215
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/xpath.pxi487
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/xslt.pxi950
-rw-r--r--.venv/lib/python3.12/site-packages/lxml/xsltext.pxi242
166 files changed, 57457 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/lxml/ElementInclude.py b/.venv/lib/python3.12/site-packages/lxml/ElementInclude.py
new file mode 100644
index 00000000..21884336
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/ElementInclude.py
@@ -0,0 +1,244 @@
+#
+# ElementTree
+# $Id: ElementInclude.py 1862 2004-06-18 07:31:02Z Fredrik $
+#
+# limited xinclude support for element trees
+#
+# history:
+# 2003-08-15 fl   created
+# 2003-11-14 fl   fixed default loader
+#
+# Copyright (c) 2003-2004 by Fredrik Lundh.  All rights reserved.
+#
+# fredrik@pythonware.com
+# http://www.pythonware.com
+#
+# --------------------------------------------------------------------
+# The ElementTree toolkit is
+#
+# Copyright (c) 1999-2004 by Fredrik Lundh
+#
+# By obtaining, using, and/or copying this software and/or its
+# associated documentation, you agree that you have read, understood,
+# and will comply with the following terms and conditions:
+#
+# Permission to use, copy, modify, and distribute this software and
+# its associated documentation for any purpose and without fee is
+# hereby granted, provided that the above copyright notice appears in
+# all copies, and that both that copyright notice and this permission
+# notice appear in supporting documentation, and that the name of
+# Secret Labs AB or the author not be used in advertising or publicity
+# pertaining to distribution of the software without specific, written
+# prior permission.
+#
+# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
+# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
+# ABILITY AND FITNESS.  IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR
+# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+# --------------------------------------------------------------------
+
+"""
+Limited XInclude support for the ElementTree package.
+
+While lxml.etree has full support for XInclude (see
+`etree.ElementTree.xinclude()`), this module provides a simpler, pure
+Python, ElementTree compatible implementation that supports a simple
+form of custom URL resolvers.
+"""
+
+from lxml import etree
+try:
+    from urlparse import urljoin
+    from urllib2 import urlopen
+except ImportError:
+    # Python 3
+    from urllib.parse import urljoin
+    from urllib.request import urlopen
+
+XINCLUDE = "{http://www.w3.org/2001/XInclude}"
+
+XINCLUDE_INCLUDE = XINCLUDE + "include"
+XINCLUDE_FALLBACK = XINCLUDE + "fallback"
+XINCLUDE_ITER_TAG = XINCLUDE + "*"
+
+# For security reasons, the inclusion depth is limited to this read-only value by default.
+DEFAULT_MAX_INCLUSION_DEPTH = 6
+
+
+##
+# Fatal include error.
+
+class FatalIncludeError(etree.LxmlSyntaxError):
+    pass
+
+
+class LimitedRecursiveIncludeError(FatalIncludeError):
+    pass
+
+
+##
+# ET compatible default loader.
+# This loader reads an included resource from disk.
+#
+# @param href Resource reference.
+# @param parse Parse mode.  Either "xml" or "text".
+# @param encoding Optional text encoding.
+# @return The expanded resource.  If the parse mode is "xml", this
+#    is an ElementTree instance.  If the parse mode is "text", this
+#    is a Unicode string.  If the loader fails, it can return None
+#    or raise an IOError exception.
+# @throws IOError If the loader fails to load the resource.
+
+def default_loader(href, parse, encoding=None):
+    file = open(href, 'rb')
+    if parse == "xml":
+        data = etree.parse(file).getroot()
+    else:
+        data = file.read()
+        if not encoding:
+            encoding = 'utf-8'
+        data = data.decode(encoding)
+    file.close()
+    return data
+
+
+##
+# Default loader used by lxml.etree - handles custom resolvers properly
+# 
+
+def _lxml_default_loader(href, parse, encoding=None, parser=None):
+    if parse == "xml":
+        data = etree.parse(href, parser).getroot()
+    else:
+        if "://" in href:
+            f = urlopen(href)
+        else:
+            f = open(href, 'rb')
+        data = f.read()
+        f.close()
+        if not encoding:
+            encoding = 'utf-8'
+        data = data.decode(encoding)
+    return data
+
+
+##
+# Wrapper for ET compatibility - drops the parser
+
+def _wrap_et_loader(loader):
+    def load(href, parse, encoding=None, parser=None):
+        return loader(href, parse, encoding)
+    return load
+
+
+##
+# Expand XInclude directives.
+#
+# @param elem Root element.
+# @param loader Optional resource loader.  If omitted, it defaults
+#     to {@link default_loader}.  If given, it should be a callable
+#     that implements the same interface as <b>default_loader</b>.
+# @param base_url The base URL of the original file, to resolve
+#     relative include file references.
+# @param max_depth The maximum number of recursive inclusions.
+#     Limited to reduce the risk of malicious content explosion.
+#     Pass None to disable the limitation.
+# @throws LimitedRecursiveIncludeError If the {@link max_depth} was exceeded.
+# @throws FatalIncludeError If the function fails to include a given
+#     resource, or if the tree contains malformed XInclude elements.
+# @throws IOError If the function fails to load a given resource.
+# @returns the node or its replacement if it was an XInclude node
+
+def include(elem, loader=None, base_url=None,
+            max_depth=DEFAULT_MAX_INCLUSION_DEPTH):
+    if max_depth is None:
+        max_depth = -1
+    elif max_depth < 0:
+        raise ValueError("expected non-negative depth or None for 'max_depth', got %r" % max_depth)
+
+    if base_url is None:
+        if hasattr(elem, 'getroot'):
+            tree = elem
+            elem = elem.getroot()
+        else:
+            tree = elem.getroottree()
+        if hasattr(tree, 'docinfo'):
+            base_url = tree.docinfo.URL
+    elif hasattr(elem, 'getroot'):
+        elem = elem.getroot()
+    _include(elem, loader, base_url, max_depth)
+
+
+def _include(elem, loader=None, base_url=None,
+             max_depth=DEFAULT_MAX_INCLUSION_DEPTH, _parent_hrefs=None):
+    if loader is not None:
+        load_include = _wrap_et_loader(loader)
+    else:
+        load_include = _lxml_default_loader
+
+    if _parent_hrefs is None:
+        _parent_hrefs = set()
+
+    parser = elem.getroottree().parser
+
+    include_elements = list(
+        elem.iter(XINCLUDE_ITER_TAG))
+
+    for e in include_elements:
+        if e.tag == XINCLUDE_INCLUDE:
+            # process xinclude directive
+            href = urljoin(base_url, e.get("href"))
+            parse = e.get("parse", "xml")
+            parent = e.getparent()
+            if parse == "xml":
+                if href in _parent_hrefs:
+                    raise FatalIncludeError(
+                        "recursive include of %r detected" % href
+                        )
+                if max_depth == 0:
+                    raise LimitedRecursiveIncludeError(
+                        "maximum xinclude depth reached when including file %s" % href)
+                node = load_include(href, parse, parser=parser)
+                if node is None:
+                    raise FatalIncludeError(
+                        "cannot load %r as %r" % (href, parse)
+                        )
+                node = _include(node, loader, href, max_depth - 1, {href} | _parent_hrefs)
+                if e.tail:
+                    node.tail = (node.tail or "") + e.tail
+                if parent is None:
+                    return node # replaced the root node!
+                parent.replace(e, node)
+            elif parse == "text":
+                text = load_include(href, parse, encoding=e.get("encoding"))
+                if text is None:
+                    raise FatalIncludeError(
+                        "cannot load %r as %r" % (href, parse)
+                        )
+                predecessor = e.getprevious()
+                if predecessor is not None:
+                    predecessor.tail = (predecessor.tail or "") + text
+                elif parent is None:
+                    return text # replaced the root node!
+                else:
+                    parent.text = (parent.text or "") + text + (e.tail or "")
+                parent.remove(e)
+            else:
+                raise FatalIncludeError(
+                    "unknown parse type in xi:include tag (%r)" % parse
+                )
+        elif e.tag == XINCLUDE_FALLBACK:
+            parent = e.getparent()
+            if parent is not None and parent.tag != XINCLUDE_INCLUDE:
+                raise FatalIncludeError(
+                    "xi:fallback tag must be child of xi:include (%r)" % e.tag
+                    )
+        else:
+            raise FatalIncludeError(
+                "Invalid element found in XInclude namespace (%r)" % e.tag
+                )
+    return elem
diff --git a/.venv/lib/python3.12/site-packages/lxml/__init__.py b/.venv/lib/python3.12/site-packages/lxml/__init__.py
new file mode 100644
index 00000000..45cee20a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/__init__.py
@@ -0,0 +1,22 @@
+# this is a package
+
+__version__ = "5.3.1"
+
+
+def get_include():
+    """
+    Returns a list of header include paths (for lxml itself, libxml2
+    and libxslt) needed to compile C code against lxml if it was built
+    with statically linked libraries.
+    """
+    import os
+    lxml_path = __path__[0]
+    include_path = os.path.join(lxml_path, 'includes')
+    includes = [include_path, lxml_path]
+
+    for name in os.listdir(include_path):
+        path = os.path.join(include_path, name)
+        if os.path.isdir(path):
+            includes.append(path)
+
+    return includes
diff --git a/.venv/lib/python3.12/site-packages/lxml/_elementpath.cpython-312-x86_64-linux-gnu.so b/.venv/lib/python3.12/site-packages/lxml/_elementpath.cpython-312-x86_64-linux-gnu.so
new file mode 100755
index 00000000..866b7c15
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/_elementpath.cpython-312-x86_64-linux-gnu.so
Binary files differdiff --git a/.venv/lib/python3.12/site-packages/lxml/_elementpath.py b/.venv/lib/python3.12/site-packages/lxml/_elementpath.py
new file mode 100644
index 00000000..6233a635
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/_elementpath.py
@@ -0,0 +1,341 @@
+# cython: language_level=2
+
+#
+# ElementTree
+# $Id: ElementPath.py 3375 2008-02-13 08:05:08Z fredrik $
+#
+# limited xpath support for element trees
+#
+# history:
+# 2003-05-23 fl   created
+# 2003-05-28 fl   added support for // etc
+# 2003-08-27 fl   fixed parsing of periods in element names
+# 2007-09-10 fl   new selection engine
+# 2007-09-12 fl   fixed parent selector
+# 2007-09-13 fl   added iterfind; changed findall to return a list
+# 2007-11-30 fl   added namespaces support
+# 2009-10-30 fl   added child element value filter
+#
+# Copyright (c) 2003-2009 by Fredrik Lundh.  All rights reserved.
+#
+# fredrik@pythonware.com
+# http://www.pythonware.com
+#
+# --------------------------------------------------------------------
+# The ElementTree toolkit is
+#
+# Copyright (c) 1999-2009 by Fredrik Lundh
+#
+# By obtaining, using, and/or copying this software and/or its
+# associated documentation, you agree that you have read, understood,
+# and will comply with the following terms and conditions:
+#
+# Permission to use, copy, modify, and distribute this software and
+# its associated documentation for any purpose and without fee is
+# hereby granted, provided that the above copyright notice appears in
+# all copies, and that both that copyright notice and this permission
+# notice appear in supporting documentation, and that the name of
+# Secret Labs AB or the author not be used in advertising or publicity
+# pertaining to distribution of the software without specific, written
+# prior permission.
+#
+# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
+# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
+# ABILITY AND FITNESS.  IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR
+# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+# --------------------------------------------------------------------
+
+##
+# Implementation module for XPath support.  There's usually no reason
+# to import this module directly; the <b>ElementTree</b> does this for
+# you, if needed.
+##
+
+
+import re
+
+xpath_tokenizer_re = re.compile(
+    "("
+    "'[^']*'|\"[^\"]*\"|"
+    "::|"
+    "//?|"
+    r"\.\.|"
+    r"\(\)|"
+    r"[/.*:\[\]\(\)@=])|"
+    r"((?:\{[^}]+\})?[^/\[\]\(\)@=\s]+)|"
+    r"\s+"
+    )
+
+def xpath_tokenizer(pattern, namespaces=None, with_prefixes=True):
+    # ElementTree uses '', lxml used None originally.
+    default_namespace = (namespaces.get(None) or namespaces.get('')) if namespaces else None
+    parsing_attribute = False
+    for token in xpath_tokenizer_re.findall(pattern):
+        ttype, tag = token
+        if tag and tag[0] != "{":
+            if ":" in tag and with_prefixes:
+                prefix, uri = tag.split(":", 1)
+                try:
+                    if not namespaces:
+                        raise KeyError
+                    yield ttype, "{%s}%s" % (namespaces[prefix], uri)
+                except KeyError:
+                    raise SyntaxError("prefix %r not found in prefix map" % prefix)
+            elif default_namespace and not parsing_attribute:
+                yield ttype, "{%s}%s" % (default_namespace, tag)
+            else:
+                yield token
+            parsing_attribute = False
+        else:
+            yield token
+            parsing_attribute = ttype == '@'
+
+
+def prepare_child(next, token):
+    tag = token[1]
+    def select(result):
+        for elem in result:
+            yield from elem.iterchildren(tag)
+    return select
+
+def prepare_star(next, token):
+    def select(result):
+        for elem in result:
+            yield from elem.iterchildren('*')
+    return select
+
+def prepare_self(next, token):
+    def select(result):
+        return result
+    return select
+
+def prepare_descendant(next, token):
+    token = next()
+    if token[0] == "*":
+        tag = "*"
+    elif not token[0]:
+        tag = token[1]
+    else:
+        raise SyntaxError("invalid descendant")
+    def select(result):
+        for elem in result:
+            yield from elem.iterdescendants(tag)
+    return select
+
+def prepare_parent(next, token):
+    def select(result):
+        for elem in result:
+            parent = elem.getparent()
+            if parent is not None:
+                yield parent
+    return select
+
+def prepare_predicate(next, token):
+    # FIXME: replace with real parser!!! refs:
+    # http://effbot.org/zone/simple-iterator-parser.htm
+    # http://javascript.crockford.com/tdop/tdop.html
+    signature = ''
+    predicate = []
+    while 1:
+        token = next()
+        if token[0] == "]":
+            break
+        if token == ('', ''):
+            # ignore whitespace
+            continue
+        if token[0] and token[0][:1] in "'\"":
+            token = "'", token[0][1:-1]
+        signature += token[0] or "-"
+        predicate.append(token[1])
+
+    # use signature to determine predicate type
+    if signature == "@-":
+        # [@attribute] predicate
+        key = predicate[1]
+        def select(result):
+            for elem in result:
+                if elem.get(key) is not None:
+                    yield elem
+        return select
+    if signature == "@-='":
+        # [@attribute='value']
+        key = predicate[1]
+        value = predicate[-1]
+        def select(result):
+            for elem in result:
+                if elem.get(key) == value:
+                    yield elem
+        return select
+    if signature == "-" and not re.match(r"-?\d+$", predicate[0]):
+        # [tag]
+        tag = predicate[0]
+        def select(result):
+            for elem in result:
+                for _ in elem.iterchildren(tag):
+                    yield elem
+                    break
+        return select
+    if signature == ".='" or (signature == "-='" and not re.match(r"-?\d+$", predicate[0])):
+        # [.='value'] or [tag='value']
+        tag = predicate[0]
+        value = predicate[-1]
+        if tag:
+            def select(result):
+                for elem in result:
+                    for e in elem.iterchildren(tag):
+                        if "".join(e.itertext()) == value:
+                            yield elem
+                            break
+        else:
+            def select(result):
+                for elem in result:
+                    if "".join(elem.itertext()) == value:
+                        yield elem
+        return select
+    if signature == "-" or signature == "-()" or signature == "-()-":
+        # [index] or [last()] or [last()-index]
+        if signature == "-":
+            # [index]
+            index = int(predicate[0]) - 1
+            if index < 0:
+                if index == -1:
+                    raise SyntaxError(
+                        "indices in path predicates are 1-based, not 0-based")
+                else:
+                    raise SyntaxError("path index >= 1 expected")
+        else:
+            if predicate[0] != "last":
+                raise SyntaxError("unsupported function")
+            if signature == "-()-":
+                try:
+                    index = int(predicate[2]) - 1
+                except ValueError:
+                    raise SyntaxError("unsupported expression")
+            else:
+                index = -1
+        def select(result):
+            for elem in result:
+                parent = elem.getparent()
+                if parent is None:
+                    continue
+                try:
+                    # FIXME: what if the selector is "*" ?
+                    elems = list(parent.iterchildren(elem.tag))
+                    if elems[index] is elem:
+                        yield elem
+                except IndexError:
+                    pass
+        return select
+    raise SyntaxError("invalid predicate")
+
+ops = {
+    "": prepare_child,
+    "*": prepare_star,
+    ".": prepare_self,
+    "..": prepare_parent,
+    "//": prepare_descendant,
+    "[": prepare_predicate,
+}
+
+
+# --------------------------------------------------------------------
+
+_cache = {}
+
+
+def _build_path_iterator(path, namespaces, with_prefixes=True):
+    """compile selector pattern"""
+    if path[-1:] == "/":
+        path += "*"  # implicit all (FIXME: keep this?)
+
+    cache_key = (path,)
+    if namespaces:
+        # lxml originally used None for the default namespace but ElementTree uses the
+        # more convenient (all-strings-dict) empty string, so we support both here,
+        # preferring the more convenient '', as long as they aren't ambiguous.
+        if None in namespaces:
+            if '' in namespaces and namespaces[None] != namespaces['']:
+                raise ValueError("Ambiguous default namespace provided: %r versus %r" % (
+                    namespaces[None], namespaces['']))
+            cache_key += (namespaces[None],) + tuple(sorted(
+                item for item in namespaces.items() if item[0] is not None))
+        else:
+            cache_key += tuple(sorted(namespaces.items()))
+
+    try:
+        return _cache[cache_key]
+    except KeyError:
+        pass
+    if len(_cache) > 100:
+        _cache.clear()
+
+    if path[:1] == "/":
+        raise SyntaxError("cannot use absolute path on element")
+    stream = iter(xpath_tokenizer(path, namespaces, with_prefixes=with_prefixes))
+    try:
+        _next = stream.next
+    except AttributeError:
+        # Python 3
+        _next = stream.__next__
+    try:
+        token = _next()
+    except StopIteration:
+        raise SyntaxError("empty path expression")
+    selector = []
+    while 1:
+        try:
+            selector.append(ops[token[0]](_next, token))
+        except StopIteration:
+            raise SyntaxError("invalid path")
+        try:
+            token = _next()
+            if token[0] == "/":
+                token = _next()
+        except StopIteration:
+            break
+    _cache[cache_key] = selector
+    return selector
+
+
+##
+# Iterate over the matching nodes
+
+def iterfind(elem, path, namespaces=None, with_prefixes=True):
+    selector = _build_path_iterator(path, namespaces, with_prefixes=with_prefixes)
+    result = iter((elem,))
+    for select in selector:
+        result = select(result)
+    return result
+
+
+##
+# Find first matching object.
+
+def find(elem, path, namespaces=None, with_prefixes=True):
+    it = iterfind(elem, path, namespaces, with_prefixes=with_prefixes)
+    try:
+        return next(it)
+    except StopIteration:
+        return None
+
+
+##
+# Find all matching objects.
+
+def findall(elem, path, namespaces=None, with_prefixes=True):
+    return list(iterfind(elem, path, namespaces))
+
+
+##
+# Find text for first matching object.
+
+def findtext(elem, path, default=None, namespaces=None, with_prefixes=True):
+    el = find(elem, path, namespaces, with_prefixes=with_prefixes)
+    if el is None:
+        return default
+    else:
+        return el.text or ''
diff --git a/.venv/lib/python3.12/site-packages/lxml/apihelpers.pxi b/.venv/lib/python3.12/site-packages/lxml/apihelpers.pxi
new file mode 100644
index 00000000..fb60af7d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/apihelpers.pxi
@@ -0,0 +1,1793 @@
+# Private/public helper functions for API functions
+
+from lxml.includes cimport uri
+
+
+cdef void displayNode(xmlNode* c_node, indent) noexcept:
+    # to help with debugging
+    cdef xmlNode* c_child
+    try:
+        print(indent * ' ', <long>c_node)
+        c_child = c_node.children
+        while c_child is not NULL:
+            displayNode(c_child, indent + 1)
+            c_child = c_child.next
+    finally:
+        return  # swallow any exceptions
+
+cdef inline bint _isHtmlDocument(_Element element) except -1:
+    cdef xmlNode* c_node = element._c_node
+    return (
+        c_node is not NULL and c_node.doc is not NULL and
+        c_node.doc.properties & tree.XML_DOC_HTML != 0
+    )
+
+cdef inline int _assertValidNode(_Element element) except -1:
+    assert element._c_node is not NULL, "invalid Element proxy at %s" % id(element)
+
+cdef inline int _assertValidDoc(_Document doc) except -1:
+    assert doc._c_doc is not NULL, "invalid Document proxy at %s" % id(doc)
+
+cdef _Document _documentOrRaise(object input):
+    """Call this to get the document of a _Document, _ElementTree or _Element
+    object, or to raise an exception if it can't be determined.
+
+    Should be used in all API functions for consistency.
+    """
+    cdef _Document doc
+    if isinstance(input, _ElementTree):
+        if (<_ElementTree>input)._context_node is not None:
+            doc = (<_ElementTree>input)._context_node._doc
+        else:
+            doc = None
+    elif isinstance(input, _Element):
+        doc = (<_Element>input)._doc
+    elif isinstance(input, _Document):
+        doc = <_Document>input
+    else:
+        raise TypeError, f"Invalid input object: {python._fqtypename(input).decode('utf8')}"
+    if doc is None:
+        raise ValueError, f"Input object has no document: {python._fqtypename(input).decode('utf8')}"
+    _assertValidDoc(doc)
+    return doc
+
+cdef _Element _rootNodeOrRaise(object input):
+    """Call this to get the root node of a _Document, _ElementTree or
+     _Element object, or to raise an exception if it can't be determined.
+
+    Should be used in all API functions for consistency.
+     """
+    cdef _Element node
+    if isinstance(input, _ElementTree):
+        node = (<_ElementTree>input)._context_node
+    elif isinstance(input, _Element):
+        node = <_Element>input
+    elif isinstance(input, _Document):
+        node = (<_Document>input).getroot()
+    else:
+        raise TypeError, f"Invalid input object: {python._fqtypename(input).decode('utf8')}"
+    if (node is None or not node._c_node or
+            node._c_node.type != tree.XML_ELEMENT_NODE):
+        raise ValueError, f"Input object is not an XML element: {python._fqtypename(input).decode('utf8')}"
+    _assertValidNode(node)
+    return node
+
+cdef bint _isAncestorOrSame(xmlNode* c_ancestor, xmlNode* c_node) noexcept:
+    while c_node:
+        if c_node is c_ancestor:
+            return True
+        c_node = c_node.parent
+    return False
+
+cdef _Element _makeElement(tag, xmlDoc* c_doc, _Document doc,
+                           _BaseParser parser, text, tail, attrib, nsmap,
+                           dict extra_attrs):
+    """Create a new element and initialize text content, namespaces and
+    attributes.
+
+    This helper function will reuse as much of the existing document as
+    possible:
+
+    If 'parser' is None, the parser will be inherited from 'doc' or the
+    default parser will be used.
+
+    If 'doc' is None, 'c_doc' is used to create a new _Document and the new
+    element is made its root node.
+
+    If 'c_doc' is also NULL, a new xmlDoc will be created.
+    """
+    cdef xmlNode* c_node
+    if doc is not None:
+        c_doc = doc._c_doc
+    ns_utf, name_utf = _getNsTag(tag)
+    if parser is not None and parser._for_html:
+        _htmlTagValidOrRaise(name_utf)
+        if c_doc is NULL:
+            c_doc = _newHTMLDoc()
+    else:
+        _tagValidOrRaise(name_utf)
+        if c_doc is NULL:
+            c_doc = _newXMLDoc()
+    c_node = _createElement(c_doc, name_utf)
+    if c_node is NULL:
+        if doc is None and c_doc is not NULL:
+            tree.xmlFreeDoc(c_doc)
+        raise MemoryError()
+    try:
+        if doc is None:
+            tree.xmlDocSetRootElement(c_doc, c_node)
+            doc = _documentFactory(c_doc, parser)
+        if text is not None:
+            _setNodeText(c_node, text)
+        if tail is not None:
+            _setTailText(c_node, tail)
+        # add namespaces to node if necessary
+        _setNodeNamespaces(c_node, doc, ns_utf, nsmap)
+        _initNodeAttributes(c_node, doc, attrib, extra_attrs)
+        return _elementFactory(doc, c_node)
+    except:
+        # free allocated c_node/c_doc unless Python does it for us
+        if c_node.doc is not c_doc:
+            # node not yet in document => will not be freed by document
+            if tail is not None:
+                _removeText(c_node.next) # tail
+            tree.xmlFreeNode(c_node)
+        if doc is None:
+            # c_doc will not be freed by doc
+            tree.xmlFreeDoc(c_doc)
+        raise
+
+cdef int _initNewElement(_Element element, bint is_html, name_utf, ns_utf,
+                         _BaseParser parser, attrib, nsmap, dict extra_attrs) except -1:
+    """Initialise a new Element object.
+
+    This is used when users instantiate a Python Element subclass
+    directly, without it being mapped to an existing XML node.
+    """
+    cdef xmlDoc* c_doc
+    cdef xmlNode* c_node
+    cdef _Document doc
+    if is_html:
+        _htmlTagValidOrRaise(name_utf)
+        c_doc = _newHTMLDoc()
+    else:
+        _tagValidOrRaise(name_utf)
+        c_doc = _newXMLDoc()
+    c_node = _createElement(c_doc, name_utf)
+    if c_node is NULL:
+        if c_doc is not NULL:
+            tree.xmlFreeDoc(c_doc)
+        raise MemoryError()
+    tree.xmlDocSetRootElement(c_doc, c_node)
+    doc = _documentFactory(c_doc, parser)
+    # add namespaces to node if necessary
+    _setNodeNamespaces(c_node, doc, ns_utf, nsmap)
+    _initNodeAttributes(c_node, doc, attrib, extra_attrs)
+    _registerProxy(element, doc, c_node)
+    element._init()
+    return 0
+
+cdef _Element _makeSubElement(_Element parent, tag, text, tail,
+                              attrib, nsmap, dict extra_attrs):
+    """Create a new child element and initialize text content, namespaces and
+    attributes.
+    """
+    cdef xmlNode* c_node
+    cdef xmlDoc* c_doc
+    if parent is None or parent._doc is None:
+        return None
+    _assertValidNode(parent)
+    ns_utf, name_utf = _getNsTag(tag)
+    c_doc = parent._doc._c_doc
+
+    if parent._doc._parser is not None and parent._doc._parser._for_html:
+        _htmlTagValidOrRaise(name_utf)
+    else:
+        _tagValidOrRaise(name_utf)
+
+    c_node = _createElement(c_doc, name_utf)
+    if c_node is NULL:
+        raise MemoryError()
+    tree.xmlAddChild(parent._c_node, c_node)
+
+    try:
+        if text is not None:
+            _setNodeText(c_node, text)
+        if tail is not None:
+            _setTailText(c_node, tail)
+
+        # add namespaces to node if necessary
+        _setNodeNamespaces(c_node, parent._doc, ns_utf, nsmap)
+        _initNodeAttributes(c_node, parent._doc, attrib, extra_attrs)
+        return _elementFactory(parent._doc, c_node)
+    except:
+        # make sure we clean up in case of an error
+        _removeNode(parent._doc, c_node)
+        raise
+
+
+cdef int _setNodeNamespaces(xmlNode* c_node, _Document doc,
+                            object node_ns_utf, object nsmap) except -1:
+    """Lookup current namespace prefixes, then set namespace structure for
+    node (if 'node_ns_utf' was provided) and register new ns-prefix mappings.
+
+    'node_ns_utf' should only be passed for a newly created node.
+    """
+    cdef xmlNs* c_ns
+    cdef list nsdefs
+
+    if nsmap:
+        for prefix, href in _iter_nsmap(nsmap):
+            href_utf = _utf8(href)
+            _uriValidOrRaise(href_utf)
+            c_href = _xcstr(href_utf)
+            if prefix is not None:
+                prefix_utf = _utf8(prefix)
+                _prefixValidOrRaise(prefix_utf)
+                c_prefix = _xcstr(prefix_utf)
+            else:
+                c_prefix = <const_xmlChar*>NULL
+            # add namespace with prefix if it is not already known
+            c_ns = tree.xmlSearchNs(doc._c_doc, c_node, c_prefix)
+            if c_ns is NULL or \
+                    c_ns.href is NULL or \
+                    tree.xmlStrcmp(c_ns.href, c_href) != 0:
+                c_ns = tree.xmlNewNs(c_node, c_href, c_prefix)
+            if href_utf == node_ns_utf:
+                tree.xmlSetNs(c_node, c_ns)
+                node_ns_utf = None
+
+    if node_ns_utf is not None:
+        _uriValidOrRaise(node_ns_utf)
+        doc._setNodeNs(c_node, _xcstr(node_ns_utf))
+    return 0
+
+
+cdef dict _build_nsmap(xmlNode* c_node):
+    """
+    Namespace prefix->URI mapping known in the context of this Element.
+    This includes all namespace declarations of the parents.
+    """
+    cdef xmlNs* c_ns
+    nsmap = {}
+    while c_node is not NULL and c_node.type == tree.XML_ELEMENT_NODE:
+        c_ns = c_node.nsDef
+        while c_ns is not NULL:
+            if c_ns.prefix or c_ns.href:
+                prefix = funicodeOrNone(c_ns.prefix)
+                if prefix not in nsmap:
+                    nsmap[prefix] = funicodeOrNone(c_ns.href)
+            c_ns = c_ns.next
+        c_node = c_node.parent
+    return nsmap
+
+
+cdef _iter_nsmap(nsmap):
+    """
+    Create a reproducibly ordered iterable from an nsmap mapping.
+    Tries to preserve an existing order and sorts if it assumes no order.
+
+    The difference to _iter_attrib() is that None doesn't sort with strings
+    in Py3.x.
+    """
+    if isinstance(nsmap, dict):
+        # dicts are insertion-ordered in Py3.6+ => keep the user provided order.
+        return nsmap.items()
+    if len(nsmap) <= 1:
+        return nsmap.items()
+    # nsmap will usually be a plain unordered dict => avoid type checking overhead
+    if type(nsmap) is not dict and isinstance(nsmap, OrderedDict):
+        return nsmap.items()  # keep existing order
+    if None not in nsmap:
+        return sorted(nsmap.items())
+
+    # Move the default namespace to the end.  This makes sure libxml2
+    # prefers a prefix if the ns is defined redundantly on the same
+    # element.  That way, users can work around a problem themselves
+    # where default namespace attributes on non-default namespaced
+    # elements serialise without prefix (i.e. into the non-default
+    # namespace).
+    default_ns = nsmap[None]
+    nsdefs = [(k, v) for k, v in nsmap.items() if k is not None]
+    nsdefs.sort()
+    nsdefs.append((None, default_ns))
+    return nsdefs
+
+
+cdef _iter_attrib(attrib):
+    """
+    Create a reproducibly ordered iterable from an attrib mapping.
+    Tries to preserve an existing order and sorts if it assumes no order.
+    """
+    # dicts are insertion-ordered in Py3.6+ => keep the user provided order.
+    if isinstance(attrib, (dict, _Attrib, OrderedDict)):
+        return attrib.items()
+    # assume it's an unordered mapping of some kind
+    return sorted(attrib.items())
+
+
+cdef _initNodeAttributes(xmlNode* c_node, _Document doc, attrib, dict extra):
+    """Initialise the attributes of an element node.
+    """
+    cdef bint is_html
+    cdef xmlNs* c_ns
+    if attrib is not None and not hasattr(attrib, 'items'):
+        raise TypeError, f"Invalid attribute dictionary: {python._fqtypename(attrib).decode('utf8')}"
+    if not attrib and not extra:
+        return  # nothing to do
+    is_html = doc._parser._for_html
+    seen = set()
+    if extra:
+        for name, value in extra.items():
+            _addAttributeToNode(c_node, doc, is_html, name, value, seen)
+    if attrib:
+        for name, value in _iter_attrib(attrib):
+            _addAttributeToNode(c_node, doc, is_html, name, value, seen)
+
+
+cdef int _addAttributeToNode(xmlNode* c_node, _Document doc, bint is_html,
+                             name, value, set seen_tags) except -1:
+    ns_utf, name_utf = tag = _getNsTag(name)
+    if tag in seen_tags:
+        return 0
+    seen_tags.add(tag)
+    if not is_html:
+        _attributeValidOrRaise(name_utf)
+    value_utf = _utf8(value)
+    if ns_utf is None:
+        tree.xmlNewProp(c_node, _xcstr(name_utf), _xcstr(value_utf))
+    else:
+        _uriValidOrRaise(ns_utf)
+        c_ns = doc._findOrBuildNodeNs(c_node, _xcstr(ns_utf), NULL, 1)
+        tree.xmlNewNsProp(c_node, c_ns,
+                          _xcstr(name_utf), _xcstr(value_utf))
+    return 0
+
+
+ctypedef struct _ns_node_ref:
+    xmlNs* ns
+    xmlNode* node
+
+
+cdef int _collectNsDefs(xmlNode* c_element, _ns_node_ref **_c_ns_list,
+                        size_t *_c_ns_list_len, size_t *_c_ns_list_size) except -1:
+    c_ns_list = _c_ns_list[0]
+    cdef size_t c_ns_list_len = _c_ns_list_len[0]
+    cdef size_t c_ns_list_size = _c_ns_list_size[0]
+
+    c_nsdef = c_element.nsDef
+    while c_nsdef is not NULL:
+        if c_ns_list_len >= c_ns_list_size:
+            if c_ns_list is NULL:
+                c_ns_list_size = 20
+            else:
+                c_ns_list_size *= 2
+            c_nsref_ptr = <_ns_node_ref*> python.lxml_realloc(
+                c_ns_list, c_ns_list_size, sizeof(_ns_node_ref))
+            if c_nsref_ptr is NULL:
+                if c_ns_list is not NULL:
+                    python.lxml_free(c_ns_list)
+                    _c_ns_list[0] = NULL
+                raise MemoryError()
+            c_ns_list = c_nsref_ptr
+
+        c_ns_list[c_ns_list_len] = _ns_node_ref(c_nsdef, c_element)
+        c_ns_list_len += 1
+        c_nsdef = c_nsdef.next
+
+    _c_ns_list_size[0] = c_ns_list_size
+    _c_ns_list_len[0] = c_ns_list_len
+    _c_ns_list[0] = c_ns_list
+
+
+cdef int _removeUnusedNamespaceDeclarations(xmlNode* c_element, set prefixes_to_keep) except -1:
+    """Remove any namespace declarations from a subtree that are not used by
+    any of its elements (or attributes).
+
+    If a 'prefixes_to_keep' is provided, it must be a set of prefixes.
+    Any corresponding namespace mappings will not be removed as part of the cleanup.
+    """
+    cdef xmlNode* c_node
+    cdef _ns_node_ref* c_ns_list = NULL
+    cdef size_t c_ns_list_size = 0
+    cdef size_t c_ns_list_len = 0
+    cdef size_t i
+
+    if c_element.parent and c_element.parent.type == tree.XML_DOCUMENT_NODE:
+        # include declarations on the document node
+        _collectNsDefs(c_element.parent, &c_ns_list, &c_ns_list_len, &c_ns_list_size)
+
+    tree.BEGIN_FOR_EACH_ELEMENT_FROM(c_element, c_element, 1)
+    # collect all new namespace declarations into the ns list
+    if c_element.nsDef:
+        _collectNsDefs(c_element, &c_ns_list, &c_ns_list_len, &c_ns_list_size)
+
+    # remove all namespace declarations from the list that are referenced
+    if c_ns_list_len and c_element.type == tree.XML_ELEMENT_NODE:
+        c_node = c_element
+        while c_node and c_ns_list_len:
+            if c_node.ns:
+                for i in range(c_ns_list_len):
+                    if c_node.ns is c_ns_list[i].ns:
+                        c_ns_list_len -= 1
+                        c_ns_list[i] = c_ns_list[c_ns_list_len]
+                        #c_ns_list[c_ns_list_len] = _ns_node_ref(NULL, NULL)
+                        break
+            if c_node is c_element:
+                # continue with attributes
+                c_node = <xmlNode*>c_element.properties
+            else:
+                c_node = c_node.next
+    tree.END_FOR_EACH_ELEMENT_FROM(c_element)
+
+    if c_ns_list is NULL:
+        return 0
+
+    # free all namespace declarations that remained in the list,
+    # except for those we should keep explicitly
+    cdef xmlNs* c_nsdef
+    for i in range(c_ns_list_len):
+        if prefixes_to_keep is not None:
+            if c_ns_list[i].ns.prefix and c_ns_list[i].ns.prefix in prefixes_to_keep:
+                continue
+        c_node = c_ns_list[i].node
+        c_nsdef = c_node.nsDef
+        if c_nsdef is c_ns_list[i].ns:
+            c_node.nsDef = c_node.nsDef.next
+        else:
+            while c_nsdef.next is not c_ns_list[i].ns:
+                c_nsdef = c_nsdef.next
+            c_nsdef.next = c_nsdef.next.next
+        tree.xmlFreeNs(c_ns_list[i].ns)
+    
+    if c_ns_list is not NULL:
+        python.lxml_free(c_ns_list)
+    return 0
+
+cdef xmlNs* _searchNsByHref(xmlNode* c_node, const_xmlChar* c_href, bint is_attribute) noexcept:
+    """Search a namespace declaration that covers a node (element or
+    attribute).
+
+    For attributes, try to find a prefixed namespace declaration
+    instead of the default namespaces.  This helps in supporting
+    round-trips for attributes on elements with a different namespace.
+    """
+    cdef xmlNs* c_ns
+    cdef xmlNs* c_default_ns = NULL
+    cdef xmlNode* c_element
+    if c_href is NULL or c_node is NULL or c_node.type == tree.XML_ENTITY_REF_NODE:
+        return NULL
+    if tree.xmlStrcmp(c_href, tree.XML_XML_NAMESPACE) == 0:
+        # no special cases here, let libxml2 handle this
+        return tree.xmlSearchNsByHref(c_node.doc, c_node, c_href)
+    if c_node.type == tree.XML_ATTRIBUTE_NODE:
+        is_attribute = 1
+    while c_node is not NULL and c_node.type != tree.XML_ELEMENT_NODE:
+        c_node = c_node.parent
+    c_element = c_node
+    while c_node is not NULL:
+        if c_node.type == tree.XML_ELEMENT_NODE:
+            c_ns = c_node.nsDef
+            while c_ns is not NULL:
+                if c_ns.href is not NULL and tree.xmlStrcmp(c_href, c_ns.href) == 0:
+                    if c_ns.prefix is NULL and is_attribute:
+                        # for attributes, continue searching a named
+                        # prefix, but keep the first default namespace
+                        # declaration that we found
+                        if c_default_ns is NULL:
+                            c_default_ns = c_ns
+                    elif tree.xmlSearchNs(
+                        c_element.doc, c_element, c_ns.prefix) is c_ns:
+                        # start node is in namespace scope => found!
+                        return c_ns
+                c_ns = c_ns.next
+            if c_node is not c_element and c_node.ns is not NULL:
+                # optimise: the node may have the namespace itself
+                c_ns = c_node.ns
+                if c_ns.href is not NULL and tree.xmlStrcmp(c_href, c_ns.href) == 0:
+                    if c_ns.prefix is NULL and is_attribute:
+                        # for attributes, continue searching a named
+                        # prefix, but keep the first default namespace
+                        # declaration that we found
+                        if c_default_ns is NULL:
+                            c_default_ns = c_ns
+                    elif tree.xmlSearchNs(
+                        c_element.doc, c_element, c_ns.prefix) is c_ns:
+                        # start node is in namespace scope => found!
+                        return c_ns
+        c_node = c_node.parent
+    # nothing found => use a matching default namespace or fail
+    if c_default_ns is not NULL:
+        if tree.xmlSearchNs(c_element.doc, c_element, NULL) is c_default_ns:
+            return c_default_ns
+    return NULL
+
+cdef int _replaceNodeByChildren(_Document doc, xmlNode* c_node) except -1:
+    # NOTE: this does not deallocate the node, just unlink it!
+    cdef xmlNode* c_parent
+    cdef xmlNode* c_child
+    if c_node.children is NULL:
+        tree.xmlUnlinkNode(c_node)
+        return 0
+
+    c_parent = c_node.parent
+    # fix parent links of children
+    c_child = c_node.children
+    while c_child is not NULL:
+        c_child.parent = c_parent
+        c_child = c_child.next
+
+    # fix namespace references of children if their parent's namespace
+    # declarations get lost
+    if c_node.nsDef is not NULL:
+        c_child = c_node.children
+        while c_child is not NULL:
+            moveNodeToDocument(doc, doc._c_doc, c_child)
+            c_child = c_child.next
+
+    # fix sibling links to/from child slice
+    if c_node.prev is NULL:
+        c_parent.children = c_node.children
+    else:
+        c_node.prev.next = c_node.children
+        c_node.children.prev = c_node.prev
+    if c_node.next is NULL:
+        c_parent.last = c_node.last
+    else:
+        c_node.next.prev = c_node.last
+        c_node.last.next = c_node.next
+
+    # unlink c_node
+    c_node.children = c_node.last = NULL
+    c_node.parent = c_node.next = c_node.prev = NULL
+    return 0
+
+cdef unicode _attributeValue(xmlNode* c_element, xmlAttr* c_attrib_node):
+    c_href = _getNs(<xmlNode*>c_attrib_node)
+    value = tree.xmlGetNsProp(c_element, c_attrib_node.name, c_href)
+    try:
+        result = funicode(value)
+    finally:
+        tree.xmlFree(value)
+    return result
+
+cdef unicode _attributeValueFromNsName(xmlNode* c_element,
+                                       const_xmlChar* c_href, const_xmlChar* c_name):
+    c_result = tree.xmlGetNsProp(c_element, c_name, c_href)
+    if c_result is NULL:
+        return None
+    try:
+        result = funicode(c_result)
+    finally:
+        tree.xmlFree(c_result)
+    return result
+
+cdef object _getNodeAttributeValue(xmlNode* c_node, key, default):
+    ns, tag = _getNsTag(key)
+    c_href = <const_xmlChar*>NULL if ns is None else _xcstr(ns)
+    c_result = tree.xmlGetNsProp(c_node, _xcstr(tag), c_href)
+    if c_result is NULL:
+        # XXX free namespace that is not in use..?
+        return default
+    try:
+        result = funicode(c_result)
+    finally:
+        tree.xmlFree(c_result)
+    return result
+
+cdef inline object _getAttributeValue(_Element element, key, default):
+    return _getNodeAttributeValue(element._c_node, key, default)
+
+cdef int _setAttributeValue(_Element element, key, value) except -1:
+    cdef const_xmlChar* c_value
+    cdef xmlNs* c_ns
+    ns, tag = _getNsTag(key)
+    is_html = element._doc._parser._for_html
+    if not is_html:
+        _attributeValidOrRaise(tag)
+    c_tag = _xcstr(tag)
+    if value is None and is_html:
+        c_value = NULL
+    else:
+        if isinstance(value, QName):
+            value = _resolveQNameText(element, value)
+        else:
+            value = _utf8(value)
+        c_value = _xcstr(value)
+    if ns is None:
+        c_ns = NULL
+    else:
+        c_ns = element._doc._findOrBuildNodeNs(element._c_node, _xcstr(ns), NULL, 1)
+    tree.xmlSetNsProp(element._c_node, c_ns, c_tag, c_value)
+    return 0
+
+cdef int _delAttribute(_Element element, key) except -1:
+    ns, tag = _getNsTag(key)
+    c_href = <const_xmlChar*>NULL if ns is None else _xcstr(ns)
+    if _delAttributeFromNsName(element._c_node, c_href, _xcstr(tag)):
+        raise KeyError, key
+    return 0
+
+cdef int _delAttributeFromNsName(xmlNode* c_node, const_xmlChar* c_href, const_xmlChar* c_name) noexcept:
+    c_attr = tree.xmlHasNsProp(c_node, c_name, c_href)
+    if c_attr is NULL:
+        # XXX free namespace that is not in use..?
+        return -1
+    tree.xmlRemoveProp(c_attr)
+    return 0
+
+cdef list _collectAttributes(xmlNode* c_node, int collecttype):
+    """Collect all attributes of a node in a list.  Depending on collecttype,
+    it collects either the name (1), the value (2) or the name-value tuples.
+    """
+    cdef Py_ssize_t count
+    c_attr = c_node.properties
+    count = 0
+    while c_attr is not NULL:
+        if c_attr.type == tree.XML_ATTRIBUTE_NODE:
+            count += 1
+        c_attr = c_attr.next
+
+    if not count:
+        return []
+
+    attributes = [None] * count
+    c_attr = c_node.properties
+    count = 0
+    while c_attr is not NULL:
+        if c_attr.type == tree.XML_ATTRIBUTE_NODE:
+            if collecttype == 1:
+                item = _namespacedName(<xmlNode*>c_attr)
+            elif collecttype == 2:
+                item = _attributeValue(c_node, c_attr)
+            else:
+                item = (_namespacedName(<xmlNode*>c_attr),
+                        _attributeValue(c_node, c_attr))
+            attributes[count] = item
+            count += 1
+        c_attr = c_attr.next
+    return attributes
+
+cdef object __RE_XML_ENCODING = re.compile(
+    r'^(<\?xml[^>]+)\s+encoding\s*=\s*["\'][^"\']*["\'](\s*\?>|)', re.U)
+
+cdef object __REPLACE_XML_ENCODING = __RE_XML_ENCODING.sub
+cdef object __HAS_XML_ENCODING = __RE_XML_ENCODING.match
+
+cdef object _stripEncodingDeclaration(object xml_string):
+    # this is a hack to remove the XML encoding declaration from unicode
+    return __REPLACE_XML_ENCODING(r'\g<1>\g<2>', xml_string)
+
+cdef bint _hasEncodingDeclaration(object xml_string) except -1:
+    # check if a (unicode) string has an XML encoding declaration
+    return __HAS_XML_ENCODING(xml_string) is not None
+
+cdef inline bint _hasText(xmlNode* c_node) noexcept:
+    return c_node is not NULL and _textNodeOrSkip(c_node.children) is not NULL
+
+cdef inline bint _hasTail(xmlNode* c_node) noexcept:
+    return c_node is not NULL and _textNodeOrSkip(c_node.next) is not NULL
+
+cdef inline bint _hasNonWhitespaceTail(xmlNode* c_node) except -1:
+    return _hasNonWhitespaceText(c_node, tail=True)
+
+cdef bint _hasNonWhitespaceText(xmlNode* c_node, bint tail=False) except -1:
+    c_text_node = c_node and _textNodeOrSkip(c_node.next if tail else c_node.children)
+    if c_text_node is NULL:
+        return False
+    while c_text_node is not NULL:
+        if c_text_node.content[0] != c'\0' and not _collectText(c_text_node).isspace():
+            return True
+        c_text_node = _textNodeOrSkip(c_text_node.next)
+    return False
+
+cdef unicode _collectText(xmlNode* c_node):
+    """Collect all text nodes and return them as a unicode string.
+
+    Start collecting at c_node.
+    
+    If there was no text to collect, return None
+    """
+    cdef Py_ssize_t scount
+    cdef xmlChar* c_text
+    cdef xmlNode* c_node_cur
+    # check for multiple text nodes
+    scount = 0
+    c_text = NULL
+    c_node_cur = c_node = _textNodeOrSkip(c_node)
+    while c_node_cur is not NULL:
+        if c_node_cur.content[0] != c'\0':
+            c_text = c_node_cur.content
+        scount += 1
+        c_node_cur = _textNodeOrSkip(c_node_cur.next)
+
+    # handle two most common cases first
+    if c_text is NULL:
+        return '' if scount > 0 else None
+    if scount == 1:
+        return funicode(c_text)
+
+    # the rest is not performance critical anymore
+    result = b''
+    while c_node is not NULL:
+        result += <unsigned char*>c_node.content
+        c_node = _textNodeOrSkip(c_node.next)
+    return funicode(<const_xmlChar*><unsigned char*>result)
+
+cdef void _removeText(xmlNode* c_node) noexcept:
+    """Remove all text nodes.
+
+    Start removing at c_node.
+    """
+    cdef xmlNode* c_next
+    c_node = _textNodeOrSkip(c_node)
+    while c_node is not NULL:
+        c_next = _textNodeOrSkip(c_node.next)
+        tree.xmlUnlinkNode(c_node)
+        tree.xmlFreeNode(c_node)
+        c_node = c_next
+
+cdef xmlNode* _createTextNode(xmlDoc* doc, value) except NULL:
+    cdef xmlNode* c_text_node
+    if isinstance(value, CDATA):
+        c_text_node = tree.xmlNewCDataBlock(
+            doc, _xcstr((<CDATA>value)._utf8_data),
+            python.PyBytes_GET_SIZE((<CDATA>value)._utf8_data))
+    else:
+        text = _utf8(value)
+        c_text_node = tree.xmlNewDocText(doc, _xcstr(text))
+    if not c_text_node:
+        raise MemoryError()
+    return c_text_node
+
+cdef int _setNodeText(xmlNode* c_node, value) except -1:
+    # remove all text nodes at the start first
+    _removeText(c_node.children)
+    if value is None:
+        return 0
+    # now add new text node with value at start
+    c_text_node = _createTextNode(c_node.doc, value)
+    if c_node.children is NULL:
+        tree.xmlAddChild(c_node, c_text_node)
+    else:
+        tree.xmlAddPrevSibling(c_node.children, c_text_node)
+    return 0
+
+cdef int _setTailText(xmlNode* c_node, value) except -1:
+    # remove all text nodes at the start first
+    _removeText(c_node.next)
+    if value is None:
+        return 0
+    # now append new text node with value
+    c_text_node = _createTextNode(c_node.doc, value)
+    tree.xmlAddNextSibling(c_node, c_text_node)
+    return 0
+
+cdef bytes _resolveQNameText(_Element element, value):
+    cdef xmlNs* c_ns
+    ns, tag = _getNsTag(value)
+    if ns is None:
+        return tag
+    else:
+        c_ns = element._doc._findOrBuildNodeNs(
+            element._c_node, _xcstr(ns), NULL, 0)
+        return python.PyBytes_FromFormat('%s:%s', c_ns.prefix, _cstr(tag))
+
+cdef inline bint _hasChild(xmlNode* c_node) noexcept:
+    return c_node is not NULL and _findChildForwards(c_node, 0) is not NULL
+
+cdef inline Py_ssize_t _countElements(xmlNode* c_node) noexcept:
+    "Counts the elements within the following siblings and the node itself."
+    cdef Py_ssize_t count
+    count = 0
+    while c_node is not NULL:
+        if _isElement(c_node):
+            count += 1
+        c_node = c_node.next
+    return count
+
+cdef int _findChildSlice(
+    slice sliceobject, xmlNode* c_parent,
+    xmlNode** c_start_node, Py_ssize_t* c_step, Py_ssize_t* c_length) except -1:
+    """Resolve a children slice.
+
+    Returns the start node, step size and the slice length in the
+    pointer arguments.
+    """
+    cdef Py_ssize_t start = 0, stop = 0, childcount
+    childcount = _countElements(c_parent.children)
+    if childcount == 0:
+        c_start_node[0] = NULL
+        c_length[0] = 0
+        if sliceobject.step is None:
+            c_step[0] = 1
+        else:
+            python._PyEval_SliceIndex(sliceobject.step, c_step)
+        return 0
+    python.PySlice_GetIndicesEx(
+        sliceobject, childcount, &start, &stop, c_step, c_length)
+    if start > childcount // 2:
+        c_start_node[0] = _findChildBackwards(c_parent, childcount - start - 1)
+    else:
+        c_start_node[0] = _findChild(c_parent, start)
+    return 0
+
+cdef bint _isFullSlice(slice sliceobject) except -1:
+    """Conservative guess if this slice is a full slice as in ``s[:]``.
+    """
+    cdef Py_ssize_t step = 0
+    if sliceobject is None:
+        return 0
+    if sliceobject.start is None and \
+            sliceobject.stop is None:
+        if sliceobject.step is None:
+            return 1
+        python._PyEval_SliceIndex(sliceobject.step, &step)
+        if step == 1:
+            return 1
+        return 0
+    return 0
+
+cdef _collectChildren(_Element element):
+    cdef xmlNode* c_node
+    cdef list result = []
+    c_node = element._c_node.children
+    if c_node is not NULL:
+        if not _isElement(c_node):
+            c_node = _nextElement(c_node)
+        while c_node is not NULL:
+            result.append(_elementFactory(element._doc, c_node))
+            c_node = _nextElement(c_node)
+    return result
+
+cdef inline xmlNode* _findChild(xmlNode* c_node, Py_ssize_t index) noexcept:
+    if index < 0:
+        return _findChildBackwards(c_node, -index - 1)
+    else:
+        return _findChildForwards(c_node, index)
+    
+cdef inline xmlNode* _findChildForwards(xmlNode* c_node, Py_ssize_t index) noexcept:
+    """Return child element of c_node with index, or return NULL if not found.
+    """
+    cdef xmlNode* c_child
+    cdef Py_ssize_t c
+    c_child = c_node.children
+    c = 0
+    while c_child is not NULL:
+        if _isElement(c_child):
+            if c == index:
+                return c_child
+            c += 1
+        c_child = c_child.next
+    return NULL
+
+cdef inline xmlNode* _findChildBackwards(xmlNode* c_node, Py_ssize_t index) noexcept:
+    """Return child element of c_node with index, or return NULL if not found.
+    Search from the end.
+    """
+    cdef xmlNode* c_child
+    cdef Py_ssize_t c
+    c_child = c_node.last
+    c = 0
+    while c_child is not NULL:
+        if _isElement(c_child):
+            if c == index:
+                return c_child
+            c += 1
+        c_child = c_child.prev
+    return NULL
+    
+cdef inline xmlNode* _textNodeOrSkip(xmlNode* c_node) noexcept nogil:
+    """Return the node if it's a text node.  Skip over ignorable nodes in a
+    series of text nodes.  Return NULL if a non-ignorable node is found.
+
+    This is used to skip over XInclude nodes when collecting adjacent text
+    nodes.
+    """
+    while c_node is not NULL:
+        if c_node.type == tree.XML_TEXT_NODE or \
+               c_node.type == tree.XML_CDATA_SECTION_NODE:
+            return c_node
+        elif c_node.type == tree.XML_XINCLUDE_START or \
+                 c_node.type == tree.XML_XINCLUDE_END:
+            c_node = c_node.next
+        else:
+            return NULL
+    return NULL
+
+cdef inline xmlNode* _nextElement(xmlNode* c_node) noexcept:
+    """Given a node, find the next sibling that is an element.
+    """
+    if c_node is NULL:
+        return NULL
+    c_node = c_node.next
+    while c_node is not NULL:
+        if _isElement(c_node):
+            return c_node
+        c_node = c_node.next
+    return NULL
+
+cdef inline xmlNode* _previousElement(xmlNode* c_node) noexcept:
+    """Given a node, find the next sibling that is an element.
+    """
+    if c_node is NULL:
+        return NULL
+    c_node = c_node.prev
+    while c_node is not NULL:
+        if _isElement(c_node):
+            return c_node
+        c_node = c_node.prev
+    return NULL
+
+cdef inline xmlNode* _parentElement(xmlNode* c_node) noexcept:
+    "Given a node, find the parent element."
+    if c_node is NULL or not _isElement(c_node):
+        return NULL
+    c_node = c_node.parent
+    if c_node is NULL or not _isElement(c_node):
+        return NULL
+    return c_node
+
+cdef inline bint _tagMatches(xmlNode* c_node, const_xmlChar* c_href, const_xmlChar* c_name) noexcept:
+    """Tests if the node matches namespace URI and tag name.
+
+    A node matches if it matches both c_href and c_name.
+
+    A node matches c_href if any of the following is true:
+    * c_href is NULL
+    * its namespace is NULL and c_href is the empty string
+    * its namespace string equals the c_href string
+
+    A node matches c_name if any of the following is true:
+    * c_name is NULL
+    * its name string equals the c_name string
+    """
+    if c_node is NULL:
+        return 0
+    if c_node.type != tree.XML_ELEMENT_NODE:
+        # not an element, only succeed if we match everything
+        return c_name is NULL and c_href is NULL
+    if c_name is NULL:
+        if c_href is NULL:
+            # always match
+            return 1
+        else:
+            c_node_href = _getNs(c_node)
+            if c_node_href is NULL:
+                return c_href[0] == c'\0'
+            else:
+                return tree.xmlStrcmp(c_node_href, c_href) == 0
+    elif c_href is NULL:
+        if _getNs(c_node) is not NULL:
+            return 0
+        return c_node.name == c_name or tree.xmlStrcmp(c_node.name, c_name) == 0
+    elif c_node.name == c_name or tree.xmlStrcmp(c_node.name, c_name) == 0:
+        c_node_href = _getNs(c_node)
+        if c_node_href is NULL:
+            return c_href[0] == c'\0'
+        else:
+            return tree.xmlStrcmp(c_node_href, c_href) == 0
+    else:
+        return 0
+
+cdef inline bint _tagMatchesExactly(xmlNode* c_node, qname* c_qname) noexcept:
+    """Tests if the node matches namespace URI and tag name.
+
+    This differs from _tagMatches() in that it does not consider a
+    NULL value in qname.href a wildcard, and that it expects the c_name
+    to be taken from the doc dict, i.e. it only compares the names by
+    address.
+
+    A node matches if it matches both href and c_name of the qname.
+
+    A node matches c_href if any of the following is true:
+    * its namespace is NULL and c_href is the empty string
+    * its namespace string equals the c_href string
+
+    A node matches c_name if any of the following is true:
+    * c_name is NULL
+    * its name string points to the same address (!) as c_name
+    """
+    return _nsTagMatchesExactly(_getNs(c_node), c_node.name, c_qname)
+
+cdef inline bint _nsTagMatchesExactly(const_xmlChar* c_node_href,
+                                      const_xmlChar* c_node_name,
+                                      qname* c_qname) noexcept:
+    """Tests if name and namespace URI match those of c_qname.
+
+    This differs from _tagMatches() in that it does not consider a
+    NULL value in qname.href a wildcard, and that it expects the c_name
+    to be taken from the doc dict, i.e. it only compares the names by
+    address.
+
+    A node matches if it matches both href and c_name of the qname.
+
+    A node matches c_href if any of the following is true:
+    * its namespace is NULL and c_href is the empty string
+    * its namespace string equals the c_href string
+
+    A node matches c_name if any of the following is true:
+    * c_name is NULL
+    * its name string points to the same address (!) as c_name
+    """
+    cdef char* c_href
+    if c_qname.c_name is not NULL and c_qname.c_name is not c_node_name:
+        return 0
+    if c_qname.href is NULL:
+        return 1
+    c_href = python.__cstr(c_qname.href)
+    if c_href[0] == b'\0':
+        return c_node_href is NULL or c_node_href[0] == b'\0'
+    elif c_node_href is NULL:
+        return 0
+    else:
+        return tree.xmlStrcmp(<const_xmlChar*>c_href, c_node_href) == 0
+
+cdef Py_ssize_t _mapTagsToQnameMatchArray(xmlDoc* c_doc, list ns_tags,
+                                          qname* c_ns_tags, bint force_into_dict) except -1:
+    """Map a sequence of (name, namespace) pairs to a qname array for efficient
+    matching with _tagMatchesExactly() above.
+
+    Note that each qname struct in the array owns its href byte string object
+    if it is not NULL.
+    """
+    cdef Py_ssize_t count = 0, i
+    cdef bytes ns, tag
+    for ns, tag in ns_tags:
+        if tag is None:
+            c_tag = <const_xmlChar*>NULL
+        elif force_into_dict:
+            c_tag = tree.xmlDictLookup(c_doc.dict, _xcstr(tag), len(tag))
+            if c_tag is NULL:
+                # clean up before raising the error
+                for i in xrange(count):
+                    cpython.ref.Py_XDECREF(c_ns_tags[i].href)
+                raise MemoryError()
+        else:
+            c_tag = tree.xmlDictExists(c_doc.dict, _xcstr(tag), len(tag))
+            if c_tag is NULL:
+                # not in the dict => not in the document
+                continue
+        c_ns_tags[count].c_name = c_tag
+        if ns is None:
+            c_ns_tags[count].href = NULL
+        else:
+            cpython.ref.Py_INCREF(ns) # keep an owned reference!
+            c_ns_tags[count].href = <python.PyObject*>ns
+        count += 1
+    return count
+
+cdef int _removeNode(_Document doc, xmlNode* c_node) except -1:
+    """Unlink and free a node and subnodes if possible.  Otherwise, make sure
+    it's self-contained.
+    """
+    cdef xmlNode* c_next
+    c_next = c_node.next
+    tree.xmlUnlinkNode(c_node)
+    _moveTail(c_next, c_node)
+    if not attemptDeallocation(c_node):
+        # make namespaces absolute
+        moveNodeToDocument(doc, c_node.doc, c_node)
+    return 0
+
+cdef int _removeSiblings(xmlNode* c_element, tree.xmlElementType node_type, bint with_tail) except -1:
+    cdef xmlNode* c_node
+    cdef xmlNode* c_next
+    c_node = c_element.next
+    while c_node is not NULL:
+        c_next = _nextElement(c_node)
+        if c_node.type == node_type:
+            if with_tail:
+                _removeText(c_node.next)
+            tree.xmlUnlinkNode(c_node)
+            attemptDeallocation(c_node)
+        c_node = c_next
+    c_node = c_element.prev
+    while c_node is not NULL:
+        c_next = _previousElement(c_node)
+        if c_node.type == node_type:
+            if with_tail:
+                _removeText(c_node.next)
+            tree.xmlUnlinkNode(c_node)
+            attemptDeallocation(c_node)
+        c_node = c_next
+    return 0
+
+cdef void _moveTail(xmlNode* c_tail, xmlNode* c_target) noexcept:
+    cdef xmlNode* c_next
+    # tail support: look for any text nodes trailing this node and 
+    # move them too
+    c_tail = _textNodeOrSkip(c_tail)
+    while c_tail is not NULL:
+        c_next = _textNodeOrSkip(c_tail.next)
+        c_target = tree.xmlAddNextSibling(c_target, c_tail)
+        c_tail = c_next
+
+cdef int _copyTail(xmlNode* c_tail, xmlNode* c_target) except -1:
+    cdef xmlNode* c_new_tail
+    # tail copying support: look for any text nodes trailing this node and
+    # copy it to the target node
+    c_tail = _textNodeOrSkip(c_tail)
+    while c_tail is not NULL:
+        if c_target.doc is not c_tail.doc:
+            c_new_tail = tree.xmlDocCopyNode(c_tail, c_target.doc, 0)
+        else:
+            c_new_tail = tree.xmlCopyNode(c_tail, 0)
+        if c_new_tail is NULL:
+            raise MemoryError()
+        c_target = tree.xmlAddNextSibling(c_target, c_new_tail)
+        c_tail = _textNodeOrSkip(c_tail.next)
+    return 0
+
+cdef int _copyNonElementSiblings(xmlNode* c_node, xmlNode* c_target) except -1:
+    cdef xmlNode* c_copy
+    cdef xmlNode* c_sibling = c_node
+    while c_sibling.prev != NULL and \
+            (c_sibling.prev.type == tree.XML_PI_NODE or
+             c_sibling.prev.type == tree.XML_COMMENT_NODE or
+             c_sibling.prev.type == tree.XML_DTD_NODE):
+        c_sibling = c_sibling.prev
+    while c_sibling != c_node:
+        if c_sibling.type == tree.XML_DTD_NODE:
+            c_copy = <xmlNode*>_copyDtd(<tree.xmlDtd*>c_sibling)
+            if c_sibling == <xmlNode*>c_node.doc.intSubset:
+                c_target.doc.intSubset = <tree.xmlDtd*>c_copy
+            else: # c_sibling == c_node.doc.extSubset
+                c_target.doc.extSubset = <tree.xmlDtd*>c_copy
+        else:
+            c_copy = tree.xmlDocCopyNode(c_sibling, c_target.doc, 1)
+            if c_copy is NULL:
+                raise MemoryError()
+        tree.xmlAddPrevSibling(c_target, c_copy)
+        c_sibling = c_sibling.next
+    while c_sibling.next != NULL and \
+            (c_sibling.next.type == tree.XML_PI_NODE or
+             c_sibling.next.type == tree.XML_COMMENT_NODE):
+        c_sibling = c_sibling.next
+        c_copy = tree.xmlDocCopyNode(c_sibling, c_target.doc, 1)
+        if c_copy is NULL:
+            raise MemoryError()
+        tree.xmlAddNextSibling(c_target, c_copy)
+
+cdef int _deleteSlice(_Document doc, xmlNode* c_node,
+                      Py_ssize_t count, Py_ssize_t step) except -1:
+    """Delete slice, ``count`` items starting with ``c_node`` with a step
+    width of ``step``.
+    """
+    cdef xmlNode* c_next
+    cdef Py_ssize_t c, i
+    cdef _node_to_node_function next_element
+    if c_node is NULL:
+        return 0
+    if step > 0:
+        next_element = _nextElement
+    else:
+        step = -step
+        next_element = _previousElement
+    # now start deleting nodes
+    c = 0
+    c_next = c_node
+    while c_node is not NULL and c < count:
+        for i in range(step):
+            c_next = next_element(c_next)
+            if c_next is NULL:
+                break
+        _removeNode(doc, c_node)
+        c += 1
+        c_node = c_next
+    return 0
+
+cdef int _replaceSlice(_Element parent, xmlNode* c_node,
+                       Py_ssize_t slicelength, Py_ssize_t step,
+                       bint left_to_right, elements) except -1:
+    """Replace the slice of ``count`` elements starting at ``c_node`` with
+    positive step width ``step`` by the Elements in ``elements``.  The
+    direction is given by the boolean argument ``left_to_right``.
+
+    ``c_node`` may be NULL to indicate the end of the children list.
+    """
+    cdef xmlNode* c_orig_neighbour
+    cdef xmlNode* c_next
+    cdef xmlDoc*  c_source_doc
+    cdef _Element element
+    cdef Py_ssize_t seqlength, i, c
+    cdef _node_to_node_function next_element
+    assert step > 0
+    if left_to_right:
+        next_element = _nextElement
+    else:
+        next_element = _previousElement
+
+    if not isinstance(elements, (list, tuple)):
+        elements = list(elements)
+
+    if step != 1 or not left_to_right:
+        # *replacing* children stepwise with list => check size!
+        seqlength = len(elements)
+        if seqlength != slicelength:
+            raise ValueError, f"attempt to assign sequence of size {seqlength} " \
+                f"to extended slice of size {slicelength}"
+
+    if c_node is NULL:
+        # no children yet => add all elements straight away
+        if left_to_right:
+            for element in elements:
+                assert element is not None, "Node must not be None"
+                _appendChild(parent, element)
+        else:
+            for element in elements:
+                assert element is not None, "Node must not be None"
+                _prependChild(parent, element)
+        return 0
+
+    # remove the elements first as some might be re-added
+    if left_to_right:
+        # L->R, remember left neighbour
+        c_orig_neighbour = _previousElement(c_node)
+    else:
+        # R->L, remember right neighbour
+        c_orig_neighbour = _nextElement(c_node)
+
+    # We remove the original slice elements one by one. Since we hold
+    # a Python reference to all elements that we will insert, it is
+    # safe to let _removeNode() try (and fail) to free them even if
+    # the element itself or one of its descendents will be reinserted.
+    c = 0
+    c_next = c_node
+    while c_node is not NULL and c < slicelength:
+        for i in range(step):
+            c_next = next_element(c_next)
+            if c_next is NULL:
+                break
+        _removeNode(parent._doc, c_node)
+        c += 1
+        c_node = c_next
+
+    # make sure each element is inserted only once
+    elements = iter(elements)
+
+    # find the first node right of the new insertion point
+    if left_to_right:
+        if c_orig_neighbour is not NULL:
+            c_node = next_element(c_orig_neighbour)
+        else:
+            # before the first element
+            c_node = _findChildForwards(parent._c_node, 0)
+    elif c_orig_neighbour is NULL:
+        # at the end, but reversed stepping
+        # append one element and go to the next insertion point
+        for element in elements:
+            assert element is not None, "Node must not be None"
+            _appendChild(parent, element)
+            c_node = element._c_node
+            if slicelength > 0:
+                slicelength -= 1
+                for i in range(1, step):
+                    c_node = next_element(c_node)
+                    if c_node is NULL:
+                        break
+            break
+    else:
+        c_node = c_orig_neighbour
+
+    if left_to_right:
+        # adjust step size after removing slice as we are not stepping
+        # over the newly inserted elements
+        step -= 1
+
+    # now insert elements where we removed them
+    if c_node is not NULL:
+        for element in elements:
+            assert element is not None, "Node must not be None"
+            _assertValidNode(element)
+            # move element and tail over
+            c_source_doc = element._c_node.doc
+            c_next = element._c_node.next
+            tree.xmlAddPrevSibling(c_node, element._c_node)
+            _moveTail(c_next, element._c_node)
+
+            # integrate element into new document
+            moveNodeToDocument(parent._doc, c_source_doc, element._c_node)
+
+            # stop at the end of the slice
+            if slicelength > 0:
+                slicelength -= 1
+                for i in range(step):
+                    c_node = next_element(c_node)
+                    if c_node is NULL:
+                        break
+                if c_node is NULL:
+                    break
+        else:
+            # everything inserted
+            return 0
+
+    # append the remaining elements at the respective end
+    if left_to_right:
+        for element in elements:
+            assert element is not None, "Node must not be None"
+            _assertValidNode(element)
+            _appendChild(parent, element)
+    else:
+        for element in elements:
+            assert element is not None, "Node must not be None"
+            _assertValidNode(element)
+            _prependChild(parent, element)
+
+    return 0
+
+
+cdef int _linkChild(xmlNode* c_parent, xmlNode* c_node) except -1:
+    """Adaptation of 'xmlAddChild()' that deep-fix the document links iteratively.
+    """
+    assert _isElement(c_node)
+    c_node.parent = c_parent
+    if c_parent.children is NULL:
+        c_parent.children = c_parent.last = c_node
+    else:
+        c_node.prev = c_parent.last
+        c_parent.last.next = c_node
+        c_parent.last = c_node
+
+    _setTreeDoc(c_node, c_parent.doc)
+    return 0
+
+
+cdef int _appendChild(_Element parent, _Element child) except -1:
+    """Append a new child to a parent element.
+    """
+    c_node = child._c_node
+    c_source_doc = c_node.doc
+    # prevent cycles
+    if _isAncestorOrSame(c_node, parent._c_node):
+        raise ValueError("cannot append parent to itself")
+    # store possible text node
+    c_next = c_node.next
+    # move node itself
+    tree.xmlUnlinkNode(c_node)
+    # do not call xmlAddChild() here since it would deep-traverse the tree
+    _linkChild(parent._c_node, c_node)
+    _moveTail(c_next, c_node)
+    # uh oh, elements may be pointing to different doc when
+    # parent element has moved; change them too..
+    moveNodeToDocument(parent._doc, c_source_doc, c_node)
+    return 0
+
+cdef int _prependChild(_Element parent, _Element child) except -1:
+    """Prepend a new child to a parent element.
+    """
+    c_node = child._c_node
+    c_source_doc = c_node.doc
+    # prevent cycles
+    if _isAncestorOrSame(c_node, parent._c_node):
+        raise ValueError("cannot append parent to itself")
+    # store possible text node
+    c_next = c_node.next
+    # move node itself
+    c_child = _findChildForwards(parent._c_node, 0)
+    if c_child is NULL:
+        tree.xmlUnlinkNode(c_node)
+        # do not call xmlAddChild() here since it would deep-traverse the tree
+        _linkChild(parent._c_node, c_node)
+    else:
+        tree.xmlAddPrevSibling(c_child, c_node)
+    _moveTail(c_next, c_node)
+    # uh oh, elements may be pointing to different doc when
+    # parent element has moved; change them too..
+    moveNodeToDocument(parent._doc, c_source_doc, c_node)
+    return 0
+
+cdef int _appendSibling(_Element element, _Element sibling) except -1:
+    """Add a new sibling behind an element.
+    """
+    return _addSibling(element, sibling, as_next=True)
+
+cdef int _prependSibling(_Element element, _Element sibling) except -1:
+    """Add a new sibling before an element.
+    """
+    return _addSibling(element, sibling, as_next=False)
+
+cdef int _addSibling(_Element element, _Element sibling, bint as_next) except -1:
+    c_node = sibling._c_node
+    c_source_doc = c_node.doc
+    # prevent cycles
+    if _isAncestorOrSame(c_node, element._c_node):
+        if element._c_node is c_node:
+            return 0  # nothing to do
+        raise ValueError("cannot add ancestor as sibling, please break cycle first")
+    # store possible text node
+    c_next = c_node.next
+    # move node itself
+    if as_next:
+        # must insert after any tail text
+        c_next_node = _nextElement(element._c_node)
+        if c_next_node is NULL:
+            c_next_node = element._c_node
+            while c_next_node.next:
+                c_next_node = c_next_node.next
+            tree.xmlAddNextSibling(c_next_node, c_node)
+        else:
+            tree.xmlAddPrevSibling(c_next_node, c_node)
+    else:
+        tree.xmlAddPrevSibling(element._c_node, c_node)
+    _moveTail(c_next, c_node)
+    # uh oh, elements may be pointing to different doc when
+    # parent element has moved; change them too..
+    moveNodeToDocument(element._doc, c_source_doc, c_node)
+    return 0
+
+cdef inline bint isutf8(const_xmlChar* s) noexcept:
+    cdef xmlChar c = s[0]
+    while c != c'\0':
+        if c & 0x80:
+            return True
+        s += 1
+        c = s[0]
+    return False
+
+cdef bint isutf8l(const_xmlChar* s, size_t length) noexcept:
+    """
+    Search for non-ASCII characters in the string, knowing its length in advance.
+    """
+    cdef unsigned int i
+    cdef unsigned long non_ascii_mask
+    cdef const unsigned long *lptr = <const unsigned long*> s
+
+    cdef const unsigned long *end = lptr + length // sizeof(unsigned long)
+    if length >= sizeof(non_ascii_mask):
+        # Build constant 0x80808080... mask (and let the C compiler fold it).
+        non_ascii_mask = 0
+        for i in range(sizeof(non_ascii_mask) // 2):
+            non_ascii_mask = (non_ascii_mask << 16) | 0x8080
+
+        # Advance to long-aligned character before we start reading longs.
+        while (<size_t>s) % sizeof(unsigned long) and s < <const_xmlChar *>end:
+            if s[0] & 0x80:
+                return True
+            s += 1
+
+        # Read one long at a time
+        lptr = <const unsigned long*> s
+        while lptr < end:
+            if lptr[0] & non_ascii_mask:
+                return True
+            lptr += 1
+        s = <const_xmlChar *>lptr
+
+    while s < (<const_xmlChar *>end + length % sizeof(unsigned long)):
+        if s[0] & 0x80:
+            return True
+        s += 1
+
+    return False
+
+cdef int _is_valid_xml_ascii(bytes pystring) except -1:
+    """Check if a string is XML ascii content."""
+    cdef signed char ch
+    # When ch is a *signed* char, non-ascii characters are negative integers
+    # and xmlIsChar_ch does not accept them.
+    for ch in pystring:
+        if not tree.xmlIsChar_ch(ch):
+            return 0
+    return 1
+
+cdef bint _is_valid_xml_utf8(bytes pystring) except -1:
+    """Check if a string is like valid UTF-8 XML content."""
+    cdef const_xmlChar* s = _xcstr(pystring)
+    cdef const_xmlChar* c_end = s + len(pystring)
+    cdef unsigned long next3 = 0
+    if s < c_end - 2:
+        next3 = (s[0] << 8) | (s[1])
+
+    while s < c_end - 2:
+        next3 = 0x00ffffff & ((next3 << 8) | s[2])
+        if s[0] & 0x80:
+            # 0xefbfbe and 0xefbfbf are utf-8 encodings of
+            # forbidden characters \ufffe and \uffff
+            if next3 == 0x00efbfbe or next3 == 0x00efbfbf:
+                return 0
+            # 0xeda080 and 0xedbfbf are utf-8 encodings of
+            # \ud800 and \udfff. Anything between them (inclusive)
+            # is forbidden, because they are surrogate blocks in utf-16.
+            if 0x00eda080 <= next3 <= 0x00edbfbf:
+                return 0
+        elif not tree.xmlIsChar_ch(s[0]):
+            return 0  # invalid ascii char
+        s += 1
+
+    while s < c_end:
+        if not s[0] & 0x80 and not tree.xmlIsChar_ch(s[0]):
+            return 0  # invalid ascii char
+        s += 1
+
+    return 1
+
+cdef inline unicode funicodeOrNone(const_xmlChar* s):
+    return funicode(s) if s is not NULL else None
+
+cdef inline unicode funicodeOrEmpty(const_xmlChar* s):
+    return funicode(s) if s is not NULL else ''
+
+cdef unicode funicode(const_xmlChar* s):
+    return s.decode('UTF-8')
+
+cdef bytes _utf8(object s):
+    """Test if a string is valid user input and encode it to UTF-8.
+    Reject all bytes/unicode input that contains non-XML characters.
+    Reject all bytes input that contains non-ASCII characters.
+    """
+    cdef int valid
+    cdef bytes utf8_string
+    if isinstance(s, unicode):
+        utf8_string = (<unicode>s).encode('utf8')
+        valid = _is_valid_xml_utf8(utf8_string)
+    elif isinstance(s, (bytes, bytearray)):
+        utf8_string = s if type(s) is bytes else bytes(s)
+        valid = _is_valid_xml_ascii(utf8_string)
+    else:
+        raise TypeError("Argument must be bytes or unicode, got '%.200s'" % type(s).__name__)
+    if not valid:
+        raise ValueError(
+            "All strings must be XML compatible: Unicode or ASCII, no NULL bytes or control characters")
+    return utf8_string
+
+
+cdef bytes _utf8orNone(object s):
+    return _utf8(s) if s is not None else None
+
+
+cdef enum:
+    NO_FILE_PATH = 0
+    ABS_UNIX_FILE_PATH = 1
+    ABS_WIN_FILE_PATH = 2
+    REL_FILE_PATH = 3
+
+
+cdef bint _isFilePath(const_xmlChar* c_path) noexcept:
+    "simple heuristic to see if a path is a filename"
+    cdef xmlChar c
+    # test if it looks like an absolute Unix path or a Windows network path
+    if c_path[0] == c'/':
+        return ABS_UNIX_FILE_PATH
+
+    # test if it looks like an absolute Windows path or URL
+    if c'a' <= c_path[0] <= c'z' or c'A' <= c_path[0] <= c'Z':
+        c_path += 1
+        if c_path[0] == c':' and c_path[1] in b'\0\\':
+            return ABS_WIN_FILE_PATH  # C: or C:\...
+
+        # test if it looks like a URL with scheme://
+        while c'a' <= c_path[0] <= c'z' or c'A' <= c_path[0] <= c'Z':
+            c_path += 1
+        if c_path[0] == c':' and c_path[1] == c'/' and c_path[2] == c'/':
+            return NO_FILE_PATH
+
+    # assume it's a relative path
+    return REL_FILE_PATH
+
+
+cdef object _getFSPathOrObject(object obj):
+    """
+    Get the __fspath__ attribute of an object if it exists.
+    Otherwise, the original object is returned.
+    """
+    if _isString(obj):
+        return obj
+    try:
+        return python.PyOS_FSPath(obj)
+    except TypeError:
+        return obj
+
+
+cdef object _encodeFilename(object filename):
+    """Make sure a filename is 8-bit encoded (or None).
+    """
+    if filename is None:
+        return None
+    elif isinstance(filename, bytes):
+        return filename
+    elif isinstance(filename, unicode):
+        filename8 = (<unicode>filename).encode('utf8')
+        if _isFilePath(<unsigned char*>filename8):
+            try:
+                return python.PyUnicode_AsEncodedString(
+                    filename, _C_FILENAME_ENCODING, NULL)
+            except UnicodeEncodeError:
+                pass
+        return filename8
+    else:
+        raise TypeError("Argument must be string or unicode.")
+
+cdef object _decodeFilename(const_xmlChar* c_path):
+    """Make the filename a unicode string if we are in Py3.
+    """
+    return _decodeFilenameWithLength(c_path, tree.xmlStrlen(c_path))
+
+cdef object _decodeFilenameWithLength(const_xmlChar* c_path, size_t c_len):
+    """Make the filename a unicode string if we are in Py3.
+    """
+    if _isFilePath(c_path):
+        try:
+            return python.PyUnicode_Decode(
+                <const_char*>c_path, c_len, _C_FILENAME_ENCODING, NULL)
+        except UnicodeDecodeError:
+            pass
+    try:
+        return (<unsigned char*>c_path)[:c_len].decode('UTF-8')
+    except UnicodeDecodeError:
+        # this is a stupid fallback, but it might still work...
+        return (<unsigned char*>c_path)[:c_len].decode('latin-1', 'replace')
+
+cdef object _encodeFilenameUTF8(object filename):
+    """Recode filename as UTF-8. Tries ASCII, local filesystem encoding and
+    UTF-8 as source encoding.
+    """
+    cdef char* c_filename
+    if filename is None:
+        return None
+    elif isinstance(filename, bytes):
+        if not isutf8l(<bytes>filename, len(<bytes>filename)):
+            # plain ASCII!
+            return filename
+        c_filename = _cstr(<bytes>filename)
+        try:
+            # try to decode with default encoding
+            filename = python.PyUnicode_Decode(
+                c_filename, len(<bytes>filename),
+                _C_FILENAME_ENCODING, NULL)
+        except UnicodeDecodeError as decode_exc:
+            try:
+                # try if it's proper UTF-8
+                (<bytes>filename).decode('utf8')
+                return filename
+            except UnicodeDecodeError:
+                raise decode_exc # otherwise re-raise original exception
+    if isinstance(filename, unicode):
+        return (<unicode>filename).encode('utf8')
+    else:
+        raise TypeError("Argument must be string or unicode.")
+
+cdef tuple _getNsTag(tag):
+    """Given a tag, find namespace URI and tag name.
+    Return None for NS uri if no namespace URI provided.
+    """
+    return __getNsTag(tag, 0)
+
+cdef tuple _getNsTagWithEmptyNs(tag):
+    """Given a tag, find namespace URI and tag name.  Return None for NS uri
+    if no namespace URI provided, or the empty string if namespace
+    part is '{}'.
+    """
+    return __getNsTag(tag, 1)
+
+cdef tuple __getNsTag(tag, bint empty_ns):
+    cdef char* c_tag
+    cdef char* c_ns_end
+    cdef Py_ssize_t taglen
+    cdef Py_ssize_t nslen
+    cdef bytes ns = None
+    # _isString() is much faster than isinstance()
+    if not _isString(tag) and isinstance(tag, QName):
+        tag = (<QName>tag).text
+    tag = _utf8(tag)
+    c_tag = _cstr(tag)
+    if c_tag[0] == c'{':
+        c_tag += 1
+        c_ns_end = cstring_h.strchr(c_tag, c'}')
+        if c_ns_end is NULL:
+            raise ValueError, "Invalid tag name"
+        nslen  = c_ns_end - c_tag
+        taglen = python.PyBytes_GET_SIZE(tag) - nslen - 2
+        if taglen == 0:
+            raise ValueError, "Empty tag name"
+        if nslen > 0:
+            ns = <bytes>c_tag[:nslen]
+        elif empty_ns:
+            ns = b''
+        tag = <bytes>c_ns_end[1:taglen+1]
+    elif python.PyBytes_GET_SIZE(tag) == 0:
+        raise ValueError, "Empty tag name"
+    return ns, tag
+
+cdef inline int _pyXmlNameIsValid(name_utf8):
+    return _xmlNameIsValid(_xcstr(name_utf8)) and b':' not in name_utf8
+
+cdef inline int _pyHtmlNameIsValid(name_utf8):
+    return _htmlNameIsValid(_xcstr(name_utf8))
+
+cdef inline int _xmlNameIsValid(const_xmlChar* c_name) noexcept:
+    return tree.xmlValidateNameValue(c_name)
+
+cdef int _htmlNameIsValid(const_xmlChar* c_name) noexcept:
+    if c_name is NULL or c_name[0] == c'\0':
+        return 0
+    while c_name[0] != c'\0':
+        if c_name[0] in b'&<>/"\'\t\n\x0B\x0C\r ':
+            return 0
+        c_name += 1
+    return 1
+
+cdef bint _characterReferenceIsValid(const_xmlChar* c_name) noexcept:
+    cdef bint is_hex
+    if c_name[0] == c'x':
+        c_name += 1
+        is_hex = 1
+    else:
+        is_hex = 0
+    if c_name[0] == c'\0':
+        return 0
+    while c_name[0] != c'\0':
+        if c_name[0] < c'0' or c_name[0] > c'9':
+            if not is_hex:
+                return 0
+            if not (c'a' <= c_name[0] <= c'f'):
+                if not (c'A' <= c_name[0] <= c'F'):
+                    return 0
+        c_name += 1
+    return 1
+
+cdef int _tagValidOrRaise(tag_utf) except -1:
+    if not _pyXmlNameIsValid(tag_utf):
+        raise ValueError(f"Invalid tag name {(<bytes>tag_utf).decode('utf8')!r}")
+    return 0
+
+cdef int _htmlTagValidOrRaise(tag_utf) except -1:
+    if not _pyHtmlNameIsValid(tag_utf):
+        raise ValueError(f"Invalid HTML tag name {(<bytes>tag_utf).decode('utf8')!r}")
+    return 0
+
+cdef int _attributeValidOrRaise(name_utf) except -1:
+    if not _pyXmlNameIsValid(name_utf):
+        raise ValueError(f"Invalid attribute name {(<bytes>name_utf).decode('utf8')!r}")
+    return 0
+
+cdef int _prefixValidOrRaise(tag_utf) except -1:
+    if not _pyXmlNameIsValid(tag_utf):
+        raise ValueError(f"Invalid namespace prefix {(<bytes>tag_utf).decode('utf8')!r}")
+    return 0
+
+cdef int _uriValidOrRaise(uri_utf) except -1:
+    cdef uri.xmlURI* c_uri = uri.xmlParseURI(_cstr(uri_utf))
+    if c_uri is NULL:
+        raise ValueError(f"Invalid namespace URI {(<bytes>uri_utf).decode('utf8')!r}")
+    uri.xmlFreeURI(c_uri)
+    return 0
+
+cdef inline unicode _namespacedName(xmlNode* c_node):
+    return _namespacedNameFromNsName(_getNs(c_node), c_node.name)
+
+
+cdef unicode _namespacedNameFromNsName(const_xmlChar* c_href, const_xmlChar* c_name):
+    name = funicode(c_name)
+    if c_href is NULL:
+        return name
+    href = funicode(c_href)
+    return f"{{{href}}}{name}"
+
+
+cdef _getFilenameForFile(source):
+    """Given a Python File or Gzip object, give filename back.
+
+    Returns None if not a file object.
+    """
+    # urllib2 provides a geturl() method
+    try:
+        return source.geturl()
+    except:
+        pass
+    # file instances have a name attribute
+    try:
+        filename = source.name
+        if _isString(filename):
+            return os_path_abspath(filename)
+    except:
+        pass
+    # gzip file instances have a filename attribute (before Py3k)
+    try:
+        filename = source.filename
+        if _isString(filename):
+            return os_path_abspath(filename)
+    except:
+        pass
+    # can't determine filename
+    return None
diff --git a/.venv/lib/python3.12/site-packages/lxml/builder.cpython-312-x86_64-linux-gnu.so b/.venv/lib/python3.12/site-packages/lxml/builder.cpython-312-x86_64-linux-gnu.so
new file mode 100755
index 00000000..262245fe
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/builder.cpython-312-x86_64-linux-gnu.so
Binary files differdiff --git a/.venv/lib/python3.12/site-packages/lxml/builder.py b/.venv/lib/python3.12/site-packages/lxml/builder.py
new file mode 100644
index 00000000..cff67b0b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/builder.py
@@ -0,0 +1,232 @@
+# cython: language_level=2
+
+#
+# Element generator factory by Fredrik Lundh.
+#
+# Source:
+#    http://online.effbot.org/2006_11_01_archive.htm#et-builder
+#    http://effbot.python-hosting.com/file/stuff/sandbox/elementlib/builder.py
+#
+# --------------------------------------------------------------------
+# The ElementTree toolkit is
+#
+# Copyright (c) 1999-2004 by Fredrik Lundh
+#
+# By obtaining, using, and/or copying this software and/or its
+# associated documentation, you agree that you have read, understood,
+# and will comply with the following terms and conditions:
+#
+# Permission to use, copy, modify, and distribute this software and
+# its associated documentation for any purpose and without fee is
+# hereby granted, provided that the above copyright notice appears in
+# all copies, and that both that copyright notice and this permission
+# notice appear in supporting documentation, and that the name of
+# Secret Labs AB or the author not be used in advertising or publicity
+# pertaining to distribution of the software without specific, written
+# prior permission.
+#
+# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
+# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
+# ABILITY AND FITNESS.  IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR
+# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+# --------------------------------------------------------------------
+
+"""
+The ``E`` Element factory for generating XML documents.
+"""
+
+
+import lxml.etree as ET
+_QName = ET.QName
+
+from functools import partial
+
+try:
+    basestring
+except NameError:
+    basestring = str
+
+try:
+    unicode
+except NameError:
+    unicode = str
+
+
+class ElementMaker:
+    """Element generator factory.
+
+    Unlike the ordinary Element factory, the E factory allows you to pass in
+    more than just a tag and some optional attributes; you can also pass in
+    text and other elements.  The text is added as either text or tail
+    attributes, and elements are inserted at the right spot.  Some small
+    examples::
+
+        >>> from lxml import etree as ET
+        >>> from lxml.builder import E
+
+        >>> ET.tostring(E("tag"))
+        '<tag/>'
+        >>> ET.tostring(E("tag", "text"))
+        '<tag>text</tag>'
+        >>> ET.tostring(E("tag", "text", key="value"))
+        '<tag key="value">text</tag>'
+        >>> ET.tostring(E("tag", E("subtag", "text"), "tail"))
+        '<tag><subtag>text</subtag>tail</tag>'
+
+    For simple tags, the factory also allows you to write ``E.tag(...)`` instead
+    of ``E('tag', ...)``::
+
+        >>> ET.tostring(E.tag())
+        '<tag/>'
+        >>> ET.tostring(E.tag("text"))
+        '<tag>text</tag>'
+        >>> ET.tostring(E.tag(E.subtag("text"), "tail"))
+        '<tag><subtag>text</subtag>tail</tag>'
+
+    Here's a somewhat larger example; this shows how to generate HTML
+    documents, using a mix of prepared factory functions for inline elements,
+    nested ``E.tag`` calls, and embedded XHTML fragments::
+
+        # some common inline elements
+        A = E.a
+        I = E.i
+        B = E.b
+
+        def CLASS(v):
+            # helper function, 'class' is a reserved word
+            return {'class': v}
+
+        page = (
+            E.html(
+                E.head(
+                    E.title("This is a sample document")
+                ),
+                E.body(
+                    E.h1("Hello!", CLASS("title")),
+                    E.p("This is a paragraph with ", B("bold"), " text in it!"),
+                    E.p("This is another paragraph, with a ",
+                        A("link", href="http://www.python.org"), "."),
+                    E.p("Here are some reserved characters: <spam&egg>."),
+                    ET.XML("<p>And finally, here is an embedded XHTML fragment.</p>"),
+                )
+            )
+        )
+
+        print ET.tostring(page)
+
+    Here's a prettyprinted version of the output from the above script::
+
+        <html>
+          <head>
+            <title>This is a sample document</title>
+          </head>
+          <body>
+            <h1 class="title">Hello!</h1>
+            <p>This is a paragraph with <b>bold</b> text in it!</p>
+            <p>This is another paragraph, with <a href="http://www.python.org">link</a>.</p>
+            <p>Here are some reserved characters: &lt;spam&amp;egg&gt;.</p>
+            <p>And finally, here is an embedded XHTML fragment.</p>
+          </body>
+        </html>
+
+    For namespace support, you can pass a namespace map (``nsmap``)
+    and/or a specific target ``namespace`` to the ElementMaker class::
+
+        >>> E = ElementMaker(namespace="http://my.ns/")
+        >>> print(ET.tostring( E.test ))
+        <test xmlns="http://my.ns/"/>
+
+        >>> E = ElementMaker(namespace="http://my.ns/", nsmap={'p':'http://my.ns/'})
+        >>> print(ET.tostring( E.test ))
+        <p:test xmlns:p="http://my.ns/"/>
+    """
+
+    def __init__(self, typemap=None,
+                 namespace=None, nsmap=None, makeelement=None):
+        self._namespace = '{' + namespace + '}' if namespace is not None else None
+        self._nsmap = dict(nsmap) if nsmap else None
+
+        assert makeelement is None or callable(makeelement)
+        self._makeelement = makeelement if makeelement is not None else ET.Element
+
+        # initialize the default type map functions for this element factory
+        typemap = dict(typemap) if typemap else {}
+
+        def add_text(elem, item):
+            try:
+                last_child = elem[-1]
+            except IndexError:
+                elem.text = (elem.text or "") + item
+            else:
+                last_child.tail = (last_child.tail or "") + item
+
+        def add_cdata(elem, cdata):
+            if elem.text:
+                raise ValueError("Can't add a CDATA section. Element already has some text: %r" % elem.text)
+            elem.text = cdata
+
+        if str not in typemap:
+            typemap[str] = add_text
+        if unicode not in typemap:
+            typemap[unicode] = add_text
+        if ET.CDATA not in typemap:
+            typemap[ET.CDATA] = add_cdata
+
+        def add_dict(elem, item):
+            attrib = elem.attrib
+            for k, v in item.items():
+                if isinstance(v, basestring):
+                    attrib[k] = v
+                else:
+                    attrib[k] = typemap[type(v)](None, v)
+
+        if dict not in typemap:
+            typemap[dict] = add_dict
+
+        self._typemap = typemap
+
+    def __call__(self, tag, *children, **attrib):
+        typemap = self._typemap
+
+        # We'll usually get a 'str', and the compiled type check is very fast.
+        if not isinstance(tag, str) and isinstance(tag, _QName):
+            # A QName is explicitly qualified, do not look at self._namespace.
+            tag = tag.text
+        elif self._namespace is not None and tag[0] != '{':
+            tag = self._namespace + tag
+        elem = self._makeelement(tag, nsmap=self._nsmap)
+        if attrib:
+            typemap[dict](elem, attrib)
+
+        for item in children:
+            if callable(item):
+                item = item()
+            t = typemap.get(type(item))
+            if t is None:
+                if ET.iselement(item):
+                    elem.append(item)
+                    continue
+                for basetype in type(item).__mro__:
+                    # See if the typemap knows of any of this type's bases.
+                    t = typemap.get(basetype)
+                    if t is not None:
+                        break
+                else:
+                    raise TypeError("bad argument type: %s(%r)" %
+                                    (type(item).__name__, item))
+            v = t(elem, item)
+            if v:
+                typemap.get(type(v))(elem, v)
+
+        return elem
+
+    def __getattr__(self, tag):
+        return partial(self, tag)
+
+
+# create factory object
+E = ElementMaker()
diff --git a/.venv/lib/python3.12/site-packages/lxml/classlookup.pxi b/.venv/lib/python3.12/site-packages/lxml/classlookup.pxi
new file mode 100644
index 00000000..92d1d47a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/classlookup.pxi
@@ -0,0 +1,580 @@
+# Configurable Element class lookup
+
+################################################################################
+# Custom Element classes
+
+cdef public class ElementBase(_Element) [ type LxmlElementBaseType,
+                                          object LxmlElementBase ]:
+    """ElementBase(*children, attrib=None, nsmap=None, **_extra)
+
+    The public Element class.  All custom Element classes must inherit
+    from this one.  To create an Element, use the `Element()` factory.
+
+    BIG FAT WARNING: Subclasses *must not* override __init__ or
+    __new__ as it is absolutely undefined when these objects will be
+    created or destroyed.  All persistent state of Elements must be
+    stored in the underlying XML.  If you really need to initialize
+    the object after creation, you can implement an ``_init(self)``
+    method that will be called directly after object creation.
+
+    Subclasses of this class can be instantiated to create a new
+    Element.  By default, the tag name will be the class name and the
+    namespace will be empty.  You can modify this with the following
+    class attributes:
+
+    * TAG - the tag name, possibly containing a namespace in Clark
+      notation
+
+    * NAMESPACE - the default namespace URI, unless provided as part
+      of the TAG attribute.
+
+    * HTML - flag if the class is an HTML tag, as opposed to an XML
+      tag.  This only applies to un-namespaced tags and defaults to
+      false (i.e. XML).
+
+    * PARSER - the parser that provides the configuration for the
+      newly created document.  Providing an HTML parser here will
+      default to creating an HTML element.
+
+    In user code, the latter three are commonly inherited in class
+    hierarchies that implement a common namespace.
+    """
+    def __init__(self, *children, attrib=None, nsmap=None, **_extra):
+        """ElementBase(*children, attrib=None, nsmap=None, **_extra)
+        """
+        cdef bint is_html = 0
+        cdef _BaseParser parser
+        cdef _Element last_child
+        # don't use normal attribute access as it might be overridden
+        _getattr = object.__getattribute__
+        try:
+            namespace = _utf8(_getattr(self, 'NAMESPACE'))
+        except AttributeError:
+            namespace = None
+        try:
+            ns, tag = _getNsTag(_getattr(self, 'TAG'))
+            if ns is not None:
+                namespace = ns
+        except AttributeError:
+            tag = _utf8(_getattr(_getattr(self, '__class__'), '__name__'))
+            if b'.' in tag:
+                tag = tag.split(b'.')[-1]
+        try:
+            parser = _getattr(self, 'PARSER')
+        except AttributeError:
+            parser = None
+            for child in children:
+                if isinstance(child, _Element):
+                    parser = (<_Element>child)._doc._parser
+                    break
+        if isinstance(parser, HTMLParser):
+            is_html = 1
+        if namespace is None:
+            try:
+                is_html = _getattr(self, 'HTML')
+            except AttributeError:
+                pass
+        _initNewElement(self, is_html, tag, namespace, parser,
+                        attrib, nsmap, _extra)
+        last_child = None
+        for child in children:
+            if _isString(child):
+                if last_child is None:
+                    _setNodeText(self._c_node,
+                                 (_collectText(self._c_node.children) or '') + child)
+                else:
+                    _setTailText(last_child._c_node,
+                                 (_collectText(last_child._c_node.next) or '') + child)
+            elif isinstance(child, _Element):
+                last_child = child
+                _appendChild(self, last_child)
+            elif isinstance(child, type) and issubclass(child, ElementBase):
+                last_child = child()
+                _appendChild(self, last_child)
+            else:
+                raise TypeError, f"Invalid child type: {type(child)!r}"
+
+cdef class CommentBase(_Comment):
+    """All custom Comment classes must inherit from this one.
+
+    To create an XML Comment instance, use the ``Comment()`` factory.
+
+    Subclasses *must not* override __init__ or __new__ as it is
+    absolutely undefined when these objects will be created or
+    destroyed.  All persistent state of Comments must be stored in the
+    underlying XML.  If you really need to initialize the object after
+    creation, you can implement an ``_init(self)`` method that will be
+    called after object creation.
+    """
+    def __init__(self, text):
+        # copied from Comment() factory
+        cdef _Document doc
+        cdef xmlDoc*   c_doc
+        if text is None:
+            text = b''
+        else:
+            text = _utf8(text)
+        c_doc = _newXMLDoc()
+        doc = _documentFactory(c_doc, None)
+        self._c_node = _createComment(c_doc, _xcstr(text))
+        if self._c_node is NULL:
+            raise MemoryError()
+        tree.xmlAddChild(<xmlNode*>c_doc, self._c_node)
+        _registerProxy(self, doc, self._c_node)
+        self._init()
+
+cdef class PIBase(_ProcessingInstruction):
+    """All custom Processing Instruction classes must inherit from this one.
+
+    To create an XML ProcessingInstruction instance, use the ``PI()``
+    factory.
+
+    Subclasses *must not* override __init__ or __new__ as it is
+    absolutely undefined when these objects will be created or
+    destroyed.  All persistent state of PIs must be stored in the
+    underlying XML.  If you really need to initialize the object after
+    creation, you can implement an ``_init(self)`` method that will be
+    called after object creation.
+    """
+    def __init__(self, target, text=None):
+        # copied from PI() factory
+        cdef _Document doc
+        cdef xmlDoc*   c_doc
+        target = _utf8(target)
+        if text is None:
+            text = b''
+        else:
+            text = _utf8(text)
+        c_doc = _newXMLDoc()
+        doc = _documentFactory(c_doc, None)
+        self._c_node = _createPI(c_doc, _xcstr(target), _xcstr(text))
+        if self._c_node is NULL:
+            raise MemoryError()
+        tree.xmlAddChild(<xmlNode*>c_doc, self._c_node)
+        _registerProxy(self, doc, self._c_node)
+        self._init()
+
+cdef class EntityBase(_Entity):
+    """All custom Entity classes must inherit from this one.
+
+    To create an XML Entity instance, use the ``Entity()`` factory.
+
+    Subclasses *must not* override __init__ or __new__ as it is
+    absolutely undefined when these objects will be created or
+    destroyed.  All persistent state of Entities must be stored in the
+    underlying XML.  If you really need to initialize the object after
+    creation, you can implement an ``_init(self)`` method that will be
+    called after object creation.
+    """
+    def __init__(self, name):
+        cdef _Document doc
+        cdef xmlDoc*   c_doc
+        name_utf = _utf8(name)
+        c_name = _xcstr(name_utf)
+        if c_name[0] == c'#':
+            if not _characterReferenceIsValid(c_name + 1):
+                raise ValueError, f"Invalid character reference: '{name}'"
+        elif not _xmlNameIsValid(c_name):
+            raise ValueError, f"Invalid entity reference: '{name}'"
+        c_doc = _newXMLDoc()
+        doc = _documentFactory(c_doc, None)
+        self._c_node = _createEntity(c_doc, c_name)
+        if self._c_node is NULL:
+            raise MemoryError()
+        tree.xmlAddChild(<xmlNode*>c_doc, self._c_node)
+        _registerProxy(self, doc, self._c_node)
+        self._init()
+
+
+cdef int _validateNodeClass(xmlNode* c_node, cls) except -1:
+    if c_node.type == tree.XML_ELEMENT_NODE:
+        expected = ElementBase
+    elif c_node.type == tree.XML_COMMENT_NODE:
+        expected = CommentBase
+    elif c_node.type == tree.XML_ENTITY_REF_NODE:
+        expected = EntityBase
+    elif c_node.type == tree.XML_PI_NODE:
+        expected = PIBase
+    else:
+        assert False, f"Unknown node type: {c_node.type}"
+
+    if not (isinstance(cls, type) and issubclass(cls, expected)):
+        raise TypeError(
+            f"result of class lookup must be subclass of {type(expected)}, got {type(cls)}")
+    return 0
+
+
+################################################################################
+# Element class lookup
+
+ctypedef public object (*_element_class_lookup_function)(object, _Document, xmlNode*)
+
+# class to store element class lookup functions
+cdef public class ElementClassLookup [ type LxmlElementClassLookupType,
+                                       object LxmlElementClassLookup ]:
+    """ElementClassLookup(self)
+    Superclass of Element class lookups.
+    """
+    cdef _element_class_lookup_function _lookup_function
+
+
+cdef public class FallbackElementClassLookup(ElementClassLookup) \
+         [ type LxmlFallbackElementClassLookupType,
+           object LxmlFallbackElementClassLookup ]:
+    """FallbackElementClassLookup(self, fallback=None)
+
+    Superclass of Element class lookups with additional fallback.
+    """
+    cdef readonly ElementClassLookup fallback
+    cdef _element_class_lookup_function _fallback_function
+    def __cinit__(self):
+        # fall back to default lookup
+        self._fallback_function = _lookupDefaultElementClass
+
+    def __init__(self, ElementClassLookup fallback=None):
+        if fallback is not None:
+            self._setFallback(fallback)
+        else:
+            self._fallback_function = _lookupDefaultElementClass
+
+    cdef void _setFallback(self, ElementClassLookup lookup):
+        """Sets the fallback scheme for this lookup method.
+        """
+        self.fallback = lookup
+        self._fallback_function = lookup._lookup_function
+        if self._fallback_function is NULL:
+            self._fallback_function = _lookupDefaultElementClass
+
+    def set_fallback(self, ElementClassLookup lookup not None):
+        """set_fallback(self, lookup)
+
+        Sets the fallback scheme for this lookup method.
+        """
+        self._setFallback(lookup)
+
+cdef inline object _callLookupFallback(FallbackElementClassLookup lookup,
+                                       _Document doc, xmlNode* c_node):
+    return lookup._fallback_function(lookup.fallback, doc, c_node)
+
+
+################################################################################
+# default lookup scheme
+
+cdef class ElementDefaultClassLookup(ElementClassLookup):
+    """ElementDefaultClassLookup(self, element=None, comment=None, pi=None, entity=None)
+    Element class lookup scheme that always returns the default Element
+    class.
+
+    The keyword arguments ``element``, ``comment``, ``pi`` and ``entity``
+    accept the respective Element classes.
+    """
+    cdef readonly object element_class
+    cdef readonly object comment_class
+    cdef readonly object pi_class
+    cdef readonly object entity_class
+    def __cinit__(self):
+        self._lookup_function = _lookupDefaultElementClass
+
+    def __init__(self, element=None, comment=None, pi=None, entity=None):
+        if element is None:
+            self.element_class = _Element
+        elif issubclass(element, ElementBase):
+            self.element_class = element
+        else:
+            raise TypeError, "element class must be subclass of ElementBase"
+
+        if comment is None:
+            self.comment_class = _Comment
+        elif issubclass(comment, CommentBase):
+            self.comment_class = comment
+        else:
+            raise TypeError, "comment class must be subclass of CommentBase"
+
+        if entity is None:
+            self.entity_class = _Entity
+        elif issubclass(entity, EntityBase):
+            self.entity_class = entity
+        else:
+            raise TypeError, "Entity class must be subclass of EntityBase"
+
+        if pi is None:
+            self.pi_class = None # special case, see below
+        elif issubclass(pi, PIBase):
+            self.pi_class = pi
+        else:
+            raise TypeError, "PI class must be subclass of PIBase"
+
+cdef object _lookupDefaultElementClass(state, _Document _doc, xmlNode* c_node):
+    "Trivial class lookup function that always returns the default class."
+    if c_node.type == tree.XML_ELEMENT_NODE:
+        if state is not None:
+            return (<ElementDefaultClassLookup>state).element_class
+        else:
+            return _Element
+    elif c_node.type == tree.XML_COMMENT_NODE:
+        if state is not None:
+            return (<ElementDefaultClassLookup>state).comment_class
+        else:
+            return _Comment
+    elif c_node.type == tree.XML_ENTITY_REF_NODE:
+        if state is not None:
+            return (<ElementDefaultClassLookup>state).entity_class
+        else:
+            return _Entity
+    elif c_node.type == tree.XML_PI_NODE:
+        if state is None or (<ElementDefaultClassLookup>state).pi_class is None:
+            # special case XSLT-PI
+            if c_node.name is not NULL and c_node.content is not NULL:
+                if tree.xmlStrcmp(c_node.name, <unsigned char*>"xml-stylesheet") == 0:
+                    if tree.xmlStrstr(c_node.content, <unsigned char*>"text/xsl") is not NULL or \
+                           tree.xmlStrstr(c_node.content, <unsigned char*>"text/xml") is not NULL:
+                        return _XSLTProcessingInstruction
+            return _ProcessingInstruction
+        else:
+            return (<ElementDefaultClassLookup>state).pi_class
+    else:
+        assert False, f"Unknown node type: {c_node.type}"
+
+
+################################################################################
+# attribute based lookup scheme
+
+cdef class AttributeBasedElementClassLookup(FallbackElementClassLookup):
+    """AttributeBasedElementClassLookup(self, attribute_name, class_mapping, fallback=None)
+    Checks an attribute of an Element and looks up the value in a
+    class dictionary.
+
+    Arguments:
+      - attribute name - '{ns}name' style string
+      - class mapping  - Python dict mapping attribute values to Element classes
+      - fallback       - optional fallback lookup mechanism
+
+    A None key in the class mapping will be checked if the attribute is
+    missing.
+    """
+    cdef object _class_mapping
+    cdef tuple _pytag
+    cdef const_xmlChar* _c_ns
+    cdef const_xmlChar* _c_name
+    def __cinit__(self):
+        self._lookup_function = _attribute_class_lookup
+
+    def __init__(self, attribute_name, class_mapping,
+                 ElementClassLookup fallback=None):
+        self._pytag = _getNsTag(attribute_name)
+        ns, name = self._pytag
+        if ns is None:
+            self._c_ns = NULL
+        else:
+            self._c_ns = _xcstr(ns)
+        self._c_name = _xcstr(name)
+        self._class_mapping = dict(class_mapping)
+
+        FallbackElementClassLookup.__init__(self, fallback)
+
+cdef object _attribute_class_lookup(state, _Document doc, xmlNode* c_node):
+    cdef AttributeBasedElementClassLookup lookup
+    cdef python.PyObject* dict_result
+
+    lookup = <AttributeBasedElementClassLookup>state
+    if c_node.type == tree.XML_ELEMENT_NODE:
+        value = _attributeValueFromNsName(
+            c_node, lookup._c_ns, lookup._c_name)
+        dict_result = python.PyDict_GetItem(lookup._class_mapping, value)
+        if dict_result is not NULL:
+            cls = <object>dict_result
+            _validateNodeClass(c_node, cls)
+            return cls
+    return _callLookupFallback(lookup, doc, c_node)
+
+
+################################################################################
+#  per-parser lookup scheme
+
+cdef class ParserBasedElementClassLookup(FallbackElementClassLookup):
+    """ParserBasedElementClassLookup(self, fallback=None)
+    Element class lookup based on the XML parser.
+    """
+    def __cinit__(self):
+        self._lookup_function = _parser_class_lookup
+
+cdef object _parser_class_lookup(state, _Document doc, xmlNode* c_node):
+    if doc._parser._class_lookup is not None:
+        return doc._parser._class_lookup._lookup_function(
+            doc._parser._class_lookup, doc, c_node)
+    return _callLookupFallback(<FallbackElementClassLookup>state, doc, c_node)
+
+
+################################################################################
+#  custom class lookup based on node type, namespace, name
+
+cdef class CustomElementClassLookup(FallbackElementClassLookup):
+    """CustomElementClassLookup(self, fallback=None)
+    Element class lookup based on a subclass method.
+
+    You can inherit from this class and override the method::
+
+        lookup(self, type, doc, namespace, name)
+
+    to lookup the element class for a node. Arguments of the method:
+    * type:      one of 'element', 'comment', 'PI', 'entity'
+    * doc:       document that the node is in
+    * namespace: namespace URI of the node (or None for comments/PIs/entities)
+    * name:      name of the element/entity, None for comments, target for PIs
+
+    If you return None from this method, the fallback will be called.
+    """
+    def __cinit__(self):
+        self._lookup_function = _custom_class_lookup
+
+    def lookup(self, type, doc, namespace, name):
+        "lookup(self, type, doc, namespace, name)"
+        return None
+
+cdef object _custom_class_lookup(state, _Document doc, xmlNode* c_node):
+    cdef CustomElementClassLookup lookup
+
+    lookup = <CustomElementClassLookup>state
+
+    if c_node.type == tree.XML_ELEMENT_NODE:
+        element_type = "element"
+    elif c_node.type == tree.XML_COMMENT_NODE:
+        element_type = "comment"
+    elif c_node.type == tree.XML_PI_NODE:
+        element_type = "PI"
+    elif c_node.type == tree.XML_ENTITY_REF_NODE:
+        element_type = "entity"
+    else:
+        element_type = "element"
+    if c_node.name is NULL:
+        name = None
+    else:
+        name = funicode(c_node.name)
+    c_str = tree._getNs(c_node)
+    ns = funicode(c_str) if c_str is not NULL else None
+
+    cls = lookup.lookup(element_type, doc, ns, name)
+    if cls is not None:
+        _validateNodeClass(c_node, cls)
+        return cls
+    return _callLookupFallback(lookup, doc, c_node)
+
+
+################################################################################
+# read-only tree based class lookup
+
+cdef class PythonElementClassLookup(FallbackElementClassLookup):
+    """PythonElementClassLookup(self, fallback=None)
+    Element class lookup based on a subclass method.
+
+    This class lookup scheme allows access to the entire XML tree in
+    read-only mode.  To use it, re-implement the ``lookup(self, doc,
+    root)`` method in a subclass::
+
+        from lxml import etree, pyclasslookup
+
+        class MyElementClass(etree.ElementBase):
+            honkey = True
+
+        class MyLookup(pyclasslookup.PythonElementClassLookup):
+            def lookup(self, doc, root):
+                if root.tag == "sometag":
+                    return MyElementClass
+                else:
+                    for child in root:
+                        if child.tag == "someothertag":
+                            return MyElementClass
+                # delegate to default
+                return None
+
+    If you return None from this method, the fallback will be called.
+
+    The first argument is the opaque document instance that contains
+    the Element.  The second argument is a lightweight Element proxy
+    implementation that is only valid during the lookup.  Do not try
+    to keep a reference to it.  Once the lookup is done, the proxy
+    will be invalid.
+
+    Also, you cannot wrap such a read-only Element in an ElementTree,
+    and you must take care not to keep a reference to them outside of
+    the `lookup()` method.
+
+    Note that the API of the Element objects is not complete.  It is
+    purely read-only and does not support all features of the normal
+    `lxml.etree` API (such as XPath, extended slicing or some
+    iteration methods).
+
+    See https://lxml.de/element_classes.html
+    """
+    def __cinit__(self):
+        self._lookup_function = _python_class_lookup
+
+    def lookup(self, doc, element):
+        """lookup(self, doc, element)
+
+        Override this method to implement your own lookup scheme.
+        """
+        return None
+
+cdef object _python_class_lookup(state, _Document doc, tree.xmlNode* c_node):
+    cdef PythonElementClassLookup lookup
+    cdef _ReadOnlyProxy proxy
+    lookup = <PythonElementClassLookup>state
+
+    proxy = _newReadOnlyProxy(None, c_node)
+    cls = lookup.lookup(doc, proxy)
+    _freeReadOnlyProxies(proxy)
+
+    if cls is not None:
+        _validateNodeClass(c_node, cls)
+        return cls
+    return _callLookupFallback(lookup, doc, c_node)
+
+################################################################################
+# Global setup
+
+cdef _element_class_lookup_function LOOKUP_ELEMENT_CLASS
+cdef object ELEMENT_CLASS_LOOKUP_STATE
+
+cdef void _setElementClassLookupFunction(
+    _element_class_lookup_function function, object state):
+    global LOOKUP_ELEMENT_CLASS, ELEMENT_CLASS_LOOKUP_STATE
+    if function is NULL:
+        state    = DEFAULT_ELEMENT_CLASS_LOOKUP
+        function = DEFAULT_ELEMENT_CLASS_LOOKUP._lookup_function
+
+    ELEMENT_CLASS_LOOKUP_STATE = state
+    LOOKUP_ELEMENT_CLASS = function
+
+def set_element_class_lookup(ElementClassLookup lookup = None):
+    """set_element_class_lookup(lookup = None)
+
+    Set the global element class lookup method.
+
+    This defines the main entry point for looking up element implementations.
+    The standard implementation uses the :class:`ParserBasedElementClassLookup`
+    to delegate to different lookup schemes for each parser. 
+
+    .. warning::
+
+        This should only be changed by applications, not by library packages.
+        In most cases, parser specific lookups should be preferred,
+        which can be configured via
+        :meth:`~lxml.etree.XMLParser.set_element_class_lookup`
+        (and the same for HTML parsers).
+
+        Globally replacing the element class lookup by something other than a
+        :class:`ParserBasedElementClassLookup` will prevent parser specific lookup
+        schemes from working. Several tools rely on parser specific lookups,
+        including :mod:`lxml.html` and :mod:`lxml.objectify`.
+    """
+    if lookup is None or lookup._lookup_function is NULL:
+        _setElementClassLookupFunction(NULL, None)
+    else:
+        _setElementClassLookupFunction(lookup._lookup_function, lookup)
+
+# default setup: parser delegation
+cdef ParserBasedElementClassLookup DEFAULT_ELEMENT_CLASS_LOOKUP
+DEFAULT_ELEMENT_CLASS_LOOKUP = ParserBasedElementClassLookup()
+
+set_element_class_lookup(DEFAULT_ELEMENT_CLASS_LOOKUP)
diff --git a/.venv/lib/python3.12/site-packages/lxml/cleanup.pxi b/.venv/lib/python3.12/site-packages/lxml/cleanup.pxi
new file mode 100644
index 00000000..8e266b33
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/cleanup.pxi
@@ -0,0 +1,215 @@
+# functions for tree cleanup and removing elements from subtrees
+
+def cleanup_namespaces(tree_or_element, top_nsmap=None, keep_ns_prefixes=None):
+    """cleanup_namespaces(tree_or_element, top_nsmap=None, keep_ns_prefixes=None)
+
+    Remove all namespace declarations from a subtree that are not used
+    by any of the elements or attributes in that tree.
+
+    If a 'top_nsmap' is provided, it must be a mapping from prefixes
+    to namespace URIs.  These namespaces will be declared on the top
+    element of the subtree before running the cleanup, which allows
+    moving namespace declarations to the top of the tree.
+
+    If a 'keep_ns_prefixes' is provided, it must be a list of prefixes.
+    These prefixes will not be removed as part of the cleanup.
+    """
+    element = _rootNodeOrRaise(tree_or_element)
+    c_element = element._c_node
+
+    if top_nsmap:
+        doc = element._doc
+        # declare namespaces from nsmap, then apply them to the subtree
+        _setNodeNamespaces(c_element, doc, None, top_nsmap)
+        moveNodeToDocument(doc, c_element.doc, c_element)
+
+    keep_ns_prefixes = (
+        set([_utf8(prefix) for prefix in keep_ns_prefixes])
+        if keep_ns_prefixes else None)
+
+    _removeUnusedNamespaceDeclarations(c_element, keep_ns_prefixes)
+
+
+def strip_attributes(tree_or_element, *attribute_names):
+    """strip_attributes(tree_or_element, *attribute_names)
+
+    Delete all attributes with the provided attribute names from an
+    Element (or ElementTree) and its descendants.
+
+    Attribute names can contain wildcards as in `_Element.iter`.
+
+    Example usage::
+
+        strip_attributes(root_element,
+                         'simpleattr',
+                         '{http://some/ns}attrname',
+                         '{http://other/ns}*')
+    """
+    cdef _MultiTagMatcher matcher
+    element = _rootNodeOrRaise(tree_or_element)
+    if not attribute_names:
+        return
+
+    matcher = _MultiTagMatcher.__new__(_MultiTagMatcher, attribute_names)
+    matcher.cacheTags(element._doc)
+    if matcher.rejectsAllAttributes():
+        return
+    _strip_attributes(element._c_node, matcher)
+
+
+cdef _strip_attributes(xmlNode* c_node, _MultiTagMatcher matcher):
+    cdef xmlAttr* c_attr
+    cdef xmlAttr* c_next_attr
+    tree.BEGIN_FOR_EACH_ELEMENT_FROM(c_node, c_node, 1)
+    if c_node.type == tree.XML_ELEMENT_NODE:
+        c_attr = c_node.properties
+        while c_attr is not NULL:
+            c_next_attr = c_attr.next
+            if matcher.matchesAttribute(c_attr):
+                tree.xmlRemoveProp(c_attr)
+            c_attr = c_next_attr
+    tree.END_FOR_EACH_ELEMENT_FROM(c_node)
+
+
+def strip_elements(tree_or_element, *tag_names, bint with_tail=True):
+    """strip_elements(tree_or_element, *tag_names, with_tail=True)
+
+    Delete all elements with the provided tag names from a tree or
+    subtree.  This will remove the elements and their entire subtree,
+    including all their attributes, text content and descendants.  It
+    will also remove the tail text of the element unless you
+    explicitly set the ``with_tail`` keyword argument option to False.
+
+    Tag names can contain wildcards as in `_Element.iter`.
+
+    Note that this will not delete the element (or ElementTree root
+    element) that you passed even if it matches.  It will only treat
+    its descendants.  If you want to include the root element, check
+    its tag name directly before even calling this function.
+
+    Example usage::
+
+        strip_elements(some_element,
+            'simpletagname',             # non-namespaced tag
+            '{http://some/ns}tagname',   # namespaced tag
+            '{http://some/other/ns}*'    # any tag from a namespace
+            lxml.etree.Comment           # comments
+            )
+    """
+    cdef _MultiTagMatcher matcher
+    doc = _documentOrRaise(tree_or_element)
+    element = _rootNodeOrRaise(tree_or_element)
+    if not tag_names:
+        return
+
+    matcher = _MultiTagMatcher.__new__(_MultiTagMatcher, tag_names)
+    matcher.cacheTags(doc)
+    if matcher.rejectsAll():
+        return
+
+    if isinstance(tree_or_element, _ElementTree):
+        # include PIs and comments next to the root node
+        if matcher.matchesType(tree.XML_COMMENT_NODE):
+            _removeSiblings(element._c_node, tree.XML_COMMENT_NODE, with_tail)
+        if matcher.matchesType(tree.XML_PI_NODE):
+            _removeSiblings(element._c_node, tree.XML_PI_NODE, with_tail)
+    _strip_elements(doc, element._c_node, matcher, with_tail)
+
+cdef _strip_elements(_Document doc, xmlNode* c_node, _MultiTagMatcher matcher,
+                     bint with_tail):
+    cdef xmlNode* c_child
+    cdef xmlNode* c_next
+
+    tree.BEGIN_FOR_EACH_ELEMENT_FROM(c_node, c_node, 1)
+    if c_node.type == tree.XML_ELEMENT_NODE:
+        # we run through the children here to prevent any problems
+        # with the tree iteration which would occur if we unlinked the
+        # c_node itself
+        c_child = _findChildForwards(c_node, 0)
+        while c_child is not NULL:
+            c_next = _nextElement(c_child)
+            if matcher.matches(c_child):
+                if c_child.type == tree.XML_ELEMENT_NODE:
+                    if not with_tail:
+                        tree.xmlUnlinkNode(c_child)
+                    _removeNode(doc, c_child)
+                else:
+                    if with_tail:
+                        _removeText(c_child.next)
+                    tree.xmlUnlinkNode(c_child)
+                    attemptDeallocation(c_child)
+            c_child = c_next
+    tree.END_FOR_EACH_ELEMENT_FROM(c_node)
+
+
+def strip_tags(tree_or_element, *tag_names):
+    """strip_tags(tree_or_element, *tag_names)
+
+    Delete all elements with the provided tag names from a tree or
+    subtree.  This will remove the elements and their attributes, but
+    *not* their text/tail content or descendants.  Instead, it will
+    merge the text content and children of the element into its
+    parent.
+
+    Tag names can contain wildcards as in `_Element.iter`.
+
+    Note that this will not delete the element (or ElementTree root
+    element) that you passed even if it matches.  It will only treat
+    its descendants.
+
+    Example usage::
+
+        strip_tags(some_element,
+            'simpletagname',             # non-namespaced tag
+            '{http://some/ns}tagname',   # namespaced tag
+            '{http://some/other/ns}*'    # any tag from a namespace
+            Comment                      # comments (including their text!)
+            )
+    """
+    cdef _MultiTagMatcher matcher
+    doc = _documentOrRaise(tree_or_element)
+    element = _rootNodeOrRaise(tree_or_element)
+    if not tag_names:
+        return
+
+    matcher = _MultiTagMatcher.__new__(_MultiTagMatcher, tag_names)
+    matcher.cacheTags(doc)
+    if matcher.rejectsAll():
+        return
+
+    if isinstance(tree_or_element, _ElementTree):
+        # include PIs and comments next to the root node
+        if matcher.matchesType(tree.XML_COMMENT_NODE):
+            _removeSiblings(element._c_node, tree.XML_COMMENT_NODE, 0)
+        if matcher.matchesType(tree.XML_PI_NODE):
+            _removeSiblings(element._c_node, tree.XML_PI_NODE, 0)
+    _strip_tags(doc, element._c_node, matcher)
+
+cdef _strip_tags(_Document doc, xmlNode* c_node, _MultiTagMatcher matcher):
+    cdef xmlNode* c_child
+    cdef xmlNode* c_next
+
+    tree.BEGIN_FOR_EACH_ELEMENT_FROM(c_node, c_node, 1)
+    if c_node.type == tree.XML_ELEMENT_NODE:
+        # we run through the children here to prevent any problems
+        # with the tree iteration which would occur if we unlinked the
+        # c_node itself
+        c_child = _findChildForwards(c_node, 0)
+        while c_child is not NULL:
+            if not matcher.matches(c_child):
+                c_child = _nextElement(c_child)
+                continue
+            if c_child.type == tree.XML_ELEMENT_NODE:
+                c_next = _findChildForwards(c_child, 0) or _nextElement(c_child)
+                _replaceNodeByChildren(doc, c_child)
+                if not attemptDeallocation(c_child):
+                    if c_child.nsDef is not NULL:
+                        # make namespaces absolute
+                        moveNodeToDocument(doc, doc._c_doc, c_child)
+                c_child = c_next
+            else:
+                c_next = _nextElement(c_child)
+                tree.xmlUnlinkNode(c_child)
+                attemptDeallocation(c_child)
+                c_child = c_next
+    tree.END_FOR_EACH_ELEMENT_FROM(c_node)
diff --git a/.venv/lib/python3.12/site-packages/lxml/cssselect.py b/.venv/lib/python3.12/site-packages/lxml/cssselect.py
new file mode 100644
index 00000000..54cd75ac
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/cssselect.py
@@ -0,0 +1,101 @@
+"""CSS Selectors based on XPath.
+
+This module supports selecting XML/HTML tags based on CSS selectors.
+See the `CSSSelector` class for details.
+
+This is a thin wrapper around cssselect 0.7 or later.
+"""
+
+
+from . import etree
+try:
+    import cssselect as external_cssselect
+except ImportError:
+    raise ImportError(
+        'cssselect does not seem to be installed. '
+        'See https://pypi.org/project/cssselect/')
+
+
+SelectorSyntaxError = external_cssselect.SelectorSyntaxError
+ExpressionError = external_cssselect.ExpressionError
+SelectorError = external_cssselect.SelectorError
+
+
+__all__ = ['SelectorSyntaxError', 'ExpressionError', 'SelectorError',
+           'CSSSelector']
+
+
+class LxmlTranslator(external_cssselect.GenericTranslator):
+    """
+    A custom CSS selector to XPath translator with lxml-specific extensions.
+    """
+    def xpath_contains_function(self, xpath, function):
+        # Defined there, removed in later drafts:
+        # http://www.w3.org/TR/2001/CR-css3-selectors-20011113/#content-selectors
+        if function.argument_types() not in (['STRING'], ['IDENT']):
+            raise ExpressionError(
+                "Expected a single string or ident for :contains(), got %r"
+                % function.arguments)
+        value = function.arguments[0].value
+        return xpath.add_condition(
+            'contains(__lxml_internal_css:lower-case(string(.)), %s)'
+            % self.xpath_literal(value.lower()))
+
+
+class LxmlHTMLTranslator(LxmlTranslator, external_cssselect.HTMLTranslator):
+    """
+    lxml extensions + HTML support.
+    """
+
+
+def _make_lower_case(context, s):
+    return s.lower()
+
+ns = etree.FunctionNamespace('http://codespeak.net/lxml/css/')
+ns.prefix = '__lxml_internal_css'
+ns['lower-case'] = _make_lower_case
+
+
+class CSSSelector(etree.XPath):
+    """A CSS selector.
+
+    Usage::
+
+        >>> from lxml import etree, cssselect
+        >>> select = cssselect.CSSSelector("a tag > child")
+
+        >>> root = etree.XML("<a><b><c/><tag><child>TEXT</child></tag></b></a>")
+        >>> [ el.tag for el in select(root) ]
+        ['child']
+
+    To use CSS namespaces, you need to pass a prefix-to-namespace
+    mapping as ``namespaces`` keyword argument::
+
+        >>> rdfns = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'
+        >>> select_ns = cssselect.CSSSelector('root > rdf|Description',
+        ...                                   namespaces={'rdf': rdfns})
+
+        >>> rdf = etree.XML((
+        ...     '<root xmlns:rdf="%s">'
+        ...       '<rdf:Description>blah</rdf:Description>'
+        ...     '</root>') % rdfns)
+        >>> [(el.tag, el.text) for el in select_ns(rdf)]
+        [('{http://www.w3.org/1999/02/22-rdf-syntax-ns#}Description', 'blah')]
+
+    """
+    def __init__(self, css, namespaces=None, translator='xml'):
+        if translator == 'xml':
+            translator = LxmlTranslator()
+        elif translator == 'html':
+            translator = LxmlHTMLTranslator()
+        elif translator == 'xhtml':
+            translator = LxmlHTMLTranslator(xhtml=True)
+        path = translator.css_to_xpath(css)
+        super().__init__(path, namespaces=namespaces)
+        self.css = css
+
+    def __repr__(self):
+        return '<%s %x for %r>' % (
+            self.__class__.__name__,
+            abs(id(self)),
+            self.css)
diff --git a/.venv/lib/python3.12/site-packages/lxml/debug.pxi b/.venv/lib/python3.12/site-packages/lxml/debug.pxi
new file mode 100644
index 00000000..e5bb0619
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/debug.pxi
@@ -0,0 +1,90 @@
+@cython.final
+@cython.internal
+cdef class _MemDebug:
+    """Debugging support for the memory allocation in libxml2.
+    """
+    def bytes_used(self):
+        """bytes_used(self)
+
+        Returns the total amount of memory (in bytes) currently used by libxml2.
+        Note that libxml2 constrains this value to a C int, which limits
+        the accuracy on 64 bit systems.
+        """
+        return tree.xmlMemUsed()
+
+    def blocks_used(self):
+        """blocks_used(self)
+
+        Returns the total number of memory blocks currently allocated by libxml2.
+        Note that libxml2 constrains this value to a C int, which limits
+        the accuracy on 64 bit systems.
+        """
+        return tree.xmlMemBlocks()
+
+    def dict_size(self):
+        """dict_size(self)
+
+        Returns the current size of the global name dictionary used by libxml2
+        for the current thread.  Each thread has its own dictionary.
+        """
+        c_dict = __GLOBAL_PARSER_CONTEXT._getThreadDict(NULL)
+        if c_dict is NULL:
+            raise MemoryError()
+        return tree.xmlDictSize(c_dict)
+
+    def dump(self, output_file=None, byte_count=None):
+        """dump(self, output_file=None, byte_count=None)
+
+        Dumps the current memory blocks allocated by libxml2 to a file.
+
+        The optional parameter 'output_file' specifies the file path.  It defaults
+        to the file ".memorylist" in the current directory.
+
+        The optional parameter 'byte_count' limits the number of bytes in the dump.
+        Note that this parameter is ignored when lxml is compiled against a libxml2
+        version before 2.7.0.
+        """
+        cdef Py_ssize_t c_count
+        if output_file is None:
+            output_file = b'.memorylist'
+        elif isinstance(output_file, unicode):
+            output_file.encode(sys.getfilesystemencoding())
+
+        f = stdio.fopen(output_file, "w")
+        if f is NULL:
+            raise IOError(f"Failed to create file {output_file.decode(sys.getfilesystemencoding())}")
+        try:
+            if byte_count is None:
+                tree.xmlMemDisplay(f)
+            else:
+                c_count = byte_count
+                tree.xmlMemDisplayLast(f, c_count)
+        finally:
+            stdio.fclose(f)
+
+    def show(self, output_file=None, block_count=None):
+        """show(self, output_file=None, block_count=None)
+
+        Dumps the current memory blocks allocated by libxml2 to a file.
+        The output file format is suitable for line diffing.
+
+        The optional parameter 'output_file' specifies the file path.  It defaults
+        to the file ".memorydump" in the current directory.
+
+        The optional parameter 'block_count' limits the number of blocks
+        in the dump.
+        """
+        if output_file is None:
+            output_file = b'.memorydump'
+        elif isinstance(output_file, unicode):
+            output_file.encode(sys.getfilesystemencoding())
+
+        f = stdio.fopen(output_file, "w")
+        if f is NULL:
+            raise IOError(f"Failed to create file {output_file.decode(sys.getfilesystemencoding())}")
+        try:
+            tree.xmlMemShow(f, block_count if block_count is not None else tree.xmlMemBlocks())
+        finally:
+            stdio.fclose(f)
+
+memory_debugger = _MemDebug()
diff --git a/.venv/lib/python3.12/site-packages/lxml/docloader.pxi b/.venv/lib/python3.12/site-packages/lxml/docloader.pxi
new file mode 100644
index 00000000..7b38f438
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/docloader.pxi
@@ -0,0 +1,178 @@
+# Custom resolver API
+
+ctypedef enum _InputDocumentDataType:
+    PARSER_DATA_INVALID
+    PARSER_DATA_EMPTY
+    PARSER_DATA_STRING
+    PARSER_DATA_FILENAME
+    PARSER_DATA_FILE
+
+@cython.final
+@cython.internal
+cdef class _InputDocument:
+    cdef _InputDocumentDataType _type
+    cdef bytes _data_bytes
+    cdef object _filename
+    cdef object _file
+    cdef bint _close_file
+
+    def __cinit__(self):
+        self._type = PARSER_DATA_INVALID
+
+
+cdef class Resolver:
+    "This is the base class of all resolvers."
+    def resolve(self, system_url, public_id, context):
+        """resolve(self, system_url, public_id, context)
+
+        Override this method to resolve an external source by
+        ``system_url`` and ``public_id``.  The third argument is an
+        opaque context object.
+
+        Return the result of one of the ``resolve_*()`` methods.
+        """
+        return None
+
+    def resolve_empty(self, context):
+        """resolve_empty(self, context)
+
+        Return an empty input document.
+
+        Pass context as parameter.
+        """
+        cdef _InputDocument doc_ref
+        doc_ref = _InputDocument()
+        doc_ref._type = PARSER_DATA_EMPTY
+        return doc_ref
+
+    def resolve_string(self, string, context, *, base_url=None):
+        """resolve_string(self, string, context, base_url=None)
+
+        Return a parsable string as input document.
+
+        Pass data string and context as parameters.  You can pass the
+        source URL or filename through the ``base_url`` keyword
+        argument.
+        """
+        cdef _InputDocument doc_ref
+        if isinstance(string, unicode):
+            string = (<unicode>string).encode('utf8')
+        elif not isinstance(string, bytes):
+            raise TypeError, "argument must be a byte string or unicode string"
+        doc_ref = _InputDocument()
+        doc_ref._type = PARSER_DATA_STRING
+        doc_ref._data_bytes = string
+        if base_url is not None:
+            doc_ref._filename = _encodeFilename(base_url)
+        return doc_ref
+
+    def resolve_filename(self, filename, context):
+        """resolve_filename(self, filename, context)
+
+        Return the name of a parsable file as input document.
+
+        Pass filename and context as parameters.  You can also pass a
+        URL with an HTTP, FTP or file target.
+        """
+        cdef _InputDocument doc_ref
+        doc_ref = _InputDocument()
+        doc_ref._type = PARSER_DATA_FILENAME
+        doc_ref._filename = _encodeFilename(filename)
+        return doc_ref
+
+    def resolve_file(self, f, context, *, base_url=None, bint close=True):
+        """resolve_file(self, f, context, base_url=None, close=True)
+
+        Return an open file-like object as input document.
+
+        Pass open file and context as parameters.  You can pass the
+        base URL or filename of the file through the ``base_url``
+        keyword argument.  If the ``close`` flag is True (the
+        default), the file will be closed after reading.
+
+        Note that using ``.resolve_filename()`` is more efficient,
+        especially in threaded environments.
+        """
+        cdef _InputDocument doc_ref
+        try:
+            f.read
+        except AttributeError:
+            raise TypeError, "Argument is not a file-like object"
+        doc_ref = _InputDocument()
+        doc_ref._type = PARSER_DATA_FILE
+        if base_url is not None:
+            doc_ref._filename = _encodeFilename(base_url)
+        else:
+            doc_ref._filename = _getFilenameForFile(f)
+        doc_ref._close_file = close
+        doc_ref._file = f
+        return doc_ref
+
+@cython.final
+@cython.internal
+cdef class _ResolverRegistry:
+    cdef object _resolvers
+    cdef Resolver _default_resolver
+    def __cinit__(self, Resolver default_resolver=None):
+        self._resolvers = set()
+        self._default_resolver = default_resolver
+
+    def add(self, Resolver resolver not None):
+        """add(self, resolver)
+
+        Register a resolver.
+
+        For each requested entity, the 'resolve' method of the resolver will
+        be called and the result will be passed to the parser.  If this method
+        returns None, the request will be delegated to other resolvers or the
+        default resolver.  The resolvers will be tested in an arbitrary order
+        until the first match is found.
+        """
+        self._resolvers.add(resolver)
+
+    def remove(self, resolver):
+        "remove(self, resolver)"
+        self._resolvers.discard(resolver)
+
+    cdef _ResolverRegistry _copy(self):
+        cdef _ResolverRegistry registry
+        registry = _ResolverRegistry(self._default_resolver)
+        registry._resolvers = self._resolvers.copy()
+        return registry
+
+    def copy(self):
+        "copy(self)"
+        return self._copy()
+
+    def resolve(self, system_url, public_id, context):
+        "resolve(self, system_url, public_id, context)"
+        for resolver in self._resolvers:
+            result = resolver.resolve(system_url, public_id, context)
+            if result is not None:
+                return result
+        if self._default_resolver is None:
+            return None
+        return self._default_resolver.resolve(system_url, public_id, context)
+
+    def __repr__(self):
+        return repr(self._resolvers)
+
+
+@cython.internal
+cdef class _ResolverContext(_ExceptionContext):
+    cdef _ResolverRegistry _resolvers
+    cdef _TempStore _storage
+
+    cdef int clear(self) except -1:
+        _ExceptionContext.clear(self)
+        self._storage.clear()
+        return 0
+
+
+cdef _initResolverContext(_ResolverContext context,
+                          _ResolverRegistry resolvers):
+    if resolvers is None:
+        context._resolvers = _ResolverRegistry()
+    else:
+        context._resolvers = resolvers
+    context._storage = _TempStore()
diff --git a/.venv/lib/python3.12/site-packages/lxml/doctestcompare.py b/.venv/lib/python3.12/site-packages/lxml/doctestcompare.py
new file mode 100644
index 00000000..8099771d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/doctestcompare.py
@@ -0,0 +1,488 @@
+"""
+lxml-based doctest output comparison.
+
+Note: normally, you should just import the `lxml.usedoctest` and
+`lxml.html.usedoctest` modules from within a doctest, instead of this
+one::
+
+    >>> import lxml.usedoctest # for XML output
+
+    >>> import lxml.html.usedoctest # for HTML output
+
+To use this module directly, you must call ``lxmldoctest.install()``,
+which will cause doctest to use this in all subsequent calls.
+
+This changes the way output is checked and comparisons are made for
+XML or HTML-like content.
+
+XML or HTML content is noticed because the example starts with ``<``
+(it's HTML if it starts with ``<html``).  You can also use the
+``PARSE_HTML`` and ``PARSE_XML`` flags to force parsing.
+
+Some rough wildcard-like things are allowed.  Whitespace is generally
+ignored (except in attributes).  In text (attributes and text in the
+body) you can use ``...`` as a wildcard.  In an example it also
+matches any trailing tags in the element, though it does not match
+leading tags.  You may create a tag ``<any>`` or include an ``any``
+attribute in the tag.  An ``any`` tag matches any tag, while the
+attribute matches any and all attributes.
+
+When a match fails, the reformatted example and gotten text is
+displayed (indented), and a rough diff-like output is given.  Anything
+marked with ``+`` is in the output but wasn't supposed to be, and
+similarly ``-`` means its in the example but wasn't in the output.
+
+You can disable parsing on one line with ``# doctest:+NOPARSE_MARKUP``
+"""
+
+from lxml import etree
+import sys
+import re
+import doctest
+try:
+    from html import escape as html_escape
+except ImportError:
+    from cgi import escape as html_escape
+
+__all__ = ['PARSE_HTML', 'PARSE_XML', 'NOPARSE_MARKUP', 'LXMLOutputChecker',
+           'LHTMLOutputChecker', 'install', 'temp_install']
+
+PARSE_HTML = doctest.register_optionflag('PARSE_HTML')
+PARSE_XML = doctest.register_optionflag('PARSE_XML')
+NOPARSE_MARKUP = doctest.register_optionflag('NOPARSE_MARKUP')
+
+OutputChecker = doctest.OutputChecker
+
+def strip(v):
+    if v is None:
+        return None
+    else:
+        return v.strip()
+
+def norm_whitespace(v):
+    return _norm_whitespace_re.sub(' ', v)
+
+_html_parser = etree.HTMLParser(recover=False, remove_blank_text=True)
+
+def html_fromstring(html):
+    return etree.fromstring(html, _html_parser)
+
+# We use this to distinguish repr()s from elements:
+_repr_re = re.compile(r'^<[^>]+ (at|object) ')
+_norm_whitespace_re = re.compile(r'[ \t\n][ \t\n]+')
+
+class LXMLOutputChecker(OutputChecker):
+
+    empty_tags = (
+        'param', 'img', 'area', 'br', 'basefont', 'input',
+        'base', 'meta', 'link', 'col')
+
+    def get_default_parser(self):
+        return etree.XML
+
+    def check_output(self, want, got, optionflags):
+        alt_self = getattr(self, '_temp_override_self', None)
+        if alt_self is not None:
+            super_method = self._temp_call_super_check_output
+            self = alt_self
+        else:
+            super_method = OutputChecker.check_output
+        parser = self.get_parser(want, got, optionflags)
+        if not parser:
+            return super_method(
+                self, want, got, optionflags)
+        try:
+            want_doc = parser(want)
+        except etree.XMLSyntaxError:
+            return False
+        try:
+            got_doc = parser(got)
+        except etree.XMLSyntaxError:
+            return False
+        return self.compare_docs(want_doc, got_doc)
+
+    def get_parser(self, want, got, optionflags):
+        parser = None
+        if NOPARSE_MARKUP & optionflags:
+            return None
+        if PARSE_HTML & optionflags:
+            parser = html_fromstring
+        elif PARSE_XML & optionflags:
+            parser = etree.XML
+        elif (want.strip().lower().startswith('<html')
+              and got.strip().startswith('<html')):
+            parser = html_fromstring
+        elif (self._looks_like_markup(want)
+              and self._looks_like_markup(got)):
+            parser = self.get_default_parser()
+        return parser
+
+    def _looks_like_markup(self, s):
+        s = s.strip()
+        return (s.startswith('<')
+                and not _repr_re.search(s))
+
+    def compare_docs(self, want, got):
+        if not self.tag_compare(want.tag, got.tag):
+            return False
+        if not self.text_compare(want.text, got.text, True):
+            return False
+        if not self.text_compare(want.tail, got.tail, True):
+            return False
+        if 'any' not in want.attrib:
+            want_keys = sorted(want.attrib.keys())
+            got_keys = sorted(got.attrib.keys())
+            if want_keys != got_keys:
+                return False
+            for key in want_keys:
+                if not self.text_compare(want.attrib[key], got.attrib[key], False):
+                    return False
+        if want.text != '...' or len(want):
+            want_children = list(want)
+            got_children = list(got)
+            while want_children or got_children:
+                if not want_children or not got_children:
+                    return False
+                want_first = want_children.pop(0)
+                got_first = got_children.pop(0)
+                if not self.compare_docs(want_first, got_first):
+                    return False
+                if not got_children and want_first.tail == '...':
+                    break
+        return True
+
+    def text_compare(self, want, got, strip):
+        want = want or ''
+        got = got or ''
+        if strip:
+            want = norm_whitespace(want).strip()
+            got = norm_whitespace(got).strip()
+        want = '^%s$' % re.escape(want)
+        want = want.replace(r'\.\.\.', '.*')
+        if re.search(want, got):
+            return True
+        else:
+            return False
+
+    def tag_compare(self, want, got):
+        if want == 'any':
+            return True
+        if (not isinstance(want, (str, bytes))
+                or not isinstance(got, (str, bytes))):
+            return want == got
+        want = want or ''
+        got = got or ''
+        if want.startswith('{...}'):
+            # Ellipsis on the namespace
+            return want.split('}')[-1] == got.split('}')[-1]
+        else:
+            return want == got
+
+    def output_difference(self, example, got, optionflags):
+        want = example.want
+        parser = self.get_parser(want, got, optionflags)
+        errors = []
+        if parser is not None:
+            try:
+                want_doc = parser(want)
+            except etree.XMLSyntaxError:
+                e = sys.exc_info()[1]
+                errors.append('In example: %s' % e)
+            try:
+                got_doc = parser(got)
+            except etree.XMLSyntaxError:
+                e = sys.exc_info()[1]
+                errors.append('In actual output: %s' % e)
+        if parser is None or errors:
+            value = OutputChecker.output_difference(
+                self, example, got, optionflags)
+            if errors:
+                errors.append(value)
+                return '\n'.join(errors)
+            else:
+                return value
+        html = parser is html_fromstring
+        diff_parts = ['Expected:',
+                      self.format_doc(want_doc, html, 2),
+                      'Got:',
+                      self.format_doc(got_doc, html, 2),
+                      'Diff:',
+                      self.collect_diff(want_doc, got_doc, html, 2)]
+        return '\n'.join(diff_parts)
+
+    def html_empty_tag(self, el, html=True):
+        if not html:
+            return False
+        if el.tag not in self.empty_tags:
+            return False
+        if el.text or len(el):
+            # This shouldn't happen (contents in an empty tag)
+            return False
+        return True
+
+    def format_doc(self, doc, html, indent, prefix=''):
+        parts = []
+        if not len(doc):
+            # No children...
+            parts.append(' '*indent)
+            parts.append(prefix)
+            parts.append(self.format_tag(doc))
+            if not self.html_empty_tag(doc, html):
+                if strip(doc.text):
+                    parts.append(self.format_text(doc.text))
+                parts.append(self.format_end_tag(doc))
+            if strip(doc.tail):
+                parts.append(self.format_text(doc.tail))
+            parts.append('\n')
+            return ''.join(parts)
+        parts.append(' '*indent)
+        parts.append(prefix)
+        parts.append(self.format_tag(doc))
+        if not self.html_empty_tag(doc, html):
+            parts.append('\n')
+            if strip(doc.text):
+                parts.append(' '*indent)
+                parts.append(self.format_text(doc.text))
+                parts.append('\n')
+            for el in doc:
+                parts.append(self.format_doc(el, html, indent+2))
+            parts.append(' '*indent)
+            parts.append(self.format_end_tag(doc))
+            parts.append('\n')
+        if strip(doc.tail):
+            parts.append(' '*indent)
+            parts.append(self.format_text(doc.tail))
+            parts.append('\n')
+        return ''.join(parts)
+
+    def format_text(self, text, strip=True):
+        if text is None:
+            return ''
+        if strip:
+            text = text.strip()
+        return html_escape(text, 1)
+
+    def format_tag(self, el):
+        attrs = []
+        if isinstance(el, etree.CommentBase):
+            # FIXME: probably PIs should be handled specially too?
+            return '<!--'
+        for name, value in sorted(el.attrib.items()):
+            attrs.append('%s="%s"' % (name, self.format_text(value, False)))
+        if not attrs:
+            return '<%s>' % el.tag
+        return '<%s %s>' % (el.tag, ' '.join(attrs))
+    
+    def format_end_tag(self, el):
+        if isinstance(el, etree.CommentBase):
+            # FIXME: probably PIs should be handled specially too?
+            return '-->'
+        return '</%s>' % el.tag
+
+    def collect_diff(self, want, got, html, indent):
+        parts = []
+        if not len(want) and not len(got):
+            parts.append(' '*indent)
+            parts.append(self.collect_diff_tag(want, got))
+            if not self.html_empty_tag(got, html):
+                parts.append(self.collect_diff_text(want.text, got.text))
+                parts.append(self.collect_diff_end_tag(want, got))
+            parts.append(self.collect_diff_text(want.tail, got.tail))
+            parts.append('\n')
+            return ''.join(parts)
+        parts.append(' '*indent)
+        parts.append(self.collect_diff_tag(want, got))
+        parts.append('\n')
+        if strip(want.text) or strip(got.text):
+            parts.append(' '*indent)
+            parts.append(self.collect_diff_text(want.text, got.text))
+            parts.append('\n')
+        want_children = list(want)
+        got_children = list(got)
+        while want_children or got_children:
+            if not want_children:
+                parts.append(self.format_doc(got_children.pop(0), html, indent+2, '+'))
+                continue
+            if not got_children:
+                parts.append(self.format_doc(want_children.pop(0), html, indent+2, '-'))
+                continue
+            parts.append(self.collect_diff(
+                want_children.pop(0), got_children.pop(0), html, indent+2))
+        parts.append(' '*indent)
+        parts.append(self.collect_diff_end_tag(want, got))
+        parts.append('\n')
+        if strip(want.tail) or strip(got.tail):
+            parts.append(' '*indent)
+            parts.append(self.collect_diff_text(want.tail, got.tail))
+            parts.append('\n')
+        return ''.join(parts)
+
+    def collect_diff_tag(self, want, got):
+        if not self.tag_compare(want.tag, got.tag):
+            tag = '%s (got: %s)' % (want.tag, got.tag)
+        else:
+            tag = got.tag
+        attrs = []
+        any = want.tag == 'any' or 'any' in want.attrib
+        for name, value in sorted(got.attrib.items()):
+            if name not in want.attrib and not any:
+                attrs.append('+%s="%s"' % (name, self.format_text(value, False)))
+            else:
+                if name in want.attrib:
+                    text = self.collect_diff_text(want.attrib[name], value, False)
+                else:
+                    text = self.format_text(value, False)
+                attrs.append('%s="%s"' % (name, text))
+        if not any:
+            for name, value in sorted(want.attrib.items()):
+                if name in got.attrib:
+                    continue
+                attrs.append('-%s="%s"' % (name, self.format_text(value, False)))
+        if attrs:
+            tag = '<%s %s>' % (tag, ' '.join(attrs))
+        else:
+            tag = '<%s>' % tag
+        return tag
+
+    def collect_diff_end_tag(self, want, got):
+        if want.tag != got.tag:
+            tag = '%s (got: %s)' % (want.tag, got.tag)
+        else:
+            tag = got.tag
+        return '</%s>' % tag
+
+    def collect_diff_text(self, want, got, strip=True):
+        if self.text_compare(want, got, strip):
+            if not got:
+                return ''
+            return self.format_text(got, strip)
+        text = '%s (got: %s)' % (want, got)
+        return self.format_text(text, strip)
+
+class LHTMLOutputChecker(LXMLOutputChecker):
+    def get_default_parser(self):
+        return html_fromstring
+    
+def install(html=False):
+    """
+    Install doctestcompare for all future doctests.
+
+    If html is true, then by default the HTML parser will be used;
+    otherwise the XML parser is used.
+    """
+    if html:
+        doctest.OutputChecker = LHTMLOutputChecker
+    else:
+        doctest.OutputChecker = LXMLOutputChecker
+
+def temp_install(html=False, del_module=None):
+    """
+    Use this *inside* a doctest to enable this checker for this
+    doctest only.
+
+    If html is true, then by default the HTML parser will be used;
+    otherwise the XML parser is used.
+    """
+    if html:
+        Checker = LHTMLOutputChecker
+    else:
+        Checker = LXMLOutputChecker
+    frame = _find_doctest_frame()
+    dt_self = frame.f_locals['self']
+    checker = Checker()
+    old_checker = dt_self._checker
+    dt_self._checker = checker
+    # The unfortunate thing is that there is a local variable 'check'
+    # in the function that runs the doctests, that is a bound method
+    # into the output checker.  We have to update that.  We can't
+    # modify the frame, so we have to modify the object in place.  The
+    # only way to do this is to actually change the func_code
+    # attribute of the method.  We change it, and then wait for
+    # __record_outcome to be run, which signals the end of the __run
+    # method, at which point we restore the previous check_output
+    # implementation.
+    check_func = frame.f_locals['check'].__func__
+    checker_check_func = checker.check_output.__func__
+    # Because we can't patch up func_globals, this is the only global
+    # in check_output that we care about:
+    doctest.etree = etree
+    _RestoreChecker(dt_self, old_checker, checker,
+                    check_func, checker_check_func,
+                    del_module)
+
+class _RestoreChecker:
+    def __init__(self, dt_self, old_checker, new_checker, check_func, clone_func,
+                 del_module):
+        self.dt_self = dt_self
+        self.checker = old_checker
+        self.checker._temp_call_super_check_output = self.call_super
+        self.checker._temp_override_self = new_checker
+        self.check_func = check_func
+        self.clone_func = clone_func
+        self.del_module = del_module
+        self.install_clone()
+        self.install_dt_self()
+    def install_clone(self):
+        self.func_code = self.check_func.__code__
+        self.func_globals = self.check_func.__globals__
+        self.check_func.__code__ = self.clone_func.__code__
+    def uninstall_clone(self):
+        self.check_func.__code__ = self.func_code
+    def install_dt_self(self):
+        self.prev_func = self.dt_self._DocTestRunner__record_outcome
+        self.dt_self._DocTestRunner__record_outcome = self
+    def uninstall_dt_self(self):
+        self.dt_self._DocTestRunner__record_outcome = self.prev_func
+    def uninstall_module(self):
+        if self.del_module:
+            import sys
+            del sys.modules[self.del_module]
+            if '.' in self.del_module:
+                package, module = self.del_module.rsplit('.', 1)
+                package_mod = sys.modules[package]
+                delattr(package_mod, module)
+    def __call__(self, *args, **kw):
+        self.uninstall_clone()
+        self.uninstall_dt_self()
+        del self.checker._temp_override_self
+        del self.checker._temp_call_super_check_output
+        result = self.prev_func(*args, **kw)
+        self.uninstall_module()
+        return result
+    def call_super(self, *args, **kw):
+        self.uninstall_clone()
+        try:
+            return self.check_func(*args, **kw)
+        finally:
+            self.install_clone()
+            
+def _find_doctest_frame():
+    import sys
+    frame = sys._getframe(1)
+    while frame:
+        l = frame.f_locals
+        if 'BOOM' in l:
+            # Sign of doctest
+            return frame
+        frame = frame.f_back
+    raise LookupError(
+        "Could not find doctest (only use this function *inside* a doctest)")
+    
+__test__ = {
+    'basic': '''
+    >>> temp_install()
+    >>> print """<xml a="1" b="2">stuff</xml>"""
+    <xml b="2" a="1">...</xml>
+    >>> print """<xml xmlns="http://example.com"><tag   attr="bar"   /></xml>"""
+    <xml xmlns="...">
+      <tag attr="..." />
+    </xml>
+    >>> print """<xml>blahblahblah<foo /></xml>""" # doctest: +NOPARSE_MARKUP, +ELLIPSIS
+    <xml>...foo /></xml>
+    '''}
+
+if __name__ == '__main__':
+    import doctest
+    doctest.testmod()
+    
+    
diff --git a/.venv/lib/python3.12/site-packages/lxml/dtd.pxi b/.venv/lib/python3.12/site-packages/lxml/dtd.pxi
new file mode 100644
index 00000000..ee1b3d47
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/dtd.pxi
@@ -0,0 +1,479 @@
+# support for DTD validation
+from lxml.includes cimport dtdvalid
+
+cdef class DTDError(LxmlError):
+    """Base class for DTD errors.
+    """
+
+cdef class DTDParseError(DTDError):
+    """Error while parsing a DTD.
+    """
+
+cdef class DTDValidateError(DTDError):
+    """Error while validating an XML document with a DTD.
+    """
+
+
+cdef inline int _assertValidDTDNode(node, void *c_node) except -1:
+    assert c_node is not NULL, "invalid DTD proxy at %s" % id(node)
+
+
+@cython.final
+@cython.internal
+@cython.freelist(8)
+cdef class _DTDElementContentDecl:
+    cdef DTD _dtd
+    cdef tree.xmlElementContent* _c_node
+
+    def __repr__(self):
+        return "<%s.%s object name=%r type=%r occur=%r at 0x%x>" % (self.__class__.__module__, self.__class__.__name__, self.name, self.type, self.occur, id(self))
+
+    @property
+    def name(self):
+       _assertValidDTDNode(self, self._c_node)
+       return funicodeOrNone(self._c_node.name)
+
+    @property
+    def type(self):
+       _assertValidDTDNode(self, self._c_node)
+       cdef int type = self._c_node.type
+       if type == tree.XML_ELEMENT_CONTENT_PCDATA:
+           return "pcdata"
+       elif type == tree.XML_ELEMENT_CONTENT_ELEMENT:
+           return "element"
+       elif type == tree.XML_ELEMENT_CONTENT_SEQ:
+           return "seq"
+       elif type == tree.XML_ELEMENT_CONTENT_OR:
+           return "or"
+       else:
+           return None
+
+    @property
+    def occur(self):
+       _assertValidDTDNode(self, self._c_node)
+       cdef int occur = self._c_node.ocur
+       if occur == tree.XML_ELEMENT_CONTENT_ONCE:
+           return "once"
+       elif occur == tree.XML_ELEMENT_CONTENT_OPT:
+           return "opt"
+       elif occur == tree.XML_ELEMENT_CONTENT_MULT:
+           return "mult"
+       elif occur == tree.XML_ELEMENT_CONTENT_PLUS:
+           return "plus"
+       else:
+           return None
+
+    @property
+    def left(self):
+       _assertValidDTDNode(self, self._c_node)
+       c1 = self._c_node.c1
+       if c1:
+           node = <_DTDElementContentDecl>_DTDElementContentDecl.__new__(_DTDElementContentDecl)
+           node._dtd = self._dtd
+           node._c_node = <tree.xmlElementContent*>c1
+           return node
+       else:
+           return None
+
+    @property
+    def right(self):
+       _assertValidDTDNode(self, self._c_node)
+       c2 = self._c_node.c2
+       if c2:
+           node = <_DTDElementContentDecl>_DTDElementContentDecl.__new__(_DTDElementContentDecl)
+           node._dtd = self._dtd
+           node._c_node = <tree.xmlElementContent*>c2
+           return node
+       else:
+           return None
+
+
+@cython.final
+@cython.internal
+@cython.freelist(8)
+cdef class _DTDAttributeDecl:
+    cdef DTD _dtd
+    cdef tree.xmlAttribute* _c_node
+
+    def __repr__(self):
+        return "<%s.%s object name=%r elemname=%r prefix=%r type=%r default=%r default_value=%r at 0x%x>" % (self.__class__.__module__, self.__class__.__name__, self.name, self.elemname, self.prefix, self.type, self.default, self.default_value, id(self))
+
+    @property
+    def name(self):
+       _assertValidDTDNode(self, self._c_node)
+       return funicodeOrNone(self._c_node.name)
+
+    @property
+    def elemname(self):
+       _assertValidDTDNode(self, self._c_node)
+       return funicodeOrNone(self._c_node.elem)
+
+    @property
+    def prefix(self):
+       _assertValidDTDNode(self, self._c_node)
+       return funicodeOrNone(self._c_node.prefix)
+
+    @property
+    def type(self):
+       _assertValidDTDNode(self, self._c_node)
+       cdef int type = self._c_node.atype
+       if type == tree.XML_ATTRIBUTE_CDATA:
+           return "cdata"
+       elif type == tree.XML_ATTRIBUTE_ID:
+           return "id"
+       elif type == tree.XML_ATTRIBUTE_IDREF:
+           return "idref"
+       elif type == tree.XML_ATTRIBUTE_IDREFS:
+           return "idrefs"
+       elif type == tree.XML_ATTRIBUTE_ENTITY:
+           return "entity"
+       elif type == tree.XML_ATTRIBUTE_ENTITIES:
+           return "entities"
+       elif type == tree.XML_ATTRIBUTE_NMTOKEN:
+           return "nmtoken"
+       elif type == tree.XML_ATTRIBUTE_NMTOKENS:
+           return "nmtokens"
+       elif type == tree.XML_ATTRIBUTE_ENUMERATION:
+           return "enumeration"
+       elif type == tree.XML_ATTRIBUTE_NOTATION:
+           return "notation"
+       else:
+           return None
+
+    @property
+    def default(self):
+       _assertValidDTDNode(self, self._c_node)
+       cdef int default = self._c_node.def_
+       if default == tree.XML_ATTRIBUTE_NONE:
+           return "none"
+       elif default == tree.XML_ATTRIBUTE_REQUIRED:
+           return "required"
+       elif default == tree.XML_ATTRIBUTE_IMPLIED:
+           return "implied"
+       elif default == tree.XML_ATTRIBUTE_FIXED:
+           return "fixed"
+       else:
+           return None
+
+    @property
+    def default_value(self):
+       _assertValidDTDNode(self, self._c_node)
+       return funicodeOrNone(self._c_node.defaultValue)
+
+    def itervalues(self):
+        _assertValidDTDNode(self, self._c_node)
+        cdef tree.xmlEnumeration *c_node = self._c_node.tree
+        while c_node is not NULL:
+            yield funicode(c_node.name)
+            c_node = c_node.next
+
+    def values(self):
+        return list(self.itervalues())
+
+
+@cython.final
+@cython.internal
+@cython.freelist(8)
+cdef class _DTDElementDecl:
+    cdef DTD _dtd
+    cdef tree.xmlElement* _c_node
+
+    def __repr__(self):
+        return "<%s.%s object name=%r prefix=%r type=%r at 0x%x>" % (self.__class__.__module__, self.__class__.__name__, self.name, self.prefix, self.type, id(self))
+
+    @property
+    def name(self):
+        _assertValidDTDNode(self, self._c_node)
+        return funicodeOrNone(self._c_node.name)
+
+    @property
+    def prefix(self):
+       _assertValidDTDNode(self, self._c_node)
+       return funicodeOrNone(self._c_node.prefix)
+
+    @property
+    def type(self):
+       _assertValidDTDNode(self, self._c_node)
+       cdef int type = self._c_node.etype
+       if type == tree.XML_ELEMENT_TYPE_UNDEFINED:
+           return "undefined"
+       elif type == tree.XML_ELEMENT_TYPE_EMPTY:
+           return "empty"
+       elif type == tree.XML_ELEMENT_TYPE_ANY:
+           return "any"
+       elif type == tree.XML_ELEMENT_TYPE_MIXED:
+           return "mixed"
+       elif type == tree.XML_ELEMENT_TYPE_ELEMENT:
+           return "element"
+       else:
+           return None
+
+    @property
+    def content(self):
+       _assertValidDTDNode(self, self._c_node)
+       cdef tree.xmlElementContent *content = self._c_node.content
+       if content:
+           node = <_DTDElementContentDecl>_DTDElementContentDecl.__new__(_DTDElementContentDecl)
+           node._dtd = self._dtd
+           node._c_node = content
+           return node
+       else:
+           return None
+
+    def iterattributes(self):
+        _assertValidDTDNode(self, self._c_node)
+        cdef tree.xmlAttribute *c_node = self._c_node.attributes
+        while c_node:
+            node = <_DTDAttributeDecl>_DTDAttributeDecl.__new__(_DTDAttributeDecl)
+            node._dtd = self._dtd
+            node._c_node = c_node
+            yield node
+            c_node = c_node.nexth
+
+    def attributes(self):
+        return list(self.iterattributes())
+
+
+@cython.final
+@cython.internal
+@cython.freelist(8)
+cdef class _DTDEntityDecl:
+    cdef DTD _dtd
+    cdef tree.xmlEntity* _c_node
+    def __repr__(self):
+        return "<%s.%s object name=%r at 0x%x>" % (self.__class__.__module__, self.__class__.__name__, self.name, id(self))
+
+    @property
+    def name(self):
+        _assertValidDTDNode(self, self._c_node)
+        return funicodeOrNone(self._c_node.name)
+
+    @property
+    def orig(self):
+        _assertValidDTDNode(self, self._c_node)
+        return funicodeOrNone(self._c_node.orig)
+
+    @property
+    def content(self):
+        _assertValidDTDNode(self, self._c_node)
+        return funicodeOrNone(self._c_node.content)
+
+    @property
+    def system_url(self):
+        _assertValidDTDNode(self, self._c_node)
+        return funicodeOrNone(self._c_node.SystemID)
+
+
+################################################################################
+# DTD
+
+cdef class DTD(_Validator):
+    """DTD(self, file=None, external_id=None)
+    A DTD validator.
+
+    Can load from filesystem directly given a filename or file-like object.
+    Alternatively, pass the keyword parameter ``external_id`` to load from a
+    catalog.
+    """
+    cdef tree.xmlDtd* _c_dtd
+    def __init__(self, file=None, *, external_id=None):
+        _Validator.__init__(self)
+        if file is not None:
+            file = _getFSPathOrObject(file)
+            if _isString(file):
+                file = _encodeFilename(file)
+                with self._error_log:
+                    orig_loader = _register_document_loader()
+                    self._c_dtd = xmlparser.xmlParseDTD(NULL, _xcstr(file))
+                    _reset_document_loader(orig_loader)
+            elif hasattr(file, 'read'):
+                orig_loader = _register_document_loader()
+                self._c_dtd = _parseDtdFromFilelike(file)
+                _reset_document_loader(orig_loader)
+            else:
+                raise DTDParseError, "file must be a filename, file-like or path-like object"
+        elif external_id is not None:
+            external_id_utf = _utf8(external_id)
+            with self._error_log:
+                orig_loader = _register_document_loader()
+                self._c_dtd = xmlparser.xmlParseDTD(<const_xmlChar*>external_id_utf, NULL)
+                _reset_document_loader(orig_loader)
+        else:
+            raise DTDParseError, "either filename or external ID required"
+
+        if self._c_dtd is NULL:
+            raise DTDParseError(
+                self._error_log._buildExceptionMessage("error parsing DTD"),
+                self._error_log)
+
+    @property
+    def name(self):
+       if self._c_dtd is NULL:
+           return None
+       return funicodeOrNone(self._c_dtd.name)
+
+    @property
+    def external_id(self):
+       if self._c_dtd is NULL:
+           return None
+       return funicodeOrNone(self._c_dtd.ExternalID)
+
+    @property
+    def system_url(self):
+       if self._c_dtd is NULL:
+           return None
+       return funicodeOrNone(self._c_dtd.SystemID)
+
+    def iterelements(self):
+        cdef tree.xmlNode *c_node = self._c_dtd.children if self._c_dtd is not NULL else NULL
+        while c_node is not NULL:
+            if c_node.type == tree.XML_ELEMENT_DECL:
+                node = _DTDElementDecl()
+                node._dtd = self
+                node._c_node = <tree.xmlElement*>c_node
+                yield node
+            c_node = c_node.next
+
+    def elements(self):
+        return list(self.iterelements())
+
+    def iterentities(self):
+        cdef tree.xmlNode *c_node = self._c_dtd.children if self._c_dtd is not NULL else NULL
+        while c_node is not NULL:
+            if c_node.type == tree.XML_ENTITY_DECL:
+                node = _DTDEntityDecl()
+                node._dtd = self
+                node._c_node = <tree.xmlEntity*>c_node
+                yield node
+            c_node = c_node.next
+
+    def entities(self):
+        return list(self.iterentities())
+
+    def __dealloc__(self):
+        tree.xmlFreeDtd(self._c_dtd)
+
+    def __call__(self, etree):
+        """__call__(self, etree)
+
+        Validate doc using the DTD.
+
+        Returns true if the document is valid, false if not.
+        """
+        cdef _Document doc
+        cdef _Element root_node
+        cdef xmlDoc* c_doc
+        cdef dtdvalid.xmlValidCtxt* valid_ctxt
+        cdef int ret = -1
+
+        assert self._c_dtd is not NULL, "DTD not initialised"
+        doc = _documentOrRaise(etree)
+        root_node = _rootNodeOrRaise(etree)
+
+        valid_ctxt = dtdvalid.xmlNewValidCtxt()
+        if valid_ctxt is NULL:
+            raise DTDError("Failed to create validation context")
+
+        # work around error reporting bug in libxml2 <= 2.9.1 (and later?)
+        # https://bugzilla.gnome.org/show_bug.cgi?id=724903
+        valid_ctxt.error = <dtdvalid.xmlValidityErrorFunc>_nullGenericErrorFunc
+        valid_ctxt.userData = NULL
+
+        try:
+            with self._error_log:
+                c_doc = _fakeRootDoc(doc._c_doc, root_node._c_node)
+                ret = dtdvalid.xmlValidateDtd(valid_ctxt, c_doc, self._c_dtd)
+                _destroyFakeDoc(doc._c_doc, c_doc)
+        finally:
+            dtdvalid.xmlFreeValidCtxt(valid_ctxt)
+
+        if ret == -1:
+            raise DTDValidateError("Internal error in DTD validation",
+                                   self._error_log)
+        return ret == 1
+
+
+cdef tree.xmlDtd* _parseDtdFromFilelike(file) except NULL:
+    cdef _ExceptionContext exc_context
+    cdef _FileReaderContext dtd_parser
+    cdef _ErrorLog error_log
+    cdef tree.xmlDtd* c_dtd = NULL
+    exc_context = _ExceptionContext()
+    dtd_parser = _FileReaderContext(file, exc_context, None)
+    error_log = _ErrorLog()
+
+    with error_log:
+        c_dtd = dtd_parser._readDtd()
+
+    exc_context._raise_if_stored()
+    if c_dtd is NULL:
+        raise DTDParseError("error parsing DTD", error_log)
+    return c_dtd
+
+cdef DTD _dtdFactory(tree.xmlDtd* c_dtd):
+    # do not run through DTD.__init__()!
+    cdef DTD dtd
+    if c_dtd is NULL:
+        return None
+    dtd = DTD.__new__(DTD)
+    dtd._c_dtd = _copyDtd(c_dtd)
+    _Validator.__init__(dtd)
+    return dtd
+
+
+cdef tree.xmlDtd* _copyDtd(tree.xmlDtd* c_orig_dtd) except NULL:
+    """
+    Copy a DTD.  libxml2 (currently) fails to set up the element->attributes
+    links when copying DTDs, so we have to rebuild them here.
+    """
+    c_dtd = tree.xmlCopyDtd(c_orig_dtd)
+    if not c_dtd:
+        raise MemoryError
+    cdef tree.xmlNode* c_node = c_dtd.children
+    while c_node:
+        if c_node.type == tree.XML_ATTRIBUTE_DECL:
+            _linkDtdAttribute(c_dtd, <tree.xmlAttribute*>c_node)
+        c_node = c_node.next
+    return c_dtd
+
+
+cdef void _linkDtdAttribute(tree.xmlDtd* c_dtd, tree.xmlAttribute* c_attr) noexcept:
+    """
+    Create the link to the DTD attribute declaration from the corresponding
+    element declaration.
+    """
+    c_elem = dtdvalid.xmlGetDtdElementDesc(c_dtd, c_attr.elem)
+    if not c_elem:
+        # no such element? something is wrong with the DTD ...
+        return
+    c_pos = c_elem.attributes
+    if not c_pos:
+        c_elem.attributes = c_attr
+        c_attr.nexth = NULL
+        return
+    # libxml2 keeps namespace declarations first, and we need to make
+    # sure we don't re-insert attributes that are already there
+    if _isDtdNsDecl(c_attr):
+        if not _isDtdNsDecl(c_pos):
+            c_elem.attributes = c_attr
+            c_attr.nexth = c_pos
+            return
+        while c_pos != c_attr and c_pos.nexth and _isDtdNsDecl(c_pos.nexth):
+            c_pos = c_pos.nexth
+    else:
+        # append at end
+        while c_pos != c_attr and c_pos.nexth:
+            c_pos = c_pos.nexth
+    if c_pos == c_attr:
+        return
+    c_attr.nexth = c_pos.nexth
+    c_pos.nexth = c_attr
+
+
+cdef bint _isDtdNsDecl(tree.xmlAttribute* c_attr) noexcept:
+    if cstring_h.strcmp(<const_char*>c_attr.name, "xmlns") == 0:
+        return True
+    if (c_attr.prefix is not NULL and
+            cstring_h.strcmp(<const_char*>c_attr.prefix, "xmlns") == 0):
+        return True
+    return False
diff --git a/.venv/lib/python3.12/site-packages/lxml/etree.cpython-312-x86_64-linux-gnu.so b/.venv/lib/python3.12/site-packages/lxml/etree.cpython-312-x86_64-linux-gnu.so
new file mode 100755
index 00000000..29c19ddb
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/etree.cpython-312-x86_64-linux-gnu.so
Binary files differdiff --git a/.venv/lib/python3.12/site-packages/lxml/etree.h b/.venv/lib/python3.12/site-packages/lxml/etree.h
new file mode 100644
index 00000000..c2a0f5ab
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/etree.h
@@ -0,0 +1,248 @@
+/* Generated by Cython 3.0.11 */
+
+#ifndef __PYX_HAVE__lxml__etree
+#define __PYX_HAVE__lxml__etree
+
+#include "Python.h"
+struct LxmlDocument;
+struct LxmlElement;
+struct LxmlElementTree;
+struct LxmlElementTagMatcher;
+struct LxmlElementIterator;
+struct LxmlElementBase;
+struct LxmlElementClassLookup;
+struct LxmlFallbackElementClassLookup;
+
+/* "lxml/etree.pyx":355
+ * 
+ * # type of a function that steps from node to node
+ * ctypedef public xmlNode* (*_node_to_node_function)(xmlNode*)             # <<<<<<<<<<<<<<
+ * 
+ * 
+ */
+typedef xmlNode *(*_node_to_node_function)(xmlNode *);
+
+/* "lxml/etree.pyx":371
+ * @cython.final
+ * @cython.freelist(8)
+ * cdef public class _Document [ type LxmlDocumentType, object LxmlDocument ]:             # <<<<<<<<<<<<<<
+ *     """Internal base class to reference a libxml document.
+ * 
+ */
+struct LxmlDocument {
+  PyObject_HEAD
+  struct __pyx_vtabstruct_4lxml_5etree__Document *__pyx_vtab;
+  int _ns_counter;
+  PyObject *_prefix_tail;
+  xmlDoc *_c_doc;
+  struct __pyx_obj_4lxml_5etree__BaseParser *_parser;
+};
+
+/* "lxml/etree.pyx":720
+ * 
+ * @cython.no_gc_clear
+ * cdef public class _Element [ type LxmlElementType, object LxmlElement ]:             # <<<<<<<<<<<<<<
+ *     """Element class.
+ * 
+ */
+struct LxmlElement {
+  PyObject_HEAD
+  struct LxmlDocument *_doc;
+  xmlNode *_c_node;
+  PyObject *_tag;
+};
+
+/* "lxml/etree.pyx":1895
+ * 
+ * 
+ * cdef public class _ElementTree [ type LxmlElementTreeType,             # <<<<<<<<<<<<<<
+ *                                  object LxmlElementTree ]:
+ *     cdef _Document _doc
+ */
+struct LxmlElementTree {
+  PyObject_HEAD
+  struct __pyx_vtabstruct_4lxml_5etree__ElementTree *__pyx_vtab;
+  struct LxmlDocument *_doc;
+  struct LxmlElement *_context_node;
+};
+
+/* "lxml/etree.pyx":2669
+ * 
+ * 
+ * cdef public class _ElementTagMatcher [ object LxmlElementTagMatcher,             # <<<<<<<<<<<<<<
+ *                                        type LxmlElementTagMatcherType ]:
+ *     """
+ */
+struct LxmlElementTagMatcher {
+  PyObject_HEAD
+  struct __pyx_vtabstruct_4lxml_5etree__ElementTagMatcher *__pyx_vtab;
+  PyObject *_pystrings;
+  int _node_type;
+  char *_href;
+  char *_name;
+};
+
+/* "lxml/etree.pyx":2700
+ *                 self._name = NULL
+ * 
+ * cdef public class _ElementIterator(_ElementTagMatcher) [             # <<<<<<<<<<<<<<
+ *     object LxmlElementIterator, type LxmlElementIteratorType ]:
+ *     """
+ */
+struct LxmlElementIterator {
+  struct LxmlElementTagMatcher __pyx_base;
+  struct LxmlElement *_node;
+  _node_to_node_function _next_element;
+};
+
+/* "src/lxml/classlookup.pxi":6
+ * # Custom Element classes
+ * 
+ * cdef public class ElementBase(_Element) [ type LxmlElementBaseType,             # <<<<<<<<<<<<<<
+ *                                           object LxmlElementBase ]:
+ *     """ElementBase(*children, attrib=None, nsmap=None, **_extra)
+ */
+struct LxmlElementBase {
+  struct LxmlElement __pyx_base;
+};
+
+/* "src/lxml/classlookup.pxi":210
+ * # Element class lookup
+ * 
+ * ctypedef public object (*_element_class_lookup_function)(object, _Document, xmlNode*)             # <<<<<<<<<<<<<<
+ * 
+ * # class to store element class lookup functions
+ */
+typedef PyObject *(*_element_class_lookup_function)(PyObject *, struct LxmlDocument *, xmlNode *);
+
+/* "src/lxml/classlookup.pxi":213
+ * 
+ * # class to store element class lookup functions
+ * cdef public class ElementClassLookup [ type LxmlElementClassLookupType,             # <<<<<<<<<<<<<<
+ *                                        object LxmlElementClassLookup ]:
+ *     """ElementClassLookup(self)
+ */
+struct LxmlElementClassLookup {
+  PyObject_HEAD
+  _element_class_lookup_function _lookup_function;
+};
+
+/* "src/lxml/classlookup.pxi":221
+ * 
+ * 
+ * cdef public class FallbackElementClassLookup(ElementClassLookup) \             # <<<<<<<<<<<<<<
+ *          [ type LxmlFallbackElementClassLookupType,
+ *            object LxmlFallbackElementClassLookup ]:
+ */
+struct LxmlFallbackElementClassLookup {
+  struct LxmlElementClassLookup __pyx_base;
+  struct __pyx_vtabstruct_4lxml_5etree_FallbackElementClassLookup *__pyx_vtab;
+  struct LxmlElementClassLookup *fallback;
+  _element_class_lookup_function _fallback_function;
+};
+
+#ifndef __PYX_HAVE_API__lxml__etree
+
+#ifdef CYTHON_EXTERN_C
+    #undef __PYX_EXTERN_C
+    #define __PYX_EXTERN_C CYTHON_EXTERN_C
+#elif defined(__PYX_EXTERN_C)
+    #ifdef _MSC_VER
+    #pragma message ("Please do not define the '__PYX_EXTERN_C' macro externally. Use 'CYTHON_EXTERN_C' instead.")
+    #else
+    #warning Please do not define the '__PYX_EXTERN_C' macro externally. Use 'CYTHON_EXTERN_C' instead.
+    #endif
+#else
+  #ifdef __cplusplus
+    #define __PYX_EXTERN_C extern "C"
+  #else
+    #define __PYX_EXTERN_C extern
+  #endif
+#endif
+
+#ifndef DL_IMPORT
+  #define DL_IMPORT(_T) _T
+#endif
+
+__PYX_EXTERN_C DL_IMPORT(PyTypeObject) LxmlDocumentType;
+__PYX_EXTERN_C DL_IMPORT(PyTypeObject) LxmlElementType;
+__PYX_EXTERN_C DL_IMPORT(PyTypeObject) LxmlElementTreeType;
+__PYX_EXTERN_C DL_IMPORT(PyTypeObject) LxmlElementTagMatcherType;
+__PYX_EXTERN_C DL_IMPORT(PyTypeObject) LxmlElementIteratorType;
+__PYX_EXTERN_C DL_IMPORT(PyTypeObject) LxmlElementBaseType;
+__PYX_EXTERN_C DL_IMPORT(PyTypeObject) LxmlElementClassLookupType;
+__PYX_EXTERN_C DL_IMPORT(PyTypeObject) LxmlFallbackElementClassLookupType;
+
+__PYX_EXTERN_C struct LxmlElement *deepcopyNodeToDocument(struct LxmlDocument *, xmlNode *);
+__PYX_EXTERN_C struct LxmlElementTree *elementTreeFactory(struct LxmlElement *);
+__PYX_EXTERN_C struct LxmlElementTree *newElementTree(struct LxmlElement *, PyObject *);
+__PYX_EXTERN_C struct LxmlElementTree *adoptExternalDocument(xmlDoc *, PyObject *, int);
+__PYX_EXTERN_C struct LxmlElement *elementFactory(struct LxmlDocument *, xmlNode *);
+__PYX_EXTERN_C struct LxmlElement *makeElement(PyObject *, struct LxmlDocument *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *);
+__PYX_EXTERN_C struct LxmlElement *makeSubElement(struct LxmlElement *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *);
+__PYX_EXTERN_C void setElementClassLookupFunction(_element_class_lookup_function, PyObject *);
+__PYX_EXTERN_C PyObject *lookupDefaultElementClass(PyObject *, PyObject *, xmlNode *);
+__PYX_EXTERN_C PyObject *lookupNamespaceElementClass(PyObject *, PyObject *, xmlNode *);
+__PYX_EXTERN_C PyObject *callLookupFallback(struct LxmlFallbackElementClassLookup *, struct LxmlDocument *, xmlNode *);
+__PYX_EXTERN_C int tagMatches(xmlNode *, const xmlChar *, const xmlChar *);
+__PYX_EXTERN_C struct LxmlDocument *documentOrRaise(PyObject *);
+__PYX_EXTERN_C struct LxmlElement *rootNodeOrRaise(PyObject *);
+__PYX_EXTERN_C int hasText(xmlNode *);
+__PYX_EXTERN_C int hasTail(xmlNode *);
+__PYX_EXTERN_C PyObject *textOf(xmlNode *);
+__PYX_EXTERN_C PyObject *tailOf(xmlNode *);
+__PYX_EXTERN_C int setNodeText(xmlNode *, PyObject *);
+__PYX_EXTERN_C int setTailText(xmlNode *, PyObject *);
+__PYX_EXTERN_C PyObject *attributeValue(xmlNode *, xmlAttr *);
+__PYX_EXTERN_C PyObject *attributeValueFromNsName(xmlNode *, const xmlChar *, const xmlChar *);
+__PYX_EXTERN_C PyObject *getAttributeValue(struct LxmlElement *, PyObject *, PyObject *);
+__PYX_EXTERN_C PyObject *iterattributes(struct LxmlElement *, int);
+__PYX_EXTERN_C PyObject *collectAttributes(xmlNode *, int);
+__PYX_EXTERN_C int setAttributeValue(struct LxmlElement *, PyObject *, PyObject *);
+__PYX_EXTERN_C int delAttribute(struct LxmlElement *, PyObject *);
+__PYX_EXTERN_C int delAttributeFromNsName(xmlNode *, const xmlChar *, const xmlChar *);
+__PYX_EXTERN_C int hasChild(xmlNode *);
+__PYX_EXTERN_C xmlNode *findChild(xmlNode *, Py_ssize_t);
+__PYX_EXTERN_C xmlNode *findChildForwards(xmlNode *, Py_ssize_t);
+__PYX_EXTERN_C xmlNode *findChildBackwards(xmlNode *, Py_ssize_t);
+__PYX_EXTERN_C xmlNode *nextElement(xmlNode *);
+__PYX_EXTERN_C xmlNode *previousElement(xmlNode *);
+__PYX_EXTERN_C void appendChild(struct LxmlElement *, struct LxmlElement *);
+__PYX_EXTERN_C int appendChildToElement(struct LxmlElement *, struct LxmlElement *);
+__PYX_EXTERN_C PyObject *pyunicode(const xmlChar *);
+__PYX_EXTERN_C PyObject *utf8(PyObject *);
+__PYX_EXTERN_C PyObject *getNsTag(PyObject *);
+__PYX_EXTERN_C PyObject *getNsTagWithEmptyNs(PyObject *);
+__PYX_EXTERN_C PyObject *namespacedName(xmlNode *);
+__PYX_EXTERN_C PyObject *namespacedNameFromNsName(const xmlChar *, const xmlChar *);
+__PYX_EXTERN_C void iteratorStoreNext(struct LxmlElementIterator *, struct LxmlElement *);
+__PYX_EXTERN_C void initTagMatch(struct LxmlElementTagMatcher *, PyObject *);
+__PYX_EXTERN_C xmlNs *findOrBuildNodeNsPrefix(struct LxmlDocument *, xmlNode *, const xmlChar *, const xmlChar *);
+
+#endif /* !__PYX_HAVE_API__lxml__etree */
+
+/* WARNING: the interface of the module init function changed in CPython 3.5. */
+/* It now returns a PyModuleDef instance instead of a PyModule instance. */
+
+#if PY_MAJOR_VERSION < 3
+PyMODINIT_FUNC initetree(void);
+#else
+/* WARNING: Use PyImport_AppendInittab("etree", PyInit_etree) instead of calling PyInit_etree directly from Python 3.5 */
+PyMODINIT_FUNC PyInit_etree(void);
+
+#if PY_VERSION_HEX >= 0x03050000 && (defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER) || (defined(__cplusplus) && __cplusplus >= 201402L))
+#if defined(__cplusplus) && __cplusplus >= 201402L
+[[deprecated("Use PyImport_AppendInittab(\"etree\", PyInit_etree) instead of calling PyInit_etree directly.")]] inline
+#elif defined(__GNUC__) || defined(__clang__)
+__attribute__ ((__deprecated__("Use PyImport_AppendInittab(\"etree\", PyInit_etree) instead of calling PyInit_etree directly."), __unused__)) __inline__
+#elif defined(_MSC_VER)
+__declspec(deprecated("Use PyImport_AppendInittab(\"etree\", PyInit_etree) instead of calling PyInit_etree directly.")) __inline
+#endif
+static PyObject* __PYX_WARN_IF_PyInit_etree_INIT_CALLED(PyObject* res) {
+  return res;
+}
+#define PyInit_etree() __PYX_WARN_IF_PyInit_etree_INIT_CALLED(PyInit_etree())
+#endif
+#endif
+
+#endif /* !__PYX_HAVE__lxml__etree */
diff --git a/.venv/lib/python3.12/site-packages/lxml/etree.pyx b/.venv/lib/python3.12/site-packages/lxml/etree.pyx
new file mode 100644
index 00000000..f7da01c1
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/etree.pyx
@@ -0,0 +1,3732 @@
+# cython: binding=True
+# cython: auto_pickle=False
+# cython: language_level=3
+
+"""
+The ``lxml.etree`` module implements the extended ElementTree API for XML.
+"""
+
+__docformat__ = "restructuredtext en"
+
+__all__ = [
+    'AttributeBasedElementClassLookup', 'C14NError', 'C14NWriterTarget', 'CDATA',
+    'Comment', 'CommentBase', 'CustomElementClassLookup', 'DEBUG',
+    'DTD', 'DTDError', 'DTDParseError', 'DTDValidateError',
+    'DocumentInvalid', 'ETCompatXMLParser', 'ETXPath', 'Element',
+    'ElementBase', 'ElementClassLookup', 'ElementDefaultClassLookup',
+    'ElementNamespaceClassLookup', 'ElementTree', 'Entity', 'EntityBase',
+    'Error', 'ErrorDomains', 'ErrorLevels', 'ErrorTypes', 'Extension',
+    'FallbackElementClassLookup', 'FunctionNamespace', 'HTML', 'HTMLParser',
+    'ICONV_COMPILED_VERSION',
+    'LIBXML_COMPILED_VERSION', 'LIBXML_VERSION',
+    'LIBXSLT_COMPILED_VERSION', 'LIBXSLT_VERSION',
+    'LXML_VERSION',
+    'LxmlError', 'LxmlRegistryError', 'LxmlSyntaxError',
+    'NamespaceRegistryError', 'PI', 'PIBase', 'ParseError',
+    'ParserBasedElementClassLookup', 'ParserError', 'ProcessingInstruction',
+    'PyErrorLog', 'PythonElementClassLookup', 'QName', 'RelaxNG',
+    'RelaxNGError', 'RelaxNGErrorTypes', 'RelaxNGParseError',
+    'RelaxNGValidateError', 'Resolver', 'Schematron', 'SchematronError',
+    'SchematronParseError', 'SchematronValidateError', 'SerialisationError',
+    'SubElement', 'TreeBuilder', 'XInclude', 'XIncludeError', 'XML',
+    'XMLDTDID', 'XMLID', 'XMLParser', 'XMLSchema', 'XMLSchemaError',
+    'XMLSchemaParseError', 'XMLSchemaValidateError', 'XMLSyntaxError',
+    'XMLTreeBuilder', 'XPath', 'XPathDocumentEvaluator', 'XPathError',
+    'XPathEvalError', 'XPathEvaluator', 'XPathFunctionError', 'XPathResultError',
+    'XPathSyntaxError', 'XSLT', 'XSLTAccessControl', 'XSLTApplyError',
+    'XSLTError', 'XSLTExtension', 'XSLTExtensionError', 'XSLTParseError',
+    'XSLTSaveError', 'canonicalize',
+    'cleanup_namespaces', 'clear_error_log', 'dump',
+    'fromstring', 'fromstringlist', 'get_default_parser', 'iselement',
+    'iterparse', 'iterwalk', 'parse', 'parseid', 'register_namespace',
+    'set_default_parser', 'set_element_class_lookup', 'strip_attributes',
+    'strip_elements', 'strip_tags', 'tostring', 'tostringlist', 'tounicode',
+    'use_global_python_log'
+    ]
+
+cimport cython
+
+from lxml cimport python
+from lxml.includes cimport tree, config
+from lxml.includes.tree cimport xmlDoc, xmlNode, xmlAttr, xmlNs, _isElement, _getNs
+from lxml.includes.tree cimport const_xmlChar, xmlChar, _xcstr
+from lxml.python cimport _cstr, _isString
+from lxml.includes cimport xpath
+from lxml.includes cimport c14n
+
+# Cython's standard declarations
+cimport cpython.mem
+cimport cpython.ref
+from libc cimport limits, stdio, stdlib
+from libc cimport string as cstring_h   # not to be confused with stdlib 'string'
+from libc.string cimport const_char
+
+cdef object os_path_abspath
+from os.path import abspath as os_path_abspath
+
+cdef object BytesIO, StringIO
+from io import BytesIO, StringIO
+
+cdef object OrderedDict
+from collections import OrderedDict
+
+cdef object _elementpath
+from lxml import _elementpath
+
+cdef object sys
+import sys
+
+cdef object re
+import re
+
+cdef object partial
+from functools import partial
+
+cdef object islice
+from itertools import islice
+
+cdef object ITER_EMPTY = iter(())
+
+cdef object MutableMapping
+from collections.abc import MutableMapping
+
+class _ImmutableMapping(MutableMapping):
+    def __getitem__(self, key):
+        raise KeyError, key
+
+    def __setitem__(self, key, value):
+        raise KeyError, key
+
+    def __delitem__(self, key):
+        raise KeyError, key
+
+    def __contains__(self, key):
+        return False
+
+    def __len__(self):
+        return 0
+
+    def __iter__(self):
+        return ITER_EMPTY
+    iterkeys = itervalues = iteritems = __iter__
+
+cdef object IMMUTABLE_EMPTY_MAPPING = _ImmutableMapping()
+del _ImmutableMapping
+
+
+# the rules
+# ---------
+# any libxml C argument/variable is prefixed with c_
+# any non-public function/class is prefixed with an underscore
+# instance creation is always through factories
+
+# what to do with libxml2/libxslt error messages?
+# 0 : drop
+# 1 : use log
+DEF __DEBUG = 1
+
+# maximum number of lines in the libxml2/xslt log if __DEBUG == 1
+DEF __MAX_LOG_SIZE = 100
+
+# make the compiled-in debug state publicly available
+DEBUG = __DEBUG
+
+# A struct to store a cached qualified tag name+href pair.
+# While we can borrow the c_name from the document dict,
+# PyPy requires us to store a Python reference for the
+# namespace in order to keep the byte buffer alive.
+cdef struct qname:
+    const_xmlChar* c_name
+    python.PyObject* href
+
+# initialize parser (and threading)
+xmlparser.xmlInitParser()
+
+# global per-thread setup
+tree.xmlThrDefIndentTreeOutput(1)
+tree.xmlThrDefLineNumbersDefaultValue(1)
+
+_initThreadLogging()
+
+# filename encoding
+cdef bytes _FILENAME_ENCODING = (sys.getfilesystemencoding() or sys.getdefaultencoding() or 'ascii').encode("UTF-8")
+cdef char* _C_FILENAME_ENCODING = _cstr(_FILENAME_ENCODING)
+
+# set up some default namespace prefixes
+cdef dict _DEFAULT_NAMESPACE_PREFIXES = {
+    b"http://www.w3.org/XML/1998/namespace": b'xml',
+    b"http://www.w3.org/1999/xhtml": b"html",
+    b"http://www.w3.org/1999/XSL/Transform": b"xsl",
+    b"http://www.w3.org/1999/02/22-rdf-syntax-ns#": b"rdf",
+    b"http://schemas.xmlsoap.org/wsdl/": b"wsdl",
+    # xml schema
+    b"http://www.w3.org/2001/XMLSchema": b"xs",
+    b"http://www.w3.org/2001/XMLSchema-instance": b"xsi",
+    # dublin core
+    b"http://purl.org/dc/elements/1.1/": b"dc",
+    # objectify
+    b"http://codespeak.net/lxml/objectify/pytype" : b"py",
+}
+
+# To avoid runtime encoding overhead, we keep a Unicode copy
+# of the uri-prefix mapping as (str, str) items view.
+cdef object _DEFAULT_NAMESPACE_PREFIXES_ITEMS = []
+
+cdef _update_default_namespace_prefixes_items():
+    cdef bytes ns, prefix
+    global _DEFAULT_NAMESPACE_PREFIXES_ITEMS
+    _DEFAULT_NAMESPACE_PREFIXES_ITEMS = {
+        ns.decode('utf-8') : prefix.decode('utf-8')
+        for ns, prefix in _DEFAULT_NAMESPACE_PREFIXES.items()
+    }.items()
+
+_update_default_namespace_prefixes_items()
+
+cdef object _check_internal_prefix = re.compile(br"ns\d+$").match
+
+def register_namespace(prefix, uri):
+    """Registers a namespace prefix that newly created Elements in that
+    namespace will use.  The registry is global, and any existing
+    mapping for either the given prefix or the namespace URI will be
+    removed.
+    """
+    prefix_utf, uri_utf = _utf8(prefix), _utf8(uri)
+    if _check_internal_prefix(prefix_utf):
+        raise ValueError("Prefix format reserved for internal use")
+    _tagValidOrRaise(prefix_utf)
+    _uriValidOrRaise(uri_utf)
+    if (uri_utf == b"http://www.w3.org/XML/1998/namespace" and prefix_utf != b'xml'
+            or prefix_utf == b'xml' and uri_utf != b"http://www.w3.org/XML/1998/namespace"):
+        raise ValueError("Cannot change the 'xml' prefix of the XML namespace")
+    for k, v in list(_DEFAULT_NAMESPACE_PREFIXES.items()):
+        if k == uri_utf or v == prefix_utf:
+            del _DEFAULT_NAMESPACE_PREFIXES[k]
+    _DEFAULT_NAMESPACE_PREFIXES[uri_utf] = prefix_utf
+    _update_default_namespace_prefixes_items()
+
+
+# Error superclass for ElementTree compatibility
+cdef class Error(Exception):
+    pass
+
+# module level superclass for all exceptions
+cdef class LxmlError(Error):
+    """Main exception base class for lxml.  All other exceptions inherit from
+    this one.
+    """
+    def __init__(self, message, error_log=None):
+        super(_Error, self).__init__(message)
+        if error_log is None:
+            self.error_log = __copyGlobalErrorLog()
+        else:
+            self.error_log = error_log.copy()
+
+cdef object _Error = Error
+
+
+# superclass for all syntax errors
+class LxmlSyntaxError(LxmlError, SyntaxError):
+    """Base class for all syntax errors.
+    """
+
+cdef class C14NError(LxmlError):
+    """Error during C14N serialisation.
+    """
+
+# version information
+cdef tuple __unpackDottedVersion(version):
+    version_list = []
+    l = (version.decode("ascii").replace('-', '.').split('.') + [0]*4)[:4]
+    for item in l:
+        try:
+            item = int(item)
+        except ValueError:
+            if item.startswith('dev'):
+                count = item[3:]
+                item = -300
+            elif item.startswith('alpha'):
+                count = item[5:]
+                item = -200
+            elif item.startswith('beta'):
+                count = item[4:]
+                item = -100
+            else:
+                count = 0
+            if count:
+                item += int(count)
+        version_list.append(item)
+    return tuple(version_list)
+
+cdef tuple __unpackIntVersion(int c_version, int base=100):
+    return (
+        ((c_version // (base*base)) % base),
+        ((c_version // base)        % base),
+        (c_version                  % base)
+        )
+
+cdef int _LIBXML_VERSION_INT
+try:
+    _LIBXML_VERSION_INT = int(
+        re.match('[0-9]+', (<unsigned char*>tree.xmlParserVersion).decode("ascii")).group(0))
+except Exception:
+    print("Unknown libxml2 version: " + (<unsigned char*>tree.xmlParserVersion).decode("latin1"))
+    _LIBXML_VERSION_INT = 0
+
+LIBXML_VERSION = __unpackIntVersion(_LIBXML_VERSION_INT)
+LIBXML_COMPILED_VERSION = __unpackIntVersion(tree.LIBXML_VERSION)
+LXML_VERSION = __unpackDottedVersion(tree.LXML_VERSION_STRING)
+
+__version__ = tree.LXML_VERSION_STRING.decode("ascii")
+
+cdef extern from *:
+    """
+    #ifdef ZLIB_VERNUM
+      #define __lxml_zlib_version (ZLIB_VERNUM >> 4)
+    #else
+      #define __lxml_zlib_version 0
+    #endif
+    #ifdef _LIBICONV_VERSION
+      #define __lxml_iconv_version (_LIBICONV_VERSION << 8)
+    #else
+      #define __lxml_iconv_version 0
+    #endif
+    """
+    # zlib isn't included automatically by libxml2's headers
+    #long ZLIB_HEX_VERSION "__lxml_zlib_version"
+    long LIBICONV_HEX_VERSION "__lxml_iconv_version"
+
+#ZLIB_COMPILED_VERSION = __unpackIntVersion(ZLIB_HEX_VERSION, base=0x10)
+ICONV_COMPILED_VERSION = __unpackIntVersion(LIBICONV_HEX_VERSION, base=0x100)[:2]
+
+
+# class for temporary storage of Python references,
+# used e.g. for XPath results
+@cython.final
+@cython.internal
+cdef class _TempStore:
+    cdef list _storage
+    def __init__(self):
+        self._storage = []
+
+    cdef int add(self, obj) except -1:
+        self._storage.append(obj)
+        return 0
+
+    cdef int clear(self) except -1:
+        del self._storage[:]
+        return 0
+
+
+# class for temporarily storing exceptions raised in extensions
+@cython.internal
+cdef class _ExceptionContext:
+    cdef object _exc_info
+    cdef int clear(self) except -1:
+        self._exc_info = None
+        return 0
+
+    cdef void _store_raised(self) noexcept:
+        try:
+            self._exc_info = sys.exc_info()
+        except BaseException as e:
+            self._store_exception(e)
+        finally:
+            return  # and swallow any further exceptions
+
+    cdef int _store_exception(self, exception) except -1:
+        self._exc_info = (exception, None, None)
+        return 0
+
+    cdef bint _has_raised(self) except -1:
+        return self._exc_info is not None
+
+    cdef int _raise_if_stored(self) except -1:
+        if self._exc_info is None:
+            return 0
+        type, value, traceback = self._exc_info
+        self._exc_info = None
+        if value is None and traceback is None:
+            raise type
+        else:
+            raise type, value, traceback
+
+
+# type of a function that steps from node to node
+ctypedef public xmlNode* (*_node_to_node_function)(xmlNode*)
+
+
+################################################################################
+# Include submodules
+
+include "proxy.pxi"        # Proxy handling (element backpointers/memory/etc.)
+include "apihelpers.pxi"   # Private helper functions
+include "xmlerror.pxi"     # Error and log handling
+
+
+################################################################################
+# Public Python API
+
+@cython.final
+@cython.freelist(8)
+cdef public class _Document [ type LxmlDocumentType, object LxmlDocument ]:
+    """Internal base class to reference a libxml document.
+
+    When instances of this class are garbage collected, the libxml
+    document is cleaned up.
+    """
+    cdef int _ns_counter
+    cdef bytes _prefix_tail
+    cdef xmlDoc* _c_doc
+    cdef _BaseParser _parser
+
+    def __dealloc__(self):
+        # if there are no more references to the document, it is safe
+        # to clean the whole thing up, as all nodes have a reference to
+        # the document
+        tree.xmlFreeDoc(self._c_doc)
+
+    @cython.final
+    cdef getroot(self):
+        # return an element proxy for the document root
+        cdef xmlNode* c_node
+        c_node = tree.xmlDocGetRootElement(self._c_doc)
+        if c_node is NULL:
+            return None
+        return _elementFactory(self, c_node)
+
+    @cython.final
+    cdef bint hasdoctype(self) noexcept:
+        # DOCTYPE gets parsed into internal subset (xmlDTD*)
+        return self._c_doc is not NULL and self._c_doc.intSubset is not NULL
+
+    @cython.final
+    cdef getdoctype(self):
+        # get doctype info: root tag, public/system ID (or None if not known)
+        cdef tree.xmlDtd* c_dtd
+        cdef xmlNode* c_root_node
+        public_id = None
+        sys_url   = None
+        c_dtd = self._c_doc.intSubset
+        if c_dtd is not NULL:
+            if c_dtd.ExternalID is not NULL:
+                public_id = funicode(c_dtd.ExternalID)
+            if c_dtd.SystemID is not NULL:
+                sys_url = funicode(c_dtd.SystemID)
+        c_dtd = self._c_doc.extSubset
+        if c_dtd is not NULL:
+            if not public_id and c_dtd.ExternalID is not NULL:
+                public_id = funicode(c_dtd.ExternalID)
+            if not sys_url and c_dtd.SystemID is not NULL:
+                sys_url = funicode(c_dtd.SystemID)
+        c_root_node = tree.xmlDocGetRootElement(self._c_doc)
+        if c_root_node is NULL:
+            root_name = None
+        else:
+            root_name = funicode(c_root_node.name)
+        return root_name, public_id, sys_url
+
+    @cython.final
+    cdef getxmlinfo(self):
+        # return XML version and encoding (or None if not known)
+        cdef xmlDoc* c_doc = self._c_doc
+        if c_doc.version is NULL:
+            version = None
+        else:
+            version = funicode(c_doc.version)
+        if c_doc.encoding is NULL:
+            encoding = None
+        else:
+            encoding = funicode(c_doc.encoding)
+        return version, encoding
+
+    @cython.final
+    cdef isstandalone(self):
+        # returns True for "standalone=true",
+        # False for "standalone=false", None if not provided
+        if self._c_doc.standalone == -1:
+            return None
+        else:
+            return <bint>(self._c_doc.standalone == 1)
+
+    @cython.final
+    cdef bytes buildNewPrefix(self):
+        # get a new unique prefix ("nsX") for this document
+        cdef bytes ns
+        if self._ns_counter < len(_PREFIX_CACHE):
+            ns = _PREFIX_CACHE[self._ns_counter]
+        else:
+            ns = python.PyBytes_FromFormat("ns%d", self._ns_counter)
+        if self._prefix_tail is not None:
+            ns += self._prefix_tail
+        self._ns_counter += 1
+        if self._ns_counter < 0:
+            # overflow!
+            self._ns_counter = 0
+            if self._prefix_tail is None:
+                self._prefix_tail = b"A"
+            else:
+                self._prefix_tail += b"A"
+        return ns
+
+    @cython.final
+    cdef xmlNs* _findOrBuildNodeNs(self, xmlNode* c_node,
+                                   const_xmlChar* c_href, const_xmlChar* c_prefix,
+                                   bint is_attribute) except NULL:
+        """Get or create namespace structure for a node.  Reuses the prefix if
+        possible.
+        """
+        cdef xmlNs* c_ns
+        cdef xmlNs* c_doc_ns
+        cdef python.PyObject* dict_result
+        if c_node.type != tree.XML_ELEMENT_NODE:
+            assert c_node.type == tree.XML_ELEMENT_NODE, \
+                "invalid node type %d, expected %d" % (
+                c_node.type, tree.XML_ELEMENT_NODE)
+        # look for existing ns declaration
+        c_ns = _searchNsByHref(c_node, c_href, is_attribute)
+        if c_ns is not NULL:
+            if is_attribute and c_ns.prefix is NULL:
+                # do not put namespaced attributes into the default
+                # namespace as this would break serialisation
+                pass
+            else:
+                return c_ns
+
+        # none found => determine a suitable new prefix
+        if c_prefix is NULL:
+            dict_result = python.PyDict_GetItem(
+                _DEFAULT_NAMESPACE_PREFIXES, <unsigned char*>c_href)
+            if dict_result is not NULL:
+                prefix = <object>dict_result
+            else:
+                prefix = self.buildNewPrefix()
+            c_prefix = _xcstr(prefix)
+
+        # make sure the prefix is not in use already
+        while tree.xmlSearchNs(self._c_doc, c_node, c_prefix) is not NULL:
+            prefix = self.buildNewPrefix()
+            c_prefix = _xcstr(prefix)
+
+        # declare the namespace and return it
+        c_ns = tree.xmlNewNs(c_node, c_href, c_prefix)
+        if c_ns is NULL:
+            raise MemoryError()
+        return c_ns
+
+    @cython.final
+    cdef int _setNodeNs(self, xmlNode* c_node, const_xmlChar* c_href) except -1:
+        "Lookup namespace structure and set it for the node."
+        c_ns = self._findOrBuildNodeNs(c_node, c_href, NULL, 0)
+        tree.xmlSetNs(c_node, c_ns)
+
+cdef tuple __initPrefixCache():
+    cdef int i
+    return tuple([ python.PyBytes_FromFormat("ns%d", i)
+                   for i in range(30) ])
+
+cdef tuple _PREFIX_CACHE = __initPrefixCache()
+
+cdef _Document _documentFactory(xmlDoc* c_doc, _BaseParser parser):
+    cdef _Document result
+    result = _Document.__new__(_Document)
+    result._c_doc = c_doc
+    result._ns_counter = 0
+    result._prefix_tail = None
+    if parser is None:
+        parser = __GLOBAL_PARSER_CONTEXT.getDefaultParser()
+    result._parser = parser
+    return result
+
+
+cdef object _find_invalid_public_id_characters = re.compile(
+    ur"[^\x20\x0D\x0Aa-zA-Z0-9'()+,./:=?;!*#@$_%-]+").search
+
+
+cdef class DocInfo:
+    "Document information provided by parser and DTD."
+    cdef _Document _doc
+    def __cinit__(self, tree):
+        "Create a DocInfo object for an ElementTree object or root Element."
+        self._doc = _documentOrRaise(tree)
+        root_name, public_id, system_url = self._doc.getdoctype()
+        if not root_name and (public_id or system_url):
+            raise ValueError, "Could not find root node"
+
+    @property
+    def root_name(self):
+        """Returns the name of the root node as defined by the DOCTYPE."""
+        root_name, public_id, system_url = self._doc.getdoctype()
+        return root_name
+
+    @cython.final
+    cdef tree.xmlDtd* _get_c_dtd(self):
+        """"Return the DTD. Create it if it does not yet exist."""
+        cdef xmlDoc* c_doc = self._doc._c_doc
+        cdef xmlNode* c_root_node
+        cdef const_xmlChar* c_name
+
+        if c_doc.intSubset:
+            return c_doc.intSubset
+
+        c_root_node = tree.xmlDocGetRootElement(c_doc)
+        c_name = c_root_node.name if c_root_node else NULL
+        return  tree.xmlCreateIntSubset(c_doc, c_name, NULL, NULL)
+
+    def clear(self):
+        """Removes DOCTYPE and internal subset from the document."""
+        cdef xmlDoc* c_doc = self._doc._c_doc
+        cdef tree.xmlNode* c_dtd = <xmlNode*>c_doc.intSubset
+        if c_dtd is NULL:
+            return
+        tree.xmlUnlinkNode(c_dtd)
+        tree.xmlFreeNode(c_dtd)
+
+    property public_id:
+        """Public ID of the DOCTYPE.
+
+        Mutable.  May be set to a valid string or None.  If a DTD does not
+        exist, setting this variable (even to None) will create one.
+        """
+        def __get__(self):
+            root_name, public_id, system_url = self._doc.getdoctype()
+            return public_id
+
+        def __set__(self, value):
+            cdef xmlChar* c_value = NULL
+            if value is not None:
+                match = _find_invalid_public_id_characters(value)
+                if match:
+                    raise ValueError, f'Invalid character(s) {match.group(0)!r} in public_id.'
+                value = _utf8(value)
+                c_value = tree.xmlStrdup(_xcstr(value))
+                if not c_value:
+                    raise MemoryError()
+
+            c_dtd = self._get_c_dtd()
+            if not c_dtd:
+                tree.xmlFree(c_value)
+                raise MemoryError()
+            if c_dtd.ExternalID:
+                tree.xmlFree(<void*>c_dtd.ExternalID)
+            c_dtd.ExternalID = c_value
+
+    property system_url:
+        """System ID of the DOCTYPE.
+
+        Mutable.  May be set to a valid string or None.  If a DTD does not
+        exist, setting this variable (even to None) will create one.
+        """
+        def __get__(self):
+            root_name, public_id, system_url = self._doc.getdoctype()
+            return system_url
+
+        def __set__(self, value):
+            cdef xmlChar* c_value = NULL
+            if value is not None:
+                bvalue = _utf8(value)
+                # sys_url may be any valid unicode string that can be
+                # enclosed in single quotes or quotes.
+                if b"'" in bvalue and b'"' in bvalue:
+                    raise ValueError(
+                        'System URL may not contain both single (\') and double quotes (").')
+                c_value = tree.xmlStrdup(_xcstr(bvalue))
+                if not c_value:
+                    raise MemoryError()
+
+            c_dtd = self._get_c_dtd()
+            if not c_dtd:
+                tree.xmlFree(c_value)
+                raise MemoryError()
+            if c_dtd.SystemID:
+                tree.xmlFree(<void*>c_dtd.SystemID)
+            c_dtd.SystemID = c_value
+
+    @property
+    def xml_version(self):
+        """Returns the XML version as declared by the document."""
+        xml_version, encoding = self._doc.getxmlinfo()
+        return xml_version
+
+    @property
+    def encoding(self):
+        """Returns the encoding name as declared by the document."""
+        xml_version, encoding = self._doc.getxmlinfo()
+        return encoding
+
+    @property
+    def standalone(self):
+        """Returns the standalone flag as declared by the document.  The possible
+        values are True (``standalone='yes'``), False
+        (``standalone='no'`` or flag not provided in the declaration),
+        and None (unknown or no declaration found).  Note that a
+        normal truth test on this value will always tell if the
+        ``standalone`` flag was set to ``'yes'`` or not.
+        """
+        return self._doc.isstandalone()
+
+    property URL:
+        "The source URL of the document (or None if unknown)."
+        def __get__(self):
+            if self._doc._c_doc.URL is NULL:
+                return None
+            return _decodeFilename(self._doc._c_doc.URL)
+        def __set__(self, url):
+            url = _encodeFilename(url)
+            c_oldurl = self._doc._c_doc.URL
+            if url is None:
+                self._doc._c_doc.URL = NULL
+            else:
+                self._doc._c_doc.URL = tree.xmlStrdup(_xcstr(url))
+            if c_oldurl is not NULL:
+                tree.xmlFree(<void*>c_oldurl)
+
+    @property
+    def doctype(self):
+        """Returns a DOCTYPE declaration string for the document."""
+        root_name, public_id, system_url = self._doc.getdoctype()
+        if system_url:
+            # If '"' in system_url, we must escape it with single
+            # quotes, otherwise escape with double quotes. If url
+            # contains both a single quote and a double quote, XML
+            # standard is being violated.
+            if '"' in system_url:
+                quoted_system_url = f"'{system_url}'"
+            else:
+                quoted_system_url = f'"{system_url}"'
+        if public_id:
+            if system_url:
+                return f'<!DOCTYPE {root_name} PUBLIC "{public_id}" {quoted_system_url}>'
+            else:
+                return f'<!DOCTYPE {root_name} PUBLIC "{public_id}">'
+        elif system_url:
+            return f'<!DOCTYPE {root_name} SYSTEM {quoted_system_url}>'
+        elif self._doc.hasdoctype():
+            return f'<!DOCTYPE {root_name}>'
+        else:
+            return ''
+
+    @property
+    def internalDTD(self):
+        """Returns a DTD validator based on the internal subset of the document."""
+        return _dtdFactory(self._doc._c_doc.intSubset)
+
+    @property
+    def externalDTD(self):
+        """Returns a DTD validator based on the external subset of the document."""
+        return _dtdFactory(self._doc._c_doc.extSubset)
+
+
+@cython.no_gc_clear
+cdef public class _Element [ type LxmlElementType, object LxmlElement ]:
+    """Element class.
+
+    References a document object and a libxml node.
+
+    By pointing to a Document instance, a reference is kept to
+    _Document as long as there is some pointer to a node in it.
+    """
+    cdef _Document _doc
+    cdef xmlNode* _c_node
+    cdef object _tag
+
+    def _init(self):
+        """_init(self)
+
+        Called after object initialisation.  Custom subclasses may override
+        this if they recursively call _init() in the superclasses.
+        """
+
+    @cython.linetrace(False)
+    @cython.profile(False)
+    def __dealloc__(self):
+        #print("trying to free node:", <int>self._c_node)
+        #displayNode(self._c_node, 0)
+        if self._c_node is not NULL:
+            _unregisterProxy(self)
+            attemptDeallocation(self._c_node)
+
+    # MANIPULATORS
+
+    def __setitem__(self, x, value):
+        """__setitem__(self, x, value)
+
+        Replaces the given subelement index or slice.
+        """
+        cdef xmlNode* c_node = NULL
+        cdef xmlNode* c_next
+        cdef xmlDoc* c_source_doc
+        cdef _Element element
+        cdef bint left_to_right
+        cdef Py_ssize_t slicelength = 0, step = 0
+        _assertValidNode(self)
+        if value is None:
+            raise ValueError, "cannot assign None"
+        if isinstance(x, slice):
+            # slice assignment
+            _findChildSlice(<slice>x, self._c_node, &c_node, &step, &slicelength)
+            if step > 0:
+                left_to_right = 1
+            else:
+                left_to_right = 0
+                step = -step
+            _replaceSlice(self, c_node, slicelength, step, left_to_right, value)
+            return
+        else:
+            # otherwise: normal item assignment
+            element = value
+            _assertValidNode(element)
+            c_node = _findChild(self._c_node, x)
+            if c_node is NULL:
+                raise IndexError, "list index out of range"
+            c_source_doc = element._c_node.doc
+            c_next = element._c_node.next
+            _removeText(c_node.next)
+            tree.xmlReplaceNode(c_node, element._c_node)
+            _moveTail(c_next, element._c_node)
+            moveNodeToDocument(self._doc, c_source_doc, element._c_node)
+            if not attemptDeallocation(c_node):
+                moveNodeToDocument(self._doc, c_node.doc, c_node)
+
+    def __delitem__(self, x):
+        """__delitem__(self, x)
+
+        Deletes the given subelement or a slice.
+        """
+        cdef xmlNode* c_node = NULL
+        cdef xmlNode* c_next
+        cdef Py_ssize_t step = 0, slicelength = 0
+        _assertValidNode(self)
+        if isinstance(x, slice):
+            # slice deletion
+            if _isFullSlice(<slice>x):
+                c_node = self._c_node.children
+                if c_node is not NULL:
+                    if not _isElement(c_node):
+                        c_node = _nextElement(c_node)
+                    while c_node is not NULL:
+                        c_next = _nextElement(c_node)
+                        _removeNode(self._doc, c_node)
+                        c_node = c_next
+            else:
+                _findChildSlice(<slice>x, self._c_node, &c_node, &step, &slicelength)
+                _deleteSlice(self._doc, c_node, slicelength, step)
+        else:
+            # item deletion
+            c_node = _findChild(self._c_node, x)
+            if c_node is NULL:
+                raise IndexError, f"index out of range: {x}"
+            _removeNode(self._doc, c_node)
+
+    def __deepcopy__(self, memo):
+        "__deepcopy__(self, memo)"
+        return self.__copy__()
+
+    def __copy__(self):
+        "__copy__(self)"
+        cdef xmlDoc* c_doc
+        cdef xmlNode* c_node
+        cdef _Document new_doc
+        _assertValidNode(self)
+        c_doc = _copyDocRoot(self._doc._c_doc, self._c_node) # recursive
+        new_doc = _documentFactory(c_doc, self._doc._parser)
+        root = new_doc.getroot()
+        if root is not None:
+            return root
+        # Comment/PI
+        c_node = c_doc.children
+        while c_node is not NULL and c_node.type != self._c_node.type:
+            c_node = c_node.next
+        if c_node is NULL:
+            return None
+        return _elementFactory(new_doc, c_node)
+
+    def set(self, key, value):
+        """set(self, key, value)
+
+        Sets an element attribute.
+        In HTML documents (not XML or XHTML), the value None is allowed and creates
+        an attribute without value (just the attribute name).
+        """
+        _assertValidNode(self)
+        _setAttributeValue(self, key, value)
+
+    def append(self, _Element element not None):
+        """append(self, element)
+
+        Adds a subelement to the end of this element.
+        """
+        _assertValidNode(self)
+        _assertValidNode(element)
+        _appendChild(self, element)
+
+    def addnext(self, _Element element not None):
+        """addnext(self, element)
+
+        Adds the element as a following sibling directly after this
+        element.
+
+        This is normally used to set a processing instruction or comment after
+        the root node of a document.  Note that tail text is automatically
+        discarded when adding at the root level.
+        """
+        _assertValidNode(self)
+        _assertValidNode(element)
+        if self._c_node.parent != NULL and not _isElement(self._c_node.parent):
+            if element._c_node.type not in (tree.XML_PI_NODE, tree.XML_COMMENT_NODE):
+                raise TypeError, "Only processing instructions and comments can be siblings of the root element"
+            element.tail = None
+        _appendSibling(self, element)
+
+    def addprevious(self, _Element element not None):
+        """addprevious(self, element)
+
+        Adds the element as a preceding sibling directly before this
+        element.
+
+        This is normally used to set a processing instruction or comment
+        before the root node of a document.  Note that tail text is
+        automatically discarded when adding at the root level.
+        """
+        _assertValidNode(self)
+        _assertValidNode(element)
+        if self._c_node.parent != NULL and not _isElement(self._c_node.parent):
+            if element._c_node.type != tree.XML_PI_NODE:
+                if element._c_node.type != tree.XML_COMMENT_NODE:
+                    raise TypeError, "Only processing instructions and comments can be siblings of the root element"
+            element.tail = None
+        _prependSibling(self, element)
+
+    def extend(self, elements):
+        """extend(self, elements)
+
+        Extends the current children by the elements in the iterable.
+        """
+        cdef _Element element
+        _assertValidNode(self)
+        for element in elements:
+            if element is None:
+                raise TypeError, "Node must not be None"
+            _assertValidNode(element)
+            _appendChild(self, element)
+
+    def clear(self, bint keep_tail=False):
+        """clear(self, keep_tail=False)
+
+        Resets an element.  This function removes all subelements, clears
+        all attributes and sets the text and tail properties to None.
+
+        Pass ``keep_tail=True`` to leave the tail text untouched.
+        """
+        cdef xmlAttr* c_attr
+        cdef xmlAttr* c_attr_next
+        cdef xmlNode* c_node
+        cdef xmlNode* c_node_next
+        _assertValidNode(self)
+        c_node = self._c_node
+        # remove self.text and self.tail
+        _removeText(c_node.children)
+        if not keep_tail:
+            _removeText(c_node.next)
+        # remove all attributes
+        c_attr = c_node.properties
+        if c_attr:
+            c_node.properties = NULL
+            tree.xmlFreePropList(c_attr)
+        # remove all subelements
+        c_node = c_node.children
+        if c_node and not _isElement(c_node):
+            c_node = _nextElement(c_node)
+        while c_node is not NULL:
+            c_node_next = _nextElement(c_node)
+            _removeNode(self._doc, c_node)
+            c_node = c_node_next
+
+    def insert(self, index: int, _Element element not None):
+        """insert(self, index, element)
+
+        Inserts a subelement at the given position in this element
+        """
+        cdef xmlNode* c_node
+        cdef xmlNode* c_next
+        cdef xmlDoc* c_source_doc
+        _assertValidNode(self)
+        _assertValidNode(element)
+        c_node = _findChild(self._c_node, index)
+        if c_node is NULL:
+            _appendChild(self, element)
+            return
+        # prevent cycles
+        if _isAncestorOrSame(element._c_node, self._c_node):
+            raise ValueError("cannot append parent to itself")
+        c_source_doc = element._c_node.doc
+        c_next = element._c_node.next
+        tree.xmlAddPrevSibling(c_node, element._c_node)
+        _moveTail(c_next, element._c_node)
+        moveNodeToDocument(self._doc, c_source_doc, element._c_node)
+
+    def remove(self, _Element element not None):
+        """remove(self, element)
+
+        Removes a matching subelement. Unlike the find methods, this
+        method compares elements based on identity, not on tag value
+        or contents.
+        """
+        cdef xmlNode* c_node
+        cdef xmlNode* c_next
+        _assertValidNode(self)
+        _assertValidNode(element)
+        c_node = element._c_node
+        if c_node.parent is not self._c_node:
+            raise ValueError, "Element is not a child of this node."
+        c_next = element._c_node.next
+        tree.xmlUnlinkNode(c_node)
+        _moveTail(c_next, c_node)
+        # fix namespace declarations
+        moveNodeToDocument(self._doc, c_node.doc, c_node)
+
+    def replace(self, _Element old_element not None,
+                _Element new_element not None):
+        """replace(self, old_element, new_element)
+
+        Replaces a subelement with the element passed as second argument.
+        """
+        cdef xmlNode* c_old_node
+        cdef xmlNode* c_old_next
+        cdef xmlNode* c_new_node
+        cdef xmlNode* c_new_next
+        cdef xmlDoc* c_source_doc
+        _assertValidNode(self)
+        _assertValidNode(old_element)
+        _assertValidNode(new_element)
+        c_old_node = old_element._c_node
+        if c_old_node.parent is not self._c_node:
+            raise ValueError, "Element is not a child of this node."
+        c_new_node = new_element._c_node
+        # prevent cycles
+        if _isAncestorOrSame(c_new_node, self._c_node):
+            raise ValueError("cannot append parent to itself")
+        # replace node
+        c_old_next = c_old_node.next
+        c_new_next = c_new_node.next
+        c_source_doc = c_new_node.doc
+        tree.xmlReplaceNode(c_old_node, c_new_node)
+        _moveTail(c_new_next, c_new_node)
+        _moveTail(c_old_next, c_old_node)
+        moveNodeToDocument(self._doc, c_source_doc, c_new_node)
+        # fix namespace declarations
+        moveNodeToDocument(self._doc, c_old_node.doc, c_old_node)
+
+    # PROPERTIES
+    property tag:
+        """Element tag
+        """
+        def __get__(self):
+            if self._tag is not None:
+                return self._tag
+            _assertValidNode(self)
+            self._tag = _namespacedName(self._c_node)
+            return self._tag
+
+        def __set__(self, value):
+            cdef _BaseParser parser
+            _assertValidNode(self)
+            ns, name = _getNsTag(value)
+            parser = self._doc._parser
+            if parser is not None and parser._for_html:
+                _htmlTagValidOrRaise(name)
+            else:
+                _tagValidOrRaise(name)
+            self._tag = value
+            tree.xmlNodeSetName(self._c_node, _xcstr(name))
+            if ns is None:
+                self._c_node.ns = NULL
+            else:
+                self._doc._setNodeNs(self._c_node, _xcstr(ns))
+
+    @property
+    def attrib(self):
+        """Element attribute dictionary. Where possible, use get(), set(),
+        keys(), values() and items() to access element attributes.
+        """
+        return _Attrib.__new__(_Attrib, self)
+
+    property text:
+        """Text before the first subelement. This is either a string or
+        the value None, if there was no text.
+        """
+        def __get__(self):
+            _assertValidNode(self)
+            return _collectText(self._c_node.children)
+
+        def __set__(self, value):
+            _assertValidNode(self)
+            if isinstance(value, QName):
+                value = _resolveQNameText(self, value).decode('utf8')
+            _setNodeText(self._c_node, value)
+
+        # using 'del el.text' is the wrong thing to do
+        #def __del__(self):
+        #    _setNodeText(self._c_node, None)
+
+    property tail:
+        """Text after this element's end tag, but before the next sibling
+        element's start tag. This is either a string or the value None, if
+        there was no text.
+        """
+        def __get__(self):
+            _assertValidNode(self)
+            return _collectText(self._c_node.next)
+
+        def __set__(self, value):
+            _assertValidNode(self)
+            _setTailText(self._c_node, value)
+
+        # using 'del el.tail' is the wrong thing to do
+        #def __del__(self):
+        #    _setTailText(self._c_node, None)
+
+    # not in ElementTree, read-only
+    @property
+    def prefix(self):
+        """Namespace prefix or None.
+        """
+        if self._c_node.ns is not NULL:
+            if self._c_node.ns.prefix is not NULL:
+                return funicode(self._c_node.ns.prefix)
+        return None
+
+    # not in ElementTree, read-only
+    property sourceline:
+        """Original line number as found by the parser or None if unknown.
+        """
+        def __get__(self):
+            cdef long line
+            _assertValidNode(self)
+            line = tree.xmlGetLineNo(self._c_node)
+            return line if line > 0 else None
+
+        def __set__(self, line):
+            _assertValidNode(self)
+            if line <= 0:
+                self._c_node.line = 0
+            else:
+                self._c_node.line = line
+
+    # not in ElementTree, read-only
+    @property
+    def nsmap(self):
+        """Namespace prefix->URI mapping known in the context of this
+        Element.  This includes all namespace declarations of the
+        parents.
+
+        Note that changing the returned dict has no effect on the Element.
+        """
+        _assertValidNode(self)
+        return _build_nsmap(self._c_node)
+
+    # not in ElementTree, read-only
+    property base:
+        """The base URI of the Element (xml:base or HTML base URL).
+        None if the base URI is unknown.
+
+        Note that the value depends on the URL of the document that
+        holds the Element if there is no xml:base attribute on the
+        Element or its ancestors.
+
+        Setting this property will set an xml:base attribute on the
+        Element, regardless of the document type (XML or HTML).
+        """
+        def __get__(self):
+            _assertValidNode(self)
+            c_base = tree.xmlNodeGetBase(self._doc._c_doc, self._c_node)
+            if c_base is NULL:
+                if self._doc._c_doc.URL is NULL:
+                    return None
+                return _decodeFilename(self._doc._c_doc.URL)
+            try:
+                base = _decodeFilename(c_base)
+            finally:
+                tree.xmlFree(c_base)
+            return base
+
+        def __set__(self, url):
+            _assertValidNode(self)
+            if url is None:
+                c_base = <const_xmlChar*>NULL
+            else:
+                url = _encodeFilename(url)
+                c_base = _xcstr(url)
+            tree.xmlNodeSetBase(self._c_node, c_base)
+
+    # ACCESSORS
+    def __repr__(self):
+        "__repr__(self)"
+        return "<Element %s at 0x%x>" % (self.tag, id(self))
+
+    def __getitem__(self, x):
+        """Returns the subelement at the given position or the requested
+        slice.
+        """
+        cdef xmlNode* c_node = NULL
+        cdef Py_ssize_t step = 0, slicelength = 0
+        cdef Py_ssize_t c, i
+        cdef _node_to_node_function next_element
+        cdef list result
+        _assertValidNode(self)
+        if isinstance(x, slice):
+            # slicing
+            if _isFullSlice(<slice>x):
+                return _collectChildren(self)
+            _findChildSlice(<slice>x, self._c_node, &c_node, &step, &slicelength)
+            if c_node is NULL:
+                return []
+            if step > 0:
+                next_element = _nextElement
+            else:
+                step = -step
+                next_element = _previousElement
+            result = []
+            c = 0
+            while c_node is not NULL and c < slicelength:
+                result.append(_elementFactory(self._doc, c_node))
+                c += 1
+                for i in range(step):
+                    c_node = next_element(c_node)
+                    if c_node is NULL:
+                        break
+            return result
+        else:
+            # indexing
+            c_node = _findChild(self._c_node, x)
+            if c_node is NULL:
+                raise IndexError, "list index out of range"
+            return _elementFactory(self._doc, c_node)
+
+    def __len__(self):
+        """__len__(self)
+
+        Returns the number of subelements.
+        """
+        _assertValidNode(self)
+        return _countElements(self._c_node.children)
+
+    def __bool__(self):
+        """__bool__(self)"""
+        import warnings
+        warnings.warn(
+            "Truth-testing of elements was a source of confusion and will always "
+            "return True in future versions. "
+            "Use specific 'len(elem)' or 'elem is not None' test instead.",
+            FutureWarning
+            )
+        # emulate old behaviour
+        _assertValidNode(self)
+        return _hasChild(self._c_node)
+
+    def __contains__(self, element):
+        "__contains__(self, element)"
+        cdef xmlNode* c_node
+        _assertValidNode(self)
+        if not isinstance(element, _Element):
+            return 0
+        c_node = (<_Element>element)._c_node
+        return c_node is not NULL and c_node.parent is self._c_node
+
+    def __iter__(self):
+        "__iter__(self)"
+        return ElementChildIterator(self)
+
+    def __reversed__(self):
+        "__reversed__(self)"
+        return ElementChildIterator(self, reversed=True)
+
+    def index(self, child: _Element, start: int = None, stop: int = None):
+        """index(self, child, start=None, stop=None)
+
+        Find the position of the child within the parent.
+
+        This method is not part of the original ElementTree API.
+        """
+        cdef Py_ssize_t k, l
+        cdef Py_ssize_t c_start, c_stop
+        cdef xmlNode* c_child
+        cdef xmlNode* c_start_node
+        _assertValidNode(self)
+        _assertValidNode(child)
+        c_child = child._c_node
+        if c_child.parent is not self._c_node:
+            raise ValueError, "Element is not a child of this node."
+
+        # handle the unbounded search straight away (normal case)
+        if stop is None and (start is None or start == 0):
+            k = 0
+            c_child = c_child.prev
+            while c_child is not NULL:
+                if _isElement(c_child):
+                    k += 1
+                c_child = c_child.prev
+            return k
+
+        # check indices
+        if start is None:
+            c_start = 0
+        else:
+            c_start = start
+        if stop is None:
+            c_stop = 0
+        else:
+            c_stop = stop
+            if c_stop == 0 or \
+                   c_start >= c_stop and (c_stop > 0 or c_start < 0):
+                raise ValueError, "list.index(x): x not in slice"
+
+        # for negative slice indices, check slice before searching index
+        if c_start < 0 or c_stop < 0:
+            # start from right, at most up to leftmost(c_start, c_stop)
+            if c_start < c_stop:
+                k = -c_start
+            else:
+                k = -c_stop
+            c_start_node = self._c_node.last
+            l = 1
+            while c_start_node != c_child and l < k:
+                if _isElement(c_start_node):
+                    l += 1
+                c_start_node = c_start_node.prev
+            if c_start_node == c_child:
+                # found! before slice end?
+                if c_stop < 0 and l <= -c_stop:
+                    raise ValueError, "list.index(x): x not in slice"
+            elif c_start < 0:
+                raise ValueError, "list.index(x): x not in slice"
+
+        # now determine the index backwards from child
+        c_child = c_child.prev
+        k = 0
+        if c_stop > 0:
+            # we can optimize: stop after c_stop elements if not found
+            while c_child != NULL and k < c_stop:
+                if _isElement(c_child):
+                    k += 1
+                c_child = c_child.prev
+            if k < c_stop:
+                return k
+        else:
+            # traverse all
+            while c_child != NULL:
+                if _isElement(c_child):
+                    k = k + 1
+                c_child = c_child.prev
+            if c_start > 0:
+                if k >= c_start:
+                    return k
+            else:
+                return k
+        if c_start != 0 or c_stop != 0:
+            raise ValueError, "list.index(x): x not in slice"
+        else:
+            raise ValueError, "list.index(x): x not in list"
+
+    def get(self, key, default=None):
+        """get(self, key, default=None)
+
+        Gets an element attribute.
+        """
+        _assertValidNode(self)
+        return _getAttributeValue(self, key, default)
+
+    def keys(self):
+        """keys(self)
+
+        Gets a list of attribute names.  The names are returned in an
+        arbitrary order (just like for an ordinary Python dictionary).
+        """
+        _assertValidNode(self)
+        return _collectAttributes(self._c_node, 1)
+
+    def values(self):
+        """values(self)
+
+        Gets element attribute values as a sequence of strings.  The
+        attributes are returned in an arbitrary order.
+        """
+        _assertValidNode(self)
+        return _collectAttributes(self._c_node, 2)
+
+    def items(self):
+        """items(self)
+
+        Gets element attributes, as a sequence. The attributes are returned in
+        an arbitrary order.
+        """
+        _assertValidNode(self)
+        return _collectAttributes(self._c_node, 3)
+
+    def getchildren(self):
+        """getchildren(self)
+
+        Returns all direct children.  The elements are returned in document
+        order.
+
+        :deprecated: Note that this method has been deprecated as of
+          ElementTree 1.3 and lxml 2.0.  New code should use
+          ``list(element)`` or simply iterate over elements.
+        """
+        _assertValidNode(self)
+        return _collectChildren(self)
+
+    def getparent(self):
+        """getparent(self)
+
+        Returns the parent of this element or None for the root element.
+        """
+        cdef xmlNode* c_node
+        #_assertValidNode(self) # not needed
+        c_node = _parentElement(self._c_node)
+        if c_node is NULL:
+            return None
+        return _elementFactory(self._doc, c_node)
+
+    def getnext(self):
+        """getnext(self)
+
+        Returns the following sibling of this element or None.
+        """
+        cdef xmlNode* c_node
+        #_assertValidNode(self) # not needed
+        c_node = _nextElement(self._c_node)
+        if c_node is NULL:
+            return None
+        return _elementFactory(self._doc, c_node)
+
+    def getprevious(self):
+        """getprevious(self)
+
+        Returns the preceding sibling of this element or None.
+        """
+        cdef xmlNode* c_node
+        #_assertValidNode(self) # not needed
+        c_node = _previousElement(self._c_node)
+        if c_node is NULL:
+            return None
+        return _elementFactory(self._doc, c_node)
+
+    def itersiblings(self, tag=None, *tags, preceding=False):
+        """itersiblings(self, tag=None, *tags, preceding=False)
+
+        Iterate over the following or preceding siblings of this element.
+
+        The direction is determined by the 'preceding' keyword which
+        defaults to False, i.e. forward iteration over the following
+        siblings.  When True, the iterator yields the preceding
+        siblings in reverse document order, i.e. starting right before
+        the current element and going backwards.
+
+        Can be restricted to find only elements with specific tags,
+        see `iter`.
+        """
+        if preceding:
+            if self._c_node and not self._c_node.prev:
+                return ITER_EMPTY
+        elif self._c_node and not self._c_node.next:
+            return ITER_EMPTY
+        if tag is not None:
+            tags += (tag,)
+        return SiblingsIterator(self, tags, preceding=preceding)
+
+    def iterancestors(self, tag=None, *tags):
+        """iterancestors(self, tag=None, *tags)
+
+        Iterate over the ancestors of this element (from parent to parent).
+
+        Can be restricted to find only elements with specific tags,
+        see `iter`.
+        """
+        if self._c_node and not self._c_node.parent:
+            return ITER_EMPTY
+        if tag is not None:
+            tags += (tag,)
+        return AncestorsIterator(self, tags)
+
+    def iterdescendants(self, tag=None, *tags):
+        """iterdescendants(self, tag=None, *tags)
+
+        Iterate over the descendants of this element in document order.
+
+        As opposed to ``el.iter()``, this iterator does not yield the element
+        itself.  The returned elements can be restricted to find only elements
+        with specific tags, see `iter`.
+        """
+        if self._c_node and not self._c_node.children:
+            return ITER_EMPTY
+        if tag is not None:
+            tags += (tag,)
+        return ElementDepthFirstIterator(self, tags, inclusive=False)
+
+    def iterchildren(self, tag=None, *tags, reversed=False):
+        """iterchildren(self, tag=None, *tags, reversed=False)
+
+        Iterate over the children of this element.
+
+        As opposed to using normal iteration on this element, the returned
+        elements can be reversed with the 'reversed' keyword and restricted
+        to find only elements with specific tags, see `iter`.
+        """
+        if self._c_node and not self._c_node.children:
+            return ITER_EMPTY
+        if tag is not None:
+            tags += (tag,)
+        return ElementChildIterator(self, tags, reversed=reversed)
+
+    def getroottree(self):
+        """getroottree(self)
+
+        Return an ElementTree for the root node of the document that
+        contains this element.
+
+        This is the same as following element.getparent() up the tree until it
+        returns None (for the root element) and then build an ElementTree for
+        the last parent that was returned."""
+        _assertValidDoc(self._doc)
+        return _elementTreeFactory(self._doc, None)
+
+    def getiterator(self, tag=None, *tags):
+        """getiterator(self, tag=None, *tags)
+
+        Returns a sequence or iterator of all elements in the subtree in
+        document order (depth first pre-order), starting with this
+        element.
+
+        Can be restricted to find only elements with specific tags,
+        see `iter`.
+
+        :deprecated: Note that this method is deprecated as of
+          ElementTree 1.3 and lxml 2.0.  It returns an iterator in
+          lxml, which diverges from the original ElementTree
+          behaviour.  If you want an efficient iterator, use the
+          ``element.iter()`` method instead.  You should only use this
+          method in new code if you require backwards compatibility
+          with older versions of lxml or ElementTree.
+        """
+        if tag is not None:
+            tags += (tag,)
+        return ElementDepthFirstIterator(self, tags)
+
+    def iter(self, tag=None, *tags):
+        """iter(self, tag=None, *tags)
+
+        Iterate over all elements in the subtree in document order (depth
+        first pre-order), starting with this element.
+
+        Can be restricted to find only elements with specific tags:
+        pass ``"{ns}localname"`` as tag. Either or both of ``ns`` and
+        ``localname`` can be ``*`` for a wildcard; ``ns`` can be empty
+        for no namespace. ``"localname"`` is equivalent to ``"{}localname"``
+        (i.e. no namespace) but ``"*"`` is ``"{*}*"`` (any or no namespace),
+        not ``"{}*"``.
+
+        You can also pass the Element, Comment, ProcessingInstruction and
+        Entity factory functions to look only for the specific element type.
+
+        Passing multiple tags (or a sequence of tags) instead of a single tag
+        will let the iterator return all elements matching any of these tags,
+        in document order.
+        """
+        if tag is not None:
+            tags += (tag,)
+        return ElementDepthFirstIterator(self, tags)
+
+    def itertext(self, tag=None, *tags, with_tail=True):
+        """itertext(self, tag=None, *tags, with_tail=True)
+
+        Iterates over the text content of a subtree.
+
+        You can pass tag names to restrict text content to specific elements,
+        see `iter`.
+
+        You can set the ``with_tail`` keyword argument to ``False`` to skip
+        over tail text.
+        """
+        if tag is not None:
+            tags += (tag,)
+        return ElementTextIterator(self, tags, with_tail=with_tail)
+
+    def makeelement(self, _tag, attrib=None, nsmap=None, **_extra):
+        """makeelement(self, _tag, attrib=None, nsmap=None, **_extra)
+
+        Creates a new element associated with the same document.
+        """
+        _assertValidDoc(self._doc)
+        return _makeElement(_tag, NULL, self._doc, None, None, None,
+                            attrib, nsmap, _extra)
+
+    def find(self, path, namespaces=None):
+        """find(self, path, namespaces=None)
+
+        Finds the first matching subelement, by tag name or path.
+
+        The optional ``namespaces`` argument accepts a
+        prefix-to-namespace mapping that allows the usage of XPath
+        prefixes in the path expression.
+        """
+        if isinstance(path, QName):
+            path = (<QName>path).text
+        return _elementpath.find(self, path, namespaces, with_prefixes=not _isHtmlDocument(self))
+
+    def findtext(self, path, default=None, namespaces=None):
+        """findtext(self, path, default=None, namespaces=None)
+
+        Finds text for the first matching subelement, by tag name or path.
+
+        The optional ``namespaces`` argument accepts a
+        prefix-to-namespace mapping that allows the usage of XPath
+        prefixes in the path expression.
+        """
+        if isinstance(path, QName):
+            path = (<QName>path).text
+        return _elementpath.findtext(self, path, default, namespaces, with_prefixes=not _isHtmlDocument(self))
+
+    def findall(self, path, namespaces=None):
+        """findall(self, path, namespaces=None)
+
+        Finds all matching subelements, by tag name or path.
+
+        The optional ``namespaces`` argument accepts a
+        prefix-to-namespace mapping that allows the usage of XPath
+        prefixes in the path expression.
+        """
+        if isinstance(path, QName):
+            path = (<QName>path).text
+        return _elementpath.findall(self, path, namespaces, with_prefixes=not _isHtmlDocument(self))
+
+    def iterfind(self, path, namespaces=None):
+        """iterfind(self, path, namespaces=None)
+
+        Iterates over all matching subelements, by tag name or path.
+
+        The optional ``namespaces`` argument accepts a
+        prefix-to-namespace mapping that allows the usage of XPath
+        prefixes in the path expression.
+        """
+        if isinstance(path, QName):
+            path = (<QName>path).text
+        return _elementpath.iterfind(self, path, namespaces, with_prefixes=not _isHtmlDocument(self))
+
+    def xpath(self, _path, *, namespaces=None, extensions=None,
+              smart_strings=True, **_variables):
+        """xpath(self, _path, namespaces=None, extensions=None, smart_strings=True, **_variables)
+
+        Evaluate an xpath expression using the element as context node.
+        """
+        evaluator = XPathElementEvaluator(self, namespaces=namespaces,
+                                          extensions=extensions,
+                                          smart_strings=smart_strings)
+        return evaluator(_path, **_variables)
+
+    def cssselect(self, expr, *, translator='xml'):
+        """
+        Run the CSS expression on this element and its children,
+        returning a list of the results.
+
+        Equivalent to lxml.cssselect.CSSSelect(expr)(self) -- note
+        that pre-compiling the expression can provide a substantial
+        speedup.
+        """
+        # Do the import here to make the dependency optional.
+        from lxml.cssselect import CSSSelector
+        return CSSSelector(expr, translator=translator)(self)
+
+
+cdef extern from "includes/etree_defs.h":
+    # macro call to 't->tp_new()' for fast instantiation
+    cdef object NEW_ELEMENT "PY_NEW" (object t)
+
+
+@cython.linetrace(False)
+cdef _Element _elementFactory(_Document doc, xmlNode* c_node):
+    cdef _Element result
+    result = getProxy(c_node)
+    if result is not None:
+        return result
+    if c_node is NULL:
+        return None
+
+    element_class = LOOKUP_ELEMENT_CLASS(
+        ELEMENT_CLASS_LOOKUP_STATE, doc, c_node)
+    if hasProxy(c_node):
+        # prevent re-entry race condition - we just called into Python
+        return getProxy(c_node)
+    result = NEW_ELEMENT(element_class)
+    if hasProxy(c_node):
+        # prevent re-entry race condition - we just called into Python
+        result._c_node = NULL
+        return getProxy(c_node)
+
+    _registerProxy(result, doc, c_node)
+    if element_class is not _Element:
+        result._init()
+    return result
+
+
+@cython.internal
+cdef class __ContentOnlyElement(_Element):
+    cdef int _raiseImmutable(self) except -1:
+        raise TypeError, "this element does not have children or attributes"
+
+    def set(self, key, value):
+        "set(self, key, value)"
+        self._raiseImmutable()
+
+    def append(self, value):
+        "append(self, value)"
+        self._raiseImmutable()
+
+    def insert(self, index, value):
+        "insert(self, index, value)"
+        self._raiseImmutable()
+
+    def __setitem__(self, index, value):
+        "__setitem__(self, index, value)"
+        self._raiseImmutable()
+
+    @property
+    def attrib(self):
+        return IMMUTABLE_EMPTY_MAPPING
+
+    property text:
+        def __get__(self):
+            _assertValidNode(self)
+            return funicodeOrEmpty(self._c_node.content)
+
+        def __set__(self, value):
+            cdef tree.xmlDict* c_dict
+            _assertValidNode(self)
+            if value is None:
+                c_text = <const_xmlChar*>NULL
+            else:
+                value = _utf8(value)
+                c_text = _xcstr(value)
+            tree.xmlNodeSetContent(self._c_node, c_text)
+
+    # ACCESSORS
+    def __getitem__(self, x):
+        "__getitem__(self, x)"
+        if isinstance(x, slice):
+            return []
+        else:
+            raise IndexError, "list index out of range"
+
+    def __len__(self):
+        "__len__(self)"
+        return 0
+
+    def get(self, key, default=None):
+        "get(self, key, default=None)"
+        return None
+
+    def keys(self):
+        "keys(self)"
+        return []
+
+    def items(self):
+        "items(self)"
+        return []
+
+    def values(self):
+        "values(self)"
+        return []
+
+cdef class _Comment(__ContentOnlyElement):
+    @property
+    def tag(self):
+        return Comment
+
+    def __repr__(self):
+        return "<!--%s-->" % self.text
+
+cdef class _ProcessingInstruction(__ContentOnlyElement):
+    @property
+    def tag(self):
+        return ProcessingInstruction
+
+    property target:
+        # not in ElementTree
+        def __get__(self):
+            _assertValidNode(self)
+            return funicode(self._c_node.name)
+
+        def __set__(self, value):
+            _assertValidNode(self)
+            value = _utf8(value)
+            c_text = _xcstr(value)
+            tree.xmlNodeSetName(self._c_node, c_text)
+
+    def __repr__(self):
+        text = self.text
+        if text:
+            return "<?%s %s?>" % (self.target, text)
+        else:
+            return "<?%s?>" % self.target
+
+    def get(self, key, default=None):
+        """get(self, key, default=None)
+
+        Try to parse pseudo-attributes from the text content of the
+        processing instruction, search for one with the given key as
+        name and return its associated value.
+
+        Note that this is only a convenience method for the most
+        common case that all text content is structured in
+        attribute-like name-value pairs with properly quoted values.
+        It is not guaranteed to work for all possible text content.
+        """
+        return self.attrib.get(key, default)
+
+    @property
+    def attrib(self):
+        """Returns a dict containing all pseudo-attributes that can be
+        parsed from the text content of this processing instruction.
+        Note that modifying the dict currently has no effect on the
+        XML node, although this is not guaranteed to stay this way.
+        """
+        return { attr : (value1 or value2)
+                 for attr, value1, value2 in _FIND_PI_ATTRIBUTES(' ' + self.text) }
+
+cdef object _FIND_PI_ATTRIBUTES = re.compile(r'\s+(\w+)\s*=\s*(?:\'([^\']*)\'|"([^"]*)")', re.U).findall
+
+cdef class _Entity(__ContentOnlyElement):
+    @property
+    def tag(self):
+        return Entity
+
+    property name:
+        # not in ElementTree
+        def __get__(self):
+            _assertValidNode(self)
+            return funicode(self._c_node.name)
+
+        def __set__(self, value):
+            _assertValidNode(self)
+            value_utf = _utf8(value)
+            if b'&' in value_utf or b';' in value_utf:
+                raise ValueError, f"Invalid entity name '{value}'"
+            tree.xmlNodeSetName(self._c_node, _xcstr(value_utf))
+
+    @property
+    def text(self):
+        # FIXME: should this be None or '&[VALUE];' or the resolved
+        # entity value ?
+        _assertValidNode(self)
+        return f'&{funicode(self._c_node.name)};'
+
+    def __repr__(self):
+        return "&%s;" % self.name
+
+
+cdef class QName:
+    """QName(text_or_uri_or_element, tag=None)
+
+    QName wrapper for qualified XML names.
+
+    Pass a tag name by itself or a namespace URI and a tag name to
+    create a qualified name.  Alternatively, pass an Element to
+    extract its tag name.  ``None`` as first argument is ignored in
+    order to allow for generic 2-argument usage.
+
+    The ``text`` property holds the qualified name in
+    ``{namespace}tagname`` notation.  The ``namespace`` and
+    ``localname`` properties hold the respective parts of the tag
+    name.
+
+    You can pass QName objects wherever a tag name is expected.  Also,
+    setting Element text from a QName will resolve the namespace prefix
+    on assignment and set a qualified text value.  This is helpful in XML
+    languages like SOAP or XML-Schema that use prefixed tag names in
+    their text content.
+    """
+    cdef readonly unicode text
+    cdef readonly unicode localname
+    cdef readonly unicode namespace
+    def __init__(self, text_or_uri_or_element, tag=None):
+        if text_or_uri_or_element is None:
+            # Allow None as no namespace.
+            text_or_uri_or_element, tag = tag, None
+        if not _isString(text_or_uri_or_element):
+            if isinstance(text_or_uri_or_element, _Element):
+                text_or_uri_or_element = (<_Element>text_or_uri_or_element).tag
+                if not _isString(text_or_uri_or_element):
+                    raise ValueError, f"Invalid input tag of type {type(text_or_uri_or_element)!r}"
+            elif isinstance(text_or_uri_or_element, QName):
+                text_or_uri_or_element = (<QName>text_or_uri_or_element).text
+            elif text_or_uri_or_element is not None:
+                text_or_uri_or_element = unicode(text_or_uri_or_element)
+            else:
+                raise ValueError, f"Invalid input tag of type {type(text_or_uri_or_element)!r}"
+
+        ns_utf, tag_utf = _getNsTag(text_or_uri_or_element)
+        if tag is not None:
+            # either ('ns', 'tag') or ('{ns}oldtag', 'newtag')
+            if ns_utf is None:
+                ns_utf = tag_utf # case 1: namespace ended up as tag name
+            tag_utf = _utf8(tag)
+        _tagValidOrRaise(tag_utf)
+        self.localname = (<bytes>tag_utf).decode('utf8')
+        if ns_utf is None:
+            self.namespace = None
+            self.text = self.localname
+        else:
+            self.namespace = (<bytes>ns_utf).decode('utf8')
+            self.text = "{%s}%s" % (self.namespace, self.localname)
+    def __str__(self):
+        return self.text
+    def __hash__(self):
+        return hash(self.text)
+    def __richcmp__(self, other, int op):
+        try:
+            if type(other) is QName:
+                other = (<QName>other).text
+            elif not isinstance(other, unicode):
+                other = unicode(other)
+        except (ValueError, UnicodeDecodeError):
+            return NotImplemented
+        return python.PyObject_RichCompare(self.text, other, op)
+
+
+cdef public class _ElementTree [ type LxmlElementTreeType,
+                                 object LxmlElementTree ]:
+    cdef _Document _doc
+    cdef _Element _context_node
+
+    # Note that _doc is only used to store the original document if we do not
+    # have a _context_node.  All methods should prefer self._context_node._doc
+    # to honour tree restructuring.  _doc can happily be None!
+
+    @cython.final
+    cdef int _assertHasRoot(self) except -1:
+        """We have to take care here: the document may not have a root node!
+        This can happen if ElementTree() is called without any argument and
+        the caller 'forgets' to call parse() afterwards, so this is a bug in
+        the caller program.
+        """
+        assert self._context_node is not None, \
+               "ElementTree not initialized, missing root"
+        return 0
+
+    def parse(self, source, _BaseParser parser=None, *, base_url=None):
+        """parse(self, source, parser=None, base_url=None)
+
+        Updates self with the content of source and returns its root.
+        """
+        cdef _Document doc = None
+        try:
+            doc = _parseDocument(source, parser, base_url)
+        except _TargetParserResult as result_container:
+            # raises a TypeError if we don't get an _Element
+            self._context_node = result_container.result
+        else:
+            self._context_node = doc.getroot()
+        self._doc = None if self._context_node is not None else doc
+        return self._context_node
+
+    def _setroot(self, _Element root not None):
+        """_setroot(self, root)
+
+        Relocate the ElementTree to a new root node.
+        """
+        _assertValidNode(root)
+        if root._c_node.type != tree.XML_ELEMENT_NODE:
+            raise TypeError, "Only elements can be the root of an ElementTree"
+        self._context_node = root
+        self._doc = None
+
+    def getroot(self):
+        """getroot(self)
+
+        Gets the root element for this tree.
+        """
+        return self._context_node
+
+    def __copy__(self):
+        return _elementTreeFactory(self._doc, self._context_node)
+
+    def __deepcopy__(self, memo):
+        cdef _Element root
+        cdef _Document doc
+        cdef xmlDoc* c_doc
+        if self._context_node is not None:
+            root = self._context_node.__copy__()
+            assert root is not None
+            _assertValidNode(root)
+            _copyNonElementSiblings(self._context_node._c_node, root._c_node)
+            return _elementTreeFactory(None, root)
+        elif self._doc is not None:
+            _assertValidDoc(self._doc)
+            c_doc = tree.xmlCopyDoc(self._doc._c_doc, 1)
+            if c_doc is NULL:
+                raise MemoryError()
+            doc = _documentFactory(c_doc, self._doc._parser)
+            return _elementTreeFactory(doc, None)
+        else:
+            # so what ...
+            return self
+
+    # not in ElementTree
+    @property
+    def docinfo(self) -> DocInfo:
+        """Information about the document provided by parser and DTD."""
+        self._assertHasRoot()
+        return DocInfo(self._context_node._doc)
+
+    # not in ElementTree, read-only
+    @property
+    def parser(self):
+        """The parser that was used to parse the document in this ElementTree.
+        """
+        if self._context_node is not None and \
+               self._context_node._doc is not None:
+            return self._context_node._doc._parser
+        if self._doc is not None:
+            return self._doc._parser
+        return None
+
+    def write(self, file, *, encoding=None, method="xml",
+              bint pretty_print=False, xml_declaration=None, bint with_tail=True,
+              standalone=None, doctype=None, compression=0,
+              bint exclusive=False, inclusive_ns_prefixes=None,
+              bint with_comments=True, bint strip_text=False,
+              docstring=None):
+        """write(self, file, encoding=None, method="xml",
+                  pretty_print=False, xml_declaration=None, with_tail=True,
+                  standalone=None, doctype=None, compression=0,
+                  exclusive=False, inclusive_ns_prefixes=None,
+                  with_comments=True, strip_text=False)
+
+        Write the tree to a filename, file or file-like object.
+
+        Defaults to ASCII encoding and writing a declaration as needed.
+
+        The keyword argument 'method' selects the output method:
+        'xml', 'html', 'text', 'c14n' or 'c14n2'.  Default is 'xml'.
+
+        With ``method="c14n"`` (C14N version 1), the options ``exclusive``,
+        ``with_comments`` and ``inclusive_ns_prefixes`` request exclusive
+        C14N, include comments, and list the inclusive prefixes respectively.
+
+        With ``method="c14n2"`` (C14N version 2), the ``with_comments`` and
+        ``strip_text`` options control the output of comments and text space
+        according to C14N 2.0.
+
+        Passing a boolean value to the ``standalone`` option will
+        output an XML declaration with the corresponding
+        ``standalone`` flag.
+
+        The ``doctype`` option allows passing in a plain string that will
+        be serialised before the XML tree.  Note that passing in non
+        well-formed content here will make the XML output non well-formed.
+        Also, an existing doctype in the document tree will not be removed
+        when serialising an ElementTree instance.
+
+        The ``compression`` option enables GZip compression level 1-9.
+
+        The ``inclusive_ns_prefixes`` should be a list of namespace strings
+        (i.e. ['xs', 'xsi']) that will be promoted to the top-level element
+        during exclusive C14N serialisation.  This parameter is ignored if
+        exclusive mode=False.
+
+        If exclusive=True and no list is provided, a namespace will only be
+        rendered if it is used by the immediate parent or one of its attributes
+        and its prefix and values have not already been rendered by an ancestor
+        of the namespace node's parent element.
+        """
+        cdef bint write_declaration
+        cdef int is_standalone
+
+        self._assertHasRoot()
+        _assertValidNode(self._context_node)
+        if compression is None or compression < 0:
+            compression = 0
+
+        # C14N serialisation
+        if method in ('c14n', 'c14n2'):
+            if encoding is not None:
+                raise ValueError("Cannot specify encoding with C14N")
+            if xml_declaration:
+                raise ValueError("Cannot enable XML declaration in C14N")
+
+            if method == 'c14n':
+                _tofilelikeC14N(file, self._context_node, exclusive, with_comments,
+                                compression, inclusive_ns_prefixes)
+            else:  # c14n2
+                with _open_utf8_file(file, compression=compression) as f:
+                    target = C14NWriterTarget(
+                        f.write, with_comments=with_comments, strip_text=strip_text)
+                    _tree_to_target(self, target)
+            return
+
+        if not with_comments:
+            raise ValueError("Can only discard comments in C14N serialisation")
+        # suppress decl. in default case (purely for ElementTree compatibility)
+        if xml_declaration is not None:
+            write_declaration = xml_declaration
+            if encoding is None:
+                encoding = 'ASCII'
+            else:
+                encoding = encoding.upper()
+        elif encoding is None:
+            encoding = 'ASCII'
+            write_declaration = 0
+        else:
+            encoding = encoding.upper()
+            write_declaration = encoding not in (
+                'US-ASCII', 'ASCII', 'UTF8', 'UTF-8')
+        if standalone is None:
+            is_standalone = -1
+        elif standalone:
+            write_declaration = 1
+            is_standalone = 1
+        else:
+            write_declaration = 1
+            is_standalone = 0
+
+        if docstring is not None and doctype is None:
+            import warnings
+            warnings.warn(
+                "The 'docstring' option is deprecated. Use 'doctype' instead.",
+                DeprecationWarning)
+            doctype = docstring
+
+        _tofilelike(file, self._context_node, encoding, doctype, method,
+                    write_declaration, 1, pretty_print, with_tail,
+                    is_standalone, compression)
+
+    def getpath(self, _Element element not None):
+        """getpath(self, element)
+
+        Returns a structural, absolute XPath expression to find the element.
+
+        For namespaced elements, the expression uses prefixes from the
+        document, which therefore need to be provided in order to make any
+        use of the expression in XPath.
+
+        Also see the method getelementpath(self, element), which returns a
+        self-contained ElementPath expression.
+        """
+        cdef _Document doc
+        cdef _Element root
+        cdef xmlDoc* c_doc
+        _assertValidNode(element)
+        if self._context_node is not None:
+            root = self._context_node
+            doc = root._doc
+        elif self._doc is not None:
+            doc = self._doc
+            root = doc.getroot()
+        else:
+            raise ValueError, "Element is not in this tree."
+        _assertValidDoc(doc)
+        _assertValidNode(root)
+        if element._doc is not doc:
+            raise ValueError, "Element is not in this tree."
+
+        c_doc = _fakeRootDoc(doc._c_doc, root._c_node)
+        c_path = tree.xmlGetNodePath(element._c_node)
+        _destroyFakeDoc(doc._c_doc, c_doc)
+        if c_path is NULL:
+            raise MemoryError()
+        path = funicode(c_path)
+        tree.xmlFree(c_path)
+        return path
+
+    def getelementpath(self, _Element element not None):
+        """getelementpath(self, element)
+
+        Returns a structural, absolute ElementPath expression to find the
+        element.  This path can be used in the .find() method to look up
+        the element, provided that the elements along the path and their
+        list of immediate children were not modified in between.
+
+        ElementPath has the advantage over an XPath expression (as returned
+        by the .getpath() method) that it does not require additional prefix
+        declarations.  It is always self-contained.
+        """
+        cdef _Element root
+        cdef Py_ssize_t count
+        _assertValidNode(element)
+        if element._c_node.type != tree.XML_ELEMENT_NODE:
+            raise ValueError, "input is not an Element"
+        if self._context_node is not None:
+            root = self._context_node
+        elif self._doc is not None:
+            root = self._doc.getroot()
+        else:
+            raise ValueError, "Element is not in this tree"
+        _assertValidNode(root)
+        if element._doc is not root._doc:
+            raise ValueError, "Element is not in this tree"
+
+        path = []
+        c_element = element._c_node
+        while c_element is not root._c_node:
+            c_name = c_element.name
+            c_href = _getNs(c_element)
+            tag = _namespacedNameFromNsName(c_href, c_name)
+            if c_href is NULL:
+                c_href = <const_xmlChar*>b''  # no namespace (NULL is wildcard)
+            # use tag[N] if there are preceding siblings with the same tag
+            count = 0
+            c_node = c_element.prev
+            while c_node is not NULL:
+                if c_node.type == tree.XML_ELEMENT_NODE:
+                    if _tagMatches(c_node, c_href, c_name):
+                        count += 1
+                c_node = c_node.prev
+            if count:
+                tag = f'{tag}[{count+1}]'
+            else:
+                # use tag[1] if there are following siblings with the same tag
+                c_node = c_element.next
+                while c_node is not NULL:
+                    if c_node.type == tree.XML_ELEMENT_NODE:
+                        if _tagMatches(c_node, c_href, c_name):
+                            tag += '[1]'
+                            break
+                    c_node = c_node.next
+
+            path.append(tag)
+            c_element = c_element.parent
+            if c_element is NULL or c_element.type != tree.XML_ELEMENT_NODE:
+                raise ValueError, "Element is not in this tree."
+        if not path:
+            return '.'
+        path.reverse()
+        return '/'.join(path)
+
+    def getiterator(self, tag=None, *tags):
+        """getiterator(self, *tags, tag=None)
+
+        Returns a sequence or iterator of all elements in document order
+        (depth first pre-order), starting with the root element.
+
+        Can be restricted to find only elements with specific tags,
+        see `_Element.iter`.
+
+        :deprecated: Note that this method is deprecated as of
+          ElementTree 1.3 and lxml 2.0.  It returns an iterator in
+          lxml, which diverges from the original ElementTree
+          behaviour.  If you want an efficient iterator, use the
+          ``tree.iter()`` method instead.  You should only use this
+          method in new code if you require backwards compatibility
+          with older versions of lxml or ElementTree.
+        """
+        root = self.getroot()
+        if root is None:
+            return ITER_EMPTY
+        if tag is not None:
+            tags += (tag,)
+        return root.getiterator(*tags)
+
+    def iter(self, tag=None, *tags):
+        """iter(self, tag=None, *tags)
+
+        Creates an iterator for the root element.  The iterator loops over
+        all elements in this tree, in document order.  Note that siblings
+        of the root element (comments or processing instructions) are not
+        returned by the iterator.
+
+        Can be restricted to find only elements with specific tags,
+        see `_Element.iter`.
+        """
+        root = self.getroot()
+        if root is None:
+            return ITER_EMPTY
+        if tag is not None:
+            tags += (tag,)
+        return root.iter(*tags)
+
+    def find(self, path, namespaces=None):
+        """find(self, path, namespaces=None)
+
+        Finds the first toplevel element with given tag.  Same as
+        ``tree.getroot().find(path)``.
+
+        The optional ``namespaces`` argument accepts a
+        prefix-to-namespace mapping that allows the usage of XPath
+        prefixes in the path expression.
+        """
+        self._assertHasRoot()
+        root = self.getroot()
+        if _isString(path):
+            if path[:1] == "/":
+                path = "." + path
+                from warnings import warn
+                warn(
+                    "This search incorrectly ignores the root element, and will be "
+                    "fixed in a future version.  If you rely on the current "
+                    f"behaviour, change it to {path!r}",
+                    FutureWarning, stacklevel=1
+                )
+        return root.find(path, namespaces)
+
+    def findtext(self, path, default=None, namespaces=None):
+        """findtext(self, path, default=None, namespaces=None)
+
+        Finds the text for the first element matching the ElementPath
+        expression.  Same as getroot().findtext(path)
+
+        The optional ``namespaces`` argument accepts a
+        prefix-to-namespace mapping that allows the usage of XPath
+        prefixes in the path expression.
+        """
+        self._assertHasRoot()
+        root = self.getroot()
+        if _isString(path):
+            if path[:1] == "/":
+                path = "." + path
+                from warnings import warn
+                warn(
+                    "This search incorrectly ignores the root element, and will be "
+                    "fixed in a future version.  If you rely on the current "
+                    f"behaviour, change it to {path!r}",
+                    FutureWarning, stacklevel=1
+                )
+        return root.findtext(path, default, namespaces)
+
+    def findall(self, path, namespaces=None):
+        """findall(self, path, namespaces=None)
+
+        Finds all elements matching the ElementPath expression.  Same as
+        getroot().findall(path).
+
+        The optional ``namespaces`` argument accepts a
+        prefix-to-namespace mapping that allows the usage of XPath
+        prefixes in the path expression.
+        """
+        self._assertHasRoot()
+        root = self.getroot()
+        if _isString(path):
+            if path[:1] == "/":
+                path = "." + path
+                from warnings import warn
+                warn(
+                    "This search incorrectly ignores the root element, and will be "
+                    "fixed in a future version.  If you rely on the current "
+                    f"behaviour, change it to {path!r}",
+                    FutureWarning, stacklevel=1
+                )
+        return root.findall(path, namespaces)
+
+    def iterfind(self, path, namespaces=None):
+        """iterfind(self, path, namespaces=None)
+
+        Iterates over all elements matching the ElementPath expression.
+        Same as getroot().iterfind(path).
+
+        The optional ``namespaces`` argument accepts a
+        prefix-to-namespace mapping that allows the usage of XPath
+        prefixes in the path expression.
+        """
+        self._assertHasRoot()
+        root = self.getroot()
+        if _isString(path):
+            if path[:1] == "/":
+                path = "." + path
+                from warnings import warn
+                warn(
+                    "This search incorrectly ignores the root element, and will be "
+                    "fixed in a future version.  If you rely on the current "
+                    f"behaviour, change it to {path!r}",
+                    FutureWarning, stacklevel=1
+                )
+        return root.iterfind(path, namespaces)
+
+    def xpath(self, _path, *, namespaces=None, extensions=None,
+              smart_strings=True, **_variables):
+        """xpath(self, _path, namespaces=None, extensions=None, smart_strings=True, **_variables)
+
+        XPath evaluate in context of document.
+
+        ``namespaces`` is an optional dictionary with prefix to namespace URI
+        mappings, used by XPath.  ``extensions`` defines additional extension
+        functions.
+
+        Returns a list (nodeset), or bool, float or string.
+
+        In case of a list result, return Element for element nodes,
+        string for text and attribute values.
+
+        Note: if you are going to apply multiple XPath expressions
+        against the same document, it is more efficient to use
+        XPathEvaluator directly.
+        """
+        self._assertHasRoot()
+        evaluator = XPathDocumentEvaluator(self, namespaces=namespaces,
+                                           extensions=extensions,
+                                           smart_strings=smart_strings)
+        return evaluator(_path, **_variables)
+
+    def xslt(self, _xslt, extensions=None, access_control=None, **_kw):
+        """xslt(self, _xslt, extensions=None, access_control=None, **_kw)
+
+        Transform this document using other document.
+
+        xslt is a tree that should be XSLT
+        keyword parameters are XSLT transformation parameters.
+
+        Returns the transformed tree.
+
+        Note: if you are going to apply the same XSLT stylesheet against
+        multiple documents, it is more efficient to use the XSLT
+        class directly.
+        """
+        self._assertHasRoot()
+        style = XSLT(_xslt, extensions=extensions,
+                     access_control=access_control)
+        return style(self, **_kw)
+
+    def relaxng(self, relaxng):
+        """relaxng(self, relaxng)
+
+        Validate this document using other document.
+
+        The relaxng argument is a tree that should contain a Relax NG schema.
+
+        Returns True or False, depending on whether validation
+        succeeded.
+
+        Note: if you are going to apply the same Relax NG schema against
+        multiple documents, it is more efficient to use the RelaxNG
+        class directly.
+        """
+        self._assertHasRoot()
+        schema = RelaxNG(relaxng)
+        return schema.validate(self)
+
+    def xmlschema(self, xmlschema):
+        """xmlschema(self, xmlschema)
+
+        Validate this document using other document.
+
+        The xmlschema argument is a tree that should contain an XML Schema.
+
+        Returns True or False, depending on whether validation
+        succeeded.
+
+        Note: If you are going to apply the same XML Schema against
+        multiple documents, it is more efficient to use the XMLSchema
+        class directly.
+        """
+        self._assertHasRoot()
+        schema = XMLSchema(xmlschema)
+        return schema.validate(self)
+
+    def xinclude(self):
+        """xinclude(self)
+
+        Process the XInclude nodes in this document and include the
+        referenced XML fragments.
+
+        There is support for loading files through the file system, HTTP and
+        FTP.
+
+        Note that XInclude does not support custom resolvers in Python space
+        due to restrictions of libxml2 <= 2.6.29.
+        """
+        self._assertHasRoot()
+        XInclude()(self._context_node)
+
+    def write_c14n(self, file, *, bint exclusive=False, bint with_comments=True,
+                   compression=0, inclusive_ns_prefixes=None):
+        """write_c14n(self, file, exclusive=False, with_comments=True,
+                       compression=0, inclusive_ns_prefixes=None)
+
+        C14N write of document. Always writes UTF-8.
+
+        The ``compression`` option enables GZip compression level 1-9.
+
+        The ``inclusive_ns_prefixes`` should be a list of namespace strings
+        (i.e. ['xs', 'xsi']) that will be promoted to the top-level element
+        during exclusive C14N serialisation.  This parameter is ignored if
+        exclusive mode=False.
+
+        If exclusive=True and no list is provided, a namespace will only be
+        rendered if it is used by the immediate parent or one of its attributes
+        and its prefix and values have not already been rendered by an ancestor
+        of the namespace node's parent element.
+
+        NOTE: This method is deprecated as of lxml 4.4 and will be removed in a
+        future release.  Use ``.write(f, method="c14n")`` instead.
+        """
+        self._assertHasRoot()
+        _assertValidNode(self._context_node)
+        if compression is None or compression < 0:
+            compression = 0
+
+        _tofilelikeC14N(file, self._context_node, exclusive, with_comments,
+                        compression, inclusive_ns_prefixes)
+
+cdef _ElementTree _elementTreeFactory(_Document doc, _Element context_node):
+    return _newElementTree(doc, context_node, _ElementTree)
+
+cdef _ElementTree _newElementTree(_Document doc, _Element context_node,
+                                  object baseclass):
+    cdef _ElementTree result
+    result = baseclass()
+    if context_node is None and doc is not None:
+        context_node = doc.getroot()
+    if context_node is None:
+        _assertValidDoc(doc)
+        result._doc = doc
+    else:
+        _assertValidNode(context_node)
+    result._context_node = context_node
+    return result
+
+
+@cython.final
+@cython.freelist(16)
+cdef class _Attrib:
+    """A dict-like proxy for the ``Element.attrib`` property.
+    """
+    cdef _Element _element
+    def __cinit__(self, _Element element not None):
+        _assertValidNode(element)
+        self._element = element
+
+    # MANIPULATORS
+    def __setitem__(self, key, value):
+        _assertValidNode(self._element)
+        _setAttributeValue(self._element, key, value)
+
+    def __delitem__(self, key):
+        _assertValidNode(self._element)
+        _delAttribute(self._element, key)
+
+    def update(self, sequence_or_dict):
+        _assertValidNode(self._element)
+        if isinstance(sequence_or_dict, (dict, _Attrib)):
+            sequence_or_dict = sequence_or_dict.items()
+        for key, value in sequence_or_dict:
+            _setAttributeValue(self._element, key, value)
+
+    def pop(self, key, *default):
+        if len(default) > 1:
+            raise TypeError, f"pop expected at most 2 arguments, got {len(default)+1}"
+        _assertValidNode(self._element)
+        result = _getAttributeValue(self._element, key, None)
+        if result is None:
+            if not default:
+                raise KeyError, key
+            result = default[0]
+        else:
+            _delAttribute(self._element, key)
+        return result
+
+    def clear(self):
+        _assertValidNode(self._element)
+        c_attrs = self._element._c_node.properties
+        if c_attrs:
+            self._element._c_node.properties = NULL
+            tree.xmlFreePropList(c_attrs)
+
+    # ACCESSORS
+    def __repr__(self):
+        _assertValidNode(self._element)
+        return repr(dict( _collectAttributes(self._element._c_node, 3) ))
+
+    def __copy__(self):
+        _assertValidNode(self._element)
+        return dict(_collectAttributes(self._element._c_node, 3))
+
+    def __deepcopy__(self, memo):
+        _assertValidNode(self._element)
+        return dict(_collectAttributes(self._element._c_node, 3))
+
+    def __getitem__(self, key):
+        _assertValidNode(self._element)
+        result = _getAttributeValue(self._element, key, None)
+        if result is None:
+            raise KeyError, key
+        return result
+
+    def __bool__(self):
+        _assertValidNode(self._element)
+        cdef xmlAttr* c_attr = self._element._c_node.properties
+        while c_attr is not NULL:
+            if c_attr.type == tree.XML_ATTRIBUTE_NODE:
+                return 1
+            c_attr = c_attr.next
+        return 0
+
+    def __len__(self):
+        _assertValidNode(self._element)
+        cdef xmlAttr* c_attr = self._element._c_node.properties
+        cdef Py_ssize_t c = 0
+        while c_attr is not NULL:
+            if c_attr.type == tree.XML_ATTRIBUTE_NODE:
+                c += 1
+            c_attr = c_attr.next
+        return c
+
+    def get(self, key, default=None):
+        _assertValidNode(self._element)
+        return _getAttributeValue(self._element, key, default)
+
+    def keys(self):
+        _assertValidNode(self._element)
+        return _collectAttributes(self._element._c_node, 1)
+
+    def __iter__(self):
+        _assertValidNode(self._element)
+        return iter(_collectAttributes(self._element._c_node, 1))
+
+    def iterkeys(self):
+        _assertValidNode(self._element)
+        return iter(_collectAttributes(self._element._c_node, 1))
+
+    def values(self):
+        _assertValidNode(self._element)
+        return _collectAttributes(self._element._c_node, 2)
+
+    def itervalues(self):
+        _assertValidNode(self._element)
+        return iter(_collectAttributes(self._element._c_node, 2))
+
+    def items(self):
+        _assertValidNode(self._element)
+        return _collectAttributes(self._element._c_node, 3)
+
+    def iteritems(self):
+        _assertValidNode(self._element)
+        return iter(_collectAttributes(self._element._c_node, 3))
+
+    def has_key(self, key):
+        _assertValidNode(self._element)
+        return key in self
+
+    def __contains__(self, key):
+        _assertValidNode(self._element)
+        cdef xmlNode* c_node
+        ns, tag = _getNsTag(key)
+        c_node = self._element._c_node
+        c_href = <const_xmlChar*>NULL if ns is None else _xcstr(ns)
+        return 1 if tree.xmlHasNsProp(c_node, _xcstr(tag), c_href) else 0
+
+    def __richcmp__(self, other, int op):
+        try:
+            one = dict(self.items())
+            if not isinstance(other, dict):
+                other = dict(other)
+        except (TypeError, ValueError):
+            return NotImplemented
+        return python.PyObject_RichCompare(one, other, op)
+
+MutableMapping.register(_Attrib)
+
+
+@cython.final
+@cython.internal
+cdef class _AttribIterator:
+    """Attribute iterator - for internal use only!
+    """
+    # XML attributes must not be removed while running!
+    cdef _Element _node
+    cdef xmlAttr* _c_attr
+    cdef int _keysvalues # 1 - keys, 2 - values, 3 - items (key, value)
+    def __iter__(self):
+        return self
+
+    def __next__(self):
+        cdef xmlAttr* c_attr
+        if self._node is None:
+            raise StopIteration
+        c_attr = self._c_attr
+        while c_attr is not NULL and c_attr.type != tree.XML_ATTRIBUTE_NODE:
+            c_attr = c_attr.next
+        if c_attr is NULL:
+            self._node = None
+            raise StopIteration
+
+        self._c_attr = c_attr.next
+        if self._keysvalues == 1:
+            return _namespacedName(<xmlNode*>c_attr)
+        elif self._keysvalues == 2:
+            return _attributeValue(self._node._c_node, c_attr)
+        else:
+            return (_namespacedName(<xmlNode*>c_attr),
+                    _attributeValue(self._node._c_node, c_attr))
+
+cdef object _attributeIteratorFactory(_Element element, int keysvalues):
+    cdef _AttribIterator attribs
+    if element._c_node.properties is NULL:
+        return ITER_EMPTY
+    attribs = _AttribIterator()
+    attribs._node = element
+    attribs._c_attr = element._c_node.properties
+    attribs._keysvalues = keysvalues
+    return attribs
+
+
+cdef public class _ElementTagMatcher [ object LxmlElementTagMatcher,
+                                       type LxmlElementTagMatcherType ]:
+    """
+    Dead but public. :)
+    """
+    cdef object _pystrings
+    cdef int _node_type
+    cdef char* _href
+    cdef char* _name
+    cdef _initTagMatch(self, tag):
+        self._href = NULL
+        self._name = NULL
+        if tag is None:
+            self._node_type = 0
+        elif tag is Comment:
+            self._node_type = tree.XML_COMMENT_NODE
+        elif tag is ProcessingInstruction:
+            self._node_type = tree.XML_PI_NODE
+        elif tag is Entity:
+            self._node_type = tree.XML_ENTITY_REF_NODE
+        elif tag is Element:
+            self._node_type = tree.XML_ELEMENT_NODE
+        else:
+            self._node_type = tree.XML_ELEMENT_NODE
+            self._pystrings = _getNsTag(tag)
+            if self._pystrings[0] is not None:
+                self._href = _cstr(self._pystrings[0])
+            self._name = _cstr(self._pystrings[1])
+            if self._name[0] == c'*' and self._name[1] == c'\0':
+                self._name = NULL
+
+cdef public class _ElementIterator(_ElementTagMatcher) [
+    object LxmlElementIterator, type LxmlElementIteratorType ]:
+    """
+    Dead but public. :)
+    """
+    # we keep Python references here to control GC
+    cdef _Element _node
+    cdef _node_to_node_function _next_element
+    def __iter__(self):
+        return self
+
+    cdef void _storeNext(self, _Element node):
+        cdef xmlNode* c_node
+        c_node = self._next_element(node._c_node)
+        while c_node is not NULL and \
+                  self._node_type != 0 and \
+                  (<tree.xmlElementType>self._node_type != c_node.type or
+                   not _tagMatches(c_node, <const_xmlChar*>self._href, <const_xmlChar*>self._name)):
+            c_node = self._next_element(c_node)
+        if c_node is NULL:
+            self._node = None
+        else:
+            # Python ref:
+            self._node = _elementFactory(node._doc, c_node)
+
+    def __next__(self):
+        cdef xmlNode* c_node
+        cdef _Element current_node
+        if self._node is None:
+            raise StopIteration
+        # Python ref:
+        current_node = self._node
+        self._storeNext(current_node)
+        return current_node
+
+@cython.final
+@cython.internal
+cdef class _MultiTagMatcher:
+    """
+    Match an xmlNode against a list of tags.
+    """
+    cdef list _py_tags
+    cdef qname* _cached_tags
+    cdef size_t _tag_count
+    cdef size_t _cached_size
+    cdef _Document _cached_doc
+    cdef int _node_types
+
+    def __cinit__(self, tags):
+        self._py_tags = []
+        self.initTagMatch(tags)
+
+    def __dealloc__(self):
+        self._clear()
+
+    cdef bint rejectsAll(self) noexcept:
+        return not self._tag_count and not self._node_types
+
+    cdef bint rejectsAllAttributes(self) noexcept:
+        return not self._tag_count
+
+    cdef bint matchesType(self, int node_type) noexcept:
+        if node_type == tree.XML_ELEMENT_NODE and self._tag_count:
+            return True
+        return self._node_types & (1 << node_type)
+
+    cdef void _clear(self) noexcept:
+        cdef size_t i, count
+        count = self._tag_count
+        self._tag_count = 0
+        if self._cached_tags:
+            for i in range(count):
+                cpython.ref.Py_XDECREF(self._cached_tags[i].href)
+            python.lxml_free(self._cached_tags)
+            self._cached_tags = NULL
+
+    cdef initTagMatch(self, tags):
+        self._cached_doc = None
+        del self._py_tags[:]
+        self._clear()
+        if tags is None or tags == ():
+            # no selection in tags argument => match anything
+            self._node_types = (
+                1 << tree.XML_COMMENT_NODE |
+                1 << tree.XML_PI_NODE |
+                1 << tree.XML_ENTITY_REF_NODE |
+                1 << tree.XML_ELEMENT_NODE)
+        else:
+            self._node_types = 0
+            self._storeTags(tags, set())
+
+    cdef _storeTags(self, tag, set seen):
+        if tag is Comment:
+            self._node_types |= 1 << tree.XML_COMMENT_NODE
+        elif tag is ProcessingInstruction:
+            self._node_types |= 1 << tree.XML_PI_NODE
+        elif tag is Entity:
+            self._node_types |= 1 << tree.XML_ENTITY_REF_NODE
+        elif tag is Element:
+            self._node_types |= 1 << tree.XML_ELEMENT_NODE
+        elif python._isString(tag):
+            if tag in seen:
+                return
+            seen.add(tag)
+            if tag in ('*', '{*}*'):
+                self._node_types |= 1 << tree.XML_ELEMENT_NODE
+            else:
+                href, name = _getNsTag(tag)
+                if name == b'*':
+                    name = None
+                if href is None:
+                    href = b''  # no namespace
+                elif href == b'*':
+                    href = None  # wildcard: any namespace, including none
+                self._py_tags.append((href, name))
+        elif isinstance(tag, QName):
+            self._storeTags(tag.text, seen)
+        else:
+            # support a sequence of tags
+            for item in tag:
+                self._storeTags(item, seen)
+
+    cdef inline int cacheTags(self, _Document doc, bint force_into_dict=False) except -1:
+        """
+        Look up the tag names in the doc dict to enable string pointer comparisons.
+        """
+        cdef size_t dict_size = tree.xmlDictSize(doc._c_doc.dict)
+        if doc is self._cached_doc and dict_size == self._cached_size:
+            # doc and dict didn't change => names already cached
+            return 0
+        self._tag_count = 0
+        if not self._py_tags:
+            self._cached_doc = doc
+            self._cached_size = dict_size
+            return 0
+        if not self._cached_tags:
+            self._cached_tags = <qname*>python.lxml_malloc(len(self._py_tags), sizeof(qname))
+            if not self._cached_tags:
+                self._cached_doc = None
+                raise MemoryError()
+        self._tag_count = <size_t>_mapTagsToQnameMatchArray(
+            doc._c_doc, self._py_tags, self._cached_tags, force_into_dict)
+        self._cached_doc = doc
+        self._cached_size = dict_size
+        return 0
+
+    cdef inline bint matches(self, xmlNode* c_node) noexcept:
+        cdef qname* c_qname
+        if self._node_types & (1 << c_node.type):
+            return True
+        elif c_node.type == tree.XML_ELEMENT_NODE:
+            for c_qname in self._cached_tags[:self._tag_count]:
+                if _tagMatchesExactly(c_node, c_qname):
+                    return True
+        return False
+
+    cdef inline bint matchesNsTag(self, const_xmlChar* c_href,
+                                  const_xmlChar* c_name) noexcept:
+        cdef qname* c_qname
+        if self._node_types & (1 << tree.XML_ELEMENT_NODE):
+            return True
+        for c_qname in self._cached_tags[:self._tag_count]:
+            if _nsTagMatchesExactly(c_href, c_name, c_qname):
+                return True
+        return False
+
+    cdef inline bint matchesAttribute(self, xmlAttr* c_attr) noexcept:
+        """Attribute matches differ from Element matches in that they do
+        not care about node types.
+        """
+        cdef qname* c_qname
+        for c_qname in self._cached_tags[:self._tag_count]:
+            if _tagMatchesExactly(<xmlNode*>c_attr, c_qname):
+                return True
+        return False
+
+cdef class _ElementMatchIterator:
+    cdef _Element _node
+    cdef _node_to_node_function _next_element
+    cdef _MultiTagMatcher _matcher
+
+    @cython.final
+    cdef _initTagMatcher(self, tags):
+        self._matcher = _MultiTagMatcher.__new__(_MultiTagMatcher, tags)
+
+    def __iter__(self):
+        return self
+
+    @cython.final
+    cdef int _storeNext(self, _Element node) except -1:
+        self._matcher.cacheTags(node._doc)
+        c_node = self._next_element(node._c_node)
+        while c_node is not NULL and not self._matcher.matches(c_node):
+            c_node = self._next_element(c_node)
+        # store Python ref to next node to make sure it's kept alive
+        self._node = _elementFactory(node._doc, c_node) if c_node is not NULL else None
+        return 0
+
+    def __next__(self):
+        cdef _Element current_node = self._node
+        if current_node is None:
+            raise StopIteration
+        self._storeNext(current_node)
+        return current_node
+
+cdef class ElementChildIterator(_ElementMatchIterator):
+    """ElementChildIterator(self, node, tag=None, reversed=False)
+    Iterates over the children of an element.
+    """
+    def __cinit__(self, _Element node not None, tag=None, *, bint reversed=False):
+        cdef xmlNode* c_node
+        _assertValidNode(node)
+        self._initTagMatcher(tag)
+        if reversed:
+            c_node = _findChildBackwards(node._c_node, 0)
+            self._next_element = _previousElement
+        else:
+            c_node = _findChildForwards(node._c_node, 0)
+            self._next_element = _nextElement
+        self._matcher.cacheTags(node._doc)
+        while c_node is not NULL and not self._matcher.matches(c_node):
+            c_node = self._next_element(c_node)
+        # store Python ref to next node to make sure it's kept alive
+        self._node = _elementFactory(node._doc, c_node) if c_node is not NULL else None
+
+cdef class SiblingsIterator(_ElementMatchIterator):
+    """SiblingsIterator(self, node, tag=None, preceding=False)
+    Iterates over the siblings of an element.
+
+    You can pass the boolean keyword ``preceding`` to specify the direction.
+    """
+    def __cinit__(self, _Element node not None, tag=None, *, bint preceding=False):
+        _assertValidNode(node)
+        self._initTagMatcher(tag)
+        if preceding:
+            self._next_element = _previousElement
+        else:
+            self._next_element = _nextElement
+        self._storeNext(node)
+
+cdef class AncestorsIterator(_ElementMatchIterator):
+    """AncestorsIterator(self, node, tag=None)
+    Iterates over the ancestors of an element (from parent to parent).
+    """
+    def __cinit__(self, _Element node not None, tag=None):
+        _assertValidNode(node)
+        self._initTagMatcher(tag)
+        self._next_element = _parentElement
+        self._storeNext(node)
+
+cdef class ElementDepthFirstIterator:
+    """ElementDepthFirstIterator(self, node, tag=None, inclusive=True)
+    Iterates over an element and its sub-elements in document order (depth
+    first pre-order).
+
+    Note that this also includes comments, entities and processing
+    instructions.  To filter them out, check if the ``tag`` property
+    of the returned element is a string (i.e. not None and not a
+    factory function), or pass the ``Element`` factory for the ``tag``
+    argument to receive only Elements.
+
+    If the optional ``tag`` argument is not None, the iterator returns only
+    the elements that match the respective name and namespace.
+
+    The optional boolean argument 'inclusive' defaults to True and can be set
+    to False to exclude the start element itself.
+
+    Note that the behaviour of this iterator is completely undefined if the
+    tree it traverses is modified during iteration.
+    """
+    # we keep Python references here to control GC
+    # keep the next Element after the one we return, and the (s)top node
+    cdef _Element _next_node
+    cdef _Element _top_node
+    cdef _MultiTagMatcher _matcher
+    def __cinit__(self, _Element node not None, tag=None, *, bint inclusive=True):
+        _assertValidNode(node)
+        self._top_node  = node
+        self._next_node = node
+        self._matcher = _MultiTagMatcher.__new__(_MultiTagMatcher, tag)
+        self._matcher.cacheTags(node._doc)
+        if not inclusive or not self._matcher.matches(node._c_node):
+            # find start node (this cannot raise StopIteration, self._next_node != None)
+            next(self)
+
+    def __iter__(self):
+        return self
+
+    def __next__(self):
+        cdef xmlNode* c_node
+        cdef _Element current_node = self._next_node
+        if current_node is None:
+            raise StopIteration
+        c_node = current_node._c_node
+        self._matcher.cacheTags(current_node._doc)
+        if not self._matcher._tag_count:
+            # no tag name was found in the dict => not in document either
+            # try to match by node type
+            c_node = self._nextNodeAnyTag(c_node)
+        else:
+            c_node = self._nextNodeMatchTag(c_node)
+        if c_node is NULL:
+            self._next_node = None
+        else:
+            self._next_node = _elementFactory(current_node._doc, c_node)
+        return current_node
+
+    @cython.final
+    cdef xmlNode* _nextNodeAnyTag(self, xmlNode* c_node) noexcept:
+        cdef int node_types = self._matcher._node_types
+        if not node_types:
+            return NULL
+        tree.BEGIN_FOR_EACH_ELEMENT_FROM(self._top_node._c_node, c_node, 0)
+        if node_types & (1 << c_node.type):
+            return c_node
+        tree.END_FOR_EACH_ELEMENT_FROM(c_node)
+        return NULL
+
+    @cython.final
+    cdef xmlNode* _nextNodeMatchTag(self, xmlNode* c_node) noexcept:
+        tree.BEGIN_FOR_EACH_ELEMENT_FROM(self._top_node._c_node, c_node, 0)
+        if self._matcher.matches(c_node):
+            return c_node
+        tree.END_FOR_EACH_ELEMENT_FROM(c_node)
+        return NULL
+
+
+cdef class ElementTextIterator:
+    """ElementTextIterator(self, element, tag=None, with_tail=True)
+    Iterates over the text content of a subtree.
+
+    You can pass the ``tag`` keyword argument to restrict text content to a
+    specific tag name.
+
+    You can set the ``with_tail`` keyword argument to ``False`` to skip over
+    tail text (e.g. if you know that it's only whitespace from pretty-printing).
+    """
+    cdef object _events
+    cdef _Element _start_element
+    def __cinit__(self, _Element element not None, tag=None, *, bint with_tail=True):
+        _assertValidNode(element)
+        if with_tail:
+            events = ("start", "comment", "pi", "end")
+        else:
+            events = ("start",)
+        self._start_element = element
+        self._events = iterwalk(element, events=events, tag=tag)
+
+    def __iter__(self):
+        return self
+
+    def __next__(self):
+        cdef _Element element
+        result = None
+        while result is None:
+            event, element = next(self._events)  # raises StopIteration
+            if event == "start":
+                result = element.text
+            elif element is not self._start_element:
+                result = element.tail
+        return result
+
+
+cdef xmlNode* _createElement(xmlDoc* c_doc, object name_utf) except NULL:
+    cdef xmlNode* c_node
+    c_node = tree.xmlNewDocNode(c_doc, NULL, _xcstr(name_utf), NULL)
+    return c_node
+
+cdef xmlNode* _createComment(xmlDoc* c_doc, const_xmlChar* text) noexcept:
+    cdef xmlNode* c_node
+    c_node = tree.xmlNewDocComment(c_doc, text)
+    return c_node
+
+cdef xmlNode* _createPI(xmlDoc* c_doc, const_xmlChar* target, const_xmlChar* text) noexcept:
+    cdef xmlNode* c_node
+    c_node = tree.xmlNewDocPI(c_doc, target, text)
+    return c_node
+
+cdef xmlNode* _createEntity(xmlDoc* c_doc, const_xmlChar* name) noexcept:
+    cdef xmlNode* c_node
+    c_node = tree.xmlNewReference(c_doc, name)
+    return c_node
+
+# module-level API for ElementTree
+
+def Element(_tag, attrib=None, nsmap=None, **_extra):
+    """Element(_tag, attrib=None, nsmap=None, **_extra)
+
+    Element factory.  This function returns an object implementing the
+    Element interface.
+
+    Also look at the `_Element.makeelement()` and
+    `_BaseParser.makeelement()` methods, which provide a faster way to
+    create an Element within a specific document or parser context.
+    """
+    return _makeElement(_tag, NULL, None, None, None, None,
+                        attrib, nsmap, _extra)
+
+
+def Comment(text=None):
+    """Comment(text=None)
+
+    Comment element factory. This factory function creates a special element that will
+    be serialized as an XML comment.
+    """
+    cdef _Document doc
+    cdef xmlNode*  c_node
+    cdef xmlDoc*   c_doc
+
+    if text is None:
+        text = b''
+    else:
+        text = _utf8(text)
+        if b'--' in text or text.endswith(b'-'):
+            raise ValueError("Comment may not contain '--' or end with '-'")
+
+    c_doc = _newXMLDoc()
+    doc = _documentFactory(c_doc, None)
+    c_node = _createComment(c_doc, _xcstr(text))
+    tree.xmlAddChild(<xmlNode*>c_doc, c_node)
+    return _elementFactory(doc, c_node)
+
+
+def ProcessingInstruction(target, text=None):
+    """ProcessingInstruction(target, text=None)
+
+    ProcessingInstruction element factory. This factory function creates a
+    special element that will be serialized as an XML processing instruction.
+    """
+    cdef _Document doc
+    cdef xmlNode*  c_node
+    cdef xmlDoc*   c_doc
+
+    target = _utf8(target)
+    _tagValidOrRaise(target)
+    if target.lower() == b'xml':
+        raise ValueError, f"Invalid PI name '{target}'"
+
+    if text is None:
+        text = b''
+    else:
+        text = _utf8(text)
+        if b'?>' in text:
+            raise ValueError, "PI text must not contain '?>'"
+
+    c_doc = _newXMLDoc()
+    doc = _documentFactory(c_doc, None)
+    c_node = _createPI(c_doc, _xcstr(target), _xcstr(text))
+    tree.xmlAddChild(<xmlNode*>c_doc, c_node)
+    return _elementFactory(doc, c_node)
+
+PI = ProcessingInstruction
+
+
+cdef class CDATA:
+    """CDATA(data)
+
+    CDATA factory.  This factory creates an opaque data object that
+    can be used to set Element text.  The usual way to use it is::
+
+        >>> el = Element('content')
+        >>> el.text = CDATA('a string')
+
+        >>> print(el.text)
+        a string
+        >>> print(tostring(el, encoding="unicode"))
+        <content><![CDATA[a string]]></content>
+    """
+    cdef bytes _utf8_data
+    def __cinit__(self, data):
+        self._utf8_data = _utf8(data)
+
+
+def Entity(name):
+    """Entity(name)
+
+    Entity factory.  This factory function creates a special element
+    that will be serialized as an XML entity reference or character
+    reference.  Note, however, that entities will not be automatically
+    declared in the document.  A document that uses entity references
+    requires a DTD to define the entities.
+    """
+    cdef _Document doc
+    cdef xmlNode*  c_node
+    cdef xmlDoc*   c_doc
+    name_utf = _utf8(name)
+    c_name = _xcstr(name_utf)
+    if c_name[0] == c'#':
+        if not _characterReferenceIsValid(c_name + 1):
+            raise ValueError, f"Invalid character reference: '{name}'"
+    elif not _xmlNameIsValid(c_name):
+        raise ValueError, f"Invalid entity reference: '{name}'"
+    c_doc = _newXMLDoc()
+    doc = _documentFactory(c_doc, None)
+    c_node = _createEntity(c_doc, c_name)
+    tree.xmlAddChild(<xmlNode*>c_doc, c_node)
+    return _elementFactory(doc, c_node)
+
+
+def SubElement(_Element _parent not None, _tag,
+               attrib=None, nsmap=None, **_extra):
+    """SubElement(_parent, _tag, attrib=None, nsmap=None, **_extra)
+
+    Subelement factory.  This function creates an element instance, and
+    appends it to an existing element.
+    """
+    return _makeSubElement(_parent, _tag, None, None, attrib, nsmap, _extra)
+
+
+def ElementTree(_Element element=None, *, file=None, _BaseParser parser=None):
+    """ElementTree(element=None, file=None, parser=None)
+
+    ElementTree wrapper class.
+    """
+    cdef xmlNode* c_next
+    cdef xmlNode* c_node
+    cdef xmlNode* c_node_copy
+    cdef xmlDoc*  c_doc
+    cdef _ElementTree etree
+    cdef _Document doc
+
+    if element is not None:
+        doc  = element._doc
+    elif file is not None:
+        try:
+            doc = _parseDocument(file, parser, None)
+        except _TargetParserResult as result_container:
+            return result_container.result
+    else:
+        c_doc = _newXMLDoc()
+        doc = _documentFactory(c_doc, parser)
+
+    return _elementTreeFactory(doc, element)
+
+
+def HTML(text, _BaseParser parser=None, *, base_url=None):
+    """HTML(text, parser=None, base_url=None)
+
+    Parses an HTML document from a string constant.  Returns the root
+    node (or the result returned by a parser target).  This function
+    can be used to embed "HTML literals" in Python code.
+
+    To override the parser with a different ``HTMLParser`` you can pass it to
+    the ``parser`` keyword argument.
+
+    The ``base_url`` keyword argument allows to set the original base URL of
+    the document to support relative Paths when looking up external entities
+    (DTD, XInclude, ...).
+    """
+    cdef _Document doc
+    if parser is None:
+        parser = __GLOBAL_PARSER_CONTEXT.getDefaultParser()
+        if not isinstance(parser, HTMLParser):
+            parser = __DEFAULT_HTML_PARSER
+    try:
+        doc = _parseMemoryDocument(text, base_url, parser)
+        return doc.getroot()
+    except _TargetParserResult as result_container:
+        return result_container.result
+
+
+def XML(text, _BaseParser parser=None, *, base_url=None):
+    """XML(text, parser=None, base_url=None)
+
+    Parses an XML document or fragment from a string constant.
+    Returns the root node (or the result returned by a parser target).
+    This function can be used to embed "XML literals" in Python code,
+    like in
+
+       >>> root = XML("<root><test/></root>")
+       >>> print(root.tag)
+       root
+
+    To override the parser with a different ``XMLParser`` you can pass it to
+    the ``parser`` keyword argument.
+
+    The ``base_url`` keyword argument allows to set the original base URL of
+    the document to support relative Paths when looking up external entities
+    (DTD, XInclude, ...).
+    """
+    cdef _Document doc
+    if parser is None:
+        parser = __GLOBAL_PARSER_CONTEXT.getDefaultParser()
+        if not isinstance(parser, XMLParser):
+            parser = __DEFAULT_XML_PARSER
+    try:
+        doc = _parseMemoryDocument(text, base_url, parser)
+        return doc.getroot()
+    except _TargetParserResult as result_container:
+        return result_container.result
+
+
+def fromstring(text, _BaseParser parser=None, *, base_url=None):
+    """fromstring(text, parser=None, base_url=None)
+
+    Parses an XML document or fragment from a string.  Returns the
+    root node (or the result returned by a parser target).
+
+    To override the default parser with a different parser you can pass it to
+    the ``parser`` keyword argument.
+
+    The ``base_url`` keyword argument allows to set the original base URL of
+    the document to support relative Paths when looking up external entities
+    (DTD, XInclude, ...).
+    """
+    cdef _Document doc
+    try:
+        doc = _parseMemoryDocument(text, base_url, parser)
+        return doc.getroot()
+    except _TargetParserResult as result_container:
+        return result_container.result
+
+
+def fromstringlist(strings, _BaseParser parser=None):
+    """fromstringlist(strings, parser=None)
+
+    Parses an XML document from a sequence of strings.  Returns the
+    root node (or the result returned by a parser target).
+
+    To override the default parser with a different parser you can pass it to
+    the ``parser`` keyword argument.
+    """
+    cdef _Document doc
+    if isinstance(strings, (bytes, unicode)):
+        raise ValueError("passing a single string into fromstringlist() is not"
+                         " efficient, use fromstring() instead")
+    if parser is None:
+        parser = __GLOBAL_PARSER_CONTEXT.getDefaultParser()
+    feed = parser.feed
+    for data in strings:
+        feed(data)
+    return parser.close()
+
+
+def iselement(element):
+    """iselement(element)
+
+    Checks if an object appears to be a valid element object.
+    """
+    return isinstance(element, _Element) and (<_Element>element)._c_node is not NULL
+
+
+def indent(tree, space="  ", *, Py_ssize_t level=0):
+    """indent(tree, space="  ", level=0)
+
+    Indent an XML document by inserting newlines and indentation space
+    after elements.
+
+    *tree* is the ElementTree or Element to modify.  The (root) element
+    itself will not be changed, but the tail text of all elements in its
+    subtree will be adapted.
+
+    *space* is the whitespace to insert for each indentation level, two
+    space characters by default.
+
+    *level* is the initial indentation level. Setting this to a higher
+    value than 0 can be used for indenting subtrees that are more deeply
+    nested inside of a document.
+    """
+    root = _rootNodeOrRaise(tree)
+    if level < 0:
+        raise ValueError(f"Initial indentation level must be >= 0, got {level}")
+    if _hasChild(root._c_node):
+        space = _utf8(space)
+        indent = b"\n" + level * space
+        _indent_children(root._c_node, 1, space, [indent, indent + space])
+
+
+cdef int _indent_children(xmlNode* c_node, Py_ssize_t level, bytes one_space, list indentations) except -1:
+    # Reuse indentation strings for speed.
+    if len(indentations) <= level:
+        indentations.append(indentations[-1] + one_space)
+
+    # Start a new indentation level for the first child.
+    child_indentation = indentations[level]
+    if not _hasNonWhitespaceText(c_node):
+        _setNodeText(c_node, child_indentation)
+
+    # Recursively indent all children.
+    cdef xmlNode* c_child = _findChildForwards(c_node, 0)
+    while c_child is not NULL:
+        if _hasChild(c_child):
+            _indent_children(c_child, level+1, one_space, indentations)
+        c_next_child = _nextElement(c_child)
+        if not _hasNonWhitespaceTail(c_child):
+            if c_next_child is NULL:
+                # Dedent after the last child.
+                child_indentation = indentations[level-1]
+            _setTailText(c_child, child_indentation)
+        c_child = c_next_child
+    return 0
+
+
+def dump(_Element elem not None, *, bint pretty_print=True, bint with_tail=True):
+    """dump(elem, pretty_print=True, with_tail=True)
+
+    Writes an element tree or element structure to sys.stdout. This function
+    should be used for debugging only.
+    """
+    xml = tostring(elem, pretty_print=pretty_print, with_tail=with_tail, encoding='unicode')
+    if not pretty_print:
+        xml += '\n'
+    sys.stdout.write(xml)
+
+
+def tostring(element_or_tree, *, encoding=None, method="xml",
+             xml_declaration=None, bint pretty_print=False, bint with_tail=True,
+             standalone=None, doctype=None,
+             # method='c14n'
+             bint exclusive=False, inclusive_ns_prefixes=None,
+             # method='c14n2'
+             bint with_comments=True, bint strip_text=False,
+             ):
+    """tostring(element_or_tree, encoding=None, method="xml",
+                 xml_declaration=None, pretty_print=False, with_tail=True,
+                 standalone=None, doctype=None,
+                 exclusive=False, inclusive_ns_prefixes=None,
+                 with_comments=True, strip_text=False,
+                 )
+
+    Serialize an element to an encoded string representation of its XML
+    tree.
+
+    Defaults to ASCII encoding without XML declaration.  This
+    behaviour can be configured with the keyword arguments 'encoding'
+    (string) and 'xml_declaration' (bool).  Note that changing the
+    encoding to a non UTF-8 compatible encoding will enable a
+    declaration by default.
+
+    You can also serialise to a Unicode string without declaration by
+    passing the name ``'unicode'`` as encoding (or the ``str`` function
+    in Py3 or ``unicode`` in Py2).  This changes the return value from
+    a byte string to an unencoded unicode string.
+
+    The keyword argument 'pretty_print' (bool) enables formatted XML.
+
+    The keyword argument 'method' selects the output method: 'xml',
+    'html', plain 'text' (text content without tags), 'c14n' or 'c14n2'.
+    Default is 'xml'.
+
+    With ``method="c14n"`` (C14N version 1), the options ``exclusive``,
+    ``with_comments`` and ``inclusive_ns_prefixes`` request exclusive
+    C14N, include comments, and list the inclusive prefixes respectively.
+
+    With ``method="c14n2"`` (C14N version 2), the ``with_comments`` and
+    ``strip_text`` options control the output of comments and text space
+    according to C14N 2.0.
+
+    Passing a boolean value to the ``standalone`` option will output
+    an XML declaration with the corresponding ``standalone`` flag.
+
+    The ``doctype`` option allows passing in a plain string that will
+    be serialised before the XML tree.  Note that passing in non
+    well-formed content here will make the XML output non well-formed.
+    Also, an existing doctype in the document tree will not be removed
+    when serialising an ElementTree instance.
+
+    You can prevent the tail text of the element from being serialised
+    by passing the boolean ``with_tail`` option.  This has no impact
+    on the tail text of children, which will always be serialised.
+    """
+    cdef bint write_declaration
+    cdef int is_standalone
+    # C14N serialisation
+    if method in ('c14n', 'c14n2'):
+        if encoding is not None:
+            raise ValueError("Cannot specify encoding with C14N")
+        if xml_declaration:
+            raise ValueError("Cannot enable XML declaration in C14N")
+        if method == 'c14n':
+            return _tostringC14N(element_or_tree, exclusive, with_comments, inclusive_ns_prefixes)
+        else:
+            out = BytesIO()
+            target = C14NWriterTarget(
+                utf8_writer(out).write,
+                with_comments=with_comments, strip_text=strip_text)
+            _tree_to_target(element_or_tree, target)
+            return out.getvalue()
+    if not with_comments:
+        raise ValueError("Can only discard comments in C14N serialisation")
+    if strip_text:
+        raise ValueError("Can only strip text in C14N 2.0 serialisation")
+    if encoding is unicode or (encoding is not None and encoding.lower() == 'unicode'):
+        if xml_declaration:
+            raise ValueError, \
+                "Serialisation to unicode must not request an XML declaration"
+        write_declaration = 0
+        encoding = unicode
+    elif xml_declaration is None:
+        # by default, write an XML declaration only for non-standard encodings
+        write_declaration = encoding is not None and encoding.upper() not in \
+                            ('ASCII', 'UTF-8', 'UTF8', 'US-ASCII')
+    else:
+        write_declaration = xml_declaration
+    if encoding is None:
+        encoding = 'ASCII'
+    if standalone is None:
+        is_standalone = -1
+    elif standalone:
+        write_declaration = 1
+        is_standalone = 1
+    else:
+        write_declaration = 1
+        is_standalone = 0
+
+    if isinstance(element_or_tree, _Element):
+        return _tostring(<_Element>element_or_tree, encoding, doctype, method,
+                         write_declaration, 0, pretty_print, with_tail,
+                         is_standalone)
+    elif isinstance(element_or_tree, _ElementTree):
+        return _tostring((<_ElementTree>element_or_tree)._context_node,
+                         encoding, doctype, method, write_declaration, 1,
+                         pretty_print, with_tail, is_standalone)
+    else:
+        raise TypeError, f"Type '{python._fqtypename(element_or_tree).decode('utf8')}' cannot be serialized."
+
+
+
+def tostringlist(element_or_tree, *args, **kwargs):
+    """tostringlist(element_or_tree, *args, **kwargs)
+
+    Serialize an element to an encoded string representation of its XML
+    tree, stored in a list of partial strings.
+
+    This is purely for ElementTree 1.3 compatibility.  The result is a
+    single string wrapped in a list.
+    """
+    return [tostring(element_or_tree, *args, **kwargs)]
+
+
+def tounicode(element_or_tree, *, method="xml", bint pretty_print=False,
+              bint with_tail=True, doctype=None):
+    """tounicode(element_or_tree, method="xml", pretty_print=False,
+                  with_tail=True, doctype=None)
+
+    Serialize an element to the Python unicode representation of its XML
+    tree.
+
+    :deprecated: use ``tostring(el, encoding='unicode')`` instead.
+
+    Note that the result does not carry an XML encoding declaration and is
+    therefore not necessarily suited for serialization to byte streams without
+    further treatment.
+
+    The boolean keyword argument 'pretty_print' enables formatted XML.
+
+    The keyword argument 'method' selects the output method: 'xml',
+    'html' or plain 'text'.
+
+    You can prevent the tail text of the element from being serialised
+    by passing the boolean ``with_tail`` option.  This has no impact
+    on the tail text of children, which will always be serialised.
+    """
+    if isinstance(element_or_tree, _Element):
+        return _tostring(<_Element>element_or_tree, unicode, doctype, method,
+                          0, 0, pretty_print, with_tail, -1)
+    elif isinstance(element_or_tree, _ElementTree):
+        return _tostring((<_ElementTree>element_or_tree)._context_node,
+                         unicode, doctype, method, 0, 1, pretty_print,
+                         with_tail, -1)
+    else:
+        raise TypeError, f"Type '{type(element_or_tree)}' cannot be serialized."
+
+
+def parse(source, _BaseParser parser=None, *, base_url=None):
+    """parse(source, parser=None, base_url=None)
+
+    Return an ElementTree object loaded with source elements.  If no parser
+    is provided as second argument, the default parser is used.
+
+    The ``source`` can be any of the following:
+
+    - a file name/path
+    - a file object
+    - a file-like object
+    - a URL using the HTTP or FTP protocol
+
+    To parse from a string, use the ``fromstring()`` function instead.
+
+    Note that it is generally faster to parse from a file path or URL
+    than from an open file object or file-like object.  Transparent
+    decompression from gzip compressed sources is supported (unless
+    explicitly disabled in libxml2).
+
+    The ``base_url`` keyword allows setting a URL for the document
+    when parsing from a file-like object.  This is needed when looking
+    up external entities (DTD, XInclude, ...) with relative paths.
+    """
+    cdef _Document doc
+    try:
+        doc = _parseDocument(source, parser, base_url)
+        return _elementTreeFactory(doc, None)
+    except _TargetParserResult as result_container:
+        return result_container.result
+
+
+def adopt_external_document(capsule, _BaseParser parser=None):
+    """adopt_external_document(capsule, parser=None)
+
+    Unpack a libxml2 document pointer from a PyCapsule and wrap it in an
+    lxml ElementTree object.
+
+    This allows external libraries to build XML/HTML trees using libxml2
+    and then pass them efficiently into lxml for further processing.
+
+    If a ``parser`` is provided, it will be used for configuring the
+    lxml document.  No parsing will be done.
+
+    The capsule must have the name ``"libxml2:xmlDoc"`` and its pointer
+    value must reference a correct libxml2 document of type ``xmlDoc*``.
+    The creator of the capsule must take care to correctly clean up the
+    document using an appropriate capsule destructor.  By default, the
+    libxml2 document will be copied to let lxml safely own the memory
+    of the internal tree that it uses.
+
+    If the capsule context is non-NULL, it must point to a C string that
+    can be compared using ``strcmp()``.  If the context string equals
+    ``"destructor:xmlFreeDoc"``, the libxml2 document will not be copied
+    but the capsule invalidated instead by clearing its destructor and
+    name.  That way, lxml takes ownership of the libxml2 document in memory
+    without creating a copy first, and the capsule destructor will not be
+    called.  The document will then eventually be cleaned up by lxml using
+    the libxml2 API function ``xmlFreeDoc()`` once it is no longer used.
+
+    If no copy is made, later modifications of the tree outside of lxml
+    should not be attempted after transferring the ownership.
+    """
+    cdef xmlDoc* c_doc
+    cdef bint is_owned = False
+    c_doc = <xmlDoc*> python.lxml_unpack_xmldoc_capsule(capsule, &is_owned)
+    doc = _adoptForeignDoc(c_doc, parser, is_owned)
+    return _elementTreeFactory(doc, None)
+
+
+################################################################################
+# Include submodules
+
+include "readonlytree.pxi" # Read-only implementation of Element proxies
+include "classlookup.pxi"  # Element class lookup mechanisms
+include "nsclasses.pxi"    # Namespace implementation and registry
+include "docloader.pxi"    # Support for custom document loaders
+include "parser.pxi"       # XML and HTML parsers
+include "saxparser.pxi"    # SAX-like Parser interface and tree builder
+include "parsertarget.pxi" # ET Parser target
+include "serializer.pxi"   # XML output functions
+include "iterparse.pxi"    # incremental XML parsing
+include "xmlid.pxi"        # XMLID and IDDict
+include "xinclude.pxi"     # XInclude
+include "cleanup.pxi"      # Cleanup and recursive element removal functions
+
+
+################################################################################
+# Include submodules for XPath and XSLT
+
+include "extensions.pxi"   # XPath/XSLT extension functions
+include "xpath.pxi"        # XPath evaluation
+include "xslt.pxi"         # XSL transformations
+include "xsltext.pxi"      # XSL extension elements
+
+
+################################################################################
+# Validation
+
+cdef class DocumentInvalid(LxmlError):
+    """Validation error.
+
+    Raised by all document validators when their ``assertValid(tree)``
+    method fails.
+    """
+
+
+cdef class _Validator:
+    "Base class for XML validators."
+    cdef _ErrorLog _error_log
+    def __cinit__(self):
+        self._error_log = _ErrorLog()
+
+    def validate(self, etree):
+        """validate(self, etree)
+
+        Validate the document using this schema.
+
+        Returns true if document is valid, false if not.
+        """
+        return self(etree)
+
+    def assertValid(self, etree):
+        """assertValid(self, etree)
+
+        Raises `DocumentInvalid` if the document does not comply with the schema.
+        """
+        if not self(etree):
+            raise DocumentInvalid(self._error_log._buildExceptionMessage(
+                    "Document does not comply with schema"),
+                                  self._error_log)
+
+    def assert_(self, etree):
+        """assert_(self, etree)
+
+        Raises `AssertionError` if the document does not comply with the schema.
+        """
+        if not self(etree):
+            raise AssertionError, self._error_log._buildExceptionMessage(
+                "Document does not comply with schema")
+
+    cpdef _append_log_message(self, int domain, int type, int level, int line,
+                              message, filename):
+        self._error_log._receiveGeneric(domain, type, level, line, message,
+                                        filename)
+
+    cpdef _clear_error_log(self):
+        self._error_log.clear()
+
+    @property
+    def error_log(self):
+        """The log of validation errors and warnings."""
+        assert self._error_log is not None, "XPath evaluator not initialised"
+        return self._error_log.copy()
+
+include "dtd.pxi"        # DTD
+include "relaxng.pxi"    # RelaxNG
+include "xmlschema.pxi"  # XMLSchema
+include "schematron.pxi" # Schematron (requires libxml2 2.6.21+)
+
+################################################################################
+# Public C API
+
+include "public-api.pxi"
+
+################################################################################
+# Other stuff
+
+include "debug.pxi"
diff --git a/.venv/lib/python3.12/site-packages/lxml/etree_api.h b/.venv/lib/python3.12/site-packages/lxml/etree_api.h
new file mode 100644
index 00000000..1c9b4616
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/etree_api.h
@@ -0,0 +1,195 @@
+/* Generated by Cython 3.0.11 */
+
+#ifndef __PYX_HAVE_API__lxml__etree
+#define __PYX_HAVE_API__lxml__etree
+#ifdef __MINGW64__
+#define MS_WIN64
+#endif
+#include "Python.h"
+#include "etree.h"
+
+static struct LxmlElement *(*__pyx_api_f_4lxml_5etree_deepcopyNodeToDocument)(struct LxmlDocument *, xmlNode *) = 0;
+#define deepcopyNodeToDocument __pyx_api_f_4lxml_5etree_deepcopyNodeToDocument
+static struct LxmlElementTree *(*__pyx_api_f_4lxml_5etree_elementTreeFactory)(struct LxmlElement *) = 0;
+#define elementTreeFactory __pyx_api_f_4lxml_5etree_elementTreeFactory
+static struct LxmlElementTree *(*__pyx_api_f_4lxml_5etree_newElementTree)(struct LxmlElement *, PyObject *) = 0;
+#define newElementTree __pyx_api_f_4lxml_5etree_newElementTree
+static struct LxmlElementTree *(*__pyx_api_f_4lxml_5etree_adoptExternalDocument)(xmlDoc *, PyObject *, int) = 0;
+#define adoptExternalDocument __pyx_api_f_4lxml_5etree_adoptExternalDocument
+static struct LxmlElement *(*__pyx_api_f_4lxml_5etree_elementFactory)(struct LxmlDocument *, xmlNode *) = 0;
+#define elementFactory __pyx_api_f_4lxml_5etree_elementFactory
+static struct LxmlElement *(*__pyx_api_f_4lxml_5etree_makeElement)(PyObject *, struct LxmlDocument *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *) = 0;
+#define makeElement __pyx_api_f_4lxml_5etree_makeElement
+static struct LxmlElement *(*__pyx_api_f_4lxml_5etree_makeSubElement)(struct LxmlElement *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *) = 0;
+#define makeSubElement __pyx_api_f_4lxml_5etree_makeSubElement
+static void (*__pyx_api_f_4lxml_5etree_setElementClassLookupFunction)(_element_class_lookup_function, PyObject *) = 0;
+#define setElementClassLookupFunction __pyx_api_f_4lxml_5etree_setElementClassLookupFunction
+static PyObject *(*__pyx_api_f_4lxml_5etree_lookupDefaultElementClass)(PyObject *, PyObject *, xmlNode *) = 0;
+#define lookupDefaultElementClass __pyx_api_f_4lxml_5etree_lookupDefaultElementClass
+static PyObject *(*__pyx_api_f_4lxml_5etree_lookupNamespaceElementClass)(PyObject *, PyObject *, xmlNode *) = 0;
+#define lookupNamespaceElementClass __pyx_api_f_4lxml_5etree_lookupNamespaceElementClass
+static PyObject *(*__pyx_api_f_4lxml_5etree_callLookupFallback)(struct LxmlFallbackElementClassLookup *, struct LxmlDocument *, xmlNode *) = 0;
+#define callLookupFallback __pyx_api_f_4lxml_5etree_callLookupFallback
+static int (*__pyx_api_f_4lxml_5etree_tagMatches)(xmlNode *, const xmlChar *, const xmlChar *) = 0;
+#define tagMatches __pyx_api_f_4lxml_5etree_tagMatches
+static struct LxmlDocument *(*__pyx_api_f_4lxml_5etree_documentOrRaise)(PyObject *) = 0;
+#define documentOrRaise __pyx_api_f_4lxml_5etree_documentOrRaise
+static struct LxmlElement *(*__pyx_api_f_4lxml_5etree_rootNodeOrRaise)(PyObject *) = 0;
+#define rootNodeOrRaise __pyx_api_f_4lxml_5etree_rootNodeOrRaise
+static int (*__pyx_api_f_4lxml_5etree_hasText)(xmlNode *) = 0;
+#define hasText __pyx_api_f_4lxml_5etree_hasText
+static int (*__pyx_api_f_4lxml_5etree_hasTail)(xmlNode *) = 0;
+#define hasTail __pyx_api_f_4lxml_5etree_hasTail
+static PyObject *(*__pyx_api_f_4lxml_5etree_textOf)(xmlNode *) = 0;
+#define textOf __pyx_api_f_4lxml_5etree_textOf
+static PyObject *(*__pyx_api_f_4lxml_5etree_tailOf)(xmlNode *) = 0;
+#define tailOf __pyx_api_f_4lxml_5etree_tailOf
+static int (*__pyx_api_f_4lxml_5etree_setNodeText)(xmlNode *, PyObject *) = 0;
+#define setNodeText __pyx_api_f_4lxml_5etree_setNodeText
+static int (*__pyx_api_f_4lxml_5etree_setTailText)(xmlNode *, PyObject *) = 0;
+#define setTailText __pyx_api_f_4lxml_5etree_setTailText
+static PyObject *(*__pyx_api_f_4lxml_5etree_attributeValue)(xmlNode *, xmlAttr *) = 0;
+#define attributeValue __pyx_api_f_4lxml_5etree_attributeValue
+static PyObject *(*__pyx_api_f_4lxml_5etree_attributeValueFromNsName)(xmlNode *, const xmlChar *, const xmlChar *) = 0;
+#define attributeValueFromNsName __pyx_api_f_4lxml_5etree_attributeValueFromNsName
+static PyObject *(*__pyx_api_f_4lxml_5etree_getAttributeValue)(struct LxmlElement *, PyObject *, PyObject *) = 0;
+#define getAttributeValue __pyx_api_f_4lxml_5etree_getAttributeValue
+static PyObject *(*__pyx_api_f_4lxml_5etree_iterattributes)(struct LxmlElement *, int) = 0;
+#define iterattributes __pyx_api_f_4lxml_5etree_iterattributes
+static PyObject *(*__pyx_api_f_4lxml_5etree_collectAttributes)(xmlNode *, int) = 0;
+#define collectAttributes __pyx_api_f_4lxml_5etree_collectAttributes
+static int (*__pyx_api_f_4lxml_5etree_setAttributeValue)(struct LxmlElement *, PyObject *, PyObject *) = 0;
+#define setAttributeValue __pyx_api_f_4lxml_5etree_setAttributeValue
+static int (*__pyx_api_f_4lxml_5etree_delAttribute)(struct LxmlElement *, PyObject *) = 0;
+#define delAttribute __pyx_api_f_4lxml_5etree_delAttribute
+static int (*__pyx_api_f_4lxml_5etree_delAttributeFromNsName)(xmlNode *, const xmlChar *, const xmlChar *) = 0;
+#define delAttributeFromNsName __pyx_api_f_4lxml_5etree_delAttributeFromNsName
+static int (*__pyx_api_f_4lxml_5etree_hasChild)(xmlNode *) = 0;
+#define hasChild __pyx_api_f_4lxml_5etree_hasChild
+static xmlNode *(*__pyx_api_f_4lxml_5etree_findChild)(xmlNode *, Py_ssize_t) = 0;
+#define findChild __pyx_api_f_4lxml_5etree_findChild
+static xmlNode *(*__pyx_api_f_4lxml_5etree_findChildForwards)(xmlNode *, Py_ssize_t) = 0;
+#define findChildForwards __pyx_api_f_4lxml_5etree_findChildForwards
+static xmlNode *(*__pyx_api_f_4lxml_5etree_findChildBackwards)(xmlNode *, Py_ssize_t) = 0;
+#define findChildBackwards __pyx_api_f_4lxml_5etree_findChildBackwards
+static xmlNode *(*__pyx_api_f_4lxml_5etree_nextElement)(xmlNode *) = 0;
+#define nextElement __pyx_api_f_4lxml_5etree_nextElement
+static xmlNode *(*__pyx_api_f_4lxml_5etree_previousElement)(xmlNode *) = 0;
+#define previousElement __pyx_api_f_4lxml_5etree_previousElement
+static void (*__pyx_api_f_4lxml_5etree_appendChild)(struct LxmlElement *, struct LxmlElement *) = 0;
+#define appendChild __pyx_api_f_4lxml_5etree_appendChild
+static int (*__pyx_api_f_4lxml_5etree_appendChildToElement)(struct LxmlElement *, struct LxmlElement *) = 0;
+#define appendChildToElement __pyx_api_f_4lxml_5etree_appendChildToElement
+static PyObject *(*__pyx_api_f_4lxml_5etree_pyunicode)(const xmlChar *) = 0;
+#define pyunicode __pyx_api_f_4lxml_5etree_pyunicode
+static PyObject *(*__pyx_api_f_4lxml_5etree_utf8)(PyObject *) = 0;
+#define utf8 __pyx_api_f_4lxml_5etree_utf8
+static PyObject *(*__pyx_api_f_4lxml_5etree_getNsTag)(PyObject *) = 0;
+#define getNsTag __pyx_api_f_4lxml_5etree_getNsTag
+static PyObject *(*__pyx_api_f_4lxml_5etree_getNsTagWithEmptyNs)(PyObject *) = 0;
+#define getNsTagWithEmptyNs __pyx_api_f_4lxml_5etree_getNsTagWithEmptyNs
+static PyObject *(*__pyx_api_f_4lxml_5etree_namespacedName)(xmlNode *) = 0;
+#define namespacedName __pyx_api_f_4lxml_5etree_namespacedName
+static PyObject *(*__pyx_api_f_4lxml_5etree_namespacedNameFromNsName)(const xmlChar *, const xmlChar *) = 0;
+#define namespacedNameFromNsName __pyx_api_f_4lxml_5etree_namespacedNameFromNsName
+static void (*__pyx_api_f_4lxml_5etree_iteratorStoreNext)(struct LxmlElementIterator *, struct LxmlElement *) = 0;
+#define iteratorStoreNext __pyx_api_f_4lxml_5etree_iteratorStoreNext
+static void (*__pyx_api_f_4lxml_5etree_initTagMatch)(struct LxmlElementTagMatcher *, PyObject *) = 0;
+#define initTagMatch __pyx_api_f_4lxml_5etree_initTagMatch
+static xmlNs *(*__pyx_api_f_4lxml_5etree_findOrBuildNodeNsPrefix)(struct LxmlDocument *, xmlNode *, const xmlChar *, const xmlChar *) = 0;
+#define findOrBuildNodeNsPrefix __pyx_api_f_4lxml_5etree_findOrBuildNodeNsPrefix
+#ifndef __PYX_HAVE_RT_ImportFunction_3_0_11
+#define __PYX_HAVE_RT_ImportFunction_3_0_11
+static int __Pyx_ImportFunction_3_0_11(PyObject *module, const char *funcname, void (**f)(void), const char *sig) {
+    PyObject *d = 0;
+    PyObject *cobj = 0;
+    union {
+        void (*fp)(void);
+        void *p;
+    } tmp;
+    d = PyObject_GetAttrString(module, (char *)"__pyx_capi__");
+    if (!d)
+        goto bad;
+    cobj = PyDict_GetItemString(d, funcname);
+    if (!cobj) {
+        PyErr_Format(PyExc_ImportError,
+            "%.200s does not export expected C function %.200s",
+                PyModule_GetName(module), funcname);
+        goto bad;
+    }
+    if (!PyCapsule_IsValid(cobj, sig)) {
+        PyErr_Format(PyExc_TypeError,
+            "C function %.200s.%.200s has wrong signature (expected %.500s, got %.500s)",
+             PyModule_GetName(module), funcname, sig, PyCapsule_GetName(cobj));
+        goto bad;
+    }
+    tmp.p = PyCapsule_GetPointer(cobj, sig);
+    *f = tmp.fp;
+    if (!(*f))
+        goto bad;
+    Py_DECREF(d);
+    return 0;
+bad:
+    Py_XDECREF(d);
+    return -1;
+}
+#endif
+
+
+static int import_lxml__etree(void) {
+  PyObject *module = 0;
+  module = PyImport_ImportModule("lxml.etree");
+  if (!module) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "deepcopyNodeToDocument", (void (**)(void))&__pyx_api_f_4lxml_5etree_deepcopyNodeToDocument, "struct LxmlElement *(struct LxmlDocument *, xmlNode *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "elementTreeFactory", (void (**)(void))&__pyx_api_f_4lxml_5etree_elementTreeFactory, "struct LxmlElementTree *(struct LxmlElement *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "newElementTree", (void (**)(void))&__pyx_api_f_4lxml_5etree_newElementTree, "struct LxmlElementTree *(struct LxmlElement *, PyObject *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "adoptExternalDocument", (void (**)(void))&__pyx_api_f_4lxml_5etree_adoptExternalDocument, "struct LxmlElementTree *(xmlDoc *, PyObject *, int)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "elementFactory", (void (**)(void))&__pyx_api_f_4lxml_5etree_elementFactory, "struct LxmlElement *(struct LxmlDocument *, xmlNode *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "makeElement", (void (**)(void))&__pyx_api_f_4lxml_5etree_makeElement, "struct LxmlElement *(PyObject *, struct LxmlDocument *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "makeSubElement", (void (**)(void))&__pyx_api_f_4lxml_5etree_makeSubElement, "struct LxmlElement *(struct LxmlElement *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "setElementClassLookupFunction", (void (**)(void))&__pyx_api_f_4lxml_5etree_setElementClassLookupFunction, "void (_element_class_lookup_function, PyObject *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "lookupDefaultElementClass", (void (**)(void))&__pyx_api_f_4lxml_5etree_lookupDefaultElementClass, "PyObject *(PyObject *, PyObject *, xmlNode *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "lookupNamespaceElementClass", (void (**)(void))&__pyx_api_f_4lxml_5etree_lookupNamespaceElementClass, "PyObject *(PyObject *, PyObject *, xmlNode *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "callLookupFallback", (void (**)(void))&__pyx_api_f_4lxml_5etree_callLookupFallback, "PyObject *(struct LxmlFallbackElementClassLookup *, struct LxmlDocument *, xmlNode *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "tagMatches", (void (**)(void))&__pyx_api_f_4lxml_5etree_tagMatches, "int (xmlNode *, const xmlChar *, const xmlChar *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "documentOrRaise", (void (**)(void))&__pyx_api_f_4lxml_5etree_documentOrRaise, "struct LxmlDocument *(PyObject *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "rootNodeOrRaise", (void (**)(void))&__pyx_api_f_4lxml_5etree_rootNodeOrRaise, "struct LxmlElement *(PyObject *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "hasText", (void (**)(void))&__pyx_api_f_4lxml_5etree_hasText, "int (xmlNode *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "hasTail", (void (**)(void))&__pyx_api_f_4lxml_5etree_hasTail, "int (xmlNode *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "textOf", (void (**)(void))&__pyx_api_f_4lxml_5etree_textOf, "PyObject *(xmlNode *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "tailOf", (void (**)(void))&__pyx_api_f_4lxml_5etree_tailOf, "PyObject *(xmlNode *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "setNodeText", (void (**)(void))&__pyx_api_f_4lxml_5etree_setNodeText, "int (xmlNode *, PyObject *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "setTailText", (void (**)(void))&__pyx_api_f_4lxml_5etree_setTailText, "int (xmlNode *, PyObject *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "attributeValue", (void (**)(void))&__pyx_api_f_4lxml_5etree_attributeValue, "PyObject *(xmlNode *, xmlAttr *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "attributeValueFromNsName", (void (**)(void))&__pyx_api_f_4lxml_5etree_attributeValueFromNsName, "PyObject *(xmlNode *, const xmlChar *, const xmlChar *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "getAttributeValue", (void (**)(void))&__pyx_api_f_4lxml_5etree_getAttributeValue, "PyObject *(struct LxmlElement *, PyObject *, PyObject *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "iterattributes", (void (**)(void))&__pyx_api_f_4lxml_5etree_iterattributes, "PyObject *(struct LxmlElement *, int)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "collectAttributes", (void (**)(void))&__pyx_api_f_4lxml_5etree_collectAttributes, "PyObject *(xmlNode *, int)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "setAttributeValue", (void (**)(void))&__pyx_api_f_4lxml_5etree_setAttributeValue, "int (struct LxmlElement *, PyObject *, PyObject *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "delAttribute", (void (**)(void))&__pyx_api_f_4lxml_5etree_delAttribute, "int (struct LxmlElement *, PyObject *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "delAttributeFromNsName", (void (**)(void))&__pyx_api_f_4lxml_5etree_delAttributeFromNsName, "int (xmlNode *, const xmlChar *, const xmlChar *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "hasChild", (void (**)(void))&__pyx_api_f_4lxml_5etree_hasChild, "int (xmlNode *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "findChild", (void (**)(void))&__pyx_api_f_4lxml_5etree_findChild, "xmlNode *(xmlNode *, Py_ssize_t)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "findChildForwards", (void (**)(void))&__pyx_api_f_4lxml_5etree_findChildForwards, "xmlNode *(xmlNode *, Py_ssize_t)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "findChildBackwards", (void (**)(void))&__pyx_api_f_4lxml_5etree_findChildBackwards, "xmlNode *(xmlNode *, Py_ssize_t)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "nextElement", (void (**)(void))&__pyx_api_f_4lxml_5etree_nextElement, "xmlNode *(xmlNode *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "previousElement", (void (**)(void))&__pyx_api_f_4lxml_5etree_previousElement, "xmlNode *(xmlNode *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "appendChild", (void (**)(void))&__pyx_api_f_4lxml_5etree_appendChild, "void (struct LxmlElement *, struct LxmlElement *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "appendChildToElement", (void (**)(void))&__pyx_api_f_4lxml_5etree_appendChildToElement, "int (struct LxmlElement *, struct LxmlElement *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "pyunicode", (void (**)(void))&__pyx_api_f_4lxml_5etree_pyunicode, "PyObject *(const xmlChar *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "utf8", (void (**)(void))&__pyx_api_f_4lxml_5etree_utf8, "PyObject *(PyObject *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "getNsTag", (void (**)(void))&__pyx_api_f_4lxml_5etree_getNsTag, "PyObject *(PyObject *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "getNsTagWithEmptyNs", (void (**)(void))&__pyx_api_f_4lxml_5etree_getNsTagWithEmptyNs, "PyObject *(PyObject *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "namespacedName", (void (**)(void))&__pyx_api_f_4lxml_5etree_namespacedName, "PyObject *(xmlNode *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "namespacedNameFromNsName", (void (**)(void))&__pyx_api_f_4lxml_5etree_namespacedNameFromNsName, "PyObject *(const xmlChar *, const xmlChar *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "iteratorStoreNext", (void (**)(void))&__pyx_api_f_4lxml_5etree_iteratorStoreNext, "void (struct LxmlElementIterator *, struct LxmlElement *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "initTagMatch", (void (**)(void))&__pyx_api_f_4lxml_5etree_initTagMatch, "void (struct LxmlElementTagMatcher *, PyObject *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "findOrBuildNodeNsPrefix", (void (**)(void))&__pyx_api_f_4lxml_5etree_findOrBuildNodeNsPrefix, "xmlNs *(struct LxmlDocument *, xmlNode *, const xmlChar *, const xmlChar *)") < 0) goto bad;
+  Py_DECREF(module); module = 0;
+  return 0;
+  bad:
+  Py_XDECREF(module);
+  return -1;
+}
+
+#endif /* !__PYX_HAVE_API__lxml__etree */
diff --git a/.venv/lib/python3.12/site-packages/lxml/extensions.pxi b/.venv/lib/python3.12/site-packages/lxml/extensions.pxi
new file mode 100644
index 00000000..2a2c94ec
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/extensions.pxi
@@ -0,0 +1,833 @@
+# support for extension functions in XPath and XSLT
+
+cdef class XPathError(LxmlError):
+    """Base class of all XPath errors.
+    """
+
+cdef class XPathEvalError(XPathError):
+    """Error during XPath evaluation.
+    """
+
+cdef class XPathFunctionError(XPathEvalError):
+    """Internal error looking up an XPath extension function.
+    """
+
+cdef class XPathResultError(XPathEvalError):
+    """Error handling an XPath result.
+    """
+
+
+# forward declarations
+
+ctypedef int (*_register_function)(void* ctxt, name_utf, ns_uri_utf)
+cdef class _ExsltRegExp
+
+################################################################################
+# Base class for XSLT and XPath evaluation contexts: functions, namespaces, ...
+
+@cython.internal
+cdef class _BaseContext:
+    cdef xpath.xmlXPathContext* _xpathCtxt
+    cdef _Document _doc
+    cdef dict _extensions
+    cdef list _namespaces
+    cdef list _global_namespaces
+    cdef dict _utf_refs
+    cdef dict _function_cache
+    cdef dict _eval_context_dict
+    cdef bint _build_smart_strings
+    # for exception handling and temporary reference keeping:
+    cdef _TempStore _temp_refs
+    cdef set _temp_documents
+    cdef _ExceptionContext _exc
+    cdef _ErrorLog _error_log
+
+    def __cinit__(self):
+        self._xpathCtxt = NULL
+
+    def __init__(self, namespaces, extensions, error_log, enable_regexp,
+                 build_smart_strings):
+        cdef _ExsltRegExp _regexp 
+        cdef dict new_extensions
+        cdef list ns
+        self._utf_refs = {}
+        self._global_namespaces = []
+        self._function_cache = {}
+        self._eval_context_dict = None
+        self._error_log = error_log
+
+        if extensions is not None:
+            # convert extensions to UTF-8
+            if isinstance(extensions, dict):
+                extensions = (extensions,)
+            # format: [ {(ns, name):function} ] -> {(ns_utf, name_utf):function}
+            new_extensions = {}
+            for extension in extensions:
+                for (ns_uri, name), function in extension.items():
+                    if name is None:
+                        raise ValueError, "extensions must have non empty names"
+                    ns_utf   = self._to_utf(ns_uri)
+                    name_utf = self._to_utf(name)
+                    new_extensions[(ns_utf, name_utf)] = function
+            extensions = new_extensions or None
+
+        if namespaces is not None:
+            if isinstance(namespaces, dict):
+                namespaces = namespaces.items()
+            if namespaces:
+                ns = []
+                for prefix, ns_uri in namespaces:
+                    if prefix is None or not prefix:
+                        raise TypeError, \
+                            "empty namespace prefix is not supported in XPath"
+                    if ns_uri is None or not ns_uri:
+                        raise TypeError, \
+                            "setting default namespace is not supported in XPath"
+                    prefix_utf = self._to_utf(prefix)
+                    ns_uri_utf = self._to_utf(ns_uri)
+                    ns.append( (prefix_utf, ns_uri_utf) )
+                namespaces = ns
+            else:
+                namespaces = None
+
+        self._doc        = None
+        self._exc        = _ExceptionContext()
+        self._extensions = extensions
+        self._namespaces = namespaces
+        self._temp_refs  = _TempStore()
+        self._temp_documents  = set()
+        self._build_smart_strings = build_smart_strings
+
+        if enable_regexp:
+            _regexp = _ExsltRegExp()
+            _regexp._register_in_context(self)
+
+    cdef _BaseContext _copy(self):
+        cdef _BaseContext context
+        if self._namespaces is not None:
+            namespaces = self._namespaces[:]
+        else:
+            namespaces = None
+        context = self.__class__(namespaces, None, self._error_log, False,
+                                 self._build_smart_strings)
+        if self._extensions is not None:
+            context._extensions = self._extensions.copy()
+        return context
+
+    cdef bytes _to_utf(self, s):
+        "Convert to UTF-8 and keep a reference to the encoded string"
+        cdef python.PyObject* dict_result
+        if s is None:
+            return None
+        dict_result = python.PyDict_GetItem(self._utf_refs, s)
+        if dict_result is not NULL:
+            return <bytes>dict_result
+        utf = _utf8(s)
+        self._utf_refs[s] = utf
+        if python.IS_PYPY:
+            # use C level refs, PyPy refs are not enough!
+            python.Py_INCREF(utf)
+        return utf
+
+    cdef void _set_xpath_context(self, xpath.xmlXPathContext* xpathCtxt) noexcept:
+        self._xpathCtxt = xpathCtxt
+        xpathCtxt.userData = <void*>self
+        # Need a cast here because older libxml2 releases do not use 'const' in the functype.
+        xpathCtxt.error = <xmlerror.xmlStructuredErrorFunc> _receiveXPathError
+
+    @cython.final
+    cdef _register_context(self, _Document doc):
+        self._doc = doc
+        self._exc.clear()
+
+    @cython.final
+    cdef _cleanup_context(self):
+        #xpath.xmlXPathRegisteredNsCleanup(self._xpathCtxt)
+        #self.unregisterGlobalNamespaces()
+        if python.IS_PYPY:
+            # clean up double refs in PyPy (see "_to_utf()" method)
+            for ref in self._utf_refs.itervalues():
+                python.Py_DECREF(ref)
+        self._utf_refs.clear()
+        self._eval_context_dict = None
+        self._doc = None
+
+    @cython.final
+    cdef _release_context(self):
+        if self._xpathCtxt is not NULL:
+            self._xpathCtxt.userData = NULL
+            self._xpathCtxt = NULL
+
+    # namespaces (internal UTF-8 methods with leading '_')
+
+    cdef addNamespace(self, prefix, ns_uri):
+        cdef list namespaces
+        if prefix is None:
+            raise TypeError, "empty prefix is not supported in XPath"
+        prefix_utf = self._to_utf(prefix)
+        ns_uri_utf = self._to_utf(ns_uri)
+        new_item = (prefix_utf, ns_uri_utf)
+        if self._namespaces is None:
+            self._namespaces = [new_item]
+        else:
+            namespaces = []
+            for item in self._namespaces:
+                if item[0] == prefix_utf:
+                    item = new_item
+                    new_item = None
+                namespaces.append(item)
+            if new_item is not None:
+                namespaces.append(new_item)
+            self._namespaces = namespaces
+        if self._xpathCtxt is not NULL:
+            xpath.xmlXPathRegisterNs(
+                self._xpathCtxt, _xcstr(prefix_utf), _xcstr(ns_uri_utf))
+
+    cdef registerNamespace(self, prefix, ns_uri):
+        if prefix is None:
+            raise TypeError, "empty prefix is not supported in XPath"
+        prefix_utf = self._to_utf(prefix)
+        ns_uri_utf = self._to_utf(ns_uri)
+        self._global_namespaces.append(prefix_utf)
+        xpath.xmlXPathRegisterNs(self._xpathCtxt,
+                                 _xcstr(prefix_utf), _xcstr(ns_uri_utf))
+
+    cdef registerLocalNamespaces(self):
+        if self._namespaces is None:
+            return
+        for prefix_utf, ns_uri_utf in self._namespaces:
+            xpath.xmlXPathRegisterNs(
+                self._xpathCtxt, _xcstr(prefix_utf), _xcstr(ns_uri_utf))
+
+    cdef registerGlobalNamespaces(self):
+        cdef list ns_prefixes = _find_all_extension_prefixes()
+        if python.PyList_GET_SIZE(ns_prefixes) > 0:
+            for prefix_utf, ns_uri_utf in ns_prefixes:
+                self._global_namespaces.append(prefix_utf)
+                xpath.xmlXPathRegisterNs(
+                    self._xpathCtxt, _xcstr(prefix_utf), _xcstr(ns_uri_utf))
+
+    cdef unregisterGlobalNamespaces(self):
+        if python.PyList_GET_SIZE(self._global_namespaces) > 0:
+            for prefix_utf in self._global_namespaces:
+                xpath.xmlXPathRegisterNs(self._xpathCtxt,
+                                         _xcstr(prefix_utf), NULL)
+            del self._global_namespaces[:]
+    
+    cdef void _unregisterNamespace(self, prefix_utf) noexcept:
+        xpath.xmlXPathRegisterNs(self._xpathCtxt,
+                                 _xcstr(prefix_utf), NULL)
+    
+    # extension functions
+
+    cdef int _addLocalExtensionFunction(self, ns_utf, name_utf, function) except -1:
+        if self._extensions is None:
+            self._extensions = {}
+        self._extensions[(ns_utf, name_utf)] = function
+        return 0
+
+    cdef registerGlobalFunctions(self, void* ctxt,
+                                 _register_function reg_func):
+        cdef python.PyObject* dict_result
+        cdef dict d
+        for ns_utf, ns_functions in __FUNCTION_NAMESPACE_REGISTRIES.iteritems():
+            dict_result = python.PyDict_GetItem(
+                self._function_cache, ns_utf)
+            if dict_result is not NULL:
+                d = <dict>dict_result
+            else:
+                d = {}
+                self._function_cache[ns_utf] = d
+            for name_utf, function in ns_functions.iteritems():
+                d[name_utf] = function
+                reg_func(ctxt, name_utf, ns_utf)
+
+    cdef registerLocalFunctions(self, void* ctxt,
+                                _register_function reg_func):
+        cdef python.PyObject* dict_result
+        cdef dict d
+        if self._extensions is None:
+            return # done
+        last_ns = None
+        d = None
+        for (ns_utf, name_utf), function in self._extensions.iteritems():
+            if ns_utf is not last_ns or d is None:
+                last_ns = ns_utf
+                dict_result = python.PyDict_GetItem(
+                    self._function_cache, ns_utf)
+                if dict_result is not NULL:
+                    d = <dict>dict_result
+                else:
+                    d = {}
+                    self._function_cache[ns_utf] = d
+            d[name_utf] = function
+            reg_func(ctxt, name_utf, ns_utf)
+
+    cdef unregisterAllFunctions(self, void* ctxt,
+                                      _register_function unreg_func):
+        for ns_utf, functions in self._function_cache.iteritems():
+            for name_utf in functions:
+                unreg_func(ctxt, name_utf, ns_utf)
+
+    cdef unregisterGlobalFunctions(self, void* ctxt,
+                                         _register_function unreg_func):
+        for ns_utf, functions in self._function_cache.items():
+            for name_utf in functions:
+                if self._extensions is None or \
+                       (ns_utf, name_utf) not in self._extensions:
+                    unreg_func(ctxt, name_utf, ns_utf)
+
+    @cython.final
+    cdef _find_cached_function(self, const_xmlChar* c_ns_uri, const_xmlChar* c_name):
+        """Lookup an extension function in the cache and return it.
+
+        Parameters: c_ns_uri may be NULL, c_name must not be NULL
+        """
+        cdef python.PyObject* c_dict
+        cdef python.PyObject* dict_result
+        c_dict = python.PyDict_GetItem(
+            self._function_cache, None if c_ns_uri is NULL else c_ns_uri)
+        if c_dict is not NULL:
+            dict_result = python.PyDict_GetItem(
+                <object>c_dict, <unsigned char*>c_name)
+            if dict_result is not NULL:
+                return <object>dict_result
+        return None
+
+    # Python access to the XPath context for extension functions
+
+    @property
+    def context_node(self):
+        cdef xmlNode* c_node
+        if self._xpathCtxt is NULL:
+            raise XPathError, \
+                "XPath context is only usable during the evaluation"
+        c_node = self._xpathCtxt.node
+        if c_node is NULL:
+            raise XPathError, "no context node"
+        if c_node.doc != self._xpathCtxt.doc:
+            raise XPathError, \
+                "document-external context nodes are not supported"
+        if self._doc is None:
+            raise XPathError, "document context is missing"
+        return _elementFactory(self._doc, c_node)
+
+    @property
+    def eval_context(self):
+        if self._eval_context_dict is None:
+            self._eval_context_dict = {}
+        return self._eval_context_dict
+
+    # Python reference keeping during XPath function evaluation
+
+    @cython.final
+    cdef _release_temp_refs(self):
+        "Free temporarily referenced objects from this context."
+        self._temp_refs.clear()
+        self._temp_documents.clear()
+
+    @cython.final
+    cdef _hold(self, obj):
+        """A way to temporarily hold references to nodes in the evaluator.
+
+        This is needed because otherwise nodes created in XPath extension
+        functions would be reference counted too soon, during the XPath
+        evaluation.  This is most important in the case of exceptions.
+        """
+        cdef _Element element
+        if isinstance(obj, _Element):
+            self._temp_refs.add(obj)
+            self._temp_documents.add((<_Element>obj)._doc)
+            return
+        elif _isString(obj) or not python.PySequence_Check(obj):
+            return
+        for o in obj:
+            if isinstance(o, _Element):
+                #print "Holding element:", <int>element._c_node
+                self._temp_refs.add(o)
+                #print "Holding document:", <int>element._doc._c_doc
+                self._temp_documents.add((<_Element>o)._doc)
+
+    @cython.final
+    cdef _Document _findDocumentForNode(self, xmlNode* c_node):
+        """If an XPath expression returns an element from a different
+        document than the current context document, we call this to
+        see if it was possibly created by an extension and is a known
+        document instance.
+        """
+        cdef _Document doc
+        for doc in self._temp_documents:
+            if doc is not None and doc._c_doc is c_node.doc:
+                return doc
+        return None
+
+
+# libxml2 keeps these error messages in a static array in its code
+# and doesn't give us access to them ...
+
+cdef tuple LIBXML2_XPATH_ERROR_MESSAGES = (
+    b"Ok",
+    b"Number encoding",
+    b"Unfinished literal",
+    b"Start of literal",
+    b"Expected $ for variable reference",
+    b"Undefined variable",
+    b"Invalid predicate",
+    b"Invalid expression",
+    b"Missing closing curly brace",
+    b"Unregistered function",
+    b"Invalid operand",
+    b"Invalid type",
+    b"Invalid number of arguments",
+    b"Invalid context size",
+    b"Invalid context position",
+    b"Memory allocation error",
+    b"Syntax error",
+    b"Resource error",
+    b"Sub resource error",
+    b"Undefined namespace prefix",
+    b"Encoding error",
+    b"Char out of XML range",
+    b"Invalid or incomplete context",
+    b"Stack usage error",
+    b"Forbidden variable\n",
+    b"?? Unknown error ??\n",
+)
+
+cdef void _forwardXPathError(void* c_ctxt, const xmlerror.xmlError* c_error) noexcept with gil:
+    cdef xmlerror.xmlError error
+    cdef int xpath_code
+    if c_error.message is not NULL:
+        error.message = c_error.message
+    else:
+        xpath_code = c_error.code - xmlerror.XML_XPATH_EXPRESSION_OK
+        if 0 <= xpath_code < len(LIBXML2_XPATH_ERROR_MESSAGES):
+            error.message = _cstr(LIBXML2_XPATH_ERROR_MESSAGES[xpath_code])
+        else:
+            error.message = b"unknown error"
+    error.domain = c_error.domain
+    error.code = c_error.code
+    error.level = c_error.level
+    error.line = c_error.line
+    error.int2 = c_error.int1 # column
+    error.file = c_error.file
+    error.node = NULL
+
+    (<_BaseContext>c_ctxt)._error_log._receive(&error)
+
+cdef void _receiveXPathError(void* c_context, const xmlerror.xmlError* error) noexcept nogil:
+    if not __DEBUG:
+        return
+    if c_context is NULL:
+        _forwardError(NULL, error)
+    else:
+        _forwardXPathError(c_context, error)
+
+
+def Extension(module, function_mapping=None, *, ns=None):
+    """Extension(module, function_mapping=None, ns=None)
+
+    Build a dictionary of extension functions from the functions
+    defined in a module or the methods of an object.
+
+    As second argument, you can pass an additional mapping of
+    attribute names to XPath function names, or a list of function
+    names that should be taken.
+
+    The ``ns`` keyword argument accepts a namespace URI for the XPath
+    functions.
+    """
+    cdef dict functions = {}
+    if isinstance(function_mapping, dict):
+        for function_name, xpath_name in function_mapping.items():
+            functions[(ns, xpath_name)] = getattr(module, function_name)
+    else:
+        if function_mapping is None:
+            function_mapping = [ name for name in dir(module)
+                                 if not name.startswith('_') ]
+        for function_name in function_mapping:
+            functions[(ns, function_name)] = getattr(module, function_name)
+    return functions
+
+################################################################################
+# EXSLT regexp implementation
+
+@cython.final
+@cython.internal
+cdef class _ExsltRegExp:
+    cdef dict _compile_map
+    def __cinit__(self):
+        self._compile_map = {}
+
+    cdef _make_string(self, value):
+        if _isString(value):
+            return value
+        elif isinstance(value, list):
+            # node set: take recursive text concatenation of first element
+            if python.PyList_GET_SIZE(value) == 0:
+                return ''
+            firstnode = value[0]
+            if _isString(firstnode):
+                return firstnode
+            elif isinstance(firstnode, _Element):
+                c_text = tree.xmlNodeGetContent((<_Element>firstnode)._c_node)
+                if c_text is NULL:
+                    raise MemoryError()
+                try:
+                    return funicode(c_text)
+                finally:
+                    tree.xmlFree(c_text)
+            else:
+                return unicode(firstnode)
+        else:
+            return unicode(value)
+
+    cdef _compile(self, rexp, ignore_case):
+        cdef python.PyObject* c_result
+        rexp = self._make_string(rexp)
+        key = (rexp, ignore_case)
+        c_result = python.PyDict_GetItem(self._compile_map, key)
+        if c_result is not NULL:
+            return <object>c_result
+        py_flags = re.UNICODE
+        if ignore_case:
+            py_flags = py_flags | re.IGNORECASE
+        rexp_compiled = re.compile(rexp, py_flags)
+        self._compile_map[key] = rexp_compiled
+        return rexp_compiled
+
+    def test(self, ctxt, s, rexp, flags=''):
+        flags = self._make_string(flags)
+        s = self._make_string(s)
+        rexpc = self._compile(rexp, 'i' in flags)
+        if rexpc.search(s) is None:
+            return False
+        else:
+            return True
+
+    def match(self, ctxt, s, rexp, flags=''):
+        cdef list result_list
+        flags = self._make_string(flags)
+        s = self._make_string(s)
+        rexpc = self._compile(rexp, 'i' in flags)
+        if 'g' in flags:
+            results = rexpc.findall(s)
+            if not results:
+                return ()
+        else:
+            result = rexpc.search(s)
+            if not result:
+                return ()
+            results = [ result.group() ]
+            results.extend( result.groups('') )
+        result_list = []
+        root = Element('matches')
+        for s_match in results:
+            if python.PyTuple_CheckExact(s_match):
+                s_match = ''.join(s_match)
+            elem = SubElement(root, 'match')
+            elem.text = s_match
+            result_list.append(elem)
+        return result_list
+
+    def replace(self, ctxt, s, rexp, flags, replacement):
+        replacement = self._make_string(replacement)
+        flags = self._make_string(flags)
+        s = self._make_string(s)
+        rexpc = self._compile(rexp, 'i' in flags)
+        count: object = 0 if 'g' in flags else 1
+        return rexpc.sub(replacement, s, count)
+
+    cdef _register_in_context(self, _BaseContext context):
+        ns = b"http://exslt.org/regular-expressions"
+        context._addLocalExtensionFunction(ns, b"test",    self.test)
+        context._addLocalExtensionFunction(ns, b"match",   self.match)
+        context._addLocalExtensionFunction(ns, b"replace", self.replace)
+
+
+################################################################################
+# helper functions
+
+cdef xpath.xmlXPathObject* _wrapXPathObject(object obj, _Document doc,
+                                            _BaseContext context) except NULL:
+    cdef xpath.xmlNodeSet* resultSet
+    cdef _Element fake_node = None
+    cdef xmlNode* c_node
+
+    if isinstance(obj, unicode):
+        obj = _utf8(obj)
+    if isinstance(obj, bytes):
+        # libxml2 copies the string value
+        return xpath.xmlXPathNewCString(_cstr(obj))
+    if isinstance(obj, bool):
+        return xpath.xmlXPathNewBoolean(obj)
+    if python.PyNumber_Check(obj):
+        return xpath.xmlXPathNewFloat(obj)
+    if obj is None:
+        resultSet = xpath.xmlXPathNodeSetCreate(NULL)
+    elif isinstance(obj, _Element):
+        resultSet = xpath.xmlXPathNodeSetCreate((<_Element>obj)._c_node)
+    elif python.PySequence_Check(obj):
+        resultSet = xpath.xmlXPathNodeSetCreate(NULL)
+        try:
+            for value in obj:
+                if isinstance(value, _Element):
+                    if context is not None:
+                        context._hold(value)
+                    xpath.xmlXPathNodeSetAdd(resultSet, (<_Element>value)._c_node)
+                else:
+                    if context is None or doc is None:
+                        raise XPathResultError, \
+                              f"Non-Element values not supported at this point - got {value!r}"
+                    # support strings by appending text nodes to an Element
+                    if isinstance(value, unicode):
+                        value = _utf8(value)
+                    if isinstance(value, bytes):
+                        if fake_node is None:
+                            fake_node = _makeElement("text-root", NULL, doc, None,
+                                                     None, None, None, None, None)
+                            context._hold(fake_node)
+                        else:
+                            # append a comment node to keep the text nodes separate
+                            c_node = tree.xmlNewDocComment(doc._c_doc, <unsigned char*>"")
+                            if c_node is NULL:
+                                raise MemoryError()
+                            tree.xmlAddChild(fake_node._c_node, c_node)
+                        context._hold(value)
+                        c_node = tree.xmlNewDocText(doc._c_doc, _xcstr(value))
+                        if c_node is NULL:
+                            raise MemoryError()
+                        tree.xmlAddChild(fake_node._c_node, c_node)
+                        xpath.xmlXPathNodeSetAdd(resultSet, c_node)
+                    else:
+                        raise XPathResultError, \
+                              f"This is not a supported node-set result: {value!r}"
+        except:
+            xpath.xmlXPathFreeNodeSet(resultSet)
+            raise
+    else:
+        raise XPathResultError, f"Unknown return type: {python._fqtypename(obj).decode('utf8')}"
+    return xpath.xmlXPathWrapNodeSet(resultSet)
+
+cdef object _unwrapXPathObject(xpath.xmlXPathObject* xpathObj,
+                               _Document doc, _BaseContext context):
+    if xpathObj.type == xpath.XPATH_UNDEFINED:
+        raise XPathResultError, "Undefined xpath result"
+    elif xpathObj.type == xpath.XPATH_NODESET:
+        return _createNodeSetResult(xpathObj, doc, context)
+    elif xpathObj.type == xpath.XPATH_BOOLEAN:
+        return xpathObj.boolval
+    elif xpathObj.type == xpath.XPATH_NUMBER:
+        return xpathObj.floatval
+    elif xpathObj.type == xpath.XPATH_STRING:
+        stringval = funicode(xpathObj.stringval)
+        if context._build_smart_strings:
+            stringval = _elementStringResultFactory(
+                stringval, None, None, False)
+        return stringval
+    elif xpathObj.type == xpath.XPATH_POINT:
+        raise NotImplementedError, "XPATH_POINT"
+    elif xpathObj.type == xpath.XPATH_RANGE:
+        raise NotImplementedError, "XPATH_RANGE"
+    elif xpathObj.type == xpath.XPATH_LOCATIONSET:
+        raise NotImplementedError, "XPATH_LOCATIONSET"
+    elif xpathObj.type == xpath.XPATH_USERS:
+        raise NotImplementedError, "XPATH_USERS"
+    elif xpathObj.type == xpath.XPATH_XSLT_TREE:
+        return _createNodeSetResult(xpathObj, doc, context)
+    else:
+        raise XPathResultError, f"Unknown xpath result {xpathObj.type}"
+
+cdef object _createNodeSetResult(xpath.xmlXPathObject* xpathObj, _Document doc,
+                                 _BaseContext context):
+    cdef xmlNode* c_node
+    cdef int i
+    cdef list result
+    result = []
+    if xpathObj.nodesetval is NULL:
+        return result
+    for i in range(xpathObj.nodesetval.nodeNr):
+        c_node = xpathObj.nodesetval.nodeTab[i]
+        _unpackNodeSetEntry(result, c_node, doc, context,
+                            xpathObj.type == xpath.XPATH_XSLT_TREE)
+    return result
+
+cdef _unpackNodeSetEntry(list results, xmlNode* c_node, _Document doc,
+                         _BaseContext context, bint is_fragment):
+    cdef xmlNode* c_child
+    if _isElement(c_node):
+        if c_node.doc != doc._c_doc and c_node.doc._private is NULL:
+            # XXX: works, but maybe not always the right thing to do?
+            # XPath: only runs when extensions create or copy trees
+            #        -> we store Python refs to these, so that is OK
+            # XSLT: can it leak when merging trees from multiple sources?
+            c_node = tree.xmlDocCopyNode(c_node, doc._c_doc, 1)
+            # FIXME: call _instantiateElementFromXPath() instead?
+        results.append(
+            _fakeDocElementFactory(doc, c_node))
+    elif c_node.type == tree.XML_TEXT_NODE or \
+             c_node.type == tree.XML_CDATA_SECTION_NODE or \
+             c_node.type == tree.XML_ATTRIBUTE_NODE:
+        results.append(
+            _buildElementStringResult(doc, c_node, context))
+    elif c_node.type == tree.XML_NAMESPACE_DECL:
+        results.append( (funicodeOrNone((<xmlNs*>c_node).prefix),
+                         funicodeOrNone((<xmlNs*>c_node).href)) )
+    elif c_node.type == tree.XML_DOCUMENT_NODE or \
+            c_node.type == tree.XML_HTML_DOCUMENT_NODE:
+        # ignored for everything but result tree fragments
+        if is_fragment:
+            c_child = c_node.children
+            while c_child is not NULL:
+                _unpackNodeSetEntry(results, c_child, doc, context, 0)
+                c_child = c_child.next
+    elif c_node.type == tree.XML_XINCLUDE_START or \
+            c_node.type == tree.XML_XINCLUDE_END:
+        pass
+    else:
+        raise NotImplementedError, \
+            f"Not yet implemented result node type: {c_node.type}"
+
+cdef void _freeXPathObject(xpath.xmlXPathObject* xpathObj) noexcept:
+    """Free the XPath object, but *never* free the *content* of node sets.
+    Python dealloc will do that for us.
+    """
+    if xpathObj.nodesetval is not NULL:
+        xpath.xmlXPathFreeNodeSet(xpathObj.nodesetval)
+        xpathObj.nodesetval = NULL
+    xpath.xmlXPathFreeObject(xpathObj)
+
+cdef _Element _instantiateElementFromXPath(xmlNode* c_node, _Document doc,
+                                           _BaseContext context):
+    # NOTE: this may copy the element - only call this when it can't leak
+    if c_node.doc != doc._c_doc and c_node.doc._private is NULL:
+        # not from the context document and not from a fake document
+        # either => may still be from a known document, e.g. one
+        # created by an extension function
+        node_doc = context._findDocumentForNode(c_node)
+        if node_doc is None:
+            # not from a known document at all! => can only make a
+            # safety copy here
+            c_node = tree.xmlDocCopyNode(c_node, doc._c_doc, 1)
+        else:
+            doc = node_doc
+    return _fakeDocElementFactory(doc, c_node)
+
+################################################################################
+# special str/unicode subclasses
+
+@cython.final
+cdef class _ElementUnicodeResult(unicode):
+    cdef _Element _parent
+    cdef readonly object attrname
+    cdef readonly bint is_tail
+
+    def getparent(self):
+        return self._parent
+
+    @property
+    def is_text(self):
+        return self._parent is not None and not (self.is_tail or self.attrname is not None)
+
+    @property
+    def is_attribute(self):
+        return self.attrname is not None
+
+cdef object _elementStringResultFactory(string_value, _Element parent,
+                                        attrname, bint is_tail):
+    result = _ElementUnicodeResult(string_value)
+    result._parent = parent
+    result.is_tail = is_tail
+    result.attrname = attrname
+    return result
+
+cdef object _buildElementStringResult(_Document doc, xmlNode* c_node,
+                                      _BaseContext context):
+    cdef _Element parent = None
+    cdef object attrname = None
+    cdef xmlNode* c_element
+    cdef bint is_tail
+
+    if c_node.type == tree.XML_ATTRIBUTE_NODE:
+        attrname = _namespacedName(c_node)
+        is_tail = 0
+        s = tree.xmlNodeGetContent(c_node)
+        try:
+            value = funicode(s)
+        finally:
+            tree.xmlFree(s)
+        c_element = NULL
+    else:
+        #assert c_node.type == tree.XML_TEXT_NODE or c_node.type == tree.XML_CDATA_SECTION_NODE, "invalid node type"
+        # may be tail text or normal text
+        value = funicode(c_node.content)
+        c_element = _previousElement(c_node)
+        is_tail = c_element is not NULL
+
+    if not context._build_smart_strings:
+        return value
+
+    if c_element is NULL:
+        # non-tail text or attribute text
+        c_element = c_node.parent
+        while c_element is not NULL and not _isElement(c_element):
+            c_element = c_element.parent
+
+    if c_element is not NULL:
+        parent = _instantiateElementFromXPath(c_element, doc, context)
+
+    return _elementStringResultFactory(
+        value, parent, attrname, is_tail)
+
+################################################################################
+# callbacks for XPath/XSLT extension functions
+
+cdef void _extension_function_call(_BaseContext context, function,
+                                   xpath.xmlXPathParserContext* ctxt, int nargs) noexcept:
+    cdef _Document doc
+    cdef xpath.xmlXPathObject* obj
+    cdef list args
+    cdef int i
+    doc = context._doc
+    try:
+        args = []
+        for i in range(nargs):
+            obj = xpath.valuePop(ctxt)
+            o = _unwrapXPathObject(obj, doc, context)
+            _freeXPathObject(obj)
+            args.append(o)
+        args.reverse()
+
+        res = function(context, *args)
+        # wrap result for XPath consumption
+        obj = _wrapXPathObject(res, doc, context)
+        # prevent Python from deallocating elements handed to libxml2
+        context._hold(res)
+        xpath.valuePush(ctxt, obj)
+    except:
+        xpath.xmlXPathErr(ctxt, xpath.XPATH_EXPR_ERROR)
+        context._exc._store_raised()
+    finally:
+        return  # swallow any further exceptions
+
+# lookup the function by name and call it
+
+cdef void _xpath_function_call(xpath.xmlXPathParserContext* ctxt,
+                               int nargs) noexcept with gil:
+    cdef _BaseContext context
+    cdef xpath.xmlXPathContext* rctxt = ctxt.context
+    context = <_BaseContext> rctxt.userData
+    try:
+        function = context._find_cached_function(rctxt.functionURI, rctxt.function)
+        if function is not None:
+            _extension_function_call(context, function, ctxt, nargs)
+        else:
+            xpath.xmlXPathErr(ctxt, xpath.XPATH_UNKNOWN_FUNC_ERROR)
+            context._exc._store_exception(XPathFunctionError(
+                f"XPath function '{_namespacedNameFromNsName(rctxt.functionURI, rctxt.function)}' not found"))
+    except:
+        # may not be the right error, but we need to tell libxml2 *something*
+        xpath.xmlXPathErr(ctxt, xpath.XPATH_UNKNOWN_FUNC_ERROR)
+        context._exc._store_raised()
+    finally:
+        return  # swallow any further exceptions
diff --git a/.venv/lib/python3.12/site-packages/lxml/html/ElementSoup.py b/.venv/lib/python3.12/site-packages/lxml/html/ElementSoup.py
new file mode 100644
index 00000000..c35365d0
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/html/ElementSoup.py
@@ -0,0 +1,10 @@
+__doc__ = """Legacy interface to the BeautifulSoup HTML parser.
+"""
+
+__all__ = ["parse", "convert_tree"]
+
+from .soupparser import convert_tree, parse as _parse
+
+def parse(file, beautifulsoup=None, makeelement=None):
+    root = _parse(file, beautifulsoup=beautifulsoup, makeelement=makeelement)
+    return root.getroot()
diff --git a/.venv/lib/python3.12/site-packages/lxml/html/__init__.py b/.venv/lib/python3.12/site-packages/lxml/html/__init__.py
new file mode 100644
index 00000000..ec55d678
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/html/__init__.py
@@ -0,0 +1,1923 @@
+# Copyright (c) 2004 Ian Bicking. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# 3. Neither the name of Ian Bicking nor the names of its contributors may
+# be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL IAN BICKING OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""The ``lxml.html`` tool set for HTML handling.
+"""
+
+
+__all__ = [
+    'document_fromstring', 'fragment_fromstring', 'fragments_fromstring', 'fromstring',
+    'tostring', 'Element', 'defs', 'open_in_browser', 'submit_form',
+    'find_rel_links', 'find_class', 'make_links_absolute',
+    'resolve_base_href', 'iterlinks', 'rewrite_links', 'parse']
+
+
+import copy
+import re
+
+from collections.abc import MutableMapping, MutableSet
+from functools import partial
+from urllib.parse import urljoin
+
+from .. import etree
+from . import defs
+from ._setmixin import SetMixin
+
+
+def __fix_docstring(s):
+    # TODO: remove and clean up doctests
+    if not s:
+        return s
+    sub = re.compile(r"^(\s*)u'", re.M).sub
+    return sub(r"\1'", s)
+
+
+XHTML_NAMESPACE = "http://www.w3.org/1999/xhtml"
+
+_rel_links_xpath = etree.XPath("descendant-or-self::a[@rel]|descendant-or-self::x:a[@rel]",
+                               namespaces={'x':XHTML_NAMESPACE})
+_options_xpath = etree.XPath("descendant-or-self::option|descendant-or-self::x:option",
+                             namespaces={'x':XHTML_NAMESPACE})
+_forms_xpath = etree.XPath("descendant-or-self::form|descendant-or-self::x:form",
+                           namespaces={'x':XHTML_NAMESPACE})
+#_class_xpath = etree.XPath(r"descendant-or-self::*[regexp:match(@class, concat('\b', $class_name, '\b'))]", {'regexp': 'http://exslt.org/regular-expressions'})
+_class_xpath = etree.XPath("descendant-or-self::*[@class and contains(concat(' ', normalize-space(@class), ' '), concat(' ', $class_name, ' '))]")
+_id_xpath = etree.XPath("descendant-or-self::*[@id=$id]")
+_collect_string_content = etree.XPath("string()")
+_iter_css_urls = re.compile(r'url\(('+'["][^"]*["]|'+"['][^']*[']|"+r'[^)]*)\)', re.I).finditer
+_iter_css_imports = re.compile(r'@import "(.*?)"').finditer
+_label_xpath = etree.XPath("//label[@for=$id]|//x:label[@for=$id]",
+                           namespaces={'x':XHTML_NAMESPACE})
+_archive_re = re.compile(r'[^ ]+')
+_parse_meta_refresh_url = re.compile(
+    r'[^;=]*;\s*(?:url\s*=\s*)?(?P<url>.*)$', re.I).search
+
+
+def _unquote_match(s, pos):
+    if s[:1] == '"' and s[-1:] == '"' or s[:1] == "'" and s[-1:] == "'":
+        return s[1:-1], pos+1
+    else:
+        return s,pos
+
+
+def _transform_result(typ, result):
+    """Convert the result back into the input type.
+    """
+    if issubclass(typ, bytes):
+        return tostring(result, encoding='utf-8')
+    elif issubclass(typ, str):
+        return tostring(result, encoding='unicode')
+    else:
+        return result
+
+
+def _nons(tag):
+    if isinstance(tag, str):
+        if tag[0] == '{' and tag[1:len(XHTML_NAMESPACE)+1] == XHTML_NAMESPACE:
+            return tag.split('}')[-1]
+    return tag
+
+
+class Classes(MutableSet):
+    """Provides access to an element's class attribute as a set-like collection.
+    Usage::
+
+        >>> el = fromstring('<p class="hidden large">Text</p>')
+        >>> classes = el.classes  # or: classes = Classes(el.attrib)
+        >>> classes |= ['block', 'paragraph']
+        >>> el.get('class')
+        'hidden large block paragraph'
+        >>> classes.toggle('hidden')
+        False
+        >>> el.get('class')
+        'large block paragraph'
+        >>> classes -= ('some', 'classes', 'block')
+        >>> el.get('class')
+        'large paragraph'
+    """
+    def __init__(self, attributes):
+        self._attributes = attributes
+        self._get_class_value = partial(attributes.get, 'class', '')
+
+    def add(self, value):
+        """
+        Add a class.
+
+        This has no effect if the class is already present.
+        """
+        if not value or re.search(r'\s', value):
+            raise ValueError("Invalid class name: %r" % value)
+        classes = self._get_class_value().split()
+        if value in classes:
+            return
+        classes.append(value)
+        self._attributes['class'] = ' '.join(classes)
+
+    def discard(self, value):
+        """
+        Remove a class if it is currently present.
+
+        If the class is not present, do nothing.
+        """
+        if not value or re.search(r'\s', value):
+            raise ValueError("Invalid class name: %r" % value)
+        classes = [name for name in self._get_class_value().split()
+                   if name != value]
+        if classes:
+            self._attributes['class'] = ' '.join(classes)
+        elif 'class' in self._attributes:
+            del self._attributes['class']
+
+    def remove(self, value):
+        """
+        Remove a class; it must currently be present.
+
+        If the class is not present, raise a KeyError.
+        """
+        if not value or re.search(r'\s', value):
+            raise ValueError("Invalid class name: %r" % value)
+        super().remove(value)
+
+    def __contains__(self, name):
+        classes = self._get_class_value()
+        return name in classes and name in classes.split()
+
+    def __iter__(self):
+        return iter(self._get_class_value().split())
+
+    def __len__(self):
+        return len(self._get_class_value().split())
+
+    # non-standard methods
+
+    def update(self, values):
+        """
+        Add all names from 'values'.
+        """
+        classes = self._get_class_value().split()
+        extended = False
+        for value in values:
+            if value not in classes:
+                classes.append(value)
+                extended = True
+        if extended:
+            self._attributes['class'] = ' '.join(classes)
+
+    def toggle(self, value):
+        """
+        Add a class name if it isn't there yet, or remove it if it exists.
+
+        Returns true if the class was added (and is now enabled) and
+        false if it was removed (and is now disabled).
+        """
+        if not value or re.search(r'\s', value):
+            raise ValueError("Invalid class name: %r" % value)
+        classes = self._get_class_value().split()
+        try:
+            classes.remove(value)
+            enabled = False
+        except ValueError:
+            classes.append(value)
+            enabled = True
+        if classes:
+            self._attributes['class'] = ' '.join(classes)
+        else:
+            del self._attributes['class']
+        return enabled
+
+
+class HtmlMixin:
+
+    def set(self, key, value=None):
+        """set(self, key, value=None)
+
+        Sets an element attribute.  If no value is provided, or if the value is None,
+        creates a 'boolean' attribute without value, e.g. "<form novalidate></form>"
+        for ``form.set('novalidate')``.
+        """
+        super().set(key, value)
+
+    @property
+    def classes(self):
+        """
+        A set-like wrapper around the 'class' attribute.
+        """
+        return Classes(self.attrib)
+
+    @classes.setter
+    def classes(self, classes):
+        assert isinstance(classes, Classes)  # only allow "el.classes |= ..." etc.
+        value = classes._get_class_value()
+        if value:
+            self.set('class', value)
+        elif self.get('class') is not None:
+            del self.attrib['class']
+
+    @property
+    def base_url(self):
+        """
+        Returns the base URL, given when the page was parsed.
+
+        Use with ``urlparse.urljoin(el.base_url, href)`` to get
+        absolute URLs.
+        """
+        return self.getroottree().docinfo.URL
+
+    @property
+    def forms(self):
+        """
+        Return a list of all the forms
+        """
+        return _forms_xpath(self)
+
+    @property
+    def body(self):
+        """
+        Return the <body> element.  Can be called from a child element
+        to get the document's head.
+        """
+        return self.xpath('//body|//x:body', namespaces={'x':XHTML_NAMESPACE})[0]
+
+    @property
+    def head(self):
+        """
+        Returns the <head> element.  Can be called from a child
+        element to get the document's head.
+        """
+        return self.xpath('//head|//x:head', namespaces={'x':XHTML_NAMESPACE})[0]
+
+    @property
+    def label(self):
+        """
+        Get or set any <label> element associated with this element.
+        """
+        id = self.get('id')
+        if not id:
+            return None
+        result = _label_xpath(self, id=id)
+        if not result:
+            return None
+        else:
+            return result[0]
+
+    @label.setter
+    def label(self, label):
+        id = self.get('id')
+        if not id:
+            raise TypeError(
+                "You cannot set a label for an element (%r) that has no id"
+                % self)
+        if _nons(label.tag) != 'label':
+            raise TypeError(
+                "You can only assign label to a label element (not %r)"
+                % label)
+        label.set('for', id)
+
+    @label.deleter
+    def label(self):
+        label = self.label
+        if label is not None:
+            del label.attrib['for']
+
+    def drop_tree(self):
+        """
+        Removes this element from the tree, including its children and
+        text.  The tail text is joined to the previous element or
+        parent.
+        """
+        parent = self.getparent()
+        assert parent is not None
+        if self.tail:
+            previous = self.getprevious()
+            if previous is None:
+                parent.text = (parent.text or '') + self.tail
+            else:
+                previous.tail = (previous.tail or '') + self.tail
+        parent.remove(self)
+
+    def drop_tag(self):
+        """
+        Remove the tag, but not its children or text.  The children and text
+        are merged into the parent.
+
+        Example::
+
+            >>> h = fragment_fromstring('<div>Hello <b>World!</b></div>')
+            >>> h.find('.//b').drop_tag()
+            >>> print(tostring(h, encoding='unicode'))
+            <div>Hello World!</div>
+        """
+        parent = self.getparent()
+        assert parent is not None
+        previous = self.getprevious()
+        if self.text and isinstance(self.tag, str):
+            # not a Comment, etc.
+            if previous is None:
+                parent.text = (parent.text or '') + self.text
+            else:
+                previous.tail = (previous.tail or '') + self.text
+        if self.tail:
+            if len(self):
+                last = self[-1]
+                last.tail = (last.tail or '') + self.tail
+            elif previous is None:
+                parent.text = (parent.text or '') + self.tail
+            else:
+                previous.tail = (previous.tail or '') + self.tail
+        index = parent.index(self)
+        parent[index:index+1] = self[:]
+
+    def find_rel_links(self, rel):
+        """
+        Find any links like ``<a rel="{rel}">...</a>``; returns a list of elements.
+        """
+        rel = rel.lower()
+        return [el for el in _rel_links_xpath(self)
+                if el.get('rel').lower() == rel]
+
+    def find_class(self, class_name):
+        """
+        Find any elements with the given class name.
+        """
+        return _class_xpath(self, class_name=class_name)
+
+    def get_element_by_id(self, id, *default):
+        """
+        Get the first element in a document with the given id.  If none is
+        found, return the default argument if provided or raise KeyError
+        otherwise.
+
+        Note that there can be more than one element with the same id,
+        and this isn't uncommon in HTML documents found in the wild.
+        Browsers return only the first match, and this function does
+        the same.
+        """
+        try:
+            # FIXME: should this check for multiple matches?
+            # browsers just return the first one
+            return _id_xpath(self, id=id)[0]
+        except IndexError:
+            if default:
+                return default[0]
+            else:
+                raise KeyError(id)
+
+    def text_content(self):
+        """
+        Return the text content of the tag (and the text in any children).
+        """
+        return _collect_string_content(self)
+
+    def cssselect(self, expr, translator='html'):
+        """
+        Run the CSS expression on this element and its children,
+        returning a list of the results.
+
+        Equivalent to lxml.cssselect.CSSSelect(expr, translator='html')(self)
+        -- note that pre-compiling the expression can provide a substantial
+        speedup.
+        """
+        # Do the import here to make the dependency optional.
+        from lxml.cssselect import CSSSelector
+        return CSSSelector(expr, translator=translator)(self)
+
+    ########################################
+    ## Link functions
+    ########################################
+
+    def make_links_absolute(self, base_url=None, resolve_base_href=True,
+                            handle_failures=None):
+        """
+        Make all links in the document absolute, given the
+        ``base_url`` for the document (the full URL where the document
+        came from), or if no ``base_url`` is given, then the ``.base_url``
+        of the document.
+
+        If ``resolve_base_href`` is true, then any ``<base href>``
+        tags in the document are used *and* removed from the document.
+        If it is false then any such tag is ignored.
+
+        If ``handle_failures`` is None (default), a failure to process
+        a URL will abort the processing.  If set to 'ignore', errors
+        are ignored.  If set to 'discard', failing URLs will be removed.
+        """
+        if base_url is None:
+            base_url = self.base_url
+            if base_url is None:
+                raise TypeError(
+                    "No base_url given, and the document has no base_url")
+        if resolve_base_href:
+            self.resolve_base_href()
+
+        if handle_failures == 'ignore':
+            def link_repl(href):
+                try:
+                    return urljoin(base_url, href)
+                except ValueError:
+                    return href
+        elif handle_failures == 'discard':
+            def link_repl(href):
+                try:
+                    return urljoin(base_url, href)
+                except ValueError:
+                    return None
+        elif handle_failures is None:
+            def link_repl(href):
+                return urljoin(base_url, href)
+        else:
+            raise ValueError(
+                "unexpected value for handle_failures: %r" % handle_failures)
+
+        self.rewrite_links(link_repl)
+
+    def resolve_base_href(self, handle_failures=None):
+        """
+        Find any ``<base href>`` tag in the document, and apply its
+        values to all links found in the document.  Also remove the
+        tag once it has been applied.
+
+        If ``handle_failures`` is None (default), a failure to process
+        a URL will abort the processing.  If set to 'ignore', errors
+        are ignored.  If set to 'discard', failing URLs will be removed.
+        """
+        base_href = None
+        basetags = self.xpath('//base[@href]|//x:base[@href]',
+                              namespaces={'x': XHTML_NAMESPACE})
+        for b in basetags:
+            base_href = b.get('href')
+            b.drop_tree()
+        if not base_href:
+            return
+        self.make_links_absolute(base_href, resolve_base_href=False,
+                                 handle_failures=handle_failures)
+
+    def iterlinks(self):
+        """
+        Yield (element, attribute, link, pos), where attribute may be None
+        (indicating the link is in the text).  ``pos`` is the position
+        where the link occurs; often 0, but sometimes something else in
+        the case of links in stylesheets or style tags.
+
+        Note: <base href> is *not* taken into account in any way.  The
+        link you get is exactly the link in the document.
+
+        Note: multiple links inside of a single text string or
+        attribute value are returned in reversed order.  This makes it
+        possible to replace or delete them from the text string value
+        based on their reported text positions.  Otherwise, a
+        modification at one text position can change the positions of
+        links reported later on.
+        """
+        link_attrs = defs.link_attrs
+        for el in self.iter(etree.Element):
+            attribs = el.attrib
+            tag = _nons(el.tag)
+            if tag == 'object':
+                codebase = None
+                ## <object> tags have attributes that are relative to
+                ## codebase
+                if 'codebase' in attribs:
+                    codebase = el.get('codebase')
+                    yield (el, 'codebase', codebase, 0)
+                for attrib in ('classid', 'data'):
+                    if attrib in attribs:
+                        value = el.get(attrib)
+                        if codebase is not None:
+                            value = urljoin(codebase, value)
+                        yield (el, attrib, value, 0)
+                if 'archive' in attribs:
+                    for match in _archive_re.finditer(el.get('archive')):
+                        value = match.group(0)
+                        if codebase is not None:
+                            value = urljoin(codebase, value)
+                        yield (el, 'archive', value, match.start())
+            else:
+                for attrib in link_attrs:
+                    if attrib in attribs:
+                        yield (el, attrib, attribs[attrib], 0)
+            if tag == 'meta':
+                http_equiv = attribs.get('http-equiv', '').lower()
+                if http_equiv == 'refresh':
+                    content = attribs.get('content', '')
+                    match = _parse_meta_refresh_url(content)
+                    url = (match.group('url') if match else content).strip()
+                    # unexpected content means the redirect won't work, but we might
+                    # as well be permissive and return the entire string.
+                    if url:
+                        url, pos = _unquote_match(
+                            url, match.start('url') if match else content.find(url))
+                        yield (el, 'content', url, pos)
+            elif tag == 'param':
+                valuetype = el.get('valuetype') or ''
+                if valuetype.lower() == 'ref':
+                    ## FIXME: while it's fine we *find* this link,
+                    ## according to the spec we aren't supposed to
+                    ## actually change the value, including resolving
+                    ## it.  It can also still be a link, even if it
+                    ## doesn't have a valuetype="ref" (which seems to be the norm)
+                    ## http://www.w3.org/TR/html401/struct/objects.html#adef-valuetype
+                    yield (el, 'value', el.get('value'), 0)
+            elif tag == 'style' and el.text:
+                urls = [
+                    # (start_pos, url)
+                    _unquote_match(match.group(1), match.start(1))[::-1]
+                    for match in _iter_css_urls(el.text)
+                    ] + [
+                    (match.start(1), match.group(1))
+                    for match in _iter_css_imports(el.text)
+                    ]
+                if urls:
+                    # sort by start pos to bring both match sets back into order
+                    # and reverse the list to report correct positions despite
+                    # modifications
+                    urls.sort(reverse=True)
+                    for start, url in urls:
+                        yield (el, None, url, start)
+            if 'style' in attribs:
+                urls = list(_iter_css_urls(attribs['style']))
+                if urls:
+                    # return in reversed order to simplify in-place modifications
+                    for match in urls[::-1]:
+                        url, start = _unquote_match(match.group(1), match.start(1))
+                        yield (el, 'style', url, start)
+
+    def rewrite_links(self, link_repl_func, resolve_base_href=True,
+                      base_href=None):
+        """
+        Rewrite all the links in the document.  For each link
+        ``link_repl_func(link)`` will be called, and the return value
+        will replace the old link.
+
+        Note that links may not be absolute (unless you first called
+        ``make_links_absolute()``), and may be internal (e.g.,
+        ``'#anchor'``).  They can also be values like
+        ``'mailto:email'`` or ``'javascript:expr'``.
+
+        If you give ``base_href`` then all links passed to
+        ``link_repl_func()`` will take that into account.
+
+        If the ``link_repl_func`` returns None, the attribute or
+        tag text will be removed completely.
+        """
+        if base_href is not None:
+            # FIXME: this can be done in one pass with a wrapper
+            # around link_repl_func
+            self.make_links_absolute(
+                base_href, resolve_base_href=resolve_base_href)
+        elif resolve_base_href:
+            self.resolve_base_href()
+
+        for el, attrib, link, pos in self.iterlinks():
+            new_link = link_repl_func(link.strip())
+            if new_link == link:
+                continue
+            if new_link is None:
+                # Remove the attribute or element content
+                if attrib is None:
+                    el.text = ''
+                else:
+                    del el.attrib[attrib]
+                continue
+
+            if attrib is None:
+                new = el.text[:pos] + new_link + el.text[pos+len(link):]
+                el.text = new
+            else:
+                cur = el.get(attrib)
+                if not pos and len(cur) == len(link):
+                    new = new_link  # most common case
+                else:
+                    new = cur[:pos] + new_link + cur[pos+len(link):]
+                el.set(attrib, new)
+
+
+class _MethodFunc:
+    """
+    An object that represents a method on an element as a function;
+    the function takes either an element or an HTML string.  It
+    returns whatever the function normally returns, or if the function
+    works in-place (and so returns None) it returns a serialized form
+    of the resulting document.
+    """
+    def __init__(self, name, copy=False, source_class=HtmlMixin):
+        self.name = name
+        self.copy = copy
+        self.__doc__ = getattr(source_class, self.name).__doc__
+    def __call__(self, doc, *args, **kw):
+        result_type = type(doc)
+        if isinstance(doc, (str, bytes)):
+            if 'copy' in kw:
+                raise TypeError(
+                    "The keyword 'copy' can only be used with element inputs to %s, not a string input" % self.name)
+            doc = fromstring(doc, **kw)
+        else:
+            if 'copy' in kw:
+                make_a_copy = kw.pop('copy')
+            else:
+                make_a_copy = self.copy
+            if make_a_copy:
+                doc = copy.deepcopy(doc)
+        meth = getattr(doc, self.name)
+        result = meth(*args, **kw)
+        # FIXME: this None test is a bit sloppy
+        if result is None:
+            # Then return what we got in
+            return _transform_result(result_type, doc)
+        else:
+            return result
+
+
+find_rel_links = _MethodFunc('find_rel_links', copy=False)
+find_class = _MethodFunc('find_class', copy=False)
+make_links_absolute = _MethodFunc('make_links_absolute', copy=True)
+resolve_base_href = _MethodFunc('resolve_base_href', copy=True)
+iterlinks = _MethodFunc('iterlinks', copy=False)
+rewrite_links = _MethodFunc('rewrite_links', copy=True)
+
+
+class HtmlComment(HtmlMixin, etree.CommentBase):
+    pass
+
+
+class HtmlElement(HtmlMixin, etree.ElementBase):
+    pass
+
+
+class HtmlProcessingInstruction(HtmlMixin, etree.PIBase):
+    pass
+
+
+class HtmlEntity(HtmlMixin, etree.EntityBase):
+    pass
+
+
+class HtmlElementClassLookup(etree.CustomElementClassLookup):
+    """A lookup scheme for HTML Element classes.
+
+    To create a lookup instance with different Element classes, pass a tag
+    name mapping of Element classes in the ``classes`` keyword argument and/or
+    a tag name mapping of Mixin classes in the ``mixins`` keyword argument.
+    The special key '*' denotes a Mixin class that should be mixed into all
+    Element classes.
+    """
+    _default_element_classes = {}
+
+    def __init__(self, classes=None, mixins=None):
+        etree.CustomElementClassLookup.__init__(self)
+        if classes is None:
+            classes = self._default_element_classes.copy()
+        if mixins:
+            mixers = {}
+            for name, value in mixins:
+                if name == '*':
+                    for n in classes.keys():
+                        mixers.setdefault(n, []).append(value)
+                else:
+                    mixers.setdefault(name, []).append(value)
+            for name, mix_bases in mixers.items():
+                cur = classes.get(name, HtmlElement)
+                bases = tuple(mix_bases + [cur])
+                classes[name] = type(cur.__name__, bases, {})
+        self._element_classes = classes
+
+    def lookup(self, node_type, document, namespace, name):
+        if node_type == 'element':
+            return self._element_classes.get(name.lower(), HtmlElement)
+        elif node_type == 'comment':
+            return HtmlComment
+        elif node_type == 'PI':
+            return HtmlProcessingInstruction
+        elif node_type == 'entity':
+            return HtmlEntity
+        # Otherwise normal lookup
+        return None
+
+
+################################################################################
+# parsing
+################################################################################
+
+_looks_like_full_html_unicode = re.compile(
+    r'^\s*<(?:html|!doctype)', re.I).match
+_looks_like_full_html_bytes = re.compile(
+    br'^\s*<(?:html|!doctype)', re.I).match
+
+
+def document_fromstring(html, parser=None, ensure_head_body=False, **kw):
+    if parser is None:
+        parser = html_parser
+    value = etree.fromstring(html, parser, **kw)
+    if value is None:
+        raise etree.ParserError(
+            "Document is empty")
+    if ensure_head_body and value.find('head') is None:
+        value.insert(0, Element('head'))
+    if ensure_head_body and value.find('body') is None:
+        value.append(Element('body'))
+    return value
+
+
+def fragments_fromstring(html, no_leading_text=False, base_url=None,
+                         parser=None, **kw):
+    """Parses several HTML elements, returning a list of elements.
+
+    The first item in the list may be a string.
+    If no_leading_text is true, then it will be an error if there is
+    leading text, and it will always be a list of only elements.
+
+    base_url will set the document's base_url attribute
+    (and the tree's docinfo.URL).
+    """
+    if parser is None:
+        parser = html_parser
+    # FIXME: check what happens when you give html with a body, head, etc.
+    if isinstance(html, bytes):
+        if not _looks_like_full_html_bytes(html):
+            # can't use %-formatting in early Py3 versions
+            html = (b'<html><body>' + html +
+                    b'</body></html>')
+    else:
+        if not _looks_like_full_html_unicode(html):
+            html = '<html><body>%s</body></html>' % html
+    doc = document_fromstring(html, parser=parser, base_url=base_url, **kw)
+    assert _nons(doc.tag) == 'html'
+    bodies = [e for e in doc if _nons(e.tag) == 'body']
+    assert len(bodies) == 1, ("too many bodies: %r in %r" % (bodies, html))
+    body = bodies[0]
+    elements = []
+    if no_leading_text and body.text and body.text.strip():
+        raise etree.ParserError(
+            "There is leading text: %r" % body.text)
+    if body.text and body.text.strip():
+        elements.append(body.text)
+    elements.extend(body)
+    # FIXME: removing the reference to the parent artificial document
+    # would be nice
+    return elements
+
+
+def fragment_fromstring(html, create_parent=False, base_url=None,
+                        parser=None, **kw):
+    """
+    Parses a single HTML element; it is an error if there is more than
+    one element, or if anything but whitespace precedes or follows the
+    element.
+
+    If ``create_parent`` is true (or is a tag name) then a parent node
+    will be created to encapsulate the HTML in a single element.  In this
+    case, leading or trailing text is also allowed, as are multiple elements
+    as result of the parsing.
+
+    Passing a ``base_url`` will set the document's ``base_url`` attribute
+    (and the tree's docinfo.URL).
+    """
+    if parser is None:
+        parser = html_parser
+
+    accept_leading_text = bool(create_parent)
+
+    elements = fragments_fromstring(
+        html, parser=parser, no_leading_text=not accept_leading_text,
+        base_url=base_url, **kw)
+
+    if create_parent:
+        if not isinstance(create_parent, str):
+            create_parent = 'div'
+        new_root = Element(create_parent)
+        if elements:
+            if isinstance(elements[0], str):
+                new_root.text = elements[0]
+                del elements[0]
+            new_root.extend(elements)
+        return new_root
+
+    if not elements:
+        raise etree.ParserError('No elements found')
+    if len(elements) > 1:
+        raise etree.ParserError(
+            "Multiple elements found (%s)"
+            % ', '.join([_element_name(e) for e in elements]))
+    el = elements[0]
+    if el.tail and el.tail.strip():
+        raise etree.ParserError(
+            "Element followed by text: %r" % el.tail)
+    el.tail = None
+    return el
+
+
+def fromstring(html, base_url=None, parser=None, **kw):
+    """
+    Parse the html, returning a single element/document.
+
+    This tries to minimally parse the chunk of text, without knowing if it
+    is a fragment or a document.
+
+    base_url will set the document's base_url attribute (and the tree's docinfo.URL)
+    """
+    if parser is None:
+        parser = html_parser
+    if isinstance(html, bytes):
+        is_full_html = _looks_like_full_html_bytes(html)
+    else:
+        is_full_html = _looks_like_full_html_unicode(html)
+    doc = document_fromstring(html, parser=parser, base_url=base_url, **kw)
+    if is_full_html:
+        return doc
+    # otherwise, lets parse it out...
+    bodies = doc.findall('body')
+    if not bodies:
+        bodies = doc.findall('{%s}body' % XHTML_NAMESPACE)
+    if bodies:
+        body = bodies[0]
+        if len(bodies) > 1:
+            # Somehow there are multiple bodies, which is bad, but just
+            # smash them into one body
+            for other_body in bodies[1:]:
+                if other_body.text:
+                    if len(body):
+                        body[-1].tail = (body[-1].tail or '') + other_body.text
+                    else:
+                        body.text = (body.text or '') + other_body.text
+                body.extend(other_body)
+                # We'll ignore tail
+                # I guess we are ignoring attributes too
+                other_body.drop_tree()
+    else:
+        body = None
+    heads = doc.findall('head')
+    if not heads:
+        heads = doc.findall('{%s}head' % XHTML_NAMESPACE)
+    if heads:
+        # Well, we have some sort of structure, so lets keep it all
+        head = heads[0]
+        if len(heads) > 1:
+            for other_head in heads[1:]:
+                head.extend(other_head)
+                # We don't care about text or tail in a head
+                other_head.drop_tree()
+        return doc
+    if body is None:
+        return doc
+    if (len(body) == 1 and (not body.text or not body.text.strip())
+        and (not body[-1].tail or not body[-1].tail.strip())):
+        # The body has just one element, so it was probably a single
+        # element passed in
+        return body[0]
+    # Now we have a body which represents a bunch of tags which have the
+    # content that was passed in.  We will create a fake container, which
+    # is the body tag, except <body> implies too much structure.
+    if _contains_block_level_tag(body):
+        body.tag = 'div'
+    else:
+        body.tag = 'span'
+    return body
+
+
+def parse(filename_or_url, parser=None, base_url=None, **kw):
+    """
+    Parse a filename, URL, or file-like object into an HTML document
+    tree.  Note: this returns a tree, not an element.  Use
+    ``parse(...).getroot()`` to get the document root.
+
+    You can override the base URL with the ``base_url`` keyword.  This
+    is most useful when parsing from a file-like object.
+    """
+    if parser is None:
+        parser = html_parser
+    return etree.parse(filename_or_url, parser, base_url=base_url, **kw)
+
+
+def _contains_block_level_tag(el):
+    # FIXME: I could do this with XPath, but would that just be
+    # unnecessarily slow?
+    for el in el.iter(etree.Element):
+        if _nons(el.tag) in defs.block_tags:
+            return True
+    return False
+
+
+def _element_name(el):
+    if isinstance(el, etree.CommentBase):
+        return 'comment'
+    elif isinstance(el, str):
+        return 'string'
+    else:
+        return _nons(el.tag)
+
+
+################################################################################
+# form handling
+################################################################################
+
+class FormElement(HtmlElement):
+    """
+    Represents a <form> element.
+    """
+
+    @property
+    def inputs(self):
+        """
+        Returns an accessor for all the input elements in the form.
+
+        See `InputGetter` for more information about the object.
+        """
+        return InputGetter(self)
+
+    @property
+    def fields(self):
+        """
+        Dictionary-like object that represents all the fields in this
+        form.  You can set values in this dictionary to effect the
+        form.
+        """
+        return FieldsDict(self.inputs)
+
+    @fields.setter
+    def fields(self, value):
+        fields = self.fields
+        prev_keys = fields.keys()
+        for key, value in value.items():
+            if key in prev_keys:
+                prev_keys.remove(key)
+            fields[key] = value
+        for key in prev_keys:
+            if key is None:
+                # Case of an unnamed input; these aren't really
+                # expressed in form_values() anyway.
+                continue
+            fields[key] = None
+
+    def _name(self):
+        if self.get('name'):
+            return self.get('name')
+        elif self.get('id'):
+            return '#' + self.get('id')
+        iter_tags = self.body.iter
+        forms = list(iter_tags('form'))
+        if not forms:
+            forms = list(iter_tags('{%s}form' % XHTML_NAMESPACE))
+        return str(forms.index(self))
+
+    def form_values(self):
+        """
+        Return a list of tuples of the field values for the form.
+        This is suitable to be passed to ``urllib.urlencode()``.
+        """
+        results = []
+        for el in self.inputs:
+            name = el.name
+            if not name or 'disabled' in el.attrib:
+                continue
+            tag = _nons(el.tag)
+            if tag == 'textarea':
+                results.append((name, el.value))
+            elif tag == 'select':
+                value = el.value
+                if el.multiple:
+                    for v in value:
+                        results.append((name, v))
+                elif value is not None:
+                    results.append((name, el.value))
+            else:
+                assert tag == 'input', (
+                    "Unexpected tag: %r" % el)
+                if el.checkable and not el.checked:
+                    continue
+                if el.type in ('submit', 'image', 'reset', 'file'):
+                    continue
+                value = el.value
+                if value is not None:
+                    results.append((name, el.value))
+        return results
+
+    @property
+    def action(self):
+        """
+        Get/set the form's ``action`` attribute.
+        """
+        base_url = self.base_url
+        action = self.get('action')
+        if base_url and action is not None:
+            return urljoin(base_url, action)
+        else:
+            return action
+
+    @action.setter
+    def action(self, value):
+        self.set('action', value)
+
+    @action.deleter
+    def action(self):
+        attrib = self.attrib
+        if 'action' in attrib:
+            del attrib['action']
+
+    @property
+    def method(self):
+        """
+        Get/set the form's method.  Always returns a capitalized
+        string, and defaults to ``'GET'``
+        """
+        return self.get('method', 'GET').upper()
+
+    @method.setter
+    def method(self, value):
+        self.set('method', value.upper())
+
+
+HtmlElementClassLookup._default_element_classes['form'] = FormElement
+
+
+def submit_form(form, extra_values=None, open_http=None):
+    """
+    Helper function to submit a form.  Returns a file-like object, as from
+    ``urllib.urlopen()``.  This object also has a ``.geturl()`` function,
+    which shows the URL if there were any redirects.
+
+    You can use this like::
+
+        form = doc.forms[0]
+        form.inputs['foo'].value = 'bar' # etc
+        response = form.submit()
+        doc = parse(response)
+        doc.make_links_absolute(response.geturl())
+
+    To change the HTTP requester, pass a function as ``open_http`` keyword
+    argument that opens the URL for you.  The function must have the following
+    signature::
+
+        open_http(method, URL, values)
+
+    The action is one of 'GET' or 'POST', the URL is the target URL as a
+    string, and the values are a sequence of ``(name, value)`` tuples with the
+    form data.
+    """
+    values = form.form_values()
+    if extra_values:
+        if hasattr(extra_values, 'items'):
+            extra_values = extra_values.items()
+        values.extend(extra_values)
+    if open_http is None:
+        open_http = open_http_urllib
+    if form.action:
+        url = form.action
+    else:
+        url = form.base_url
+    return open_http(form.method, url, values)
+
+
+def open_http_urllib(method, url, values):
+    if not url:
+        raise ValueError("cannot submit, no URL provided")
+    ## FIXME: should test that it's not a relative URL or something
+    try:
+        from urllib import urlencode, urlopen
+    except ImportError: # Python 3
+        from urllib.request import urlopen
+        from urllib.parse import urlencode
+    if method == 'GET':
+        if '?' in url:
+            url += '&'
+        else:
+            url += '?'
+        url += urlencode(values)
+        data = None
+    else:
+        data = urlencode(values)
+        if not isinstance(data, bytes):
+            data = data.encode('ASCII')
+    return urlopen(url, data)
+
+
+class FieldsDict(MutableMapping):
+
+    def __init__(self, inputs):
+        self.inputs = inputs
+    def __getitem__(self, item):
+        return self.inputs[item].value
+    def __setitem__(self, item, value):
+        self.inputs[item].value = value
+    def __delitem__(self, item):
+        raise KeyError(
+            "You cannot remove keys from ElementDict")
+    def keys(self):
+        return self.inputs.keys()
+    def __contains__(self, item):
+        return item in self.inputs
+    def __iter__(self):
+        return iter(self.inputs.keys())
+    def __len__(self):
+        return len(self.inputs)
+
+    def __repr__(self):
+        return '<%s for form %s>' % (
+            self.__class__.__name__,
+            self.inputs.form._name())
+
+
+class InputGetter:
+
+    """
+    An accessor that represents all the input fields in a form.
+
+    You can get fields by name from this, with
+    ``form.inputs['field_name']``.  If there are a set of checkboxes
+    with the same name, they are returned as a list (a `CheckboxGroup`
+    which also allows value setting).  Radio inputs are handled
+    similarly.  Use ``.keys()`` and ``.items()`` to process all fields
+    in this way.
+
+    You can also iterate over this to get all input elements.  This
+    won't return the same thing as if you get all the names, as
+    checkboxes and radio elements are returned individually.
+    """
+
+    def __init__(self, form):
+        self.form = form
+
+    def __repr__(self):
+        return '<%s for form %s>' % (
+            self.__class__.__name__,
+            self.form._name())
+
+    ## FIXME: there should be more methods, and it's unclear if this is
+    ## a dictionary-like object or list-like object
+
+    def __getitem__(self, name):
+        fields = [field for field in self if field.name == name]
+        if not fields:
+            raise KeyError("No input element with the name %r" % name)
+
+        input_type = fields[0].get('type')
+        if input_type == 'radio' and len(fields) > 1:
+            group = RadioGroup(fields)
+            group.name = name
+            return group
+        elif input_type == 'checkbox' and len(fields) > 1:
+            group = CheckboxGroup(fields)
+            group.name = name
+            return group
+        else:
+            # I don't like throwing away elements like this
+            return fields[0]
+
+    def __contains__(self, name):
+        for field in self:
+            if field.name == name:
+                return True
+        return False
+
+    def keys(self):
+        """
+        Returns all unique field names, in document order.
+
+        :return: A list of all unique field names.
+        """
+        names = []
+        seen = {None}
+        for el in self:
+            name = el.name
+            if name not in seen:
+                names.append(name)
+                seen.add(name)
+        return names
+
+    def items(self):
+        """
+        Returns all fields with their names, similar to dict.items().
+
+        :return: A list of (name, field) tuples.
+        """
+        items = []
+        seen = set()
+        for el in self:
+            name = el.name
+            if name not in seen:
+                seen.add(name)
+                items.append((name, self[name]))
+        return items
+
+    def __iter__(self):
+        return self.form.iter('select', 'input', 'textarea')
+
+    def __len__(self):
+        return sum(1 for _ in self)
+
+
+class InputMixin:
+    """
+    Mix-in for all input elements (input, select, and textarea)
+    """
+    @property
+    def name(self):
+        """
+        Get/set the name of the element
+        """
+        return self.get('name')
+
+    @name.setter
+    def name(self, value):
+        self.set('name', value)
+
+    @name.deleter
+    def name(self):
+        attrib = self.attrib
+        if 'name' in attrib:
+            del attrib['name']
+
+    def __repr__(self):
+        type_name = getattr(self, 'type', None)
+        if type_name:
+            type_name = ' type=%r' % type_name
+        else:
+            type_name = ''
+        return '<%s %x name=%r%s>' % (
+            self.__class__.__name__, id(self), self.name, type_name)
+
+
+class TextareaElement(InputMixin, HtmlElement):
+    """
+    ``<textarea>`` element.  You can get the name with ``.name`` and
+    get/set the value with ``.value``
+    """
+    @property
+    def value(self):
+        """
+        Get/set the value (which is the contents of this element)
+        """
+        content = self.text or ''
+        if self.tag.startswith("{%s}" % XHTML_NAMESPACE):
+            serialisation_method = 'xml'
+        else:
+            serialisation_method = 'html'
+        for el in self:
+            # it's rare that we actually get here, so let's not use ''.join()
+            content += etree.tostring(
+                el, method=serialisation_method, encoding='unicode')
+        return content
+
+    @value.setter
+    def value(self, value):
+        del self[:]
+        self.text = value
+
+    @value.deleter
+    def value(self):
+        self.text = ''
+        del self[:]
+
+
+HtmlElementClassLookup._default_element_classes['textarea'] = TextareaElement
+
+
+class SelectElement(InputMixin, HtmlElement):
+    """
+    ``<select>`` element.  You can get the name with ``.name``.
+
+    ``.value`` will be the value of the selected option, unless this
+    is a multi-select element (``<select multiple>``), in which case
+    it will be a set-like object.  In either case ``.value_options``
+    gives the possible values.
+
+    The boolean attribute ``.multiple`` shows if this is a
+    multi-select.
+    """
+    @property
+    def value(self):
+        """
+        Get/set the value of this select (the selected option).
+
+        If this is a multi-select, this is a set-like object that
+        represents all the selected options.
+        """
+        if self.multiple:
+            return MultipleSelectOptions(self)
+        options = _options_xpath(self)
+
+        try:
+            selected_option = next(el for el in reversed(options) if el.get('selected') is not None)
+        except StopIteration:
+            try:
+                selected_option = next(el for el in options if el.get('disabled') is None)
+            except StopIteration:
+                return None
+        value = selected_option.get('value')
+        if value is None:
+            value = (selected_option.text or '').strip()
+        return value
+
+    @value.setter
+    def value(self, value):
+        if self.multiple:
+            if isinstance(value, str):
+                raise TypeError("You must pass in a sequence")
+            values = self.value
+            values.clear()
+            values.update(value)
+            return
+        checked_option = None
+        if value is not None:
+            for el in _options_xpath(self):
+                opt_value = el.get('value')
+                if opt_value is None:
+                    opt_value = (el.text or '').strip()
+                if opt_value == value:
+                    checked_option = el
+                    break
+            else:
+                raise ValueError(
+                    "There is no option with the value of %r" % value)
+        for el in _options_xpath(self):
+            if 'selected' in el.attrib:
+                del el.attrib['selected']
+        if checked_option is not None:
+            checked_option.set('selected', '')
+
+    @value.deleter
+    def value(self):
+        # FIXME: should del be allowed at all?
+        if self.multiple:
+            self.value.clear()
+        else:
+            self.value = None
+
+    @property
+    def value_options(self):
+        """
+        All the possible values this select can have (the ``value``
+        attribute of all the ``<option>`` elements.
+        """
+        options = []
+        for el in _options_xpath(self):
+            value = el.get('value')
+            if value is None:
+                value = (el.text or '').strip()
+            options.append(value)
+        return options
+
+    @property
+    def multiple(self):
+        """
+        Boolean attribute: is there a ``multiple`` attribute on this element.
+        """
+        return 'multiple' in self.attrib
+
+    @multiple.setter
+    def multiple(self, value):
+        if value:
+            self.set('multiple', '')
+        elif 'multiple' in self.attrib:
+            del self.attrib['multiple']
+
+
+HtmlElementClassLookup._default_element_classes['select'] = SelectElement
+
+
+class MultipleSelectOptions(SetMixin):
+    """
+    Represents all the selected options in a ``<select multiple>`` element.
+
+    You can add to this set-like option to select an option, or remove
+    to unselect the option.
+    """
+
+    def __init__(self, select):
+        self.select = select
+
+    @property
+    def options(self):
+        """
+        Iterator of all the ``<option>`` elements.
+        """
+        return iter(_options_xpath(self.select))
+
+    def __iter__(self):
+        for option in self.options:
+            if 'selected' in option.attrib:
+                opt_value = option.get('value')
+                if opt_value is None:
+                    opt_value = (option.text or '').strip()
+                yield opt_value
+
+    def add(self, item):
+        for option in self.options:
+            opt_value = option.get('value')
+            if opt_value is None:
+                opt_value = (option.text or '').strip()
+            if opt_value == item:
+                option.set('selected', '')
+                break
+        else:
+            raise ValueError(
+                "There is no option with the value %r" % item)
+
+    def remove(self, item):
+        for option in self.options:
+            opt_value = option.get('value')
+            if opt_value is None:
+                opt_value = (option.text or '').strip()
+            if opt_value == item:
+                if 'selected' in option.attrib:
+                    del option.attrib['selected']
+                else:
+                    raise ValueError(
+                        "The option %r is not currently selected" % item)
+                break
+        else:
+            raise ValueError(
+                "There is not option with the value %r" % item)
+
+    def __repr__(self):
+        return '<%s {%s} for select name=%r>' % (
+            self.__class__.__name__,
+            ', '.join([repr(v) for v in self]),
+            self.select.name)
+
+
+class RadioGroup(list):
+    """
+    This object represents several ``<input type=radio>`` elements
+    that have the same name.
+
+    You can use this like a list, but also use the property
+    ``.value`` to check/uncheck inputs.  Also you can use
+    ``.value_options`` to get the possible values.
+    """
+    @property
+    def value(self):
+        """
+        Get/set the value, which checks the radio with that value (and
+        unchecks any other value).
+        """
+        for el in self:
+            if 'checked' in el.attrib:
+                return el.get('value')
+        return None
+
+    @value.setter
+    def value(self, value):
+        checked_option = None
+        if value is not None:
+            for el in self:
+                if el.get('value') == value:
+                    checked_option = el
+                    break
+            else:
+                raise ValueError("There is no radio input with the value %r" % value)
+        for el in self:
+            if 'checked' in el.attrib:
+                del el.attrib['checked']
+        if checked_option is not None:
+            checked_option.set('checked', '')
+
+    @value.deleter
+    def value(self):
+        self.value = None
+
+    @property
+    def value_options(self):
+        """
+        Returns a list of all the possible values.
+        """
+        return [el.get('value') for el in self]
+
+    def __repr__(self):
+        return '%s(%s)' % (
+            self.__class__.__name__,
+            list.__repr__(self))
+
+
+class CheckboxGroup(list):
+    """
+    Represents a group of checkboxes (``<input type=checkbox>``) that
+    have the same name.
+
+    In addition to using this like a list, the ``.value`` attribute
+    returns a set-like object that you can add to or remove from to
+    check and uncheck checkboxes.  You can also use ``.value_options``
+    to get the possible values.
+    """
+    @property
+    def value(self):
+        """
+        Return a set-like object that can be modified to check or
+        uncheck individual checkboxes according to their value.
+        """
+        return CheckboxValues(self)
+
+    @value.setter
+    def value(self, value):
+        values = self.value
+        values.clear()
+        if not hasattr(value, '__iter__'):
+            raise ValueError(
+                "A CheckboxGroup (name=%r) must be set to a sequence (not %r)"
+                % (self[0].name, value))
+        values.update(value)
+
+    @value.deleter
+    def value(self):
+        self.value.clear()
+
+    @property
+    def value_options(self):
+        """
+        Returns a list of all the possible values.
+        """
+        return [el.get('value') for el in self]
+
+    def __repr__(self):
+        return '%s(%s)' % (
+            self.__class__.__name__, list.__repr__(self))
+
+
+class CheckboxValues(SetMixin):
+    """
+    Represents the values of the checked checkboxes in a group of
+    checkboxes with the same name.
+    """
+
+    def __init__(self, group):
+        self.group = group
+
+    def __iter__(self):
+        return iter([
+            el.get('value')
+            for el in self.group
+            if 'checked' in el.attrib])
+
+    def add(self, value):
+        for el in self.group:
+            if el.get('value') == value:
+                el.set('checked', '')
+                break
+        else:
+            raise KeyError("No checkbox with value %r" % value)
+
+    def remove(self, value):
+        for el in self.group:
+            if el.get('value') == value:
+                if 'checked' in el.attrib:
+                    del el.attrib['checked']
+                else:
+                    raise KeyError(
+                        "The checkbox with value %r was already unchecked" % value)
+                break
+        else:
+            raise KeyError(
+                "No checkbox with value %r" % value)
+
+    def __repr__(self):
+        return '<%s {%s} for checkboxes name=%r>' % (
+            self.__class__.__name__,
+            ', '.join([repr(v) for v in self]),
+            self.group.name)
+
+
+class InputElement(InputMixin, HtmlElement):
+    """
+    Represents an ``<input>`` element.
+
+    You can get the type with ``.type`` (which is lower-cased and
+    defaults to ``'text'``).
+
+    Also you can get and set the value with ``.value``
+
+    Checkboxes and radios have the attribute ``input.checkable ==
+    True`` (for all others it is false) and a boolean attribute
+    ``.checked``.
+
+    """
+
+    ## FIXME: I'm a little uncomfortable with the use of .checked
+    @property
+    def value(self):
+        """
+        Get/set the value of this element, using the ``value`` attribute.
+
+        Also, if this is a checkbox and it has no value, this defaults
+        to ``'on'``.  If it is a checkbox or radio that is not
+        checked, this returns None.
+        """
+        if self.checkable:
+            if self.checked:
+                return self.get('value') or 'on'
+            else:
+                return None
+        return self.get('value')
+
+    @value.setter
+    def value(self, value):
+        if self.checkable:
+            if not value:
+                self.checked = False
+            else:
+                self.checked = True
+                if isinstance(value, str):
+                    self.set('value', value)
+        else:
+            self.set('value', value)
+
+    @value.deleter
+    def value(self):
+        if self.checkable:
+            self.checked = False
+        else:
+            if 'value' in self.attrib:
+                del self.attrib['value']
+
+    @property
+    def type(self):
+        """
+        Return the type of this element (using the type attribute).
+        """
+        return self.get('type', 'text').lower()
+
+    @type.setter
+    def type(self, value):
+        self.set('type', value)
+
+    @property
+    def checkable(self):
+        """
+        Boolean: can this element be checked?
+        """
+        return self.type in ('checkbox', 'radio')
+
+    @property
+    def checked(self):
+        """
+        Boolean attribute to get/set the presence of the ``checked``
+        attribute.
+
+        You can only use this on checkable input types.
+        """
+        if not self.checkable:
+            raise AttributeError('Not a checkable input type')
+        return 'checked' in self.attrib
+
+    @checked.setter
+    def checked(self, value):
+        if not self.checkable:
+            raise AttributeError('Not a checkable input type')
+        if value:
+            self.set('checked', '')
+        else:
+            attrib = self.attrib
+            if 'checked' in attrib:
+                del attrib['checked']
+
+
+HtmlElementClassLookup._default_element_classes['input'] = InputElement
+
+
+class LabelElement(HtmlElement):
+    """
+    Represents a ``<label>`` element.
+
+    Label elements are linked to other elements with their ``for``
+    attribute.  You can access this element with ``label.for_element``.
+    """
+    @property
+    def for_element(self):
+        """
+        Get/set the element this label points to.  Return None if it
+        can't be found.
+        """
+        id = self.get('for')
+        if not id:
+            return None
+        return self.body.get_element_by_id(id)
+
+    @for_element.setter
+    def for_element(self, other):
+        id = other.get('id')
+        if not id:
+            raise TypeError(
+                "Element %r has no id attribute" % other)
+        self.set('for', id)
+
+    @for_element.deleter
+    def for_element(self):
+        attrib = self.attrib
+        if 'id' in attrib:
+            del attrib['id']
+
+
+HtmlElementClassLookup._default_element_classes['label'] = LabelElement
+
+
+############################################################
+## Serialization
+############################################################
+
+def html_to_xhtml(html):
+    """Convert all tags in an HTML tree to XHTML by moving them to the
+    XHTML namespace.
+    """
+    try:
+        html = html.getroot()
+    except AttributeError:
+        pass
+    prefix = "{%s}" % XHTML_NAMESPACE
+    for el in html.iter(etree.Element):
+        tag = el.tag
+        if tag[0] != '{':
+            el.tag = prefix + tag
+
+
+def xhtml_to_html(xhtml):
+    """Convert all tags in an XHTML tree to HTML by removing their
+    XHTML namespace.
+    """
+    try:
+        xhtml = xhtml.getroot()
+    except AttributeError:
+        pass
+    prefix = "{%s}" % XHTML_NAMESPACE
+    prefix_len = len(prefix)
+    for el in xhtml.iter(prefix + "*"):
+        el.tag = el.tag[prefix_len:]
+
+
+# This isn't a general match, but it's a match for what libxml2
+# specifically serialises:
+__str_replace_meta_content_type = re.compile(
+    r'<meta http-equiv="Content-Type"[^>]*>').sub
+__bytes_replace_meta_content_type = re.compile(
+    br'<meta http-equiv="Content-Type"[^>]*>').sub
+
+
+def tostring(doc, pretty_print=False, include_meta_content_type=False,
+             encoding=None, method="html", with_tail=True, doctype=None):
+    """Return an HTML string representation of the document.
+
+    Note: if include_meta_content_type is true this will create a
+    ``<meta http-equiv="Content-Type" ...>`` tag in the head;
+    regardless of the value of include_meta_content_type any existing
+    ``<meta http-equiv="Content-Type" ...>`` tag will be removed
+
+    The ``encoding`` argument controls the output encoding (defaults to
+    ASCII, with &#...; character references for any characters outside
+    of ASCII).  Note that you can pass the name ``'unicode'`` as
+    ``encoding`` argument to serialise to a Unicode string.
+
+    The ``method`` argument defines the output method.  It defaults to
+    'html', but can also be 'xml' for xhtml output, or 'text' to
+    serialise to plain text without markup.
+
+    To leave out the tail text of the top-level element that is being
+    serialised, pass ``with_tail=False``.
+
+    The ``doctype`` option allows passing in a plain string that will
+    be serialised before the XML tree.  Note that passing in non
+    well-formed content here will make the XML output non well-formed.
+    Also, an existing doctype in the document tree will not be removed
+    when serialising an ElementTree instance.
+
+    Example::
+
+        >>> from lxml import html
+        >>> root = html.fragment_fromstring('<p>Hello<br>world!</p>')
+
+        >>> html.tostring(root)
+        b'<p>Hello<br>world!</p>'
+        >>> html.tostring(root, method='html')
+        b'<p>Hello<br>world!</p>'
+
+        >>> html.tostring(root, method='xml')
+        b'<p>Hello<br/>world!</p>'
+
+        >>> html.tostring(root, method='text')
+        b'Helloworld!'
+
+        >>> html.tostring(root, method='text', encoding='unicode')
+        u'Helloworld!'
+
+        >>> root = html.fragment_fromstring('<div><p>Hello<br>world!</p>TAIL</div>')
+        >>> html.tostring(root[0], method='text', encoding='unicode')
+        u'Helloworld!TAIL'
+
+        >>> html.tostring(root[0], method='text', encoding='unicode', with_tail=False)
+        u'Helloworld!'
+
+        >>> doc = html.document_fromstring('<p>Hello<br>world!</p>')
+        >>> html.tostring(doc, method='html', encoding='unicode')
+        u'<html><body><p>Hello<br>world!</p></body></html>'
+
+        >>> print(html.tostring(doc, method='html', encoding='unicode',
+        ...          doctype='<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"'
+        ...                  ' "http://www.w3.org/TR/html4/strict.dtd">'))
+        <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+        <html><body><p>Hello<br>world!</p></body></html>
+    """
+    html = etree.tostring(doc, method=method, pretty_print=pretty_print,
+                          encoding=encoding, with_tail=with_tail,
+                          doctype=doctype)
+    if method == 'html' and not include_meta_content_type:
+        if isinstance(html, str):
+            html = __str_replace_meta_content_type('', html)
+        else:
+            html = __bytes_replace_meta_content_type(b'', html)
+    return html
+
+
+tostring.__doc__ = __fix_docstring(tostring.__doc__)
+
+
+def open_in_browser(doc, encoding=None):
+    """
+    Open the HTML document in a web browser, saving it to a temporary
+    file to open it.  Note that this does not delete the file after
+    use.  This is mainly meant for debugging.
+    """
+    import os
+    import webbrowser
+    import tempfile
+    if not isinstance(doc, etree._ElementTree):
+        doc = etree.ElementTree(doc)
+    handle, fn = tempfile.mkstemp(suffix='.html')
+    f = os.fdopen(handle, 'wb')
+    try:
+        doc.write(f, method="html", encoding=encoding or doc.docinfo.encoding or "UTF-8")
+    finally:
+        # we leak the file itself here, but we should at least close it
+        f.close()
+    url = 'file://' + fn.replace(os.path.sep, '/')
+    print(url)
+    webbrowser.open(url)
+
+
+################################################################################
+# configure Element class lookup
+################################################################################
+
+class HTMLParser(etree.HTMLParser):
+    """An HTML parser that is configured to return lxml.html Element
+    objects.
+    """
+    def __init__(self, **kwargs):
+        super().__init__(**kwargs)
+        self.set_element_class_lookup(HtmlElementClassLookup())
+
+
+class XHTMLParser(etree.XMLParser):
+    """An XML parser that is configured to return lxml.html Element
+    objects.
+
+    Note that this parser is not really XHTML aware unless you let it
+    load a DTD that declares the HTML entities.  To do this, make sure
+    you have the XHTML DTDs installed in your catalogs, and create the
+    parser like this::
+
+        >>> parser = XHTMLParser(load_dtd=True)
+
+    If you additionally want to validate the document, use this::
+
+        >>> parser = XHTMLParser(dtd_validation=True)
+
+    For catalog support, see http://www.xmlsoft.org/catalog.html.
+    """
+    def __init__(self, **kwargs):
+        super().__init__(**kwargs)
+        self.set_element_class_lookup(HtmlElementClassLookup())
+
+
+def Element(*args, **kw):
+    """Create a new HTML Element.
+
+    This can also be used for XHTML documents.
+    """
+    v = html_parser.makeelement(*args, **kw)
+    return v
+
+
+html_parser = HTMLParser()
+xhtml_parser = XHTMLParser()
diff --git a/.venv/lib/python3.12/site-packages/lxml/html/_diffcommand.py b/.venv/lib/python3.12/site-packages/lxml/html/_diffcommand.py
new file mode 100644
index 00000000..b045a2b1
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/html/_diffcommand.py
@@ -0,0 +1,86 @@
+import optparse
+import sys
+import re
+import os
+from .diff import htmldiff
+
+description = """\
+"""
+
+parser = optparse.OptionParser(
+    usage="%prog [OPTIONS] FILE1 FILE2\n"
+    "%prog --annotate [OPTIONS] INFO1 FILE1 INFO2 FILE2 ...",
+    description=description,
+    )
+
+parser.add_option(
+    '-o', '--output',
+    metavar="FILE",
+    dest="output",
+    default="-",
+    help="File to write the difference to",
+    )
+
+parser.add_option(
+    '-a', '--annotation',
+    action="store_true",
+    dest="annotation",
+    help="Do an annotation")
+
+def main(args=None):
+    if args is None:
+        args = sys.argv[1:]
+    options, args = parser.parse_args(args)
+    if options.annotation:
+        return annotate(options, args)
+    if len(args) != 2:
+        print('Error: you must give two files')
+        parser.print_help()
+        sys.exit(1)
+    file1, file2 = args
+    input1 = read_file(file1)
+    input2 = read_file(file2)
+    body1 = split_body(input1)[1]
+    pre, body2, post = split_body(input2)
+    result = htmldiff(body1, body2)
+    result = pre + result + post
+    if options.output == '-':
+        if not result.endswith('\n'):
+            result += '\n'
+        sys.stdout.write(result)
+    else:
+        with open(options.output, 'wb') as f:
+            f.write(result)
+
+def read_file(filename):
+    if filename == '-':
+        c = sys.stdin.read()
+    elif not os.path.exists(filename):
+        raise OSError(
+            "Input file %s does not exist" % filename)
+    else:
+        with open(filename, 'rb') as f:
+            c = f.read()
+    return c
+
+body_start_re = re.compile(
+    r"<body.*?>", re.I|re.S)
+body_end_re = re.compile(
+    r"</body.*?>", re.I|re.S)
+    
+def split_body(html):
+    pre = post = ''
+    match = body_start_re.search(html)
+    if match:
+        pre = html[:match.end()]
+        html = html[match.end():]
+    match = body_end_re.search(html)
+    if match:
+        post = html[match.start():]
+        html = html[:match.start()]
+    return pre, html, post
+
+def annotate(options, args):
+    print("Not yet implemented")
+    sys.exit(1)
+    
diff --git a/.venv/lib/python3.12/site-packages/lxml/html/_html5builder.py b/.venv/lib/python3.12/site-packages/lxml/html/_html5builder.py
new file mode 100644
index 00000000..a88ed944
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/html/_html5builder.py
@@ -0,0 +1,100 @@
+"""
+Legacy module - don't use in new code!
+
+html5lib now has its own proper implementation.
+
+This module implements a tree builder for html5lib that generates lxml
+html element trees.  This module uses camelCase as it follows the
+html5lib style guide.
+"""
+
+from html5lib.treebuilders import _base, etree as etree_builders
+from lxml import html, etree
+
+
+class DocumentType:
+
+    def __init__(self, name, publicId, systemId):
+        self.name = name
+        self.publicId = publicId
+        self.systemId = systemId
+
+class Document:
+
+    def __init__(self):
+        self._elementTree = None
+        self.childNodes = []
+
+    def appendChild(self, element):
+        self._elementTree.getroot().addnext(element._element)
+
+
+class TreeBuilder(_base.TreeBuilder):
+    documentClass = Document
+    doctypeClass = DocumentType
+    elementClass = None
+    commentClass = None
+    fragmentClass = Document
+
+    def __init__(self, *args, **kwargs):
+        html_builder = etree_builders.getETreeModule(html, fullTree=False)
+        etree_builder = etree_builders.getETreeModule(etree, fullTree=False)
+        self.elementClass = html_builder.Element
+        self.commentClass = etree_builder.Comment
+        _base.TreeBuilder.__init__(self, *args, **kwargs)
+
+    def reset(self):
+        _base.TreeBuilder.reset(self)
+        self.rootInserted = False
+        self.initialComments = []
+        self.doctype = None
+
+    def getDocument(self):
+        return self.document._elementTree
+
+    def getFragment(self):
+        fragment = []
+        element = self.openElements[0]._element
+        if element.text:
+            fragment.append(element.text)
+        fragment.extend(element.getchildren())
+        if element.tail:
+            fragment.append(element.tail)
+        return fragment
+
+    def insertDoctype(self, name, publicId, systemId):
+        doctype = self.doctypeClass(name, publicId, systemId)
+        self.doctype = doctype
+
+    def insertComment(self, data, parent=None):
+        if not self.rootInserted:
+            self.initialComments.append(data)
+        else:
+            _base.TreeBuilder.insertComment(self, data, parent)
+
+    def insertRoot(self, name):
+        buf = []
+        if self.doctype and self.doctype.name:
+            buf.append('<!DOCTYPE %s' % self.doctype.name)
+            if self.doctype.publicId is not None or self.doctype.systemId is not None:
+                buf.append(' PUBLIC "%s" "%s"' % (self.doctype.publicId,
+                                                  self.doctype.systemId))
+            buf.append('>')
+        buf.append('<html></html>')
+        root = html.fromstring(''.join(buf))
+
+        # Append the initial comments:
+        for comment in self.initialComments:
+            root.addprevious(etree.Comment(comment))
+
+        # Create the root document and add the ElementTree to it
+        self.document = self.documentClass()
+        self.document._elementTree = root.getroottree()
+
+        # Add the root element to the internal child/open data structures
+        root_element = self.elementClass(name)
+        root_element._element = root
+        self.document.childNodes.append(root_element)
+        self.openElements.append(root_element)
+
+        self.rootInserted = True
diff --git a/.venv/lib/python3.12/site-packages/lxml/html/_setmixin.py b/.venv/lib/python3.12/site-packages/lxml/html/_setmixin.py
new file mode 100644
index 00000000..0be2bac4
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/html/_setmixin.py
@@ -0,0 +1,56 @@
+try:
+    from collections.abc import MutableSet
+except ImportError:
+    from collections.abc import MutableSet
+
+
+class SetMixin(MutableSet):
+
+    """
+    Mix-in for sets.  You must define __iter__, add, remove
+    """
+
+    def __len__(self):
+        length = 0
+        for item in self:
+            length += 1
+        return length
+
+    def __contains__(self, item):
+        for has_item in self:
+            if item == has_item:
+                return True
+        return False
+
+    issubset = MutableSet.__le__
+    issuperset = MutableSet.__ge__
+
+    union = MutableSet.__or__
+    intersection = MutableSet.__and__
+    difference = MutableSet.__sub__
+    symmetric_difference = MutableSet.__xor__
+
+    def copy(self):
+        return set(self)
+
+    def update(self, other):
+        self |= other
+
+    def intersection_update(self, other):
+        self &= other
+
+    def difference_update(self, other):
+        self -= other
+
+    def symmetric_difference_update(self, other):
+        self ^= other
+
+    def discard(self, item):
+        try:
+            self.remove(item)
+        except KeyError:
+            pass
+
+    @classmethod
+    def _from_iterable(cls, it):
+        return set(it)
diff --git a/.venv/lib/python3.12/site-packages/lxml/html/builder.py b/.venv/lib/python3.12/site-packages/lxml/html/builder.py
new file mode 100644
index 00000000..8a074ecf
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/html/builder.py
@@ -0,0 +1,133 @@
+# --------------------------------------------------------------------
+# The ElementTree toolkit is
+# Copyright (c) 1999-2004 by Fredrik Lundh
+# --------------------------------------------------------------------
+
+"""
+A set of HTML generator tags for building HTML documents.
+
+Usage::
+
+    >>> from lxml.html.builder import *
+    >>> html = HTML(
+    ...            HEAD( TITLE("Hello World") ),
+    ...            BODY( CLASS("main"),
+    ...                  H1("Hello World !")
+    ...            )
+    ...        )
+
+    >>> import lxml.etree
+    >>> print lxml.etree.tostring(html, pretty_print=True)
+    <html>
+      <head>
+        <title>Hello World</title>
+      </head>
+      <body class="main">
+        <h1>Hello World !</h1>
+      </body>
+    </html>
+
+"""
+
+from lxml.builder import ElementMaker
+from lxml.html import html_parser
+
+E = ElementMaker(makeelement=html_parser.makeelement)
+
+# elements
+A = E.a  #: anchor
+ABBR = E.abbr  #: abbreviated form (e.g., WWW, HTTP, etc.)
+ACRONYM = E.acronym  #: 
+ADDRESS = E.address  #: information on author
+APPLET = E.applet  #: Java applet (DEPRECATED)
+AREA = E.area  #: client-side image map area
+B = E.b  #: bold text style
+BASE = E.base  #: document base URI
+BASEFONT = E.basefont  #: base font size (DEPRECATED)
+BDO = E.bdo  #: I18N BiDi over-ride
+BIG = E.big  #: large text style
+BLOCKQUOTE = E.blockquote  #: long quotation
+BODY = E.body  #: document body
+BR = E.br  #: forced line break
+BUTTON = E.button  #: push button
+CAPTION = E.caption  #: table caption
+CENTER = E.center  #: shorthand for DIV align=center (DEPRECATED)
+CITE = E.cite  #: citation
+CODE = E.code  #: computer code fragment
+COL = E.col  #: table column
+COLGROUP = E.colgroup  #: table column group
+DD = E.dd  #: definition description
+DEL = getattr(E, 'del')  #: deleted text
+DFN = E.dfn  #: instance definition
+DIR = E.dir  #: directory list (DEPRECATED)
+DIV = E.div  #: generic language/style container
+DL = E.dl  #: definition list
+DT = E.dt  #: definition term
+EM = E.em  #: emphasis
+FIELDSET = E.fieldset  #: form control group
+FONT = E.font  #: local change to font (DEPRECATED)
+FORM = E.form  #: interactive form
+FRAME = E.frame  #: subwindow
+FRAMESET = E.frameset  #: window subdivision
+H1 = E.h1  #: heading
+H2 = E.h2  #: heading
+H3 = E.h3  #: heading
+H4 = E.h4  #: heading
+H5 = E.h5  #: heading
+H6 = E.h6  #: heading
+HEAD = E.head  #: document head
+HR = E.hr  #: horizontal rule
+HTML = E.html  #: document root element
+I = E.i  #: italic text style
+IFRAME = E.iframe  #: inline subwindow
+IMG = E.img  #: Embedded image
+INPUT = E.input  #: form control
+INS = E.ins  #: inserted text
+ISINDEX = E.isindex  #: single line prompt (DEPRECATED)
+KBD = E.kbd  #: text to be entered by the user
+LABEL = E.label  #: form field label text
+LEGEND = E.legend  #: fieldset legend
+LI = E.li  #: list item
+LINK = E.link  #: a media-independent link
+MAP = E.map  #: client-side image map
+MENU = E.menu  #: menu list (DEPRECATED)
+META = E.meta  #: generic metainformation
+NOFRAMES = E.noframes  #: alternate content container for non frame-based rendering
+NOSCRIPT = E.noscript  #: alternate content container for non script-based rendering
+OBJECT = E.object  #: generic embedded object
+OL = E.ol  #: ordered list
+OPTGROUP = E.optgroup  #: option group
+OPTION = E.option  #: selectable choice
+P = E.p  #: paragraph
+PARAM = E.param  #: named property value
+PRE = E.pre  #: preformatted text
+Q = E.q  #: short inline quotation
+S = E.s  #: strike-through text style (DEPRECATED)
+SAMP = E.samp  #: sample program output, scripts, etc.
+SCRIPT = E.script  #: script statements
+SELECT = E.select  #: option selector
+SMALL = E.small  #: small text style
+SPAN = E.span  #: generic language/style container
+STRIKE = E.strike  #: strike-through text (DEPRECATED)
+STRONG = E.strong  #: strong emphasis
+STYLE = E.style  #: style info
+SUB = E.sub  #: subscript
+SUP = E.sup  #: superscript
+TABLE = E.table  #: 
+TBODY = E.tbody  #: table body
+TD = E.td  #: table data cell
+TEXTAREA = E.textarea  #: multi-line text field
+TFOOT = E.tfoot  #: table footer
+TH = E.th  #: table header cell
+THEAD = E.thead  #: table header
+TITLE = E.title  #: document title
+TR = E.tr  #: table row
+TT = E.tt  #: teletype or monospaced text style
+U = E.u  #: underlined text style (DEPRECATED)
+UL = E.ul  #: unordered list
+VAR = E.var  #: instance of a variable or program argument
+
+# attributes (only reserved words are included here)
+ATTR = dict
+def CLASS(v): return {'class': v}
+def FOR(v): return {'for': v}
diff --git a/.venv/lib/python3.12/site-packages/lxml/html/clean.py b/.venv/lib/python3.12/site-packages/lxml/html/clean.py
new file mode 100644
index 00000000..d4b9e96d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/html/clean.py
@@ -0,0 +1,21 @@
+# cython: language_level=3str
+
+"""Backward-compatibility module for lxml_html_clean"""
+
+try:
+    from lxml_html_clean import *
+
+    __all__ = [
+        "clean_html",
+        "clean",
+        "Cleaner",
+        "autolink",
+        "autolink_html",
+        "word_break",
+        "word_break_html",
+    ]
+except ImportError:
+    raise ImportError(
+        "lxml.html.clean module is now a separate project lxml_html_clean.\n"
+        "Install lxml[html_clean] or lxml_html_clean directly."
+    ) from None
diff --git a/.venv/lib/python3.12/site-packages/lxml/html/defs.py b/.venv/lib/python3.12/site-packages/lxml/html/defs.py
new file mode 100644
index 00000000..2058ea33
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/html/defs.py
@@ -0,0 +1,135 @@
+# FIXME: this should all be confirmed against what a DTD says
+# (probably in a test; this may not match the DTD exactly, but we
+# should document just how it differs).
+
+"""
+Data taken from https://www.w3.org/TR/html401/index/elements.html
+and https://www.w3.org/community/webed/wiki/HTML/New_HTML5_Elements
+for html5_tags.
+"""
+
+empty_tags = frozenset([
+    'area', 'base', 'basefont', 'br', 'col', 'frame', 'hr',
+    'img', 'input', 'isindex', 'link', 'meta', 'param', 'source', 'track'])
+
+deprecated_tags = frozenset([
+    'applet', 'basefont', 'center', 'dir', 'font', 'isindex',
+    'menu', 's', 'strike', 'u'])
+
+# archive actually takes a space-separated list of URIs
+link_attrs = frozenset([
+    'action', 'archive', 'background', 'cite', 'classid',
+    'codebase', 'data', 'href', 'longdesc', 'profile', 'src',
+    'usemap',
+    # Not standard:
+    'dynsrc', 'lowsrc',
+    # HTML5 formaction
+    'formaction'
+    ])
+
+# Not in the HTML 4 spec:
+# onerror, onresize
+event_attrs = frozenset([
+    'onblur', 'onchange', 'onclick', 'ondblclick', 'onerror',
+    'onfocus', 'onkeydown', 'onkeypress', 'onkeyup', 'onload',
+    'onmousedown', 'onmousemove', 'onmouseout', 'onmouseover',
+    'onmouseup', 'onreset', 'onresize', 'onselect', 'onsubmit',
+    'onunload',
+    ])
+
+safe_attrs = frozenset([
+    'abbr', 'accept', 'accept-charset', 'accesskey', 'action', 'align',
+    'alt', 'axis', 'border', 'cellpadding', 'cellspacing', 'char', 'charoff',
+    'charset', 'checked', 'cite', 'class', 'clear', 'cols', 'colspan',
+    'color', 'compact', 'coords', 'datetime', 'dir', 'disabled', 'enctype',
+    'for', 'frame', 'headers', 'height', 'href', 'hreflang', 'hspace', 'id',
+    'ismap', 'label', 'lang', 'longdesc', 'maxlength', 'media', 'method',
+    'multiple', 'name', 'nohref', 'noshade', 'nowrap', 'prompt', 'readonly',
+    'rel', 'rev', 'rows', 'rowspan', 'rules', 'scope', 'selected', 'shape',
+    'size', 'span', 'src', 'start', 'summary', 'tabindex', 'target', 'title',
+    'type', 'usemap', 'valign', 'value', 'vspace', 'width'])
+
+# From http://htmlhelp.com/reference/html40/olist.html
+top_level_tags = frozenset([
+    'html', 'head', 'body', 'frameset',
+    ])
+
+head_tags = frozenset([
+    'base', 'isindex', 'link', 'meta', 'script', 'style', 'title',
+    ])
+
+general_block_tags = frozenset([
+    'address',
+    'blockquote',
+    'center',
+    'del',
+    'div',
+    'h1',
+    'h2',
+    'h3',
+    'h4',
+    'h5',
+    'h6',
+    'hr',
+    'ins',
+    'isindex',
+    'noscript',
+    'p',
+    'pre',
+    ])
+
+list_tags = frozenset([
+    'dir', 'dl', 'dt', 'dd', 'li', 'menu', 'ol', 'ul',
+    ])
+
+table_tags = frozenset([
+    'table', 'caption', 'colgroup', 'col',
+    'thead', 'tfoot', 'tbody', 'tr', 'td', 'th',
+    ])
+
+# just this one from
+# http://www.georgehernandez.com/h/XComputers/HTML/2BlockLevel.htm
+block_tags = general_block_tags | list_tags | table_tags | frozenset([
+    # Partial form tags
+    'fieldset', 'form', 'legend', 'optgroup', 'option',
+    ])
+
+form_tags = frozenset([
+    'form', 'button', 'fieldset', 'legend', 'input', 'label',
+    'select', 'optgroup', 'option', 'textarea',
+    ])
+
+special_inline_tags = frozenset([
+    'a', 'applet', 'basefont', 'bdo', 'br', 'embed', 'font', 'iframe',
+    'img', 'map', 'area', 'object', 'param', 'q', 'script',
+    'span', 'sub', 'sup',
+    ])
+
+phrase_tags = frozenset([
+    'abbr', 'acronym', 'cite', 'code', 'del', 'dfn', 'em',
+    'ins', 'kbd', 'samp', 'strong', 'var',
+    ])
+
+font_style_tags = frozenset([
+    'b', 'big', 'i', 's', 'small', 'strike', 'tt', 'u',
+    ])
+
+frame_tags = frozenset([
+    'frameset', 'frame', 'noframes',
+    ])
+    
+html5_tags = frozenset([
+    'article', 'aside', 'audio', 'canvas', 'command', 'datalist',
+    'details', 'embed', 'figcaption', 'figure', 'footer', 'header',
+    'hgroup', 'keygen', 'mark', 'math', 'meter', 'nav', 'output',
+    'progress', 'rp', 'rt', 'ruby', 'section', 'source', 'summary',
+    'svg', 'time', 'track', 'video', 'wbr'
+    ])
+
+# These tags aren't standard
+nonstandard_tags = frozenset(['blink', 'marquee'])
+
+
+tags = (top_level_tags | head_tags | general_block_tags | list_tags
+        | table_tags | form_tags | special_inline_tags | phrase_tags
+        | font_style_tags | nonstandard_tags | html5_tags)
diff --git a/.venv/lib/python3.12/site-packages/lxml/html/diff.cpython-312-x86_64-linux-gnu.so b/.venv/lib/python3.12/site-packages/lxml/html/diff.cpython-312-x86_64-linux-gnu.so
new file mode 100755
index 00000000..d43b3fef
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/html/diff.cpython-312-x86_64-linux-gnu.so
Binary files differdiff --git a/.venv/lib/python3.12/site-packages/lxml/html/diff.py b/.venv/lib/python3.12/site-packages/lxml/html/diff.py
new file mode 100644
index 00000000..56d28057
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/html/diff.py
@@ -0,0 +1,878 @@
+# cython: language_level=3
+
+
+import difflib
+from lxml import etree
+from lxml.html import fragment_fromstring
+import re
+
+__all__ = ['html_annotate', 'htmldiff']
+
+try:
+    from html import escape as html_escape
+except ImportError:
+    from cgi import escape as html_escape
+try:
+    _unicode = unicode
+except NameError:
+    # Python 3
+    _unicode = str
+try:
+    basestring
+except NameError:
+    # Python 3
+    basestring = str
+
+############################################################
+## Annotation
+############################################################
+
+def default_markup(text, version):
+    return '<span title="%s">%s</span>' % (
+        html_escape(_unicode(version), 1), text)
+
+def html_annotate(doclist, markup=default_markup):
+    """
+    doclist should be ordered from oldest to newest, like::
+
+        >>> version1 = 'Hello World'
+        >>> version2 = 'Goodbye World'
+        >>> print(html_annotate([(version1, 'version 1'),
+        ...                      (version2, 'version 2')]))
+        <span title="version 2">Goodbye</span> <span title="version 1">World</span>
+
+    The documents must be *fragments* (str/UTF8 or unicode), not
+    complete documents
+
+    The markup argument is a function to markup the spans of words.
+    This function is called like markup('Hello', 'version 2'), and
+    returns HTML.  The first argument is text and never includes any
+    markup.  The default uses a span with a title:
+
+        >>> print(default_markup('Some Text', 'by Joe'))
+        <span title="by Joe">Some Text</span>
+    """
+    # The basic strategy we have is to split the documents up into
+    # logical tokens (which are words with attached markup).  We then
+    # do diffs of each of the versions to track when a token first
+    # appeared in the document; the annotation attached to the token
+    # is the version where it first appeared.
+    tokenlist = [tokenize_annotated(doc, version)
+                 for doc, version in doclist]
+    cur_tokens = tokenlist[0]
+    for tokens in tokenlist[1:]:
+        html_annotate_merge_annotations(cur_tokens, tokens)
+        cur_tokens = tokens
+
+    # After we've tracked all the tokens, we can combine spans of text
+    # that are adjacent and have the same annotation
+    cur_tokens = compress_tokens(cur_tokens)
+    # And finally add markup
+    result = markup_serialize_tokens(cur_tokens, markup)
+    return ''.join(result).strip()
+
+def tokenize_annotated(doc, annotation): 
+    """Tokenize a document and add an annotation attribute to each token
+    """
+    tokens = tokenize(doc, include_hrefs=False)
+    for tok in tokens: 
+        tok.annotation = annotation
+    return tokens
+
+def html_annotate_merge_annotations(tokens_old, tokens_new): 
+    """Merge the annotations from tokens_old into tokens_new, when the
+    tokens in the new document already existed in the old document.
+    """
+    s = InsensitiveSequenceMatcher(a=tokens_old, b=tokens_new)
+    commands = s.get_opcodes()
+
+    for command, i1, i2, j1, j2 in commands:
+        if command == 'equal': 
+            eq_old = tokens_old[i1:i2]
+            eq_new = tokens_new[j1:j2]
+            copy_annotations(eq_old, eq_new)
+
+def copy_annotations(src, dest): 
+    """
+    Copy annotations from the tokens listed in src to the tokens in dest
+    """
+    assert len(src) == len(dest)
+    for src_tok, dest_tok in zip(src, dest): 
+        dest_tok.annotation = src_tok.annotation
+
+def compress_tokens(tokens):
+    """
+    Combine adjacent tokens when there is no HTML between the tokens, 
+    and they share an annotation
+    """
+    result = [tokens[0]] 
+    for tok in tokens[1:]: 
+        if (not result[-1].post_tags and 
+            not tok.pre_tags and 
+            result[-1].annotation == tok.annotation): 
+            compress_merge_back(result, tok)
+        else: 
+            result.append(tok)
+    return result
+
+def compress_merge_back(tokens, tok): 
+    """ Merge tok into the last element of tokens (modifying the list of
+    tokens in-place).  """
+    last = tokens[-1]
+    if type(last) is not token or type(tok) is not token: 
+        tokens.append(tok)
+    else:
+        text = _unicode(last)
+        if last.trailing_whitespace:
+            text += last.trailing_whitespace
+        text += tok
+        merged = token(text,
+                       pre_tags=last.pre_tags,
+                       post_tags=tok.post_tags,
+                       trailing_whitespace=tok.trailing_whitespace)
+        merged.annotation = last.annotation
+        tokens[-1] = merged
+    
+def markup_serialize_tokens(tokens, markup_func):
+    """
+    Serialize the list of tokens into a list of text chunks, calling
+    markup_func around text to add annotations.
+    """
+    for token in tokens:
+        yield from token.pre_tags
+        html = token.html()
+        html = markup_func(html, token.annotation)
+        if token.trailing_whitespace:
+            html += token.trailing_whitespace
+        yield html
+        yield from token.post_tags
+
+
+############################################################
+## HTML Diffs
+############################################################
+
+def htmldiff(old_html, new_html):
+    ## FIXME: this should take parsed documents too, and use their body
+    ## or other content.
+    """ Do a diff of the old and new document.  The documents are HTML
+    *fragments* (str/UTF8 or unicode), they are not complete documents
+    (i.e., no <html> tag).
+
+    Returns HTML with <ins> and <del> tags added around the
+    appropriate text.  
+
+    Markup is generally ignored, with the markup from new_html
+    preserved, and possibly some markup from old_html (though it is
+    considered acceptable to lose some of the old markup).  Only the
+    words in the HTML are diffed.  The exception is <img> tags, which
+    are treated like words, and the href attribute of <a> tags, which
+    are noted inside the tag itself when there are changes.
+    """ 
+    old_html_tokens = tokenize(old_html)
+    new_html_tokens = tokenize(new_html)
+    result = htmldiff_tokens(old_html_tokens, new_html_tokens)
+    result = ''.join(result).strip()
+    return fixup_ins_del_tags(result)
+
+def htmldiff_tokens(html1_tokens, html2_tokens):
+    """ Does a diff on the tokens themselves, returning a list of text
+    chunks (not tokens).
+    """
+    # There are several passes as we do the differences.  The tokens
+    # isolate the portion of the content we care to diff; difflib does
+    # all the actual hard work at that point.  
+    #
+    # Then we must create a valid document from pieces of both the old
+    # document and the new document.  We generally prefer to take
+    # markup from the new document, and only do a best effort attempt
+    # to keep markup from the old document; anything that we can't
+    # resolve we throw away.  Also we try to put the deletes as close
+    # to the location where we think they would have been -- because
+    # we are only keeping the markup from the new document, it can be
+    # fuzzy where in the new document the old text would have gone.
+    # Again we just do a best effort attempt.
+    s = InsensitiveSequenceMatcher(a=html1_tokens, b=html2_tokens)
+    commands = s.get_opcodes()
+    result = []
+    for command, i1, i2, j1, j2 in commands:
+        if command == 'equal':
+            result.extend(expand_tokens(html2_tokens[j1:j2], equal=True))
+            continue
+        if command == 'insert' or command == 'replace':
+            ins_tokens = expand_tokens(html2_tokens[j1:j2])
+            merge_insert(ins_tokens, result)
+        if command == 'delete' or command == 'replace':
+            del_tokens = expand_tokens(html1_tokens[i1:i2])
+            merge_delete(del_tokens, result)
+    # If deletes were inserted directly as <del> then we'd have an
+    # invalid document at this point.  Instead we put in special
+    # markers, and when the complete diffed document has been created
+    # we try to move the deletes around and resolve any problems.
+    result = cleanup_delete(result)
+
+    return result
+
+def expand_tokens(tokens, equal=False):
+    """Given a list of tokens, return a generator of the chunks of
+    text for the data in the tokens.
+    """
+    for token in tokens:
+        yield from token.pre_tags
+        if not equal or not token.hide_when_equal:
+            if token.trailing_whitespace:
+                yield token.html() + token.trailing_whitespace
+            else:
+                yield token.html()
+        yield from token.post_tags
+
+def merge_insert(ins_chunks, doc):
+    """ doc is the already-handled document (as a list of text chunks);
+    here we add <ins>ins_chunks</ins> to the end of that.  """
+    # Though we don't throw away unbalanced_start or unbalanced_end
+    # (we assume there is accompanying markup later or earlier in the
+    # document), we only put <ins> around the balanced portion.
+    unbalanced_start, balanced, unbalanced_end = split_unbalanced(ins_chunks)
+    doc.extend(unbalanced_start)
+    if doc and not doc[-1].endswith(' '):
+        # Fix up the case where the word before the insert didn't end with 
+        # a space
+        doc[-1] += ' '
+    doc.append('<ins>')
+    if balanced and balanced[-1].endswith(' '):
+        # We move space outside of </ins>
+        balanced[-1] = balanced[-1][:-1]
+    doc.extend(balanced)
+    doc.append('</ins> ')
+    doc.extend(unbalanced_end)
+
+# These are sentinels to represent the start and end of a <del>
+# segment, until we do the cleanup phase to turn them into proper
+# markup:
+class DEL_START:
+    pass
+class DEL_END:
+    pass
+
+class NoDeletes(Exception):
+    """ Raised when the document no longer contains any pending deletes
+    (DEL_START/DEL_END) """
+
+def merge_delete(del_chunks, doc):
+    """ Adds the text chunks in del_chunks to the document doc (another
+    list of text chunks) with marker to show it is a delete.
+    cleanup_delete later resolves these markers into <del> tags."""
+    doc.append(DEL_START)
+    doc.extend(del_chunks)
+    doc.append(DEL_END)
+
+def cleanup_delete(chunks):
+    """ Cleans up any DEL_START/DEL_END markers in the document, replacing
+    them with <del></del>.  To do this while keeping the document
+    valid, it may need to drop some tags (either start or end tags).
+
+    It may also move the del into adjacent tags to try to move it to a
+    similar location where it was originally located (e.g., moving a
+    delete into preceding <div> tag, if the del looks like (DEL_START,
+    'Text</div>', DEL_END)"""
+    while 1:
+        # Find a pending DEL_START/DEL_END, splitting the document
+        # into stuff-preceding-DEL_START, stuff-inside, and
+        # stuff-following-DEL_END
+        try:
+            pre_delete, delete, post_delete = split_delete(chunks)
+        except NoDeletes:
+            # Nothing found, we've cleaned up the entire doc
+            break
+        # The stuff-inside-DEL_START/END may not be well balanced
+        # markup.  First we figure out what unbalanced portions there are:
+        unbalanced_start, balanced, unbalanced_end = split_unbalanced(delete)
+        # Then we move the span forward and/or backward based on these
+        # unbalanced portions:
+        locate_unbalanced_start(unbalanced_start, pre_delete, post_delete)
+        locate_unbalanced_end(unbalanced_end, pre_delete, post_delete)
+        doc = pre_delete
+        if doc and not doc[-1].endswith(' '):
+            # Fix up case where the word before us didn't have a trailing space
+            doc[-1] += ' '
+        doc.append('<del>')
+        if balanced and balanced[-1].endswith(' '):
+            # We move space outside of </del>
+            balanced[-1] = balanced[-1][:-1]
+        doc.extend(balanced)
+        doc.append('</del> ')
+        doc.extend(post_delete)
+        chunks = doc
+    return chunks
+
+def split_unbalanced(chunks):
+    """Return (unbalanced_start, balanced, unbalanced_end), where each is
+    a list of text and tag chunks.
+
+    unbalanced_start is a list of all the tags that are opened, but
+    not closed in this span.  Similarly, unbalanced_end is a list of
+    tags that are closed but were not opened.  Extracting these might
+    mean some reordering of the chunks."""
+    start = []
+    end = []
+    tag_stack = []
+    balanced = []
+    for chunk in chunks:
+        if not chunk.startswith('<'):
+            balanced.append(chunk)
+            continue
+        endtag = chunk[1] == '/'
+        name = chunk.split()[0].strip('<>/')
+        if name in empty_tags:
+            balanced.append(chunk)
+            continue
+        if endtag:
+            if tag_stack and tag_stack[-1][0] == name:
+                balanced.append(chunk)
+                name, pos, tag = tag_stack.pop()
+                balanced[pos] = tag
+            elif tag_stack:
+                start.extend([tag for name, pos, tag in tag_stack])
+                tag_stack = []
+                end.append(chunk)
+            else:
+                end.append(chunk)
+        else:
+            tag_stack.append((name, len(balanced), chunk))
+            balanced.append(None)
+    start.extend(
+        [chunk for name, pos, chunk in tag_stack])
+    balanced = [chunk for chunk in balanced if chunk is not None]
+    return start, balanced, end
+
+def split_delete(chunks):
+    """ Returns (stuff_before_DEL_START, stuff_inside_DEL_START_END,
+    stuff_after_DEL_END).  Returns the first case found (there may be
+    more DEL_STARTs in stuff_after_DEL_END).  Raises NoDeletes if
+    there's no DEL_START found. """
+    try:
+        pos = chunks.index(DEL_START)
+    except ValueError:
+        raise NoDeletes
+    pos2 = chunks.index(DEL_END)
+    return chunks[:pos], chunks[pos+1:pos2], chunks[pos2+1:]
+
+def locate_unbalanced_start(unbalanced_start, pre_delete, post_delete):
+    """ pre_delete and post_delete implicitly point to a place in the
+    document (where the two were split).  This moves that point (by
+    popping items from one and pushing them onto the other).  It moves
+    the point to try to find a place where unbalanced_start applies.
+
+    As an example::
+
+        >>> unbalanced_start = ['<div>']
+        >>> doc = ['<p>', 'Text', '</p>', '<div>', 'More Text', '</div>']
+        >>> pre, post = doc[:3], doc[3:]
+        >>> pre, post
+        (['<p>', 'Text', '</p>'], ['<div>', 'More Text', '</div>'])
+        >>> locate_unbalanced_start(unbalanced_start, pre, post)
+        >>> pre, post
+        (['<p>', 'Text', '</p>', '<div>'], ['More Text', '</div>'])
+
+    As you can see, we moved the point so that the dangling <div> that
+    we found will be effectively replaced by the div in the original
+    document.  If this doesn't work out, we just throw away
+    unbalanced_start without doing anything.
+    """
+    while 1:
+        if not unbalanced_start:
+            # We have totally succeeded in finding the position
+            break
+        finding = unbalanced_start[0]
+        finding_name = finding.split()[0].strip('<>')
+        if not post_delete:
+            break
+        next = post_delete[0]
+        if next is DEL_START or not next.startswith('<'):
+            # Reached a word, we can't move the delete text forward
+            break
+        if next[1] == '/':
+            # Reached a closing tag, can we go further?  Maybe not...
+            break
+        name = next.split()[0].strip('<>')
+        if name == 'ins':
+            # Can't move into an insert
+            break
+        assert name != 'del', (
+            "Unexpected delete tag: %r" % next)
+        if name == finding_name:
+            unbalanced_start.pop(0)
+            pre_delete.append(post_delete.pop(0))
+        else:
+            # Found a tag that doesn't match
+            break
+
+def locate_unbalanced_end(unbalanced_end, pre_delete, post_delete):
+    """ like locate_unbalanced_start, except handling end tags and
+    possibly moving the point earlier in the document.  """
+    while 1:
+        if not unbalanced_end:
+            # Success
+            break
+        finding = unbalanced_end[-1]
+        finding_name = finding.split()[0].strip('<>/')
+        if not pre_delete:
+            break
+        next = pre_delete[-1]
+        if next is DEL_END or not next.startswith('</'):
+            # A word or a start tag
+            break
+        name = next.split()[0].strip('<>/')
+        if name == 'ins' or name == 'del':
+            # Can't move into an insert or delete
+            break
+        if name == finding_name:
+            unbalanced_end.pop()
+            post_delete.insert(0, pre_delete.pop())
+        else:
+            # Found a tag that doesn't match
+            break
+
+class token(_unicode):
+    """ Represents a diffable token, generally a word that is displayed to
+    the user.  Opening tags are attached to this token when they are
+    adjacent (pre_tags) and closing tags that follow the word
+    (post_tags).  Some exceptions occur when there are empty tags
+    adjacent to a word, so there may be close tags in pre_tags, or
+    open tags in post_tags.
+
+    We also keep track of whether the word was originally followed by
+    whitespace, even though we do not want to treat the word as
+    equivalent to a similar word that does not have a trailing
+    space."""
+
+    # When this is true, the token will be eliminated from the
+    # displayed diff if no change has occurred:
+    hide_when_equal = False
+
+    def __new__(cls, text, pre_tags=None, post_tags=None, trailing_whitespace=""):
+        obj = _unicode.__new__(cls, text)
+
+        if pre_tags is not None:
+            obj.pre_tags = pre_tags
+        else:
+            obj.pre_tags = []
+
+        if post_tags is not None:
+            obj.post_tags = post_tags
+        else:
+            obj.post_tags = []
+
+        obj.trailing_whitespace = trailing_whitespace
+
+        return obj
+
+    def __repr__(self):
+        return 'token(%s, %r, %r, %r)' % (_unicode.__repr__(self), self.pre_tags,
+                                          self.post_tags, self.trailing_whitespace)
+
+    def html(self):
+        return _unicode(self)
+
+class tag_token(token):
+
+    """ Represents a token that is actually a tag.  Currently this is just
+    the <img> tag, which takes up visible space just like a word but
+    is only represented in a document by a tag.  """
+
+    def __new__(cls, tag, data, html_repr, pre_tags=None, 
+                post_tags=None, trailing_whitespace=""):
+        obj = token.__new__(cls, "%s: %s" % (type, data), 
+                            pre_tags=pre_tags, 
+                            post_tags=post_tags, 
+                            trailing_whitespace=trailing_whitespace)
+        obj.tag = tag
+        obj.data = data
+        obj.html_repr = html_repr
+        return obj
+
+    def __repr__(self):
+        return 'tag_token(%s, %s, html_repr=%s, post_tags=%r, pre_tags=%r, trailing_whitespace=%r)' % (
+            self.tag, 
+            self.data, 
+            self.html_repr, 
+            self.pre_tags, 
+            self.post_tags, 
+            self.trailing_whitespace)
+    def html(self):
+        return self.html_repr
+
+class href_token(token):
+
+    """ Represents the href in an anchor tag.  Unlike other words, we only
+    show the href when it changes.  """
+
+    hide_when_equal = True
+
+    def html(self):
+        return ' Link: %s' % self
+
+def tokenize(html, include_hrefs=True):
+    """
+    Parse the given HTML and returns token objects (words with attached tags).
+
+    This parses only the content of a page; anything in the head is
+    ignored, and the <head> and <body> elements are themselves
+    optional.  The content is then parsed by lxml, which ensures the
+    validity of the resulting parsed document (though lxml may make
+    incorrect guesses when the markup is particular bad).
+
+    <ins> and <del> tags are also eliminated from the document, as
+    that gets confusing.
+
+    If include_hrefs is true, then the href attribute of <a> tags is
+    included as a special kind of diffable token."""
+    if etree.iselement(html):
+        body_el = html
+    else:
+        body_el = parse_html(html, cleanup=True)
+    # Then we split the document into text chunks for each tag, word, and end tag:
+    chunks = flatten_el(body_el, skip_tag=True, include_hrefs=include_hrefs)
+    # Finally re-joining them into token objects:
+    return fixup_chunks(chunks)
+
+def parse_html(html, cleanup=True):
+    """
+    Parses an HTML fragment, returning an lxml element.  Note that the HTML will be
+    wrapped in a <div> tag that was not in the original document.
+
+    If cleanup is true, make sure there's no <head> or <body>, and get
+    rid of any <ins> and <del> tags.
+    """
+    if cleanup:
+        # This removes any extra markup or structure like <head>:
+        html = cleanup_html(html)
+    return fragment_fromstring(html, create_parent=True)
+
+_body_re = re.compile(r'<body.*?>', re.I|re.S)
+_end_body_re = re.compile(r'</body.*?>', re.I|re.S)
+_ins_del_re = re.compile(r'</?(ins|del).*?>', re.I|re.S)
+
+def cleanup_html(html):
+    """ This 'cleans' the HTML, meaning that any page structure is removed
+    (only the contents of <body> are used, if there is any <body).
+    Also <ins> and <del> tags are removed.  """
+    match = _body_re.search(html)
+    if match:
+        html = html[match.end():]
+    match = _end_body_re.search(html)
+    if match:
+        html = html[:match.start()]
+    html = _ins_del_re.sub('', html)
+    return html
+    
+
+end_whitespace_re = re.compile(r'[ \t\n\r]$')
+
+def split_trailing_whitespace(word):
+    """
+    This function takes a word, such as 'test\n\n' and returns ('test','\n\n')
+    """
+    stripped_length = len(word.rstrip())
+    return word[0:stripped_length], word[stripped_length:]
+
+
+def fixup_chunks(chunks):
+    """
+    This function takes a list of chunks and produces a list of tokens.
+    """
+    tag_accum = []
+    cur_word = None
+    result = []
+    for chunk in chunks:
+        if isinstance(chunk, tuple):
+            if chunk[0] == 'img':
+                src = chunk[1]
+                tag, trailing_whitespace = split_trailing_whitespace(chunk[2])
+                cur_word = tag_token('img', src, html_repr=tag,
+                                     pre_tags=tag_accum,
+                                     trailing_whitespace=trailing_whitespace)
+                tag_accum = []
+                result.append(cur_word)
+
+            elif chunk[0] == 'href':
+                href = chunk[1]
+                cur_word = href_token(href, pre_tags=tag_accum, trailing_whitespace=" ")
+                tag_accum = []
+                result.append(cur_word)
+            continue
+
+        if is_word(chunk):
+            chunk, trailing_whitespace = split_trailing_whitespace(chunk)
+            cur_word = token(chunk, pre_tags=tag_accum, trailing_whitespace=trailing_whitespace)
+            tag_accum = []
+            result.append(cur_word)
+
+        elif is_start_tag(chunk):
+            tag_accum.append(chunk)
+
+        elif is_end_tag(chunk):
+            if tag_accum:
+                tag_accum.append(chunk)
+            else:
+                assert cur_word, (
+                    "Weird state, cur_word=%r, result=%r, chunks=%r of %r"
+                    % (cur_word, result, chunk, chunks))
+                cur_word.post_tags.append(chunk)
+        else:
+            assert False
+
+    if not result:
+        return [token('', pre_tags=tag_accum)]
+    else:
+        result[-1].post_tags.extend(tag_accum)
+
+    return result
+
+
+# All the tags in HTML that don't require end tags:
+empty_tags = (
+    'param', 'img', 'area', 'br', 'basefont', 'input',
+    'base', 'meta', 'link', 'col')
+
+block_level_tags = (
+    'address',
+    'blockquote',
+    'center',
+    'dir',
+    'div',
+    'dl',
+    'fieldset',
+    'form',
+    'h1',
+    'h2',
+    'h3',
+    'h4',
+    'h5',
+    'h6',
+    'hr',
+    'isindex',
+    'menu',
+    'noframes',
+    'noscript',
+    'ol',
+    'p',
+    'pre',
+    'table',
+    'ul',
+    )
+
+block_level_container_tags = (
+    'dd',
+    'dt',
+    'frameset',
+    'li',
+    'tbody',
+    'td',
+    'tfoot',
+    'th',
+    'thead',
+    'tr',
+    )
+
+
+def flatten_el(el, include_hrefs, skip_tag=False):
+    """ Takes an lxml element el, and generates all the text chunks for
+    that tag.  Each start tag is a chunk, each word is a chunk, and each
+    end tag is a chunk.
+
+    If skip_tag is true, then the outermost container tag is
+    not returned (just its contents)."""
+    if not skip_tag:
+        if el.tag == 'img':
+            yield ('img', el.get('src'), start_tag(el))
+        else:
+            yield start_tag(el)
+    if el.tag in empty_tags and not el.text and not len(el) and not el.tail:
+        return
+    start_words = split_words(el.text)
+    for word in start_words:
+        yield html_escape(word)
+    for child in el:
+        yield from flatten_el(child, include_hrefs=include_hrefs)
+    if el.tag == 'a' and el.get('href') and include_hrefs:
+        yield ('href', el.get('href'))
+    if not skip_tag:
+        yield end_tag(el)
+        end_words = split_words(el.tail)
+        for word in end_words:
+            yield html_escape(word)
+
+split_words_re = re.compile(r'\S+(?:\s+|$)', re.U)
+
+def split_words(text):
+    """ Splits some text into words. Includes trailing whitespace
+    on each word when appropriate.  """
+    if not text or not text.strip():
+        return []
+
+    words = split_words_re.findall(text)
+    return words
+
+start_whitespace_re = re.compile(r'^[ \t\n\r]')
+
+def start_tag(el):
+    """
+    The text representation of the start tag for a tag.
+    """
+    return '<%s%s>' % (
+        el.tag, ''.join([' %s="%s"' % (name, html_escape(value, True))
+                         for name, value in el.attrib.items()]))
+
+def end_tag(el):
+    """ The text representation of an end tag for a tag.  Includes
+    trailing whitespace when appropriate.  """
+    if el.tail and start_whitespace_re.search(el.tail):
+        extra = ' '
+    else:
+        extra = ''
+    return '</%s>%s' % (el.tag, extra)
+
+def is_word(tok):
+    return not tok.startswith('<')
+
+def is_end_tag(tok):
+    return tok.startswith('</')
+
+def is_start_tag(tok):
+    return tok.startswith('<') and not tok.startswith('</')
+
+def fixup_ins_del_tags(html):
+    """ Given an html string, move any <ins> or <del> tags inside of any
+    block-level elements, e.g. transform <ins><p>word</p></ins> to
+    <p><ins>word</ins></p> """
+    doc = parse_html(html, cleanup=False)
+    _fixup_ins_del_tags(doc)
+    html = serialize_html_fragment(doc, skip_outer=True)
+    return html
+
+def serialize_html_fragment(el, skip_outer=False):
+    """ Serialize a single lxml element as HTML.  The serialized form
+    includes the elements tail.  
+
+    If skip_outer is true, then don't serialize the outermost tag
+    """
+    assert not isinstance(el, basestring), (
+        "You should pass in an element, not a string like %r" % el)
+    html = etree.tostring(el, method="html", encoding=_unicode)
+    if skip_outer:
+        # Get rid of the extra starting tag:
+        html = html[html.find('>')+1:]
+        # Get rid of the extra end tag:
+        html = html[:html.rfind('<')]
+        return html.strip()
+    else:
+        return html
+
+def _fixup_ins_del_tags(doc):
+    """fixup_ins_del_tags that works on an lxml document in-place
+    """
+    for tag in ['ins', 'del']:
+        for el in doc.xpath('descendant-or-self::%s' % tag):
+            if not _contains_block_level_tag(el):
+                continue
+            _move_el_inside_block(el, tag=tag)
+            el.drop_tag()
+            #_merge_element_contents(el)
+
+def _contains_block_level_tag(el):
+    """True if the element contains any block-level elements, like <p>, <td>, etc.
+    """
+    if el.tag in block_level_tags or el.tag in block_level_container_tags:
+        return True
+    for child in el:
+        if _contains_block_level_tag(child):
+            return True
+    return False
+
+def _move_el_inside_block(el, tag):
+    """ helper for _fixup_ins_del_tags; actually takes the <ins> etc tags
+    and moves them inside any block-level tags.  """
+    for child in el:
+        if _contains_block_level_tag(child):
+            break
+    else:
+        # No block-level tags in any child
+        children_tag = etree.Element(tag)
+        children_tag.text = el.text
+        el.text = None
+        children_tag.extend(list(el))
+        el[:] = [children_tag]
+        return
+    for child in list(el):
+        if _contains_block_level_tag(child):
+            _move_el_inside_block(child, tag)
+            if child.tail:
+                tail_tag = etree.Element(tag)
+                tail_tag.text = child.tail
+                child.tail = None
+                el.insert(el.index(child)+1, tail_tag)
+        else:
+            child_tag = etree.Element(tag)
+            el.replace(child, child_tag)
+            child_tag.append(child)
+    if el.text:
+        text_tag = etree.Element(tag)
+        text_tag.text = el.text
+        el.text = None
+        el.insert(0, text_tag)
+            
+def _merge_element_contents(el):
+    """
+    Removes an element, but merges its contents into its place, e.g.,
+    given <p>Hi <i>there!</i></p>, if you remove the <i> element you get
+    <p>Hi there!</p>
+    """
+    parent = el.getparent()
+    text = el.text or ''
+    if el.tail:
+        if not len(el):
+            text += el.tail
+        else:
+            if el[-1].tail:
+                el[-1].tail += el.tail
+            else:
+                el[-1].tail = el.tail
+    index = parent.index(el)
+    if text:
+        if index == 0:
+            previous = None
+        else:
+            previous = parent[index-1]
+        if previous is None:
+            if parent.text:
+                parent.text += text
+            else:
+                parent.text = text
+        else:
+            if previous.tail:
+                previous.tail += text
+            else:
+                previous.tail = text
+    parent[index:index+1] = el.getchildren()
+
+class InsensitiveSequenceMatcher(difflib.SequenceMatcher):
+    """
+    Acts like SequenceMatcher, but tries not to find very small equal
+    blocks amidst large spans of changes
+    """
+
+    threshold = 2
+    
+    def get_matching_blocks(self):
+        size = min(len(self.b), len(self.b))
+        threshold = min(self.threshold, size / 4)
+        actual = difflib.SequenceMatcher.get_matching_blocks(self)
+        return [item for item in actual
+                if item[2] > threshold
+                or not item[2]]
+
+if __name__ == '__main__':
+    from lxml.html import _diffcommand
+    _diffcommand.main()
+    
diff --git a/.venv/lib/python3.12/site-packages/lxml/html/formfill.py b/.venv/lib/python3.12/site-packages/lxml/html/formfill.py
new file mode 100644
index 00000000..9741c28b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/html/formfill.py
@@ -0,0 +1,299 @@
+from lxml.etree import XPath, ElementBase
+from lxml.html import fromstring, XHTML_NAMESPACE
+from lxml.html import _forms_xpath, _options_xpath, _nons, _transform_result
+from lxml.html import defs
+import copy
+
+try:
+    basestring
+except NameError:
+    # Python 3
+    basestring = str
+
+__all__ = ['FormNotFound', 'fill_form', 'fill_form_html',
+           'insert_errors', 'insert_errors_html',
+           'DefaultErrorCreator']
+
+class FormNotFound(LookupError):
+    """
+    Raised when no form can be found
+    """
+
+_form_name_xpath = XPath('descendant-or-self::form[name=$name]|descendant-or-self::x:form[name=$name]', namespaces={'x':XHTML_NAMESPACE})
+_input_xpath = XPath('|'.join(['descendant-or-self::'+_tag for _tag in ('input','select','textarea','x:input','x:select','x:textarea')]),
+                               namespaces={'x':XHTML_NAMESPACE})
+_label_for_xpath = XPath('//label[@for=$for_id]|//x:label[@for=$for_id]',
+                               namespaces={'x':XHTML_NAMESPACE})
+_name_xpath = XPath('descendant-or-self::*[@name=$name]')
+
+def fill_form(
+    el,
+    values,
+    form_id=None,
+    form_index=None,
+    ):
+    el = _find_form(el, form_id=form_id, form_index=form_index)
+    _fill_form(el, values)
+
+def fill_form_html(html, values, form_id=None, form_index=None):
+    result_type = type(html)
+    if isinstance(html, basestring):
+        doc = fromstring(html)
+    else:
+        doc = copy.deepcopy(html)
+    fill_form(doc, values, form_id=form_id, form_index=form_index)
+    return _transform_result(result_type, doc)
+
+def _fill_form(el, values):
+    counts = {}
+    if hasattr(values, 'mixed'):
+        # For Paste request parameters
+        values = values.mixed()
+    inputs = _input_xpath(el)
+    for input in inputs:
+        name = input.get('name')
+        if not name:
+            continue
+        if _takes_multiple(input):
+            value = values.get(name, [])
+            if not isinstance(value, (list, tuple)):
+                value = [value]
+            _fill_multiple(input, value)
+        elif name not in values:
+            continue
+        else:
+            index = counts.get(name, 0)
+            counts[name] = index + 1
+            value = values[name]
+            if isinstance(value, (list, tuple)):
+                try:
+                    value = value[index]
+                except IndexError:
+                    continue
+            elif index > 0:
+                continue
+            _fill_single(input, value)
+
+def _takes_multiple(input):
+    if _nons(input.tag) == 'select' and input.get('multiple'):
+        # FIXME: multiple="0"?
+        return True
+    type = input.get('type', '').lower()
+    if type in ('radio', 'checkbox'):
+        return True
+    return False
+
+def _fill_multiple(input, value):
+    type = input.get('type', '').lower()
+    if type == 'checkbox':
+        v = input.get('value')
+        if v is None:
+            if not value:
+                result = False
+            else:
+                result = value[0]
+                if isinstance(value, basestring):
+                    # The only valid "on" value for an unnamed checkbox is 'on'
+                    result = result == 'on'
+            _check(input, result)
+        else:
+            _check(input, v in value)
+    elif type == 'radio':
+        v = input.get('value')
+        _check(input, v in value)
+    else:
+        assert _nons(input.tag) == 'select'
+        for option in _options_xpath(input):
+            v = option.get('value')
+            if v is None:
+                # This seems to be the default, at least on IE
+                # FIXME: but I'm not sure
+                v = option.text_content()
+            _select(option, v in value)
+
+def _check(el, check):
+    if check:
+        el.set('checked', '')
+    else:
+        if 'checked' in el.attrib:
+            del el.attrib['checked']
+
+def _select(el, select):
+    if select:
+        el.set('selected', '')
+    else:
+        if 'selected' in el.attrib:
+            del el.attrib['selected']
+
+def _fill_single(input, value):
+    if _nons(input.tag) == 'textarea':
+        input.text = value
+    else:
+        input.set('value', value)
+
+def _find_form(el, form_id=None, form_index=None):
+    if form_id is None and form_index is None:
+        forms = _forms_xpath(el)
+        for form in forms:
+            return form
+        raise FormNotFound(
+            "No forms in page")
+    if form_id is not None:
+        form = el.get_element_by_id(form_id)
+        if form is not None:
+            return form
+        forms = _form_name_xpath(el, name=form_id)
+        if forms:
+            return forms[0]
+        else:
+            raise FormNotFound(
+                "No form with the name or id of %r (forms: %s)"
+                % (id, ', '.join(_find_form_ids(el))))               
+    if form_index is not None:
+        forms = _forms_xpath(el)
+        try:
+            return forms[form_index]
+        except IndexError:
+            raise FormNotFound(
+                "There is no form with the index %r (%i forms found)"
+                % (form_index, len(forms)))
+
+def _find_form_ids(el):
+    forms = _forms_xpath(el)
+    if not forms:
+        yield '(no forms)'
+        return
+    for index, form in enumerate(forms):
+        if form.get('id'):
+            if form.get('name'):
+                yield '%s or %s' % (form.get('id'),
+                                     form.get('name'))
+            else:
+                yield form.get('id')
+        elif form.get('name'):
+            yield form.get('name')
+        else:
+            yield '(unnamed form %s)' % index
+
+############################################################
+## Error filling
+############################################################
+
+class DefaultErrorCreator:
+    insert_before = True
+    block_inside = True
+    error_container_tag = 'div'
+    error_message_class = 'error-message'
+    error_block_class = 'error-block'
+    default_message = "Invalid"
+
+    def __init__(self, **kw):
+        for name, value in kw.items():
+            if not hasattr(self, name):
+                raise TypeError(
+                    "Unexpected keyword argument: %s" % name)
+            setattr(self, name, value)
+
+    def __call__(self, el, is_block, message):
+        error_el = el.makeelement(self.error_container_tag)
+        if self.error_message_class:
+            error_el.set('class', self.error_message_class)
+        if is_block and self.error_block_class:
+            error_el.set('class', error_el.get('class', '')+' '+self.error_block_class)
+        if message is None or message == '':
+            message = self.default_message
+        if isinstance(message, ElementBase):
+            error_el.append(message)
+        else:
+            assert isinstance(message, basestring), (
+                "Bad message; should be a string or element: %r" % message)
+            error_el.text = message or self.default_message
+        if is_block and self.block_inside:
+            if self.insert_before:
+                error_el.tail = el.text
+                el.text = None
+                el.insert(0, error_el)
+            else:
+                el.append(error_el)
+        else:
+            parent = el.getparent()
+            pos = parent.index(el)
+            if self.insert_before:
+                parent.insert(pos, error_el)
+            else:
+                error_el.tail = el.tail
+                el.tail = None
+                parent.insert(pos+1, error_el)
+
+default_error_creator = DefaultErrorCreator()
+    
+
+def insert_errors(
+    el,
+    errors,
+    form_id=None,
+    form_index=None,
+    error_class="error",
+    error_creator=default_error_creator,
+    ):
+    el = _find_form(el, form_id=form_id, form_index=form_index)
+    for name, error in errors.items():
+        if error is None:
+            continue
+        for error_el, message in _find_elements_for_name(el, name, error):
+            assert isinstance(message, (basestring, type(None), ElementBase)), (
+                "Bad message: %r" % message)
+            _insert_error(error_el, message, error_class, error_creator)
+
+def insert_errors_html(html, values, **kw):
+    result_type = type(html)
+    if isinstance(html, basestring):
+        doc = fromstring(html)
+    else:
+        doc = copy.deepcopy(html)
+    insert_errors(doc, values, **kw)
+    return _transform_result(result_type, doc)
+
+def _insert_error(el, error, error_class, error_creator):
+    if _nons(el.tag) in defs.empty_tags or _nons(el.tag) == 'textarea':
+        is_block = False
+    else:
+        is_block = True
+    if _nons(el.tag) != 'form' and error_class:
+        _add_class(el, error_class)
+    if el.get('id'):
+        labels = _label_for_xpath(el, for_id=el.get('id'))
+        if labels:
+            for label in labels:
+                _add_class(label, error_class)
+    error_creator(el, is_block, error)
+
+def _add_class(el, class_name):
+    if el.get('class'):
+        el.set('class', el.get('class')+' '+class_name)
+    else:
+        el.set('class', class_name)
+
+def _find_elements_for_name(form, name, error):
+    if name is None:
+        # An error for the entire form
+        yield form, error
+        return
+    if name.startswith('#'):
+        # By id
+        el = form.get_element_by_id(name[1:])
+        if el is not None:
+            yield el, error
+        return
+    els = _name_xpath(form, name=name)
+    if not els:
+        # FIXME: should this raise an exception?
+        return
+    if not isinstance(error, (list, tuple)):
+        yield els[0], error
+        return
+    # FIXME: if error is longer than els, should it raise an error?
+    for el, err in zip(els, error):
+        if err is None:
+            continue
+        yield el, err
diff --git a/.venv/lib/python3.12/site-packages/lxml/html/html5parser.py b/.venv/lib/python3.12/site-packages/lxml/html/html5parser.py
new file mode 100644
index 00000000..2f7be156
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/html/html5parser.py
@@ -0,0 +1,260 @@
+"""
+An interface to html5lib that mimics the lxml.html interface.
+"""
+import sys
+import string
+
+from html5lib import HTMLParser as _HTMLParser
+from html5lib.treebuilders.etree_lxml import TreeBuilder
+from lxml import etree
+from lxml.html import Element, XHTML_NAMESPACE, _contains_block_level_tag
+
+# python3 compatibility
+try:
+    _strings = basestring
+except NameError:
+    _strings = (bytes, str)
+try:
+    from urllib2 import urlopen
+except ImportError:
+    from urllib.request import urlopen
+try:
+    from urlparse import urlparse
+except ImportError:
+    from urllib.parse import urlparse
+
+
+class HTMLParser(_HTMLParser):
+    """An html5lib HTML parser with lxml as tree."""
+
+    def __init__(self, strict=False, **kwargs):
+        _HTMLParser.__init__(self, strict=strict, tree=TreeBuilder, **kwargs)
+
+
+try:
+    from html5lib import XHTMLParser as _XHTMLParser
+except ImportError:
+    pass
+else:
+    class XHTMLParser(_XHTMLParser):
+        """An html5lib XHTML Parser with lxml as tree."""
+
+        def __init__(self, strict=False, **kwargs):
+            _XHTMLParser.__init__(self, strict=strict, tree=TreeBuilder, **kwargs)
+
+    xhtml_parser = XHTMLParser()
+
+
+def _find_tag(tree, tag):
+    elem = tree.find(tag)
+    if elem is not None:
+        return elem
+    return tree.find('{%s}%s' % (XHTML_NAMESPACE, tag))
+
+
+def document_fromstring(html, guess_charset=None, parser=None):
+    """
+    Parse a whole document into a string.
+
+    If `guess_charset` is true, or if the input is not Unicode but a
+    byte string, the `chardet` library will perform charset guessing
+    on the string.
+    """
+    if not isinstance(html, _strings):
+        raise TypeError('string required')
+
+    if parser is None:
+        parser = html_parser
+
+    options = {}
+    if guess_charset is None and isinstance(html, bytes):
+        # html5lib does not accept useChardet as an argument, if it
+        # detected the html argument would produce unicode objects.
+        guess_charset = True
+    if guess_charset is not None:
+        options['useChardet'] = guess_charset
+    return parser.parse(html, **options).getroot()
+
+
+def fragments_fromstring(html, no_leading_text=False,
+                         guess_charset=None, parser=None):
+    """Parses several HTML elements, returning a list of elements.
+
+    The first item in the list may be a string.  If no_leading_text is true,
+    then it will be an error if there is leading text, and it will always be
+    a list of only elements.
+
+    If `guess_charset` is true, the `chardet` library will perform charset
+    guessing on the string.
+    """
+    if not isinstance(html, _strings):
+        raise TypeError('string required')
+
+    if parser is None:
+        parser = html_parser
+
+    options = {}
+    if guess_charset is None and isinstance(html, bytes):
+        # html5lib does not accept useChardet as an argument, if it
+        # detected the html argument would produce unicode objects.
+        guess_charset = False
+    if guess_charset is not None:
+        options['useChardet'] = guess_charset
+    children = parser.parseFragment(html, 'div', **options)
+    if children and isinstance(children[0], _strings):
+        if no_leading_text:
+            if children[0].strip():
+                raise etree.ParserError('There is leading text: %r' %
+                                        children[0])
+            del children[0]
+    return children
+
+
+def fragment_fromstring(html, create_parent=False,
+                        guess_charset=None, parser=None):
+    """Parses a single HTML element; it is an error if there is more than
+    one element, or if anything but whitespace precedes or follows the
+    element.
+
+    If 'create_parent' is true (or is a tag name) then a parent node
+    will be created to encapsulate the HTML in a single element.  In
+    this case, leading or trailing text is allowed.
+
+    If `guess_charset` is true, the `chardet` library will perform charset
+    guessing on the string.
+    """
+    if not isinstance(html, _strings):
+        raise TypeError('string required')
+
+    accept_leading_text = bool(create_parent)
+
+    elements = fragments_fromstring(
+        html, guess_charset=guess_charset, parser=parser,
+        no_leading_text=not accept_leading_text)
+
+    if create_parent:
+        if not isinstance(create_parent, _strings):
+            create_parent = 'div'
+        new_root = Element(create_parent)
+        if elements:
+            if isinstance(elements[0], _strings):
+                new_root.text = elements[0]
+                del elements[0]
+            new_root.extend(elements)
+        return new_root
+
+    if not elements:
+        raise etree.ParserError('No elements found')
+    if len(elements) > 1:
+        raise etree.ParserError('Multiple elements found')
+    result = elements[0]
+    if result.tail and result.tail.strip():
+        raise etree.ParserError('Element followed by text: %r' % result.tail)
+    result.tail = None
+    return result
+
+
+def fromstring(html, guess_charset=None, parser=None):
+    """Parse the html, returning a single element/document.
+
+    This tries to minimally parse the chunk of text, without knowing if it
+    is a fragment or a document.
+
+    'base_url' will set the document's base_url attribute (and the tree's
+    docinfo.URL)
+
+    If `guess_charset` is true, or if the input is not Unicode but a
+    byte string, the `chardet` library will perform charset guessing
+    on the string.
+    """
+    if not isinstance(html, _strings):
+        raise TypeError('string required')
+    doc = document_fromstring(html, parser=parser,
+                              guess_charset=guess_charset)
+
+    # document starts with doctype or <html>, full document!
+    start = html[:50]
+    if isinstance(start, bytes):
+        # Allow text comparison in python3.
+        # Decode as ascii, that also covers latin-1 and utf-8 for the
+        # characters we need.
+        start = start.decode('ascii', 'replace')
+
+    start = start.lstrip().lower()
+    if start.startswith('<html') or start.startswith('<!doctype'):
+        return doc
+
+    head = _find_tag(doc, 'head')
+
+    # if the head is not empty we have a full document
+    if len(head):
+        return doc
+
+    body = _find_tag(doc, 'body')
+
+    # The body has just one element, so it was probably a single
+    # element passed in
+    if (len(body) == 1 and (not body.text or not body.text.strip())
+        and (not body[-1].tail or not body[-1].tail.strip())):
+        return body[0]
+
+    # Now we have a body which represents a bunch of tags which have the
+    # content that was passed in.  We will create a fake container, which
+    # is the body tag, except <body> implies too much structure.
+    if _contains_block_level_tag(body):
+        body.tag = 'div'
+    else:
+        body.tag = 'span'
+    return body
+
+
+def parse(filename_url_or_file, guess_charset=None, parser=None):
+    """Parse a filename, URL, or file-like object into an HTML document
+    tree.  Note: this returns a tree, not an element.  Use
+    ``parse(...).getroot()`` to get the document root.
+
+    If ``guess_charset`` is true, the ``useChardet`` option is passed into
+    html5lib to enable character detection.  This option is on by default
+    when parsing from URLs, off by default when parsing from file(-like)
+    objects (which tend to return Unicode more often than not), and on by
+    default when parsing from a file path (which is read in binary mode).
+    """
+    if parser is None:
+        parser = html_parser
+    if not isinstance(filename_url_or_file, _strings):
+        fp = filename_url_or_file
+        if guess_charset is None:
+            # assume that file-like objects return Unicode more often than bytes
+            guess_charset = False
+    elif _looks_like_url(filename_url_or_file):
+        fp = urlopen(filename_url_or_file)
+        if guess_charset is None:
+            # assume that URLs return bytes
+            guess_charset = True
+    else:
+        fp = open(filename_url_or_file, 'rb')
+        if guess_charset is None:
+            guess_charset = True
+
+    options = {}
+    # html5lib does not accept useChardet as an argument, if it
+    # detected the html argument would produce unicode objects.
+    if guess_charset:
+        options['useChardet'] = guess_charset
+    return parser.parse(fp, **options)
+
+
+def _looks_like_url(str):
+    scheme = urlparse(str)[0]
+    if not scheme:
+        return False
+    elif (sys.platform == 'win32' and
+            scheme in string.ascii_letters
+            and len(scheme) == 1):
+        # looks like a 'normal' absolute path
+        return False
+    else:
+        return True
+
+
+html_parser = HTMLParser()
diff --git a/.venv/lib/python3.12/site-packages/lxml/html/soupparser.py b/.venv/lib/python3.12/site-packages/lxml/html/soupparser.py
new file mode 100644
index 00000000..b288a8a1
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/html/soupparser.py
@@ -0,0 +1,314 @@
+"""External interface to the BeautifulSoup HTML parser.
+"""
+
+__all__ = ["fromstring", "parse", "convert_tree"]
+
+import re
+from lxml import etree, html
+
+try:
+    from bs4 import (
+        BeautifulSoup, Tag, Comment, ProcessingInstruction, NavigableString,
+        Declaration, Doctype)
+    _DECLARATION_OR_DOCTYPE = (Declaration, Doctype)
+except ImportError:
+    from BeautifulSoup import (
+        BeautifulSoup, Tag, Comment, ProcessingInstruction, NavigableString,
+        Declaration)
+    _DECLARATION_OR_DOCTYPE = Declaration
+
+
+def fromstring(data, beautifulsoup=None, makeelement=None, **bsargs):
+    """Parse a string of HTML data into an Element tree using the
+    BeautifulSoup parser.
+
+    Returns the root ``<html>`` Element of the tree.
+
+    You can pass a different BeautifulSoup parser through the
+    `beautifulsoup` keyword, and a diffent Element factory function
+    through the `makeelement` keyword.  By default, the standard
+    ``BeautifulSoup`` class and the default factory of `lxml.html` are
+    used.
+    """
+    return _parse(data, beautifulsoup, makeelement, **bsargs)
+
+
+def parse(file, beautifulsoup=None, makeelement=None, **bsargs):
+    """Parse a file into an ElemenTree using the BeautifulSoup parser.
+
+    You can pass a different BeautifulSoup parser through the
+    `beautifulsoup` keyword, and a diffent Element factory function
+    through the `makeelement` keyword.  By default, the standard
+    ``BeautifulSoup`` class and the default factory of `lxml.html` are
+    used.
+    """
+    if not hasattr(file, 'read'):
+        file = open(file)
+    root = _parse(file, beautifulsoup, makeelement, **bsargs)
+    return etree.ElementTree(root)
+
+
+def convert_tree(beautiful_soup_tree, makeelement=None):
+    """Convert a BeautifulSoup tree to a list of Element trees.
+
+    Returns a list instead of a single root Element to support
+    HTML-like soup with more than one root element.
+
+    You can pass a different Element factory through the `makeelement`
+    keyword.
+    """
+    root = _convert_tree(beautiful_soup_tree, makeelement)
+    children = root.getchildren()
+    for child in children:
+        root.remove(child)
+    return children
+
+
+# helpers
+
+def _parse(source, beautifulsoup, makeelement, **bsargs):
+    if beautifulsoup is None:
+        beautifulsoup = BeautifulSoup
+    if hasattr(beautifulsoup, "HTML_ENTITIES"):  # bs3
+        if 'convertEntities' not in bsargs:
+            bsargs['convertEntities'] = 'html'
+    if hasattr(beautifulsoup, "DEFAULT_BUILDER_FEATURES"):  # bs4
+        if 'features' not in bsargs:
+            bsargs['features'] = 'html.parser'  # use Python html parser
+    tree = beautifulsoup(source, **bsargs)
+    root = _convert_tree(tree, makeelement)
+    # from ET: wrap the document in a html root element, if necessary
+    if len(root) == 1 and root[0].tag == "html":
+        return root[0]
+    root.tag = "html"
+    return root
+
+
+_parse_doctype_declaration = re.compile(
+    r'(?:\s|[<!])*DOCTYPE\s*HTML'
+    r'(?:\s+PUBLIC)?(?:\s+(\'[^\']*\'|"[^"]*"))?'
+    r'(?:\s+(\'[^\']*\'|"[^"]*"))?',
+    re.IGNORECASE).match
+
+
+class _PseudoTag:
+    # Minimal imitation of BeautifulSoup.Tag
+    def __init__(self, contents):
+        self.name = 'html'
+        self.attrs = []
+        self.contents = contents
+
+    def __iter__(self):
+        return self.contents.__iter__()
+
+
+def _convert_tree(beautiful_soup_tree, makeelement):
+    if makeelement is None:
+        makeelement = html.html_parser.makeelement
+
+    # Split the tree into three parts:
+    # i) everything before the root element: document type
+    # declaration, comments, processing instructions, whitespace
+    # ii) the root(s),
+    # iii) everything after the root: comments, processing
+    # instructions, whitespace
+    first_element_idx = last_element_idx = None
+    html_root = declaration = None
+    for i, e in enumerate(beautiful_soup_tree):
+        if isinstance(e, Tag):
+            if first_element_idx is None:
+                first_element_idx = i
+            last_element_idx = i
+            if html_root is None and e.name and e.name.lower() == 'html':
+                html_root = e
+        elif declaration is None and isinstance(e, _DECLARATION_OR_DOCTYPE):
+            declaration = e
+
+    # For a nice, well-formatted document, the variable roots below is
+    # a list consisting of a single <html> element. However, the document
+    # may be a soup like '<meta><head><title>Hello</head><body>Hi
+    # all<\p>'. In this example roots is a list containing meta, head
+    # and body elements.
+    if first_element_idx is None:
+        pre_root = post_root = []
+        roots = beautiful_soup_tree.contents
+    else:
+        pre_root = beautiful_soup_tree.contents[:first_element_idx]
+        roots = beautiful_soup_tree.contents[first_element_idx:last_element_idx+1]
+        post_root = beautiful_soup_tree.contents[last_element_idx+1:]
+
+    # Reorganize so that there is one <html> root...
+    if html_root is not None:
+        # ... use existing one if possible, ...
+        i = roots.index(html_root)
+        html_root.contents = roots[:i] + html_root.contents + roots[i+1:]
+    else:
+        # ... otherwise create a new one.
+        html_root = _PseudoTag(roots)
+
+    convert_node = _init_node_converters(makeelement)
+
+    # Process pre_root
+    res_root = convert_node(html_root)
+    prev = res_root
+    for e in reversed(pre_root):
+        converted = convert_node(e)
+        if converted is not None:
+            prev.addprevious(converted)
+            prev = converted
+
+    # ditto for post_root
+    prev = res_root
+    for e in post_root:
+        converted = convert_node(e)
+        if converted is not None:
+            prev.addnext(converted)
+            prev = converted
+
+    if declaration is not None:
+        try:
+            # bs4 provides full Doctype string
+            doctype_string = declaration.output_ready()
+        except AttributeError:
+            doctype_string = declaration.string
+
+        match = _parse_doctype_declaration(doctype_string)
+        if not match:
+            # Something is wrong if we end up in here. Since soupparser should
+            # tolerate errors, do not raise Exception, just let it pass.
+            pass
+        else:
+            external_id, sys_uri = match.groups()
+            docinfo = res_root.getroottree().docinfo
+            # strip quotes and update DOCTYPE values (any of None, '', '...')
+            docinfo.public_id = external_id and external_id[1:-1]
+            docinfo.system_url = sys_uri and sys_uri[1:-1]
+
+    return res_root
+
+
+def _init_node_converters(makeelement):
+    converters = {}
+    ordered_node_types = []
+
+    def converter(*types):
+        def add(handler):
+            for t in types:
+                converters[t] = handler
+                ordered_node_types.append(t)
+            return handler
+        return add
+
+    def find_best_converter(node):
+        for t in ordered_node_types:
+            if isinstance(node, t):
+                return converters[t]
+        return None
+
+    def convert_node(bs_node, parent=None):
+        # duplicated in convert_tag() below
+        try:
+            handler = converters[type(bs_node)]
+        except KeyError:
+            handler = converters[type(bs_node)] = find_best_converter(bs_node)
+        if handler is None:
+            return None
+        return handler(bs_node, parent)
+
+    def map_attrs(bs_attrs):
+        if isinstance(bs_attrs, dict):  # bs4
+            attribs = {}
+            for k, v in bs_attrs.items():
+                if isinstance(v, list):
+                    v = " ".join(v)
+                attribs[k] = unescape(v)
+        else:
+            attribs = {k: unescape(v) for k, v in bs_attrs}
+        return attribs
+
+    def append_text(parent, text):
+        if len(parent) == 0:
+            parent.text = (parent.text or '') + text
+        else:
+            parent[-1].tail = (parent[-1].tail or '') + text
+
+    # converters are tried in order of their definition
+
+    @converter(Tag, _PseudoTag)
+    def convert_tag(bs_node, parent):
+        attrs = bs_node.attrs
+        if parent is not None:
+            attribs = map_attrs(attrs) if attrs else None
+            res = etree.SubElement(parent, bs_node.name, attrib=attribs)
+        else:
+            attribs = map_attrs(attrs) if attrs else {}
+            res = makeelement(bs_node.name, attrib=attribs)
+
+        for child in bs_node:
+            # avoid double recursion by inlining convert_node(), see above
+            try:
+                handler = converters[type(child)]
+            except KeyError:
+                pass
+            else:
+                if handler is not None:
+                    handler(child, res)
+                continue
+            convert_node(child, res)
+        return res
+
+    @converter(Comment)
+    def convert_comment(bs_node, parent):
+        res = html.HtmlComment(bs_node)
+        if parent is not None:
+            parent.append(res)
+        return res
+
+    @converter(ProcessingInstruction)
+    def convert_pi(bs_node, parent):
+        if bs_node.endswith('?'):
+            # The PI is of XML style (<?as df?>) but BeautifulSoup
+            # interpreted it as being SGML style (<?as df>). Fix.
+            bs_node = bs_node[:-1]
+        res = etree.ProcessingInstruction(*bs_node.split(' ', 1))
+        if parent is not None:
+            parent.append(res)
+        return res
+
+    @converter(NavigableString)
+    def convert_text(bs_node, parent):
+        if parent is not None:
+            append_text(parent, unescape(bs_node))
+        return None
+
+    return convert_node
+
+
+# copied from ET's ElementSoup
+
+try:
+    from html.entities import name2codepoint  # Python 3
+except ImportError:
+    from htmlentitydefs import name2codepoint
+
+
+handle_entities = re.compile(r"&(\w+);").sub
+
+
+try:
+    unichr
+except NameError:
+    # Python 3
+    unichr = chr
+
+
+def unescape(string):
+    if not string:
+        return ''
+    # work around oddities in BeautifulSoup's entity handling
+    def unescape_entity(m):
+        try:
+            return unichr(name2codepoint[m.group(1)])
+        except KeyError:
+            return m.group(0)  # use as is
+    return handle_entities(unescape_entity, string)
diff --git a/.venv/lib/python3.12/site-packages/lxml/html/usedoctest.py b/.venv/lib/python3.12/site-packages/lxml/html/usedoctest.py
new file mode 100644
index 00000000..f352a1cc
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/html/usedoctest.py
@@ -0,0 +1,13 @@
+"""Doctest module for HTML comparison.
+
+Usage::
+
+   >>> import lxml.html.usedoctest
+   >>> # now do your HTML doctests ...
+
+See `lxml.doctestcompare`.
+"""
+
+from lxml import doctestcompare
+
+doctestcompare.temp_install(html=True, del_module=__name__)
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/__init__.pxd b/.venv/lib/python3.12/site-packages/lxml/includes/__init__.pxd
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/__init__.pxd
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/__init__.py b/.venv/lib/python3.12/site-packages/lxml/includes/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/__init__.py
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/c14n.pxd b/.venv/lib/python3.12/site-packages/lxml/includes/c14n.pxd
new file mode 100644
index 00000000..8b1f3c4c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/c14n.pxd
@@ -0,0 +1,25 @@
+from lxml.includes.tree cimport xmlDoc, xmlOutputBuffer, xmlChar
+from lxml.includes.xpath cimport xmlNodeSet
+
+cdef extern from "libxml/c14n.h" nogil:
+    cdef int xmlC14NDocDumpMemory(xmlDoc* doc,
+                                  xmlNodeSet* nodes,
+                                  int exclusive,
+                                  xmlChar** inclusive_ns_prefixes,
+                                  int with_comments,
+                                  xmlChar** doc_txt_ptr)
+
+    cdef int xmlC14NDocSave(xmlDoc* doc,
+                            xmlNodeSet* nodes,
+                            int exclusive,
+                            xmlChar** inclusive_ns_prefixes,
+                            int with_comments,
+                            char* filename,
+                            int compression)
+
+    cdef int xmlC14NDocSaveTo(xmlDoc* doc,
+                              xmlNodeSet* nodes,
+                              int exclusive,
+                              xmlChar** inclusive_ns_prefixes,
+                              int with_comments,
+                              xmlOutputBuffer* buffer)
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/config.pxd b/.venv/lib/python3.12/site-packages/lxml/includes/config.pxd
new file mode 100644
index 00000000..9c04438f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/config.pxd
@@ -0,0 +1,3 @@
+cdef extern from "etree_defs.h":
+    cdef bint ENABLE_THREADING
+    cdef bint ENABLE_SCHEMATRON
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/dtdvalid.pxd b/.venv/lib/python3.12/site-packages/lxml/includes/dtdvalid.pxd
new file mode 100644
index 00000000..2ad49db1
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/dtdvalid.pxd
@@ -0,0 +1,18 @@
+from lxml.includes cimport tree
+from lxml.includes.tree cimport xmlDoc, xmlDtd
+
+cdef extern from "libxml/valid.h" nogil:
+    ctypedef void (*xmlValidityErrorFunc)(void * ctx, const char * msg, ...) noexcept
+    ctypedef void (*xmlValidityWarningFunc)(void * ctx, const char * msg, ...) noexcept
+
+    ctypedef struct xmlValidCtxt:
+        void *userData
+        xmlValidityErrorFunc error
+        xmlValidityWarningFunc warning
+
+    cdef xmlValidCtxt* xmlNewValidCtxt()
+    cdef void xmlFreeValidCtxt(xmlValidCtxt* cur)
+
+    cdef int xmlValidateDtd(xmlValidCtxt* ctxt, xmlDoc* doc, xmlDtd* dtd)
+    cdef tree.xmlElement* xmlGetDtdElementDesc(
+        xmlDtd* dtd, tree.const_xmlChar* name)
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/etree_defs.h b/.venv/lib/python3.12/site-packages/lxml/includes/etree_defs.h
new file mode 100644
index 00000000..17d470d0
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/etree_defs.h
@@ -0,0 +1,379 @@
+#ifndef HAS_ETREE_DEFS_H
+#define HAS_ETREE_DEFS_H
+
+/* quick check for Python/libxml2/libxslt devel setup */
+#include "Python.h"
+#ifndef PY_VERSION_HEX
+#  error the development package of Python (header files etc.) is not installed correctly
+#elif PY_VERSION_HEX < 0x03060000
+#  error this version of lxml requires Python 3.6 or later
+#endif
+
+#include "libxml/xmlversion.h"
+#ifndef LIBXML_VERSION
+#  error the development package of libxml2 (header files etc.) is not installed correctly
+#elif LIBXML_VERSION < 20700
+#  error minimum required version of libxml2 is 2.7.0
+#endif
+
+#include "libxslt/xsltconfig.h"
+#ifndef LIBXSLT_VERSION
+#  error the development package of libxslt (header files etc.) is not installed correctly
+#elif LIBXSLT_VERSION < 10123
+#  error minimum required version of libxslt is 1.1.23
+#endif
+
+
+/* v_arg functions */
+#define va_int(ap)     va_arg(ap, int)
+#define va_charptr(ap) va_arg(ap, char *)
+
+#ifdef PYPY_VERSION
+#    define IS_PYPY 1
+#else
+#    define IS_PYPY 0
+#endif
+
+/* unused */
+#define IS_PYTHON2 0
+#define IS_PYTHON3 1
+#undef LXML_UNICODE_STRINGS
+#define LXML_UNICODE_STRINGS 1
+
+#if !IS_PYPY
+#  define PyWeakref_LockObject(obj)          (NULL)
+#endif
+
+/* Threading is not currently supported by PyPy */
+#if IS_PYPY
+#  ifndef WITHOUT_THREADING
+#    define WITHOUT_THREADING
+#  endif
+#endif
+
+#if IS_PYPY
+#  ifndef PyUnicode_FromFormat
+#    define PyUnicode_FromFormat  PyString_FromFormat
+#  endif
+#  if !defined(PyBytes_FromFormat)
+#    ifdef PyString_FromFormat
+#      define PyBytes_FromFormat  PyString_FromFormat
+#    else
+#include <stdarg.h>
+static PyObject* PyBytes_FromFormat(const char* format, ...) {
+    PyObject *string;
+    va_list vargs;
+#ifdef HAVE_STDARG_PROTOTYPES
+    va_start(vargs, format);
+#else
+    va_start(vargs);
+#endif
+    string = PyUnicode_FromFormatV(format, vargs);
+    va_end(vargs);
+    if (string && PyUnicode_Check(string)) {
+        PyObject *bstring = PyUnicode_AsUTF8String(string);
+        Py_DECREF(string);
+        string = bstring;
+    }
+    if (string && !PyBytes_CheckExact(string)) {
+        Py_DECREF(string);
+        string = NULL;
+        PyErr_SetString(PyExc_TypeError, "String formatting and encoding failed to return bytes object");
+    }
+    return string;
+}
+#    endif
+#  endif
+#endif
+
+#if PY_VERSION_HEX >= 0x030B00A1
+/* Python 3.12 doesn't have wstr Unicode strings any more. */
+#undef PyUnicode_GET_DATA_SIZE
+#define PyUnicode_GET_DATA_SIZE(ustr)  (0)
+#undef PyUnicode_AS_DATA
+#define PyUnicode_AS_DATA(ustr)  (NULL)
+#undef PyUnicode_IS_READY
+#define PyUnicode_IS_READY(ustr)  (1)
+#endif
+
+#ifdef WITHOUT_THREADING
+#  undef PyEval_SaveThread
+#  define PyEval_SaveThread() (NULL)
+#  undef PyEval_RestoreThread
+#  define PyEval_RestoreThread(state)  if (state); else {}
+#  undef PyGILState_Ensure
+#  define PyGILState_Ensure() (PyGILState_UNLOCKED)
+#  undef PyGILState_Release
+#  define PyGILState_Release(state)  if (state); else {}
+#  undef  Py_UNBLOCK_THREADS
+#  define Py_UNBLOCK_THREADS  _save = NULL;
+#  undef  Py_BLOCK_THREADS
+#  define Py_BLOCK_THREADS  if (_save); else {}
+#endif
+
+#ifdef WITHOUT_THREADING
+#  define ENABLE_THREADING 0
+#else
+#  define ENABLE_THREADING 1
+#endif
+
+#if LIBXML_VERSION < 20704
+/* FIXME: hack to make new error reporting compile in old libxml2 versions */
+#  define xmlStructuredErrorContext NULL
+#  define xmlXIncludeProcessTreeFlagsData(n,o,d) xmlXIncludeProcessTreeFlags(n,o)
+#endif
+
+/* schematron was added in libxml2 2.6.21 */
+#ifdef LIBXML_SCHEMATRON_ENABLED
+#  define ENABLE_SCHEMATRON 1
+#else
+#  define ENABLE_SCHEMATRON 0
+#  define XML_SCHEMATRON_OUT_QUIET 0
+#  define XML_SCHEMATRON_OUT_XML 0
+#  define XML_SCHEMATRON_OUT_ERROR 0
+   typedef void xmlSchematron;
+   typedef void xmlSchematronParserCtxt;
+   typedef void xmlSchematronValidCtxt;
+#  define xmlSchematronNewDocParserCtxt(doc) NULL
+#  define xmlSchematronNewParserCtxt(file) NULL
+#  define xmlSchematronParse(ctxt) NULL
+#  define xmlSchematronFreeParserCtxt(ctxt)
+#  define xmlSchematronFree(schema)
+#  define xmlSchematronNewValidCtxt(schema, options) NULL
+#  define xmlSchematronValidateDoc(ctxt, doc) 0
+#  define xmlSchematronFreeValidCtxt(ctxt)
+#  define xmlSchematronSetValidStructuredErrors(ctxt, errorfunc, data)
+#endif
+
+#if LIBXML_VERSION < 20708
+#  define HTML_PARSE_NODEFDTD 4
+#endif
+#if LIBXML_VERSION < 20900
+#  define XML_PARSE_BIG_LINES 4194304
+#endif
+
+#include "libxml/tree.h"
+#ifndef LIBXML2_NEW_BUFFER
+   typedef xmlBuffer xmlBuf;
+#  define xmlBufContent(buf) xmlBufferContent(buf)
+#  define xmlBufUse(buf) xmlBufferLength(buf)
+#endif
+
+/* libexslt 1.1.25+ support EXSLT functions in XPath */
+#if LIBXSLT_VERSION < 10125
+#define exsltDateXpathCtxtRegister(ctxt, prefix)
+#define exsltSetsXpathCtxtRegister(ctxt, prefix)
+#define exsltMathXpathCtxtRegister(ctxt, prefix)
+#define exsltStrXpathCtxtRegister(ctxt, prefix)
+#endif
+
+#define LXML_GET_XSLT_ENCODING(result_var, style) XSLT_GET_IMPORT_PTR(result_var, style, encoding)
+
+/* work around MSDEV 6.0 */
+#if (_MSC_VER == 1200) && (WINVER < 0x0500)
+long _ftol( double ); //defined by VC6 C libs
+long _ftol2( double dblSource ) { return _ftol( dblSource ); }
+#endif
+
+#ifdef __GNUC__
+/* Test for GCC > 2.95 */
+#if __GNUC__ > 2 || (__GNUC__ == 2 && (__GNUC_MINOR__ > 95)) 
+#define unlikely_condition(x) __builtin_expect((x), 0)
+#else /* __GNUC__ > 2 ... */
+#define unlikely_condition(x) (x)
+#endif /* __GNUC__ > 2 ... */
+#else /* __GNUC__ */
+#define unlikely_condition(x) (x)
+#endif /* __GNUC__ */
+
+#ifndef Py_TYPE
+  #define Py_TYPE(ob)   (((PyObject*)(ob))->ob_type)
+#endif
+
+#define PY_NEW(T) \
+     (((PyTypeObject*)(T))->tp_new( \
+             (PyTypeObject*)(T), __pyx_empty_tuple, NULL))
+
+#define _fqtypename(o)  ((Py_TYPE(o))->tp_name)
+
+#define lxml_malloc(count, item_size) \
+    (unlikely_condition((size_t)(count) > (size_t) (PY_SSIZE_T_MAX / item_size)) ? NULL : \
+     (PyMem_Malloc((count) * item_size)))
+
+#define lxml_realloc(mem, count, item_size) \
+    (unlikely_condition((size_t)(count) > (size_t) (PY_SSIZE_T_MAX / item_size)) ? NULL : \
+     (PyMem_Realloc(mem, (count) * item_size)))
+
+#define lxml_free(mem)  PyMem_Free(mem)
+
+#define _isString(obj)   (PyUnicode_Check(obj) || PyBytes_Check(obj))
+
+#define _isElement(c_node) \
+        (((c_node)->type == XML_ELEMENT_NODE) || \
+         ((c_node)->type == XML_COMMENT_NODE) || \
+         ((c_node)->type == XML_ENTITY_REF_NODE) || \
+         ((c_node)->type == XML_PI_NODE))
+
+#define _isElementOrXInclude(c_node) \
+        (_isElement(c_node)                     || \
+         ((c_node)->type == XML_XINCLUDE_START) || \
+         ((c_node)->type == XML_XINCLUDE_END))
+
+#define _getNs(c_node) \
+        (((c_node)->ns == 0) ? 0 : ((c_node)->ns->href))
+
+
+#include "string.h"
+static void* lxml_unpack_xmldoc_capsule(PyObject* capsule, int* is_owned) {
+    xmlDoc *c_doc;
+    void *context;
+    *is_owned = 0;
+    if (unlikely_condition(!PyCapsule_IsValid(capsule, (const char*)"libxml2:xmlDoc"))) {
+        PyErr_SetString(
+                PyExc_TypeError,
+                "Not a valid capsule. The capsule argument must be a capsule object with name libxml2:xmlDoc");
+        return NULL;
+    }
+    c_doc = (xmlDoc*) PyCapsule_GetPointer(capsule, (const char*)"libxml2:xmlDoc");
+    if (unlikely_condition(!c_doc)) return NULL;
+
+    if (unlikely_condition(c_doc->type != XML_DOCUMENT_NODE && c_doc->type != XML_HTML_DOCUMENT_NODE)) {
+        PyErr_Format(
+            PyExc_ValueError,
+            "Illegal document provided: expected XML or HTML, found %d", (int)c_doc->type);
+        return NULL;
+    }
+
+    context = PyCapsule_GetContext(capsule);
+    if (unlikely_condition(!context && PyErr_Occurred())) return NULL;
+    if (context && strcmp((const char*) context, "destructor:xmlFreeDoc") == 0) {
+        /* take ownership by setting destructor to NULL */
+        if (PyCapsule_SetDestructor(capsule, NULL) == 0) {
+            /* ownership transferred => invalidate capsule by clearing its name */
+            if (unlikely_condition(PyCapsule_SetName(capsule, NULL))) {
+                /* this should never happen since everything above succeeded */
+                xmlFreeDoc(c_doc);
+                return NULL;
+            }
+            *is_owned = 1;
+        }
+    }
+    return c_doc;
+}
+
+/* Macro pair implementation of a depth first tree walker
+ *
+ * Calls the code block between the BEGIN and END macros for all elements
+ * below c_tree_top (exclusively), starting at c_node (inclusively iff
+ * 'inclusive' is 1).  The _ELEMENT_ variants will only stop on nodes
+ * that match _isElement(), the normal variant will stop on every node
+ * except text nodes.
+ * 
+ * To traverse the node and all of its children and siblings in Pyrex, call
+ *    cdef xmlNode* some_node
+ *    BEGIN_FOR_EACH_ELEMENT_FROM(some_node.parent, some_node, 1)
+ *    # do something with some_node
+ *    END_FOR_EACH_ELEMENT_FROM(some_node)
+ *
+ * To traverse only the children and siblings of a node, call
+ *    cdef xmlNode* some_node
+ *    BEGIN_FOR_EACH_ELEMENT_FROM(some_node.parent, some_node, 0)
+ *    # do something with some_node
+ *    END_FOR_EACH_ELEMENT_FROM(some_node)
+ *
+ * To traverse only the children, do:
+ *    cdef xmlNode* some_node
+ *    some_node = parent_node.children
+ *    BEGIN_FOR_EACH_ELEMENT_FROM(parent_node, some_node, 1)
+ *    # do something with some_node
+ *    END_FOR_EACH_ELEMENT_FROM(some_node)
+ *
+ * NOTE: 'some_node' MUST be a plain 'xmlNode*' !
+ *
+ * NOTE: parent modification during the walk can divert the iterator, but
+ *       should not segfault !
+ */
+
+#define _LX__ELEMENT_MATCH(c_node, only_elements)  \
+    ((only_elements) ? (_isElement(c_node)) : 1)
+
+#define _LX__ADVANCE_TO_NEXT(c_node, only_elements)                        \
+    while ((c_node != 0) && (!_LX__ELEMENT_MATCH(c_node, only_elements)))  \
+        c_node = c_node->next;
+
+#define _LX__TRAVERSE_TO_NEXT(c_stop_node, c_node, only_elements)   \
+{                                                                   \
+    /* walk through children first */                               \
+    xmlNode* _lx__next = c_node->children;		            \
+    if (_lx__next != 0) {                                           \
+        if (c_node->type == XML_ENTITY_REF_NODE || c_node->type == XML_DTD_NODE) { \
+            _lx__next = 0;                                          \
+        } else {                                                    \
+            _LX__ADVANCE_TO_NEXT(_lx__next, only_elements)	    \
+        }                                                           \
+    }							            \
+    if ((_lx__next == 0) && (c_node != c_stop_node)) {              \
+        /* try siblings */                                          \
+        _lx__next = c_node->next;                                   \
+        _LX__ADVANCE_TO_NEXT(_lx__next, only_elements)              \
+        /* back off through parents */                              \
+        while (_lx__next == 0) {                                    \
+            c_node = c_node->parent;                                \
+            if (c_node == 0)                                        \
+                break;                                              \
+            if (c_node == c_stop_node)                              \
+                break;                                              \
+            if ((only_elements) && !_isElement(c_node))	            \
+                break;                                              \
+            /* we already traversed the parents -> siblings */      \
+            _lx__next = c_node->next;                               \
+            _LX__ADVANCE_TO_NEXT(_lx__next, only_elements)	    \
+        }                                                           \
+    }                                                               \
+    c_node = _lx__next;                                             \
+}
+
+#define _LX__BEGIN_FOR_EACH_FROM(c_tree_top, c_node, inclusive, only_elements)     \
+{									      \
+    if (c_node != 0) {							      \
+        const xmlNode* _lx__tree_top = (c_tree_top);                          \
+        const int _lx__only_elements = (only_elements);                       \
+        /* make sure we start at an element */                   	      \
+        if (!_LX__ELEMENT_MATCH(c_node, _lx__only_elements)) {		      \
+            /* we skip the node, so 'inclusive' is irrelevant */              \
+            if (c_node == _lx__tree_top)                                      \
+                c_node = 0; /* nothing to traverse */                         \
+            else {                                                            \
+                c_node = c_node->next;                                        \
+                _LX__ADVANCE_TO_NEXT(c_node, _lx__only_elements)              \
+            }                                                                 \
+        } else if (! (inclusive)) {                                           \
+            /* skip the first node */                                         \
+            _LX__TRAVERSE_TO_NEXT(_lx__tree_top, c_node, _lx__only_elements)  \
+        }                                                                     \
+                                                                              \
+        /* now run the user code on the elements we find */                   \
+        while (c_node != 0) {                                                 \
+            /* here goes the code to be run for each element */
+
+#define _LX__END_FOR_EACH_FROM(c_node)                                        \
+            _LX__TRAVERSE_TO_NEXT(_lx__tree_top, c_node, _lx__only_elements)  \
+        }                                                                     \
+    }                                                                         \
+}
+
+
+#define BEGIN_FOR_EACH_ELEMENT_FROM(c_tree_top, c_node, inclusive)   \
+    _LX__BEGIN_FOR_EACH_FROM(c_tree_top, c_node, inclusive, 1)
+
+#define END_FOR_EACH_ELEMENT_FROM(c_node)   \
+    _LX__END_FOR_EACH_FROM(c_node)
+
+#define BEGIN_FOR_EACH_FROM(c_tree_top, c_node, inclusive)   \
+    _LX__BEGIN_FOR_EACH_FROM(c_tree_top, c_node, inclusive, 0)
+
+#define END_FOR_EACH_FROM(c_node)   \
+    _LX__END_FOR_EACH_FROM(c_node)
+
+
+#endif /* HAS_ETREE_DEFS_H */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/etreepublic.pxd b/.venv/lib/python3.12/site-packages/lxml/includes/etreepublic.pxd
new file mode 100644
index 00000000..7ef001b1
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/etreepublic.pxd
@@ -0,0 +1,237 @@
+# public Cython/C interface to lxml.etree
+
+from lxml.includes cimport tree
+from lxml.includes.tree cimport const_xmlChar
+
+cdef extern from "lxml-version.h":
+    cdef char* LXML_VERSION_STRING
+
+cdef extern from "etree_defs.h":
+    # test if c_node is considered an Element (i.e. Element, Comment, etc.)
+    cdef bint _isElement(tree.xmlNode* c_node) noexcept nogil
+
+    # return the namespace URI of the node or NULL
+    cdef const_xmlChar* _getNs(tree.xmlNode* node) noexcept nogil
+
+    # pair of macros for tree traversal
+    cdef void BEGIN_FOR_EACH_ELEMENT_FROM(tree.xmlNode* tree_top,
+                                          tree.xmlNode* start_node,
+                                          int start_node_inclusive) noexcept nogil
+    cdef void END_FOR_EACH_ELEMENT_FROM(tree.xmlNode* start_node) noexcept nogil
+
+cdef extern from "etree_api.h":
+
+    # first function to call!
+    cdef int import_lxml__etree() except -1
+
+    ##########################################################################
+    # public ElementTree API classes
+
+    cdef class lxml.etree._Document [ object LxmlDocument ]:
+        cdef tree.xmlDoc* _c_doc
+
+    cdef class lxml.etree._Element [ object LxmlElement ]:
+        cdef _Document _doc
+        cdef tree.xmlNode* _c_node
+
+    cdef class lxml.etree.ElementBase(_Element) [ object LxmlElementBase ]:
+        pass
+
+    cdef class lxml.etree._ElementTree [ object LxmlElementTree ]:
+        cdef _Document _doc
+        cdef _Element  _context_node
+
+    cdef class lxml.etree.ElementClassLookup [ object LxmlElementClassLookup ]:
+        cdef object (*_lookup_function)(object, _Document, tree.xmlNode*)
+
+    cdef class lxml.etree.FallbackElementClassLookup(ElementClassLookup) \
+             [ object LxmlFallbackElementClassLookup ]:
+        cdef ElementClassLookup fallback
+        cdef object (*_fallback_function)(object, _Document, tree.xmlNode*)
+
+    ##########################################################################
+    # creating Element objects
+
+    # create an Element for a C-node in the Document
+    cdef _Element elementFactory(_Document doc, tree.xmlNode* c_node)
+
+    # create an ElementTree for an Element
+    cdef _ElementTree elementTreeFactory(_Element context_node)
+
+    # create an ElementTree subclass for an Element
+    cdef _ElementTree newElementTree(_Element context_node, object subclass)
+
+    # create an ElementTree from an external document
+    cdef _ElementTree adoptExternalDocument(tree.xmlDoc* c_doc, parser, bint is_owned)
+
+    # create a new Element for an existing or new document (doc = None)
+    # builds Python object after setting text, tail, namespaces and attributes
+    cdef _Element makeElement(tag, _Document doc, parser,
+                              text, tail, attrib, nsmap)
+
+    # create a new SubElement for an existing parent
+    # builds Python object after setting text, tail, namespaces and attributes
+    cdef _Element makeSubElement(_Element parent, tag, text, tail,
+                                 attrib, nsmap)
+
+    # deep copy a node to include it in the Document
+    cdef _Element deepcopyNodeToDocument(_Document doc, tree.xmlNode* c_root)
+
+    # set the internal lookup function for Element/Comment/PI classes
+    # use setElementClassLookupFunction(NULL, None) to reset it
+    # note that the lookup function *must always* return an _Element subclass!
+    cdef void setElementClassLookupFunction(
+         object (*function)(object, _Document, tree.xmlNode*), object state)
+
+    # lookup function that always returns the default Element class
+    # note that the first argument is expected to be None!
+    cdef object lookupDefaultElementClass(_1, _Document _2,
+                                          tree.xmlNode* c_node)
+
+    # lookup function for namespace/tag specific Element classes
+    # note that the first argument is expected to be None!
+    cdef object lookupNamespaceElementClass(_1, _Document _2,
+                                            tree.xmlNode* c_node)
+
+    # call the fallback lookup function of a FallbackElementClassLookup
+    cdef object callLookupFallback(FallbackElementClassLookup lookup,
+                                   _Document doc, tree.xmlNode* c_node)
+
+    ##########################################################################
+    # XML attribute access
+
+    # return an attribute value for a C attribute on a C element node
+    cdef unicode attributeValue(tree.xmlNode* c_element,
+                                tree.xmlAttr* c_attrib_node)
+
+    # return the value of the attribute with 'ns' and 'name' (or None)
+    cdef unicode attributeValueFromNsName(tree.xmlNode* c_element,
+                                          const_xmlChar* c_ns, const_xmlChar* c_name)
+
+    # return the value of attribute "{ns}name", or the default value
+    cdef object getAttributeValue(_Element element, key, default)
+
+    # return an iterator over attribute names (1), values (2) or items (3)
+    # attributes must not be removed during iteration!
+    cdef object iterattributes(_Element element, int keysvalues)
+
+    # return the list of all attribute names (1), values (2) or items (3)
+    cdef list collectAttributes(tree.xmlNode* c_element, int keysvalues)
+
+    # set an attribute value on an element
+    # on failure, sets an exception and returns -1
+    cdef int setAttributeValue(_Element element, key, value) except -1
+
+    # delete an attribute
+    # on failure, sets an exception and returns -1
+    cdef int delAttribute(_Element element, key) except -1
+
+    # delete an attribute based on name and namespace URI
+    # returns -1 if the attribute was not found (no exception)
+    cdef int delAttributeFromNsName(tree.xmlNode* c_element,
+                                    const_xmlChar* c_href, const_xmlChar* c_name) noexcept
+
+    ##########################################################################
+    # XML node helper functions
+
+    # check if the element has at least one child
+    cdef bint hasChild(tree.xmlNode* c_node) noexcept nogil
+
+    # find child element number 'index' (supports negative indexes)
+    cdef tree.xmlNode* findChild(tree.xmlNode* c_node,
+                                 Py_ssize_t index) noexcept nogil
+
+    # find child element number 'index' starting at first one
+    cdef tree.xmlNode* findChildForwards(tree.xmlNode* c_node,
+                                         Py_ssize_t index) nogil
+
+    # find child element number 'index' starting at last one
+    cdef tree.xmlNode* findChildBackwards(tree.xmlNode* c_node,
+                                          Py_ssize_t index) nogil
+
+    # return next/previous sibling element of the node
+    cdef tree.xmlNode* nextElement(tree.xmlNode* c_node) nogil
+    cdef tree.xmlNode* previousElement(tree.xmlNode* c_node) nogil
+
+    ##########################################################################
+    # iterators (DEPRECATED API, don't use in new code!)
+
+    cdef class lxml.etree._ElementTagMatcher [ object LxmlElementTagMatcher ]:
+        cdef char* _href
+        cdef char* _name
+
+    # store "{ns}tag" (or None) filter for this matcher or element iterator
+    # ** unless _href *and* _name are set up 'by hand', this function *must*
+    # ** be called when subclassing the iterator below!
+    cdef void initTagMatch(_ElementTagMatcher matcher, tag)
+
+    cdef class lxml.etree._ElementIterator(_ElementTagMatcher) [
+        object LxmlElementIterator ]:
+        cdef _Element _node
+        cdef tree.xmlNode* (*_next_element)(tree.xmlNode*)
+
+    # store the initial node of the iterator if it matches the required tag
+    # or its next matching sibling if not
+    cdef void iteratorStoreNext(_ElementIterator iterator, _Element node)
+
+    ##########################################################################
+    # other helper functions
+
+    # check if a C node matches a tag name and namespace
+    # (NULL allowed for each => always matches)
+    cdef int tagMatches(tree.xmlNode* c_node, const_xmlChar* c_href, const_xmlChar* c_name)
+
+    # convert a UTF-8 char* to a Python unicode string
+    cdef unicode pyunicode(const_xmlChar* s)
+
+    # convert the string to UTF-8 using the normal lxml.etree semantics
+    cdef bytes utf8(object s)
+
+    # split a tag into a (URI, name) tuple, return None as URI for '{}tag'
+    cdef tuple getNsTag(object tag)
+
+    # split a tag into a (URI, name) tuple, return b'' as URI for '{}tag'
+    cdef tuple getNsTagWithEmptyNs(object tag)
+
+    # get the "{ns}tag" string for a C node
+    cdef unicode namespacedName(tree.xmlNode* c_node)
+
+    # get the "{ns}tag" string for a href/tagname pair (c_ns may be NULL)
+    cdef unicode namespacedNameFromNsName(const_xmlChar* c_ns, const_xmlChar* c_tag)
+
+    # check if the node has a text value (which may be '')
+    cdef bint hasText(tree.xmlNode* c_node) nogil
+
+    # check if the node has a tail value (which may be '')
+    cdef bint hasTail(tree.xmlNode* c_node) nogil
+
+    # get the text content of an element (or None)
+    cdef unicode textOf(tree.xmlNode* c_node)
+
+    # get the tail content of an element (or None)
+    cdef unicode tailOf(tree.xmlNode* c_node)
+
+    # set the text value of an element
+    cdef int setNodeText(tree.xmlNode* c_node, text) except -1
+
+    # set the tail text value of an element
+    cdef int setTailText(tree.xmlNode* c_node, text) except -1
+
+    # append an element to the children of a parent element
+    # deprecated: don't use, does not propagate exceptions!
+    # use appendChildToElement() instead
+    cdef void appendChild(_Element parent, _Element child)
+
+    # added in lxml 3.3 as a safe replacement for appendChild()
+    # return -1 for exception, 0 for ok
+    cdef int appendChildToElement(_Element parent, _Element child) except -1
+
+    # recursively lookup a namespace in element or ancestors, or create it
+    cdef tree.xmlNs* findOrBuildNodeNsPrefix(
+        _Document doc, tree.xmlNode* c_node, const_xmlChar* href, const_xmlChar* prefix)
+
+    # find the Document of an Element, ElementTree or Document (itself!)
+    cdef _Document documentOrRaise(object input)
+
+    # find the root Element of an Element (itself!), ElementTree or Document
+    cdef _Element rootNodeOrRaise(object input)
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/extlibs/__init__.py b/.venv/lib/python3.12/site-packages/lxml/includes/extlibs/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/extlibs/__init__.py
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/extlibs/libcharset.h b/.venv/lib/python3.12/site-packages/lxml/includes/extlibs/libcharset.h
new file mode 100644
index 00000000..fcf22748
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/extlibs/libcharset.h
@@ -0,0 +1,45 @@
+/* Copyright (C) 2003 Free Software Foundation, Inc.
+   This file is part of the GNU CHARSET Library.
+
+   The GNU CHARSET Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU CHARSET Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with the GNU CHARSET Library; see the file COPYING.LIB.  If not,
+   see <https://www.gnu.org/licenses/>.  */
+
+#ifndef _LIBCHARSET_H
+#define _LIBCHARSET_H
+
+#include <localcharset.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Support for relocatable packages.  */
+
+/* Sets the original and the current installation prefix of the package.
+   Relocation simply replaces a pathname starting with the original prefix
+   by the corresponding pathname with the current prefix instead.  Both
+   prefixes should be directory names without trailing slash (i.e. use ""
+   instead of "/").  */
+extern void libcharset_set_relocation_prefix (const char *orig_prefix,
+                                              const char *curr_prefix);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _LIBCHARSET_H */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/extlibs/localcharset.h b/.venv/lib/python3.12/site-packages/lxml/includes/extlibs/localcharset.h
new file mode 100644
index 00000000..34ce0add
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/extlibs/localcharset.h
@@ -0,0 +1,137 @@
+/* Determine a canonical name for the current locale's character encoding.
+   Copyright (C) 2000-2003, 2009-2019 Free Software Foundation, Inc.
+   This file is part of the GNU CHARSET Library.
+
+   This program is free software; you can redistribute it and/or modify it
+   under the terms of the GNU Lesser General Public License as published
+   by the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program; if not, see <https://www.gnu.org/licenses/>.  */
+
+#ifndef _LOCALCHARSET_H
+#define _LOCALCHARSET_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Determine the current locale's character encoding, and canonicalize it
+   into one of the canonical names listed below.
+   The result must not be freed; it is statically allocated.  The result
+   becomes invalid when setlocale() is used to change the global locale, or
+   when the value of one of the environment variables LC_ALL, LC_CTYPE, LANG
+   is changed; threads in multithreaded programs should not do this.
+   If the canonical name cannot be determined, the result is a non-canonical
+   name.  */
+extern const char * locale_charset (void);
+
+/* About GNU canonical names for character encodings:
+
+   Every canonical name must be supported by GNU libiconv.  Support by GNU libc
+   is also desirable.
+
+   The name is case insensitive.  Usually an upper case MIME charset name is
+   preferred.
+
+   The current list of these GNU canonical names is:
+
+       name              MIME?             used by which systems
+                                    (darwin = Mac OS X, windows = native Windows)
+
+   ASCII, ANSI_X3.4-1968       glibc solaris freebsd netbsd darwin minix cygwin
+   ISO-8859-1              Y   glibc aix hpux irix osf solaris freebsd netbsd openbsd darwin cygwin zos
+   ISO-8859-2              Y   glibc aix hpux irix osf solaris freebsd netbsd openbsd darwin cygwin zos
+   ISO-8859-3              Y   glibc solaris cygwin
+   ISO-8859-4              Y   hpux osf solaris freebsd netbsd openbsd darwin
+   ISO-8859-5              Y   glibc aix hpux irix osf solaris freebsd netbsd openbsd darwin cygwin zos
+   ISO-8859-6              Y   glibc aix hpux solaris cygwin
+   ISO-8859-7              Y   glibc aix hpux irix osf solaris freebsd netbsd openbsd darwin cygwin zos
+   ISO-8859-8              Y   glibc aix hpux osf solaris cygwin zos
+   ISO-8859-9              Y   glibc aix hpux irix osf solaris freebsd darwin cygwin zos
+   ISO-8859-13                 glibc hpux solaris freebsd netbsd openbsd darwin cygwin
+   ISO-8859-14                 glibc cygwin
+   ISO-8859-15                 glibc aix irix osf solaris freebsd netbsd openbsd darwin cygwin
+   KOI8-R                  Y   glibc hpux solaris freebsd netbsd openbsd darwin
+   KOI8-U                  Y   glibc freebsd netbsd openbsd darwin cygwin
+   KOI8-T                      glibc
+   CP437                       dos
+   CP775                       dos
+   CP850                       aix osf dos
+   CP852                       dos
+   CP855                       dos
+   CP856                       aix
+   CP857                       dos
+   CP861                       dos
+   CP862                       dos
+   CP864                       dos
+   CP865                       dos
+   CP866                       freebsd netbsd openbsd darwin dos
+   CP869                       dos
+   CP874                       windows dos
+   CP922                       aix
+   CP932                       aix cygwin windows dos
+   CP943                       aix zos
+   CP949                       osf darwin windows dos
+   CP950                       windows dos
+   CP1046                      aix
+   CP1124                      aix
+   CP1125                      dos
+   CP1129                      aix
+   CP1131                      freebsd darwin
+   CP1250                      windows
+   CP1251                      glibc hpux solaris freebsd netbsd openbsd darwin cygwin windows
+   CP1252                      aix windows
+   CP1253                      windows
+   CP1254                      windows
+   CP1255                      glibc windows
+   CP1256                      windows
+   CP1257                      windows
+   GB2312                  Y   glibc aix hpux irix solaris freebsd netbsd darwin cygwin zos
+   EUC-JP                  Y   glibc aix hpux irix osf solaris freebsd netbsd darwin cygwin
+   EUC-KR                  Y   glibc aix hpux irix osf solaris freebsd netbsd darwin cygwin zos
+   EUC-TW                      glibc aix hpux irix osf solaris netbsd
+   BIG5                    Y   glibc aix hpux osf solaris freebsd netbsd darwin cygwin zos
+   BIG5-HKSCS                  glibc hpux solaris netbsd darwin
+   GBK                         glibc aix osf solaris freebsd darwin cygwin windows dos
+   GB18030                     glibc hpux solaris freebsd netbsd darwin
+   SHIFT_JIS               Y   hpux osf solaris freebsd netbsd darwin
+   JOHAB                       glibc solaris windows
+   TIS-620                     glibc aix hpux osf solaris cygwin zos
+   VISCII                  Y   glibc
+   TCVN5712-1                  glibc
+   ARMSCII-8                   glibc freebsd netbsd darwin
+   GEORGIAN-PS                 glibc cygwin
+   PT154                       glibc netbsd cygwin
+   HP-ROMAN8                   hpux
+   HP-ARABIC8                  hpux
+   HP-GREEK8                   hpux
+   HP-HEBREW8                  hpux
+   HP-TURKISH8                 hpux
+   HP-KANA8                    hpux
+   DEC-KANJI                   osf
+   DEC-HANYU                   osf
+   UTF-8                   Y   glibc aix hpux osf solaris netbsd darwin cygwin zos
+
+   Note: Names which are not marked as being a MIME name should not be used in
+   Internet protocols for information interchange (mail, news, etc.).
+
+   Note: ASCII and ANSI_X3.4-1968 are synonymous canonical names.  Applications
+   must understand both names and treat them as equivalent.
+ */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _LOCALCHARSET_H */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/extlibs/zconf.h b/.venv/lib/python3.12/site-packages/lxml/includes/extlibs/zconf.h
new file mode 100644
index 00000000..ede3c82e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/extlibs/zconf.h
@@ -0,0 +1,543 @@
+/* zconf.h -- configuration of the zlib compression library
+ * Copyright (C) 1995-2024 Jean-loup Gailly, Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id$ */
+
+#ifndef ZCONF_H
+#define ZCONF_H
+
+/*
+ * If you *really* need a unique prefix for all types and library functions,
+ * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it.
+ * Even better than compiling with -DZ_PREFIX would be to use configure to set
+ * this permanently in zconf.h using "./configure --zprefix".
+ */
+#ifdef Z_PREFIX     /* may be set to #if 1 by ./configure */
+#  define Z_PREFIX_SET
+
+/* all linked symbols and init macros */
+#  define _dist_code            z__dist_code
+#  define _length_code          z__length_code
+#  define _tr_align             z__tr_align
+#  define _tr_flush_bits        z__tr_flush_bits
+#  define _tr_flush_block       z__tr_flush_block
+#  define _tr_init              z__tr_init
+#  define _tr_stored_block      z__tr_stored_block
+#  define _tr_tally             z__tr_tally
+#  define adler32               z_adler32
+#  define adler32_combine       z_adler32_combine
+#  define adler32_combine64     z_adler32_combine64
+#  define adler32_z             z_adler32_z
+#  ifndef Z_SOLO
+#    define compress              z_compress
+#    define compress2             z_compress2
+#    define compressBound         z_compressBound
+#  endif
+#  define crc32                 z_crc32
+#  define crc32_combine         z_crc32_combine
+#  define crc32_combine64       z_crc32_combine64
+#  define crc32_combine_gen     z_crc32_combine_gen
+#  define crc32_combine_gen64   z_crc32_combine_gen64
+#  define crc32_combine_op      z_crc32_combine_op
+#  define crc32_z               z_crc32_z
+#  define deflate               z_deflate
+#  define deflateBound          z_deflateBound
+#  define deflateCopy           z_deflateCopy
+#  define deflateEnd            z_deflateEnd
+#  define deflateGetDictionary  z_deflateGetDictionary
+#  define deflateInit           z_deflateInit
+#  define deflateInit2          z_deflateInit2
+#  define deflateInit2_         z_deflateInit2_
+#  define deflateInit_          z_deflateInit_
+#  define deflateParams         z_deflateParams
+#  define deflatePending        z_deflatePending
+#  define deflatePrime          z_deflatePrime
+#  define deflateReset          z_deflateReset
+#  define deflateResetKeep      z_deflateResetKeep
+#  define deflateSetDictionary  z_deflateSetDictionary
+#  define deflateSetHeader      z_deflateSetHeader
+#  define deflateTune           z_deflateTune
+#  define deflate_copyright     z_deflate_copyright
+#  define get_crc_table         z_get_crc_table
+#  ifndef Z_SOLO
+#    define gz_error              z_gz_error
+#    define gz_intmax             z_gz_intmax
+#    define gz_strwinerror        z_gz_strwinerror
+#    define gzbuffer              z_gzbuffer
+#    define gzclearerr            z_gzclearerr
+#    define gzclose               z_gzclose
+#    define gzclose_r             z_gzclose_r
+#    define gzclose_w             z_gzclose_w
+#    define gzdirect              z_gzdirect
+#    define gzdopen               z_gzdopen
+#    define gzeof                 z_gzeof
+#    define gzerror               z_gzerror
+#    define gzflush               z_gzflush
+#    define gzfread               z_gzfread
+#    define gzfwrite              z_gzfwrite
+#    define gzgetc                z_gzgetc
+#    define gzgetc_               z_gzgetc_
+#    define gzgets                z_gzgets
+#    define gzoffset              z_gzoffset
+#    define gzoffset64            z_gzoffset64
+#    define gzopen                z_gzopen
+#    define gzopen64              z_gzopen64
+#    ifdef _WIN32
+#      define gzopen_w              z_gzopen_w
+#    endif
+#    define gzprintf              z_gzprintf
+#    define gzputc                z_gzputc
+#    define gzputs                z_gzputs
+#    define gzread                z_gzread
+#    define gzrewind              z_gzrewind
+#    define gzseek                z_gzseek
+#    define gzseek64              z_gzseek64
+#    define gzsetparams           z_gzsetparams
+#    define gztell                z_gztell
+#    define gztell64              z_gztell64
+#    define gzungetc              z_gzungetc
+#    define gzvprintf             z_gzvprintf
+#    define gzwrite               z_gzwrite
+#  endif
+#  define inflate               z_inflate
+#  define inflateBack           z_inflateBack
+#  define inflateBackEnd        z_inflateBackEnd
+#  define inflateBackInit       z_inflateBackInit
+#  define inflateBackInit_      z_inflateBackInit_
+#  define inflateCodesUsed      z_inflateCodesUsed
+#  define inflateCopy           z_inflateCopy
+#  define inflateEnd            z_inflateEnd
+#  define inflateGetDictionary  z_inflateGetDictionary
+#  define inflateGetHeader      z_inflateGetHeader
+#  define inflateInit           z_inflateInit
+#  define inflateInit2          z_inflateInit2
+#  define inflateInit2_         z_inflateInit2_
+#  define inflateInit_          z_inflateInit_
+#  define inflateMark           z_inflateMark
+#  define inflatePrime          z_inflatePrime
+#  define inflateReset          z_inflateReset
+#  define inflateReset2         z_inflateReset2
+#  define inflateResetKeep      z_inflateResetKeep
+#  define inflateSetDictionary  z_inflateSetDictionary
+#  define inflateSync           z_inflateSync
+#  define inflateSyncPoint      z_inflateSyncPoint
+#  define inflateUndermine      z_inflateUndermine
+#  define inflateValidate       z_inflateValidate
+#  define inflate_copyright     z_inflate_copyright
+#  define inflate_fast          z_inflate_fast
+#  define inflate_table         z_inflate_table
+#  ifndef Z_SOLO
+#    define uncompress            z_uncompress
+#    define uncompress2           z_uncompress2
+#  endif
+#  define zError                z_zError
+#  ifndef Z_SOLO
+#    define zcalloc               z_zcalloc
+#    define zcfree                z_zcfree
+#  endif
+#  define zlibCompileFlags      z_zlibCompileFlags
+#  define zlibVersion           z_zlibVersion
+
+/* all zlib typedefs in zlib.h and zconf.h */
+#  define Byte                  z_Byte
+#  define Bytef                 z_Bytef
+#  define alloc_func            z_alloc_func
+#  define charf                 z_charf
+#  define free_func             z_free_func
+#  ifndef Z_SOLO
+#    define gzFile                z_gzFile
+#  endif
+#  define gz_header             z_gz_header
+#  define gz_headerp            z_gz_headerp
+#  define in_func               z_in_func
+#  define intf                  z_intf
+#  define out_func              z_out_func
+#  define uInt                  z_uInt
+#  define uIntf                 z_uIntf
+#  define uLong                 z_uLong
+#  define uLongf                z_uLongf
+#  define voidp                 z_voidp
+#  define voidpc                z_voidpc
+#  define voidpf                z_voidpf
+
+/* all zlib structs in zlib.h and zconf.h */
+#  define gz_header_s           z_gz_header_s
+#  define internal_state        z_internal_state
+
+#endif
+
+#if defined(__MSDOS__) && !defined(MSDOS)
+#  define MSDOS
+#endif
+#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2)
+#  define OS2
+#endif
+#if defined(_WINDOWS) && !defined(WINDOWS)
+#  define WINDOWS
+#endif
+#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__)
+#  ifndef WIN32
+#    define WIN32
+#  endif
+#endif
+#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32)
+#  if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__)
+#    ifndef SYS16BIT
+#      define SYS16BIT
+#    endif
+#  endif
+#endif
+
+/*
+ * Compile with -DMAXSEG_64K if the alloc function cannot allocate more
+ * than 64k bytes at a time (needed on systems with 16-bit int).
+ */
+#ifdef SYS16BIT
+#  define MAXSEG_64K
+#endif
+#ifdef MSDOS
+#  define UNALIGNED_OK
+#endif
+
+#ifdef __STDC_VERSION__
+#  ifndef STDC
+#    define STDC
+#  endif
+#  if __STDC_VERSION__ >= 199901L
+#    ifndef STDC99
+#      define STDC99
+#    endif
+#  endif
+#endif
+#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus))
+#  define STDC
+#endif
+#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__))
+#  define STDC
+#endif
+#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32))
+#  define STDC
+#endif
+#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__))
+#  define STDC
+#endif
+
+#if defined(__OS400__) && !defined(STDC)    /* iSeries (formerly AS/400). */
+#  define STDC
+#endif
+
+#ifndef STDC
+#  ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */
+#    define const       /* note: need a more gentle solution here */
+#  endif
+#endif
+
+#if defined(ZLIB_CONST) && !defined(z_const)
+#  define z_const const
+#else
+#  define z_const
+#endif
+
+#ifdef Z_SOLO
+#  ifdef _WIN64
+     typedef unsigned long long z_size_t;
+#  else
+     typedef unsigned long z_size_t;
+#  endif
+#else
+#  define z_longlong long long
+#  if defined(NO_SIZE_T)
+     typedef unsigned NO_SIZE_T z_size_t;
+#  elif defined(STDC)
+#    include <stddef.h>
+     typedef size_t z_size_t;
+#  else
+     typedef unsigned long z_size_t;
+#  endif
+#  undef z_longlong
+#endif
+
+/* Maximum value for memLevel in deflateInit2 */
+#ifndef MAX_MEM_LEVEL
+#  ifdef MAXSEG_64K
+#    define MAX_MEM_LEVEL 8
+#  else
+#    define MAX_MEM_LEVEL 9
+#  endif
+#endif
+
+/* Maximum value for windowBits in deflateInit2 and inflateInit2.
+ * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files
+ * created by gzip. (Files created by minigzip can still be extracted by
+ * gzip.)
+ */
+#ifndef MAX_WBITS
+#  define MAX_WBITS   15 /* 32K LZ77 window */
+#endif
+
+/* The memory requirements for deflate are (in bytes):
+            (1 << (windowBits+2)) +  (1 << (memLevel+9))
+ that is: 128K for windowBits=15  +  128K for memLevel = 8  (default values)
+ plus a few kilobytes for small objects. For example, if you want to reduce
+ the default memory requirements from 256K to 128K, compile with
+     make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
+ Of course this will generally degrade compression (there's no free lunch).
+
+   The memory requirements for inflate are (in bytes) 1 << windowBits
+ that is, 32K for windowBits=15 (default value) plus about 7 kilobytes
+ for small objects.
+*/
+
+                        /* Type declarations */
+
+#ifndef OF /* function prototypes */
+#  ifdef STDC
+#    define OF(args)  args
+#  else
+#    define OF(args)  ()
+#  endif
+#endif
+
+/* The following definitions for FAR are needed only for MSDOS mixed
+ * model programming (small or medium model with some far allocations).
+ * This was tested only with MSC; for other MSDOS compilers you may have
+ * to define NO_MEMCPY in zutil.h.  If you don't need the mixed model,
+ * just define FAR to be empty.
+ */
+#ifdef SYS16BIT
+#  if defined(M_I86SM) || defined(M_I86MM)
+     /* MSC small or medium model */
+#    define SMALL_MEDIUM
+#    ifdef _MSC_VER
+#      define FAR _far
+#    else
+#      define FAR far
+#    endif
+#  endif
+#  if (defined(__SMALL__) || defined(__MEDIUM__))
+     /* Turbo C small or medium model */
+#    define SMALL_MEDIUM
+#    ifdef __BORLANDC__
+#      define FAR _far
+#    else
+#      define FAR far
+#    endif
+#  endif
+#endif
+
+#if defined(WINDOWS) || defined(WIN32)
+   /* If building or using zlib as a DLL, define ZLIB_DLL.
+    * This is not mandatory, but it offers a little performance increase.
+    */
+#  ifdef ZLIB_DLL
+#    if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500))
+#      ifdef ZLIB_INTERNAL
+#        define ZEXTERN extern __declspec(dllexport)
+#      else
+#        define ZEXTERN extern __declspec(dllimport)
+#      endif
+#    endif
+#  endif  /* ZLIB_DLL */
+   /* If building or using zlib with the WINAPI/WINAPIV calling convention,
+    * define ZLIB_WINAPI.
+    * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI.
+    */
+#  ifdef ZLIB_WINAPI
+#    ifdef FAR
+#      undef FAR
+#    endif
+#    ifndef WIN32_LEAN_AND_MEAN
+#      define WIN32_LEAN_AND_MEAN
+#    endif
+#    include <windows.h>
+     /* No need for _export, use ZLIB.DEF instead. */
+     /* For complete Windows compatibility, use WINAPI, not __stdcall. */
+#    define ZEXPORT WINAPI
+#    ifdef WIN32
+#      define ZEXPORTVA WINAPIV
+#    else
+#      define ZEXPORTVA FAR CDECL
+#    endif
+#  endif
+#endif
+
+#if defined (__BEOS__)
+#  ifdef ZLIB_DLL
+#    ifdef ZLIB_INTERNAL
+#      define ZEXPORT   __declspec(dllexport)
+#      define ZEXPORTVA __declspec(dllexport)
+#    else
+#      define ZEXPORT   __declspec(dllimport)
+#      define ZEXPORTVA __declspec(dllimport)
+#    endif
+#  endif
+#endif
+
+#ifndef ZEXTERN
+#  define ZEXTERN extern
+#endif
+#ifndef ZEXPORT
+#  define ZEXPORT
+#endif
+#ifndef ZEXPORTVA
+#  define ZEXPORTVA
+#endif
+
+#ifndef FAR
+#  define FAR
+#endif
+
+#if !defined(__MACTYPES__)
+typedef unsigned char  Byte;  /* 8 bits */
+#endif
+typedef unsigned int   uInt;  /* 16 bits or more */
+typedef unsigned long  uLong; /* 32 bits or more */
+
+#ifdef SMALL_MEDIUM
+   /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */
+#  define Bytef Byte FAR
+#else
+   typedef Byte  FAR Bytef;
+#endif
+typedef char  FAR charf;
+typedef int   FAR intf;
+typedef uInt  FAR uIntf;
+typedef uLong FAR uLongf;
+
+#ifdef STDC
+   typedef void const *voidpc;
+   typedef void FAR   *voidpf;
+   typedef void       *voidp;
+#else
+   typedef Byte const *voidpc;
+   typedef Byte FAR   *voidpf;
+   typedef Byte       *voidp;
+#endif
+
+#if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC)
+#  include <limits.h>
+#  if (UINT_MAX == 0xffffffffUL)
+#    define Z_U4 unsigned
+#  elif (ULONG_MAX == 0xffffffffUL)
+#    define Z_U4 unsigned long
+#  elif (USHRT_MAX == 0xffffffffUL)
+#    define Z_U4 unsigned short
+#  endif
+#endif
+
+#ifdef Z_U4
+   typedef Z_U4 z_crc_t;
+#else
+   typedef unsigned long z_crc_t;
+#endif
+
+#if 1    /* was set to #if 1 by ./configure */
+#  define Z_HAVE_UNISTD_H
+#endif
+
+#if 1    /* was set to #if 1 by ./configure */
+#  define Z_HAVE_STDARG_H
+#endif
+
+#ifdef STDC
+#  ifndef Z_SOLO
+#    include <sys/types.h>      /* for off_t */
+#  endif
+#endif
+
+#if defined(STDC) || defined(Z_HAVE_STDARG_H)
+#  ifndef Z_SOLO
+#    include <stdarg.h>         /* for va_list */
+#  endif
+#endif
+
+#ifdef _WIN32
+#  ifndef Z_SOLO
+#    include <stddef.h>         /* for wchar_t */
+#  endif
+#endif
+
+/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and
+ * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even
+ * though the former does not conform to the LFS document), but considering
+ * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as
+ * equivalently requesting no 64-bit operations
+ */
+#if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1
+#  undef _LARGEFILE64_SOURCE
+#endif
+
+#ifndef Z_HAVE_UNISTD_H
+#  ifdef __WATCOMC__
+#    define Z_HAVE_UNISTD_H
+#  endif
+#endif
+#ifndef Z_HAVE_UNISTD_H
+#  if defined(_LARGEFILE64_SOURCE) && !defined(_WIN32)
+#    define Z_HAVE_UNISTD_H
+#  endif
+#endif
+#ifndef Z_SOLO
+#  if defined(Z_HAVE_UNISTD_H)
+#    include <unistd.h>         /* for SEEK_*, off_t, and _LFS64_LARGEFILE */
+#    ifdef VMS
+#      include <unixio.h>       /* for off_t */
+#    endif
+#    ifndef z_off_t
+#      define z_off_t off_t
+#    endif
+#  endif
+#endif
+
+#if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0
+#  define Z_LFS64
+#endif
+
+#if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64)
+#  define Z_LARGE64
+#endif
+
+#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64)
+#  define Z_WANT64
+#endif
+
+#if !defined(SEEK_SET) && !defined(Z_SOLO)
+#  define SEEK_SET        0       /* Seek from beginning of file.  */
+#  define SEEK_CUR        1       /* Seek from current position.  */
+#  define SEEK_END        2       /* Set file pointer to EOF plus "offset" */
+#endif
+
+#ifndef z_off_t
+#  define z_off_t long
+#endif
+
+#if !defined(_WIN32) && defined(Z_LARGE64)
+#  define z_off64_t off64_t
+#else
+#  if defined(_WIN32) && !defined(__GNUC__)
+#    define z_off64_t __int64
+#  else
+#    define z_off64_t z_off_t
+#  endif
+#endif
+
+/* MVS linker does not support external names larger than 8 bytes */
+#if defined(__MVS__)
+  #pragma map(deflateInit_,"DEIN")
+  #pragma map(deflateInit2_,"DEIN2")
+  #pragma map(deflateEnd,"DEEND")
+  #pragma map(deflateBound,"DEBND")
+  #pragma map(inflateInit_,"ININ")
+  #pragma map(inflateInit2_,"ININ2")
+  #pragma map(inflateEnd,"INEND")
+  #pragma map(inflateSync,"INSY")
+  #pragma map(inflateSetDictionary,"INSEDI")
+  #pragma map(compressBound,"CMBND")
+  #pragma map(inflate_table,"INTABL")
+  #pragma map(inflate_fast,"INFA")
+  #pragma map(inflate_copyright,"INCOPY")
+#endif
+
+#endif /* ZCONF_H */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/extlibs/zlib.h b/.venv/lib/python3.12/site-packages/lxml/includes/extlibs/zlib.h
new file mode 100644
index 00000000..8d4b932e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/extlibs/zlib.h
@@ -0,0 +1,1938 @@
+/* zlib.h -- interface of the 'zlib' general purpose compression library
+  version 1.3.1, January 22nd, 2024
+
+  Copyright (C) 1995-2024 Jean-loup Gailly and Mark Adler
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  Jean-loup Gailly        Mark Adler
+  jloup@gzip.org          madler@alumni.caltech.edu
+
+
+  The data format used by the zlib library is described by RFCs (Request for
+  Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950
+  (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format).
+*/
+
+#ifndef ZLIB_H
+#define ZLIB_H
+
+#include "zconf.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ZLIB_VERSION "1.3.1"
+#define ZLIB_VERNUM 0x1310
+#define ZLIB_VER_MAJOR 1
+#define ZLIB_VER_MINOR 3
+#define ZLIB_VER_REVISION 1
+#define ZLIB_VER_SUBREVISION 0
+
+/*
+    The 'zlib' compression library provides in-memory compression and
+  decompression functions, including integrity checks of the uncompressed data.
+  This version of the library supports only one compression method (deflation)
+  but other algorithms will be added later and will have the same stream
+  interface.
+
+    Compression can be done in a single step if the buffers are large enough,
+  or can be done by repeated calls of the compression function.  In the latter
+  case, the application must provide more input and/or consume the output
+  (providing more output space) before each call.
+
+    The compressed data format used by default by the in-memory functions is
+  the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped
+  around a deflate stream, which is itself documented in RFC 1951.
+
+    The library also supports reading and writing files in gzip (.gz) format
+  with an interface similar to that of stdio using the functions that start
+  with "gz".  The gzip format is different from the zlib format.  gzip is a
+  gzip wrapper, documented in RFC 1952, wrapped around a deflate stream.
+
+    This library can optionally read and write gzip and raw deflate streams in
+  memory as well.
+
+    The zlib format was designed to be compact and fast for use in memory
+  and on communications channels.  The gzip format was designed for single-
+  file compression on file systems, has a larger header than zlib to maintain
+  directory information, and uses a different, slower check method than zlib.
+
+    The library does not install any signal handler.  The decoder checks
+  the consistency of the compressed data, so the library should never crash
+  even in the case of corrupted input.
+*/
+
+typedef voidpf (*alloc_func)(voidpf opaque, uInt items, uInt size);
+typedef void   (*free_func)(voidpf opaque, voidpf address);
+
+struct internal_state;
+
+typedef struct z_stream_s {
+    z_const Bytef *next_in;     /* next input byte */
+    uInt     avail_in;  /* number of bytes available at next_in */
+    uLong    total_in;  /* total number of input bytes read so far */
+
+    Bytef    *next_out; /* next output byte will go here */
+    uInt     avail_out; /* remaining free space at next_out */
+    uLong    total_out; /* total number of bytes output so far */
+
+    z_const char *msg;  /* last error message, NULL if no error */
+    struct internal_state FAR *state; /* not visible by applications */
+
+    alloc_func zalloc;  /* used to allocate the internal state */
+    free_func  zfree;   /* used to free the internal state */
+    voidpf     opaque;  /* private data object passed to zalloc and zfree */
+
+    int     data_type;  /* best guess about the data type: binary or text
+                           for deflate, or the decoding state for inflate */
+    uLong   adler;      /* Adler-32 or CRC-32 value of the uncompressed data */
+    uLong   reserved;   /* reserved for future use */
+} z_stream;
+
+typedef z_stream FAR *z_streamp;
+
+/*
+     gzip header information passed to and from zlib routines.  See RFC 1952
+  for more details on the meanings of these fields.
+*/
+typedef struct gz_header_s {
+    int     text;       /* true if compressed data believed to be text */
+    uLong   time;       /* modification time */
+    int     xflags;     /* extra flags (not used when writing a gzip file) */
+    int     os;         /* operating system */
+    Bytef   *extra;     /* pointer to extra field or Z_NULL if none */
+    uInt    extra_len;  /* extra field length (valid if extra != Z_NULL) */
+    uInt    extra_max;  /* space at extra (only when reading header) */
+    Bytef   *name;      /* pointer to zero-terminated file name or Z_NULL */
+    uInt    name_max;   /* space at name (only when reading header) */
+    Bytef   *comment;   /* pointer to zero-terminated comment or Z_NULL */
+    uInt    comm_max;   /* space at comment (only when reading header) */
+    int     hcrc;       /* true if there was or will be a header crc */
+    int     done;       /* true when done reading gzip header (not used
+                           when writing a gzip file) */
+} gz_header;
+
+typedef gz_header FAR *gz_headerp;
+
+/*
+     The application must update next_in and avail_in when avail_in has dropped
+   to zero.  It must update next_out and avail_out when avail_out has dropped
+   to zero.  The application must initialize zalloc, zfree and opaque before
+   calling the init function.  All other fields are set by the compression
+   library and must not be updated by the application.
+
+     The opaque value provided by the application will be passed as the first
+   parameter for calls of zalloc and zfree.  This can be useful for custom
+   memory management.  The compression library attaches no meaning to the
+   opaque value.
+
+     zalloc must return Z_NULL if there is not enough memory for the object.
+   If zlib is used in a multi-threaded application, zalloc and zfree must be
+   thread safe.  In that case, zlib is thread-safe.  When zalloc and zfree are
+   Z_NULL on entry to the initialization function, they are set to internal
+   routines that use the standard library functions malloc() and free().
+
+     On 16-bit systems, the functions zalloc and zfree must be able to allocate
+   exactly 65536 bytes, but will not be required to allocate more than this if
+   the symbol MAXSEG_64K is defined (see zconf.h).  WARNING: On MSDOS, pointers
+   returned by zalloc for objects of exactly 65536 bytes *must* have their
+   offset normalized to zero.  The default allocation function provided by this
+   library ensures this (see zutil.c).  To reduce memory requirements and avoid
+   any allocation of 64K objects, at the expense of compression ratio, compile
+   the library with -DMAX_WBITS=14 (see zconf.h).
+
+     The fields total_in and total_out can be used for statistics or progress
+   reports.  After compression, total_in holds the total size of the
+   uncompressed data and may be saved for use by the decompressor (particularly
+   if the decompressor wants to decompress everything in a single step).
+*/
+
+                        /* constants */
+
+#define Z_NO_FLUSH      0
+#define Z_PARTIAL_FLUSH 1
+#define Z_SYNC_FLUSH    2
+#define Z_FULL_FLUSH    3
+#define Z_FINISH        4
+#define Z_BLOCK         5
+#define Z_TREES         6
+/* Allowed flush values; see deflate() and inflate() below for details */
+
+#define Z_OK            0
+#define Z_STREAM_END    1
+#define Z_NEED_DICT     2
+#define Z_ERRNO        (-1)
+#define Z_STREAM_ERROR (-2)
+#define Z_DATA_ERROR   (-3)
+#define Z_MEM_ERROR    (-4)
+#define Z_BUF_ERROR    (-5)
+#define Z_VERSION_ERROR (-6)
+/* Return codes for the compression/decompression functions. Negative values
+ * are errors, positive values are used for special but normal events.
+ */
+
+#define Z_NO_COMPRESSION         0
+#define Z_BEST_SPEED             1
+#define Z_BEST_COMPRESSION       9
+#define Z_DEFAULT_COMPRESSION  (-1)
+/* compression levels */
+
+#define Z_FILTERED            1
+#define Z_HUFFMAN_ONLY        2
+#define Z_RLE                 3
+#define Z_FIXED               4
+#define Z_DEFAULT_STRATEGY    0
+/* compression strategy; see deflateInit2() below for details */
+
+#define Z_BINARY   0
+#define Z_TEXT     1
+#define Z_ASCII    Z_TEXT   /* for compatibility with 1.2.2 and earlier */
+#define Z_UNKNOWN  2
+/* Possible values of the data_type field for deflate() */
+
+#define Z_DEFLATED   8
+/* The deflate compression method (the only one supported in this version) */
+
+#define Z_NULL  0  /* for initializing zalloc, zfree, opaque */
+
+#define zlib_version zlibVersion()
+/* for compatibility with versions < 1.0.2 */
+
+
+                        /* basic functions */
+
+ZEXTERN const char * ZEXPORT zlibVersion(void);
+/* The application can compare zlibVersion and ZLIB_VERSION for consistency.
+   If the first character differs, the library code actually used is not
+   compatible with the zlib.h header file used by the application.  This check
+   is automatically made by deflateInit and inflateInit.
+ */
+
+/*
+ZEXTERN int ZEXPORT deflateInit(z_streamp strm, int level);
+
+     Initializes the internal stream state for compression.  The fields
+   zalloc, zfree and opaque must be initialized before by the caller.  If
+   zalloc and zfree are set to Z_NULL, deflateInit updates them to use default
+   allocation functions.  total_in, total_out, adler, and msg are initialized.
+
+     The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9:
+   1 gives best speed, 9 gives best compression, 0 gives no compression at all
+   (the input data is simply copied a block at a time).  Z_DEFAULT_COMPRESSION
+   requests a default compromise between speed and compression (currently
+   equivalent to level 6).
+
+     deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough
+   memory, Z_STREAM_ERROR if level is not a valid compression level, or
+   Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible
+   with the version assumed by the caller (ZLIB_VERSION).  msg is set to null
+   if there is no error message.  deflateInit does not perform any compression:
+   this will be done by deflate().
+*/
+
+
+ZEXTERN int ZEXPORT deflate(z_streamp strm, int flush);
+/*
+    deflate compresses as much data as possible, and stops when the input
+  buffer becomes empty or the output buffer becomes full.  It may introduce
+  some output latency (reading input without producing any output) except when
+  forced to flush.
+
+    The detailed semantics are as follows.  deflate performs one or both of the
+  following actions:
+
+  - Compress more input starting at next_in and update next_in and avail_in
+    accordingly.  If not all input can be processed (because there is not
+    enough room in the output buffer), next_in and avail_in are updated and
+    processing will resume at this point for the next call of deflate().
+
+  - Generate more output starting at next_out and update next_out and avail_out
+    accordingly.  This action is forced if the parameter flush is non zero.
+    Forcing flush frequently degrades the compression ratio, so this parameter
+    should be set only when necessary.  Some output may be provided even if
+    flush is zero.
+
+    Before the call of deflate(), the application should ensure that at least
+  one of the actions is possible, by providing more input and/or consuming more
+  output, and updating avail_in or avail_out accordingly; avail_out should
+  never be zero before the call.  The application can consume the compressed
+  output when it wants, for example when the output buffer is full (avail_out
+  == 0), or after each call of deflate().  If deflate returns Z_OK and with
+  zero avail_out, it must be called again after making room in the output
+  buffer because there might be more output pending. See deflatePending(),
+  which can be used if desired to determine whether or not there is more output
+  in that case.
+
+    Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to
+  decide how much data to accumulate before producing output, in order to
+  maximize compression.
+
+    If the parameter flush is set to Z_SYNC_FLUSH, all pending output is
+  flushed to the output buffer and the output is aligned on a byte boundary, so
+  that the decompressor can get all input data available so far.  (In
+  particular avail_in is zero after the call if enough output space has been
+  provided before the call.) Flushing may degrade compression for some
+  compression algorithms and so it should be used only when necessary.  This
+  completes the current deflate block and follows it with an empty stored block
+  that is three bits plus filler bits to the next byte, followed by four bytes
+  (00 00 ff ff).
+
+    If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the
+  output buffer, but the output is not aligned to a byte boundary.  All of the
+  input data so far will be available to the decompressor, as for Z_SYNC_FLUSH.
+  This completes the current deflate block and follows it with an empty fixed
+  codes block that is 10 bits long.  This assures that enough bytes are output
+  in order for the decompressor to finish the block before the empty fixed
+  codes block.
+
+    If flush is set to Z_BLOCK, a deflate block is completed and emitted, as
+  for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to
+  seven bits of the current block are held to be written as the next byte after
+  the next deflate block is completed.  In this case, the decompressor may not
+  be provided enough bits at this point in order to complete decompression of
+  the data provided so far to the compressor.  It may need to wait for the next
+  block to be emitted.  This is for advanced applications that need to control
+  the emission of deflate blocks.
+
+    If flush is set to Z_FULL_FLUSH, all output is flushed as with
+  Z_SYNC_FLUSH, and the compression state is reset so that decompression can
+  restart from this point if previous compressed data has been damaged or if
+  random access is desired.  Using Z_FULL_FLUSH too often can seriously degrade
+  compression.
+
+    If deflate returns with avail_out == 0, this function must be called again
+  with the same value of the flush parameter and more output space (updated
+  avail_out), until the flush is complete (deflate returns with non-zero
+  avail_out).  In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that
+  avail_out is greater than six when the flush marker begins, in order to avoid
+  repeated flush markers upon calling deflate() again when avail_out == 0.
+
+    If the parameter flush is set to Z_FINISH, pending input is processed,
+  pending output is flushed and deflate returns with Z_STREAM_END if there was
+  enough output space.  If deflate returns with Z_OK or Z_BUF_ERROR, this
+  function must be called again with Z_FINISH and more output space (updated
+  avail_out) but no more input data, until it returns with Z_STREAM_END or an
+  error.  After deflate has returned Z_STREAM_END, the only possible operations
+  on the stream are deflateReset or deflateEnd.
+
+    Z_FINISH can be used in the first deflate call after deflateInit if all the
+  compression is to be done in a single step.  In order to complete in one
+  call, avail_out must be at least the value returned by deflateBound (see
+  below).  Then deflate is guaranteed to return Z_STREAM_END.  If not enough
+  output space is provided, deflate will not return Z_STREAM_END, and it must
+  be called again as described above.
+
+    deflate() sets strm->adler to the Adler-32 checksum of all input read
+  so far (that is, total_in bytes).  If a gzip stream is being generated, then
+  strm->adler will be the CRC-32 checksum of the input read so far.  (See
+  deflateInit2 below.)
+
+    deflate() may update strm->data_type if it can make a good guess about
+  the input data type (Z_BINARY or Z_TEXT).  If in doubt, the data is
+  considered binary.  This field is only for information purposes and does not
+  affect the compression algorithm in any manner.
+
+    deflate() returns Z_OK if some progress has been made (more input
+  processed or more output produced), Z_STREAM_END if all input has been
+  consumed and all output has been produced (only when flush is set to
+  Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example
+  if next_in or next_out was Z_NULL or the state was inadvertently written over
+  by the application), or Z_BUF_ERROR if no progress is possible (for example
+  avail_in or avail_out was zero).  Note that Z_BUF_ERROR is not fatal, and
+  deflate() can be called again with more input and more output space to
+  continue compressing.
+*/
+
+
+ZEXTERN int ZEXPORT deflateEnd(z_streamp strm);
+/*
+     All dynamically allocated data structures for this stream are freed.
+   This function discards any unprocessed input and does not flush any pending
+   output.
+
+     deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the
+   stream state was inconsistent, Z_DATA_ERROR if the stream was freed
+   prematurely (some input or output was discarded).  In the error case, msg
+   may be set but then points to a static string (which must not be
+   deallocated).
+*/
+
+
+/*
+ZEXTERN int ZEXPORT inflateInit(z_streamp strm);
+
+     Initializes the internal stream state for decompression.  The fields
+   next_in, avail_in, zalloc, zfree and opaque must be initialized before by
+   the caller.  In the current version of inflate, the provided input is not
+   read or consumed.  The allocation of a sliding window will be deferred to
+   the first call of inflate (if the decompression does not complete on the
+   first call).  If zalloc and zfree are set to Z_NULL, inflateInit updates
+   them to use default allocation functions.  total_in, total_out, adler, and
+   msg are initialized.
+
+     inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough
+   memory, Z_VERSION_ERROR if the zlib library version is incompatible with the
+   version assumed by the caller, or Z_STREAM_ERROR if the parameters are
+   invalid, such as a null pointer to the structure.  msg is set to null if
+   there is no error message.  inflateInit does not perform any decompression.
+   Actual decompression will be done by inflate().  So next_in, and avail_in,
+   next_out, and avail_out are unused and unchanged.  The current
+   implementation of inflateInit() does not process any header information --
+   that is deferred until inflate() is called.
+*/
+
+
+ZEXTERN int ZEXPORT inflate(z_streamp strm, int flush);
+/*
+    inflate decompresses as much data as possible, and stops when the input
+  buffer becomes empty or the output buffer becomes full.  It may introduce
+  some output latency (reading input without producing any output) except when
+  forced to flush.
+
+  The detailed semantics are as follows.  inflate performs one or both of the
+  following actions:
+
+  - Decompress more input starting at next_in and update next_in and avail_in
+    accordingly.  If not all input can be processed (because there is not
+    enough room in the output buffer), then next_in and avail_in are updated
+    accordingly, and processing will resume at this point for the next call of
+    inflate().
+
+  - Generate more output starting at next_out and update next_out and avail_out
+    accordingly.  inflate() provides as much output as possible, until there is
+    no more input data or no more space in the output buffer (see below about
+    the flush parameter).
+
+    Before the call of inflate(), the application should ensure that at least
+  one of the actions is possible, by providing more input and/or consuming more
+  output, and updating the next_* and avail_* values accordingly.  If the
+  caller of inflate() does not provide both available input and available
+  output space, it is possible that there will be no progress made.  The
+  application can consume the uncompressed output when it wants, for example
+  when the output buffer is full (avail_out == 0), or after each call of
+  inflate().  If inflate returns Z_OK and with zero avail_out, it must be
+  called again after making room in the output buffer because there might be
+  more output pending.
+
+    The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH,
+  Z_BLOCK, or Z_TREES.  Z_SYNC_FLUSH requests that inflate() flush as much
+  output as possible to the output buffer.  Z_BLOCK requests that inflate()
+  stop if and when it gets to the next deflate block boundary.  When decoding
+  the zlib or gzip format, this will cause inflate() to return immediately
+  after the header and before the first block.  When doing a raw inflate,
+  inflate() will go ahead and process the first block, and will return when it
+  gets to the end of that block, or when it runs out of data.
+
+    The Z_BLOCK option assists in appending to or combining deflate streams.
+  To assist in this, on return inflate() always sets strm->data_type to the
+  number of unused bits in the last byte taken from strm->next_in, plus 64 if
+  inflate() is currently decoding the last block in the deflate stream, plus
+  128 if inflate() returned immediately after decoding an end-of-block code or
+  decoding the complete header up to just before the first byte of the deflate
+  stream.  The end-of-block will not be indicated until all of the uncompressed
+  data from that block has been written to strm->next_out.  The number of
+  unused bits may in general be greater than seven, except when bit 7 of
+  data_type is set, in which case the number of unused bits will be less than
+  eight.  data_type is set as noted here every time inflate() returns for all
+  flush options, and so can be used to determine the amount of currently
+  consumed input in bits.
+
+    The Z_TREES option behaves as Z_BLOCK does, but it also returns when the
+  end of each deflate block header is reached, before any actual data in that
+  block is decoded.  This allows the caller to determine the length of the
+  deflate block header for later use in random access within a deflate block.
+  256 is added to the value of strm->data_type when inflate() returns
+  immediately after reaching the end of the deflate block header.
+
+    inflate() should normally be called until it returns Z_STREAM_END or an
+  error.  However if all decompression is to be performed in a single step (a
+  single call of inflate), the parameter flush should be set to Z_FINISH.  In
+  this case all pending input is processed and all pending output is flushed;
+  avail_out must be large enough to hold all of the uncompressed data for the
+  operation to complete.  (The size of the uncompressed data may have been
+  saved by the compressor for this purpose.)  The use of Z_FINISH is not
+  required to perform an inflation in one step.  However it may be used to
+  inform inflate that a faster approach can be used for the single inflate()
+  call.  Z_FINISH also informs inflate to not maintain a sliding window if the
+  stream completes, which reduces inflate's memory footprint.  If the stream
+  does not complete, either because not all of the stream is provided or not
+  enough output space is provided, then a sliding window will be allocated and
+  inflate() can be called again to continue the operation as if Z_NO_FLUSH had
+  been used.
+
+     In this implementation, inflate() always flushes as much output as
+  possible to the output buffer, and always uses the faster approach on the
+  first call.  So the effects of the flush parameter in this implementation are
+  on the return value of inflate() as noted below, when inflate() returns early
+  when Z_BLOCK or Z_TREES is used, and when inflate() avoids the allocation of
+  memory for a sliding window when Z_FINISH is used.
+
+     If a preset dictionary is needed after this call (see inflateSetDictionary
+  below), inflate sets strm->adler to the Adler-32 checksum of the dictionary
+  chosen by the compressor and returns Z_NEED_DICT; otherwise it sets
+  strm->adler to the Adler-32 checksum of all output produced so far (that is,
+  total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described
+  below.  At the end of the stream, inflate() checks that its computed Adler-32
+  checksum is equal to that saved by the compressor and returns Z_STREAM_END
+  only if the checksum is correct.
+
+    inflate() can decompress and check either zlib-wrapped or gzip-wrapped
+  deflate data.  The header type is detected automatically, if requested when
+  initializing with inflateInit2().  Any information contained in the gzip
+  header is not retained unless inflateGetHeader() is used.  When processing
+  gzip-wrapped deflate data, strm->adler32 is set to the CRC-32 of the output
+  produced so far.  The CRC-32 is checked against the gzip trailer, as is the
+  uncompressed length, modulo 2^32.
+
+    inflate() returns Z_OK if some progress has been made (more input processed
+  or more output produced), Z_STREAM_END if the end of the compressed data has
+  been reached and all uncompressed output has been produced, Z_NEED_DICT if a
+  preset dictionary is needed at this point, Z_DATA_ERROR if the input data was
+  corrupted (input stream not conforming to the zlib format or incorrect check
+  value, in which case strm->msg points to a string with a more specific
+  error), Z_STREAM_ERROR if the stream structure was inconsistent (for example
+  next_in or next_out was Z_NULL, or the state was inadvertently written over
+  by the application), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR
+  if no progress was possible or if there was not enough room in the output
+  buffer when Z_FINISH is used.  Note that Z_BUF_ERROR is not fatal, and
+  inflate() can be called again with more input and more output space to
+  continue decompressing.  If Z_DATA_ERROR is returned, the application may
+  then call inflateSync() to look for a good compression block if a partial
+  recovery of the data is to be attempted.
+*/
+
+
+ZEXTERN int ZEXPORT inflateEnd(z_streamp strm);
+/*
+     All dynamically allocated data structures for this stream are freed.
+   This function discards any unprocessed input and does not flush any pending
+   output.
+
+     inflateEnd returns Z_OK if success, or Z_STREAM_ERROR if the stream state
+   was inconsistent.
+*/
+
+
+                        /* Advanced functions */
+
+/*
+    The following functions are needed only in some special applications.
+*/
+
+/*
+ZEXTERN int ZEXPORT deflateInit2(z_streamp strm,
+                                 int level,
+                                 int method,
+                                 int windowBits,
+                                 int memLevel,
+                                 int strategy);
+
+     This is another version of deflateInit with more compression options.  The
+   fields zalloc, zfree and opaque must be initialized before by the caller.
+
+     The method parameter is the compression method.  It must be Z_DEFLATED in
+   this version of the library.
+
+     The windowBits parameter is the base two logarithm of the window size
+   (the size of the history buffer).  It should be in the range 8..15 for this
+   version of the library.  Larger values of this parameter result in better
+   compression at the expense of memory usage.  The default value is 15 if
+   deflateInit is used instead.
+
+     For the current implementation of deflate(), a windowBits value of 8 (a
+   window size of 256 bytes) is not supported.  As a result, a request for 8
+   will result in 9 (a 512-byte window).  In that case, providing 8 to
+   inflateInit2() will result in an error when the zlib header with 9 is
+   checked against the initialization of inflate().  The remedy is to not use 8
+   with deflateInit2() with this initialization, or at least in that case use 9
+   with inflateInit2().
+
+     windowBits can also be -8..-15 for raw deflate.  In this case, -windowBits
+   determines the window size.  deflate() will then generate raw deflate data
+   with no zlib header or trailer, and will not compute a check value.
+
+     windowBits can also be greater than 15 for optional gzip encoding.  Add
+   16 to windowBits to write a simple gzip header and trailer around the
+   compressed data instead of a zlib wrapper.  The gzip header will have no
+   file name, no extra data, no comment, no modification time (set to zero), no
+   header crc, and the operating system will be set to the appropriate value,
+   if the operating system was determined at compile time.  If a gzip stream is
+   being written, strm->adler is a CRC-32 instead of an Adler-32.
+
+     For raw deflate or gzip encoding, a request for a 256-byte window is
+   rejected as invalid, since only the zlib header provides a means of
+   transmitting the window size to the decompressor.
+
+     The memLevel parameter specifies how much memory should be allocated
+   for the internal compression state.  memLevel=1 uses minimum memory but is
+   slow and reduces compression ratio; memLevel=9 uses maximum memory for
+   optimal speed.  The default value is 8.  See zconf.h for total memory usage
+   as a function of windowBits and memLevel.
+
+     The strategy parameter is used to tune the compression algorithm.  Use the
+   value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a
+   filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no
+   string match), or Z_RLE to limit match distances to one (run-length
+   encoding).  Filtered data consists mostly of small values with a somewhat
+   random distribution.  In this case, the compression algorithm is tuned to
+   compress them better.  The effect of Z_FILTERED is to force more Huffman
+   coding and less string matching; it is somewhat intermediate between
+   Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY.  Z_RLE is designed to be almost as
+   fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data.  The
+   strategy parameter only affects the compression ratio but not the
+   correctness of the compressed output even if it is not set appropriately.
+   Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler
+   decoder for special applications.
+
+     deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+   memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid
+   method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is
+   incompatible with the version assumed by the caller (ZLIB_VERSION).  msg is
+   set to null if there is no error message.  deflateInit2 does not perform any
+   compression: this will be done by deflate().
+*/
+
+ZEXTERN int ZEXPORT deflateSetDictionary(z_streamp strm,
+                                         const Bytef *dictionary,
+                                         uInt  dictLength);
+/*
+     Initializes the compression dictionary from the given byte sequence
+   without producing any compressed output.  When using the zlib format, this
+   function must be called immediately after deflateInit, deflateInit2 or
+   deflateReset, and before any call of deflate.  When doing raw deflate, this
+   function must be called either before any call of deflate, or immediately
+   after the completion of a deflate block, i.e. after all input has been
+   consumed and all output has been delivered when using any of the flush
+   options Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, or Z_FULL_FLUSH.  The
+   compressor and decompressor must use exactly the same dictionary (see
+   inflateSetDictionary).
+
+     The dictionary should consist of strings (byte sequences) that are likely
+   to be encountered later in the data to be compressed, with the most commonly
+   used strings preferably put towards the end of the dictionary.  Using a
+   dictionary is most useful when the data to be compressed is short and can be
+   predicted with good accuracy; the data can then be compressed better than
+   with the default empty dictionary.
+
+     Depending on the size of the compression data structures selected by
+   deflateInit or deflateInit2, a part of the dictionary may in effect be
+   discarded, for example if the dictionary is larger than the window size
+   provided in deflateInit or deflateInit2.  Thus the strings most likely to be
+   useful should be put at the end of the dictionary, not at the front.  In
+   addition, the current implementation of deflate will use at most the window
+   size minus 262 bytes of the provided dictionary.
+
+     Upon return of this function, strm->adler is set to the Adler-32 value
+   of the dictionary; the decompressor may later use this value to determine
+   which dictionary has been used by the compressor.  (The Adler-32 value
+   applies to the whole dictionary even if only a subset of the dictionary is
+   actually used by the compressor.) If a raw deflate was requested, then the
+   Adler-32 value is not computed and strm->adler is not set.
+
+     deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a
+   parameter is invalid (e.g.  dictionary being Z_NULL) or the stream state is
+   inconsistent (for example if deflate has already been called for this stream
+   or if not at a block boundary for raw deflate).  deflateSetDictionary does
+   not perform any compression: this will be done by deflate().
+*/
+
+ZEXTERN int ZEXPORT deflateGetDictionary(z_streamp strm,
+                                         Bytef *dictionary,
+                                         uInt  *dictLength);
+/*
+     Returns the sliding dictionary being maintained by deflate.  dictLength is
+   set to the number of bytes in the dictionary, and that many bytes are copied
+   to dictionary.  dictionary must have enough space, where 32768 bytes is
+   always enough.  If deflateGetDictionary() is called with dictionary equal to
+   Z_NULL, then only the dictionary length is returned, and nothing is copied.
+   Similarly, if dictLength is Z_NULL, then it is not set.
+
+     deflateGetDictionary() may return a length less than the window size, even
+   when more than the window size in input has been provided. It may return up
+   to 258 bytes less in that case, due to how zlib's implementation of deflate
+   manages the sliding window and lookahead for matches, where matches can be
+   up to 258 bytes long. If the application needs the last window-size bytes of
+   input, then that would need to be saved by the application outside of zlib.
+
+     deflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the
+   stream state is inconsistent.
+*/
+
+ZEXTERN int ZEXPORT deflateCopy(z_streamp dest,
+                                z_streamp source);
+/*
+     Sets the destination stream as a complete copy of the source stream.
+
+     This function can be useful when several compression strategies will be
+   tried, for example when there are several ways of pre-processing the input
+   data with a filter.  The streams that will be discarded should then be freed
+   by calling deflateEnd.  Note that deflateCopy duplicates the internal
+   compression state which can be quite large, so this strategy is slow and can
+   consume lots of memory.
+
+     deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
+   enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
+   (such as zalloc being Z_NULL).  msg is left unchanged in both source and
+   destination.
+*/
+
+ZEXTERN int ZEXPORT deflateReset(z_streamp strm);
+/*
+     This function is equivalent to deflateEnd followed by deflateInit, but
+   does not free and reallocate the internal compression state.  The stream
+   will leave the compression level and any other attributes that may have been
+   set unchanged.  total_in, total_out, adler, and msg are initialized.
+
+     deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+   stream state was inconsistent (such as zalloc or state being Z_NULL).
+*/
+
+ZEXTERN int ZEXPORT deflateParams(z_streamp strm,
+                                  int level,
+                                  int strategy);
+/*
+     Dynamically update the compression level and compression strategy.  The
+   interpretation of level and strategy is as in deflateInit2().  This can be
+   used to switch between compression and straight copy of the input data, or
+   to switch to a different kind of input data requiring a different strategy.
+   If the compression approach (which is a function of the level) or the
+   strategy is changed, and if there have been any deflate() calls since the
+   state was initialized or reset, then the input available so far is
+   compressed with the old level and strategy using deflate(strm, Z_BLOCK).
+   There are three approaches for the compression levels 0, 1..3, and 4..9
+   respectively.  The new level and strategy will take effect at the next call
+   of deflate().
+
+     If a deflate(strm, Z_BLOCK) is performed by deflateParams(), and it does
+   not have enough output space to complete, then the parameter change will not
+   take effect.  In this case, deflateParams() can be called again with the
+   same parameters and more output space to try again.
+
+     In order to assure a change in the parameters on the first try, the
+   deflate stream should be flushed using deflate() with Z_BLOCK or other flush
+   request until strm.avail_out is not zero, before calling deflateParams().
+   Then no more input data should be provided before the deflateParams() call.
+   If this is done, the old level and strategy will be applied to the data
+   compressed before deflateParams(), and the new level and strategy will be
+   applied to the data compressed after deflateParams().
+
+     deflateParams returns Z_OK on success, Z_STREAM_ERROR if the source stream
+   state was inconsistent or if a parameter was invalid, or Z_BUF_ERROR if
+   there was not enough output space to complete the compression of the
+   available input data before a change in the strategy or approach.  Note that
+   in the case of a Z_BUF_ERROR, the parameters are not changed.  A return
+   value of Z_BUF_ERROR is not fatal, in which case deflateParams() can be
+   retried with more output space.
+*/
+
+ZEXTERN int ZEXPORT deflateTune(z_streamp strm,
+                                int good_length,
+                                int max_lazy,
+                                int nice_length,
+                                int max_chain);
+/*
+     Fine tune deflate's internal compression parameters.  This should only be
+   used by someone who understands the algorithm used by zlib's deflate for
+   searching for the best matching string, and even then only by the most
+   fanatic optimizer trying to squeeze out the last compressed bit for their
+   specific input data.  Read the deflate.c source code for the meaning of the
+   max_lazy, good_length, nice_length, and max_chain parameters.
+
+     deflateTune() can be called after deflateInit() or deflateInit2(), and
+   returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream.
+ */
+
+ZEXTERN uLong ZEXPORT deflateBound(z_streamp strm,
+                                   uLong sourceLen);
+/*
+     deflateBound() returns an upper bound on the compressed size after
+   deflation of sourceLen bytes.  It must be called after deflateInit() or
+   deflateInit2(), and after deflateSetHeader(), if used.  This would be used
+   to allocate an output buffer for deflation in a single pass, and so would be
+   called before deflate().  If that first deflate() call is provided the
+   sourceLen input bytes, an output buffer allocated to the size returned by
+   deflateBound(), and the flush value Z_FINISH, then deflate() is guaranteed
+   to return Z_STREAM_END.  Note that it is possible for the compressed size to
+   be larger than the value returned by deflateBound() if flush options other
+   than Z_FINISH or Z_NO_FLUSH are used.
+*/
+
+ZEXTERN int ZEXPORT deflatePending(z_streamp strm,
+                                   unsigned *pending,
+                                   int *bits);
+/*
+     deflatePending() returns the number of bytes and bits of output that have
+   been generated, but not yet provided in the available output.  The bytes not
+   provided would be due to the available output space having being consumed.
+   The number of bits of output not provided are between 0 and 7, where they
+   await more bits to join them in order to fill out a full byte.  If pending
+   or bits are Z_NULL, then those values are not set.
+
+     deflatePending returns Z_OK if success, or Z_STREAM_ERROR if the source
+   stream state was inconsistent.
+ */
+
+ZEXTERN int ZEXPORT deflatePrime(z_streamp strm,
+                                 int bits,
+                                 int value);
+/*
+     deflatePrime() inserts bits in the deflate output stream.  The intent
+   is that this function is used to start off the deflate output with the bits
+   leftover from a previous deflate stream when appending to it.  As such, this
+   function can only be used for raw deflate, and must be used before the first
+   deflate() call after a deflateInit2() or deflateReset().  bits must be less
+   than or equal to 16, and that many of the least significant bits of value
+   will be inserted in the output.
+
+     deflatePrime returns Z_OK if success, Z_BUF_ERROR if there was not enough
+   room in the internal buffer to insert the bits, or Z_STREAM_ERROR if the
+   source stream state was inconsistent.
+*/
+
+ZEXTERN int ZEXPORT deflateSetHeader(z_streamp strm,
+                                     gz_headerp head);
+/*
+     deflateSetHeader() provides gzip header information for when a gzip
+   stream is requested by deflateInit2().  deflateSetHeader() may be called
+   after deflateInit2() or deflateReset() and before the first call of
+   deflate().  The text, time, os, extra field, name, and comment information
+   in the provided gz_header structure are written to the gzip header (xflag is
+   ignored -- the extra flags are set according to the compression level).  The
+   caller must assure that, if not Z_NULL, name and comment are terminated with
+   a zero byte, and that if extra is not Z_NULL, that extra_len bytes are
+   available there.  If hcrc is true, a gzip header crc is included.  Note that
+   the current versions of the command-line version of gzip (up through version
+   1.3.x) do not support header crc's, and will report that it is a "multi-part
+   gzip file" and give up.
+
+     If deflateSetHeader is not used, the default gzip header has text false,
+   the time set to zero, and os set to the current operating system, with no
+   extra, name, or comment fields.  The gzip header is returned to the default
+   state by deflateReset().
+
+     deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source
+   stream state was inconsistent.
+*/
+
+/*
+ZEXTERN int ZEXPORT inflateInit2(z_streamp strm,
+                                 int windowBits);
+
+     This is another version of inflateInit with an extra parameter.  The
+   fields next_in, avail_in, zalloc, zfree and opaque must be initialized
+   before by the caller.
+
+     The windowBits parameter is the base two logarithm of the maximum window
+   size (the size of the history buffer).  It should be in the range 8..15 for
+   this version of the library.  The default value is 15 if inflateInit is used
+   instead.  windowBits must be greater than or equal to the windowBits value
+   provided to deflateInit2() while compressing, or it must be equal to 15 if
+   deflateInit2() was not used.  If a compressed stream with a larger window
+   size is given as input, inflate() will return with the error code
+   Z_DATA_ERROR instead of trying to allocate a larger window.
+
+     windowBits can also be zero to request that inflate use the window size in
+   the zlib header of the compressed stream.
+
+     windowBits can also be -8..-15 for raw inflate.  In this case, -windowBits
+   determines the window size.  inflate() will then process raw deflate data,
+   not looking for a zlib or gzip header, not generating a check value, and not
+   looking for any check values for comparison at the end of the stream.  This
+   is for use with other formats that use the deflate compressed data format
+   such as zip.  Those formats provide their own check values.  If a custom
+   format is developed using the raw deflate format for compressed data, it is
+   recommended that a check value such as an Adler-32 or a CRC-32 be applied to
+   the uncompressed data as is done in the zlib, gzip, and zip formats.  For
+   most applications, the zlib format should be used as is.  Note that comments
+   above on the use in deflateInit2() applies to the magnitude of windowBits.
+
+     windowBits can also be greater than 15 for optional gzip decoding.  Add
+   32 to windowBits to enable zlib and gzip decoding with automatic header
+   detection, or add 16 to decode only the gzip format (the zlib format will
+   return a Z_DATA_ERROR).  If a gzip stream is being decoded, strm->adler is a
+   CRC-32 instead of an Adler-32.  Unlike the gunzip utility and gzread() (see
+   below), inflate() will *not* automatically decode concatenated gzip members.
+   inflate() will return Z_STREAM_END at the end of the gzip member.  The state
+   would need to be reset to continue decoding a subsequent gzip member.  This
+   *must* be done if there is more data after a gzip member, in order for the
+   decompression to be compliant with the gzip standard (RFC 1952).
+
+     inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+   memory, Z_VERSION_ERROR if the zlib library version is incompatible with the
+   version assumed by the caller, or Z_STREAM_ERROR if the parameters are
+   invalid, such as a null pointer to the structure.  msg is set to null if
+   there is no error message.  inflateInit2 does not perform any decompression
+   apart from possibly reading the zlib header if present: actual decompression
+   will be done by inflate().  (So next_in and avail_in may be modified, but
+   next_out and avail_out are unused and unchanged.) The current implementation
+   of inflateInit2() does not process any header information -- that is
+   deferred until inflate() is called.
+*/
+
+ZEXTERN int ZEXPORT inflateSetDictionary(z_streamp strm,
+                                         const Bytef *dictionary,
+                                         uInt  dictLength);
+/*
+     Initializes the decompression dictionary from the given uncompressed byte
+   sequence.  This function must be called immediately after a call of inflate,
+   if that call returned Z_NEED_DICT.  The dictionary chosen by the compressor
+   can be determined from the Adler-32 value returned by that call of inflate.
+   The compressor and decompressor must use exactly the same dictionary (see
+   deflateSetDictionary).  For raw inflate, this function can be called at any
+   time to set the dictionary.  If the provided dictionary is smaller than the
+   window and there is already data in the window, then the provided dictionary
+   will amend what's there.  The application must insure that the dictionary
+   that was used for compression is provided.
+
+     inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a
+   parameter is invalid (e.g.  dictionary being Z_NULL) or the stream state is
+   inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the
+   expected one (incorrect Adler-32 value).  inflateSetDictionary does not
+   perform any decompression: this will be done by subsequent calls of
+   inflate().
+*/
+
+ZEXTERN int ZEXPORT inflateGetDictionary(z_streamp strm,
+                                         Bytef *dictionary,
+                                         uInt  *dictLength);
+/*
+     Returns the sliding dictionary being maintained by inflate.  dictLength is
+   set to the number of bytes in the dictionary, and that many bytes are copied
+   to dictionary.  dictionary must have enough space, where 32768 bytes is
+   always enough.  If inflateGetDictionary() is called with dictionary equal to
+   Z_NULL, then only the dictionary length is returned, and nothing is copied.
+   Similarly, if dictLength is Z_NULL, then it is not set.
+
+     inflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the
+   stream state is inconsistent.
+*/
+
+ZEXTERN int ZEXPORT inflateSync(z_streamp strm);
+/*
+     Skips invalid compressed data until a possible full flush point (see above
+   for the description of deflate with Z_FULL_FLUSH) can be found, or until all
+   available input is skipped.  No output is provided.
+
+     inflateSync searches for a 00 00 FF FF pattern in the compressed data.
+   All full flush points have this pattern, but not all occurrences of this
+   pattern are full flush points.
+
+     inflateSync returns Z_OK if a possible full flush point has been found,
+   Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point
+   has been found, or Z_STREAM_ERROR if the stream structure was inconsistent.
+   In the success case, the application may save the current value of total_in
+   which indicates where valid compressed data was found.  In the error case,
+   the application may repeatedly call inflateSync, providing more input each
+   time, until success or end of the input data.
+*/
+
+ZEXTERN int ZEXPORT inflateCopy(z_streamp dest,
+                                z_streamp source);
+/*
+     Sets the destination stream as a complete copy of the source stream.
+
+     This function can be useful when randomly accessing a large stream.  The
+   first pass through the stream can periodically record the inflate state,
+   allowing restarting inflate at those points when randomly accessing the
+   stream.
+
+     inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
+   enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
+   (such as zalloc being Z_NULL).  msg is left unchanged in both source and
+   destination.
+*/
+
+ZEXTERN int ZEXPORT inflateReset(z_streamp strm);
+/*
+     This function is equivalent to inflateEnd followed by inflateInit,
+   but does not free and reallocate the internal decompression state.  The
+   stream will keep attributes that may have been set by inflateInit2.
+   total_in, total_out, adler, and msg are initialized.
+
+     inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+   stream state was inconsistent (such as zalloc or state being Z_NULL).
+*/
+
+ZEXTERN int ZEXPORT inflateReset2(z_streamp strm,
+                                  int windowBits);
+/*
+     This function is the same as inflateReset, but it also permits changing
+   the wrap and window size requests.  The windowBits parameter is interpreted
+   the same as it is for inflateInit2.  If the window size is changed, then the
+   memory allocated for the window is freed, and the window will be reallocated
+   by inflate() if needed.
+
+     inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source
+   stream state was inconsistent (such as zalloc or state being Z_NULL), or if
+   the windowBits parameter is invalid.
+*/
+
+ZEXTERN int ZEXPORT inflatePrime(z_streamp strm,
+                                 int bits,
+                                 int value);
+/*
+     This function inserts bits in the inflate input stream.  The intent is
+   that this function is used to start inflating at a bit position in the
+   middle of a byte.  The provided bits will be used before any bytes are used
+   from next_in.  This function should only be used with raw inflate, and
+   should be used before the first inflate() call after inflateInit2() or
+   inflateReset().  bits must be less than or equal to 16, and that many of the
+   least significant bits of value will be inserted in the input.
+
+     If bits is negative, then the input stream bit buffer is emptied.  Then
+   inflatePrime() can be called again to put bits in the buffer.  This is used
+   to clear out bits leftover after feeding inflate a block description prior
+   to feeding inflate codes.
+
+     inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source
+   stream state was inconsistent.
+*/
+
+ZEXTERN long ZEXPORT inflateMark(z_streamp strm);
+/*
+     This function returns two values, one in the lower 16 bits of the return
+   value, and the other in the remaining upper bits, obtained by shifting the
+   return value down 16 bits.  If the upper value is -1 and the lower value is
+   zero, then inflate() is currently decoding information outside of a block.
+   If the upper value is -1 and the lower value is non-zero, then inflate is in
+   the middle of a stored block, with the lower value equaling the number of
+   bytes from the input remaining to copy.  If the upper value is not -1, then
+   it is the number of bits back from the current bit position in the input of
+   the code (literal or length/distance pair) currently being processed.  In
+   that case the lower value is the number of bytes already emitted for that
+   code.
+
+     A code is being processed if inflate is waiting for more input to complete
+   decoding of the code, or if it has completed decoding but is waiting for
+   more output space to write the literal or match data.
+
+     inflateMark() is used to mark locations in the input data for random
+   access, which may be at bit positions, and to note those cases where the
+   output of a code may span boundaries of random access blocks.  The current
+   location in the input stream can be determined from avail_in and data_type
+   as noted in the description for the Z_BLOCK flush parameter for inflate.
+
+     inflateMark returns the value noted above, or -65536 if the provided
+   source stream state was inconsistent.
+*/
+
+ZEXTERN int ZEXPORT inflateGetHeader(z_streamp strm,
+                                     gz_headerp head);
+/*
+     inflateGetHeader() requests that gzip header information be stored in the
+   provided gz_header structure.  inflateGetHeader() may be called after
+   inflateInit2() or inflateReset(), and before the first call of inflate().
+   As inflate() processes the gzip stream, head->done is zero until the header
+   is completed, at which time head->done is set to one.  If a zlib stream is
+   being decoded, then head->done is set to -1 to indicate that there will be
+   no gzip header information forthcoming.  Note that Z_BLOCK or Z_TREES can be
+   used to force inflate() to return immediately after header processing is
+   complete and before any actual data is decompressed.
+
+     The text, time, xflags, and os fields are filled in with the gzip header
+   contents.  hcrc is set to true if there is a header CRC.  (The header CRC
+   was valid if done is set to one.) If extra is not Z_NULL, then extra_max
+   contains the maximum number of bytes to write to extra.  Once done is true,
+   extra_len contains the actual extra field length, and extra contains the
+   extra field, or that field truncated if extra_max is less than extra_len.
+   If name is not Z_NULL, then up to name_max characters are written there,
+   terminated with a zero unless the length is greater than name_max.  If
+   comment is not Z_NULL, then up to comm_max characters are written there,
+   terminated with a zero unless the length is greater than comm_max.  When any
+   of extra, name, or comment are not Z_NULL and the respective field is not
+   present in the header, then that field is set to Z_NULL to signal its
+   absence.  This allows the use of deflateSetHeader() with the returned
+   structure to duplicate the header.  However if those fields are set to
+   allocated memory, then the application will need to save those pointers
+   elsewhere so that they can be eventually freed.
+
+     If inflateGetHeader is not used, then the header information is simply
+   discarded.  The header is always checked for validity, including the header
+   CRC if present.  inflateReset() will reset the process to discard the header
+   information.  The application would need to call inflateGetHeader() again to
+   retrieve the header from the next gzip stream.
+
+     inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source
+   stream state was inconsistent.
+*/
+
+/*
+ZEXTERN int ZEXPORT inflateBackInit(z_streamp strm, int windowBits,
+                                    unsigned char FAR *window);
+
+     Initialize the internal stream state for decompression using inflateBack()
+   calls.  The fields zalloc, zfree and opaque in strm must be initialized
+   before the call.  If zalloc and zfree are Z_NULL, then the default library-
+   derived memory allocation routines are used.  windowBits is the base two
+   logarithm of the window size, in the range 8..15.  window is a caller
+   supplied buffer of that size.  Except for special applications where it is
+   assured that deflate was used with small window sizes, windowBits must be 15
+   and a 32K byte window must be supplied to be able to decompress general
+   deflate streams.
+
+     See inflateBack() for the usage of these routines.
+
+     inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of
+   the parameters are invalid, Z_MEM_ERROR if the internal state could not be
+   allocated, or Z_VERSION_ERROR if the version of the library does not match
+   the version of the header file.
+*/
+
+typedef unsigned (*in_func)(void FAR *,
+                            z_const unsigned char FAR * FAR *);
+typedef int (*out_func)(void FAR *, unsigned char FAR *, unsigned);
+
+ZEXTERN int ZEXPORT inflateBack(z_streamp strm,
+                                in_func in, void FAR *in_desc,
+                                out_func out, void FAR *out_desc);
+/*
+     inflateBack() does a raw inflate with a single call using a call-back
+   interface for input and output.  This is potentially more efficient than
+   inflate() for file i/o applications, in that it avoids copying between the
+   output and the sliding window by simply making the window itself the output
+   buffer.  inflate() can be faster on modern CPUs when used with large
+   buffers.  inflateBack() trusts the application to not change the output
+   buffer passed by the output function, at least until inflateBack() returns.
+
+     inflateBackInit() must be called first to allocate the internal state
+   and to initialize the state with the user-provided window buffer.
+   inflateBack() may then be used multiple times to inflate a complete, raw
+   deflate stream with each call.  inflateBackEnd() is then called to free the
+   allocated state.
+
+     A raw deflate stream is one with no zlib or gzip header or trailer.
+   This routine would normally be used in a utility that reads zip or gzip
+   files and writes out uncompressed files.  The utility would decode the
+   header and process the trailer on its own, hence this routine expects only
+   the raw deflate stream to decompress.  This is different from the default
+   behavior of inflate(), which expects a zlib header and trailer around the
+   deflate stream.
+
+     inflateBack() uses two subroutines supplied by the caller that are then
+   called by inflateBack() for input and output.  inflateBack() calls those
+   routines until it reads a complete deflate stream and writes out all of the
+   uncompressed data, or until it encounters an error.  The function's
+   parameters and return types are defined above in the in_func and out_func
+   typedefs.  inflateBack() will call in(in_desc, &buf) which should return the
+   number of bytes of provided input, and a pointer to that input in buf.  If
+   there is no input available, in() must return zero -- buf is ignored in that
+   case -- and inflateBack() will return a buffer error.  inflateBack() will
+   call out(out_desc, buf, len) to write the uncompressed data buf[0..len-1].
+   out() should return zero on success, or non-zero on failure.  If out()
+   returns non-zero, inflateBack() will return with an error.  Neither in() nor
+   out() are permitted to change the contents of the window provided to
+   inflateBackInit(), which is also the buffer that out() uses to write from.
+   The length written by out() will be at most the window size.  Any non-zero
+   amount of input may be provided by in().
+
+     For convenience, inflateBack() can be provided input on the first call by
+   setting strm->next_in and strm->avail_in.  If that input is exhausted, then
+   in() will be called.  Therefore strm->next_in must be initialized before
+   calling inflateBack().  If strm->next_in is Z_NULL, then in() will be called
+   immediately for input.  If strm->next_in is not Z_NULL, then strm->avail_in
+   must also be initialized, and then if strm->avail_in is not zero, input will
+   initially be taken from strm->next_in[0 ..  strm->avail_in - 1].
+
+     The in_desc and out_desc parameters of inflateBack() is passed as the
+   first parameter of in() and out() respectively when they are called.  These
+   descriptors can be optionally used to pass any information that the caller-
+   supplied in() and out() functions need to do their job.
+
+     On return, inflateBack() will set strm->next_in and strm->avail_in to
+   pass back any unused input that was provided by the last in() call.  The
+   return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR
+   if in() or out() returned an error, Z_DATA_ERROR if there was a format error
+   in the deflate stream (in which case strm->msg is set to indicate the nature
+   of the error), or Z_STREAM_ERROR if the stream was not properly initialized.
+   In the case of Z_BUF_ERROR, an input or output error can be distinguished
+   using strm->next_in which will be Z_NULL only if in() returned an error.  If
+   strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning
+   non-zero.  (in() will always be called before out(), so strm->next_in is
+   assured to be defined if out() returns non-zero.)  Note that inflateBack()
+   cannot return Z_OK.
+*/
+
+ZEXTERN int ZEXPORT inflateBackEnd(z_streamp strm);
+/*
+     All memory allocated by inflateBackInit() is freed.
+
+     inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream
+   state was inconsistent.
+*/
+
+ZEXTERN uLong ZEXPORT zlibCompileFlags(void);
+/* Return flags indicating compile-time options.
+
+    Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other:
+     1.0: size of uInt
+     3.2: size of uLong
+     5.4: size of voidpf (pointer)
+     7.6: size of z_off_t
+
+    Compiler, assembler, and debug options:
+     8: ZLIB_DEBUG
+     9: ASMV or ASMINF -- use ASM code
+     10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention
+     11: 0 (reserved)
+
+    One-time table building (smaller code, but not thread-safe if true):
+     12: BUILDFIXED -- build static block decoding tables when needed
+     13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed
+     14,15: 0 (reserved)
+
+    Library content (indicates missing functionality):
+     16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking
+                          deflate code when not needed)
+     17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect
+                    and decode gzip streams (to avoid linking crc code)
+     18-19: 0 (reserved)
+
+    Operation variations (changes in library functionality):
+     20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate
+     21: FASTEST -- deflate algorithm with only one, lowest compression level
+     22,23: 0 (reserved)
+
+    The sprintf variant used by gzprintf (zero is best):
+     24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format
+     25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure!
+     26: 0 = returns value, 1 = void -- 1 means inferred string length returned
+
+    Remainder:
+     27-31: 0 (reserved)
+ */
+
+#ifndef Z_SOLO
+
+                        /* utility functions */
+
+/*
+     The following utility functions are implemented on top of the basic
+   stream-oriented functions.  To simplify the interface, some default options
+   are assumed (compression level and memory usage, standard memory allocation
+   functions).  The source code of these utility functions can be modified if
+   you need special options.
+*/
+
+ZEXTERN int ZEXPORT compress(Bytef *dest,   uLongf *destLen,
+                             const Bytef *source, uLong sourceLen);
+/*
+     Compresses the source buffer into the destination buffer.  sourceLen is
+   the byte length of the source buffer.  Upon entry, destLen is the total size
+   of the destination buffer, which must be at least the value returned by
+   compressBound(sourceLen).  Upon exit, destLen is the actual size of the
+   compressed data.  compress() is equivalent to compress2() with a level
+   parameter of Z_DEFAULT_COMPRESSION.
+
+     compress returns Z_OK if success, Z_MEM_ERROR if there was not
+   enough memory, Z_BUF_ERROR if there was not enough room in the output
+   buffer.
+*/
+
+ZEXTERN int ZEXPORT compress2(Bytef *dest,   uLongf *destLen,
+                              const Bytef *source, uLong sourceLen,
+                              int level);
+/*
+     Compresses the source buffer into the destination buffer.  The level
+   parameter has the same meaning as in deflateInit.  sourceLen is the byte
+   length of the source buffer.  Upon entry, destLen is the total size of the
+   destination buffer, which must be at least the value returned by
+   compressBound(sourceLen).  Upon exit, destLen is the actual size of the
+   compressed data.
+
+     compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+   memory, Z_BUF_ERROR if there was not enough room in the output buffer,
+   Z_STREAM_ERROR if the level parameter is invalid.
+*/
+
+ZEXTERN uLong ZEXPORT compressBound(uLong sourceLen);
+/*
+     compressBound() returns an upper bound on the compressed size after
+   compress() or compress2() on sourceLen bytes.  It would be used before a
+   compress() or compress2() call to allocate the destination buffer.
+*/
+
+ZEXTERN int ZEXPORT uncompress(Bytef *dest,   uLongf *destLen,
+                               const Bytef *source, uLong sourceLen);
+/*
+     Decompresses the source buffer into the destination buffer.  sourceLen is
+   the byte length of the source buffer.  Upon entry, destLen is the total size
+   of the destination buffer, which must be large enough to hold the entire
+   uncompressed data.  (The size of the uncompressed data must have been saved
+   previously by the compressor and transmitted to the decompressor by some
+   mechanism outside the scope of this compression library.) Upon exit, destLen
+   is the actual size of the uncompressed data.
+
+     uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
+   enough memory, Z_BUF_ERROR if there was not enough room in the output
+   buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete.  In
+   the case where there is not enough room, uncompress() will fill the output
+   buffer with the uncompressed data up to that point.
+*/
+
+ZEXTERN int ZEXPORT uncompress2(Bytef *dest,   uLongf *destLen,
+                                const Bytef *source, uLong *sourceLen);
+/*
+     Same as uncompress, except that sourceLen is a pointer, where the
+   length of the source is *sourceLen.  On return, *sourceLen is the number of
+   source bytes consumed.
+*/
+
+                        /* gzip file access functions */
+
+/*
+     This library supports reading and writing files in gzip (.gz) format with
+   an interface similar to that of stdio, using the functions that start with
+   "gz".  The gzip format is different from the zlib format.  gzip is a gzip
+   wrapper, documented in RFC 1952, wrapped around a deflate stream.
+*/
+
+typedef struct gzFile_s *gzFile;    /* semi-opaque gzip file descriptor */
+
+/*
+ZEXTERN gzFile ZEXPORT gzopen(const char *path, const char *mode);
+
+     Open the gzip (.gz) file at path for reading and decompressing, or
+   compressing and writing.  The mode parameter is as in fopen ("rb" or "wb")
+   but can also include a compression level ("wb9") or a strategy: 'f' for
+   filtered data as in "wb6f", 'h' for Huffman-only compression as in "wb1h",
+   'R' for run-length encoding as in "wb1R", or 'F' for fixed code compression
+   as in "wb9F".  (See the description of deflateInit2 for more information
+   about the strategy parameter.)  'T' will request transparent writing or
+   appending with no compression and not using the gzip format.
+
+     "a" can be used instead of "w" to request that the gzip stream that will
+   be written be appended to the file.  "+" will result in an error, since
+   reading and writing to the same gzip file is not supported.  The addition of
+   "x" when writing will create the file exclusively, which fails if the file
+   already exists.  On systems that support it, the addition of "e" when
+   reading or writing will set the flag to close the file on an execve() call.
+
+     These functions, as well as gzip, will read and decode a sequence of gzip
+   streams in a file.  The append function of gzopen() can be used to create
+   such a file.  (Also see gzflush() for another way to do this.)  When
+   appending, gzopen does not test whether the file begins with a gzip stream,
+   nor does it look for the end of the gzip streams to begin appending.  gzopen
+   will simply append a gzip stream to the existing file.
+
+     gzopen can be used to read a file which is not in gzip format; in this
+   case gzread will directly read from the file without decompression.  When
+   reading, this will be detected automatically by looking for the magic two-
+   byte gzip header.
+
+     gzopen returns NULL if the file could not be opened, if there was
+   insufficient memory to allocate the gzFile state, or if an invalid mode was
+   specified (an 'r', 'w', or 'a' was not provided, or '+' was provided).
+   errno can be checked to determine if the reason gzopen failed was that the
+   file could not be opened.
+*/
+
+ZEXTERN gzFile ZEXPORT gzdopen(int fd, const char *mode);
+/*
+     Associate a gzFile with the file descriptor fd.  File descriptors are
+   obtained from calls like open, dup, creat, pipe or fileno (if the file has
+   been previously opened with fopen).  The mode parameter is as in gzopen.
+
+     The next call of gzclose on the returned gzFile will also close the file
+   descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor
+   fd.  If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd,
+   mode);.  The duplicated descriptor should be saved to avoid a leak, since
+   gzdopen does not close fd if it fails.  If you are using fileno() to get the
+   file descriptor from a FILE *, then you will have to use dup() to avoid
+   double-close()ing the file descriptor.  Both gzclose() and fclose() will
+   close the associated file descriptor, so they need to have different file
+   descriptors.
+
+     gzdopen returns NULL if there was insufficient memory to allocate the
+   gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not
+   provided, or '+' was provided), or if fd is -1.  The file descriptor is not
+   used until the next gz* read, write, seek, or close operation, so gzdopen
+   will not detect if fd is invalid (unless fd is -1).
+*/
+
+ZEXTERN int ZEXPORT gzbuffer(gzFile file, unsigned size);
+/*
+     Set the internal buffer size used by this library's functions for file to
+   size.  The default buffer size is 8192 bytes.  This function must be called
+   after gzopen() or gzdopen(), and before any other calls that read or write
+   the file.  The buffer memory allocation is always deferred to the first read
+   or write.  Three times that size in buffer space is allocated.  A larger
+   buffer size of, for example, 64K or 128K bytes will noticeably increase the
+   speed of decompression (reading).
+
+     The new buffer size also affects the maximum length for gzprintf().
+
+     gzbuffer() returns 0 on success, or -1 on failure, such as being called
+   too late.
+*/
+
+ZEXTERN int ZEXPORT gzsetparams(gzFile file, int level, int strategy);
+/*
+     Dynamically update the compression level and strategy for file.  See the
+   description of deflateInit2 for the meaning of these parameters. Previously
+   provided data is flushed before applying the parameter changes.
+
+     gzsetparams returns Z_OK if success, Z_STREAM_ERROR if the file was not
+   opened for writing, Z_ERRNO if there is an error writing the flushed data,
+   or Z_MEM_ERROR if there is a memory allocation error.
+*/
+
+ZEXTERN int ZEXPORT gzread(gzFile file, voidp buf, unsigned len);
+/*
+     Read and decompress up to len uncompressed bytes from file into buf.  If
+   the input file is not in gzip format, gzread copies the given number of
+   bytes into the buffer directly from the file.
+
+     After reaching the end of a gzip stream in the input, gzread will continue
+   to read, looking for another gzip stream.  Any number of gzip streams may be
+   concatenated in the input file, and will all be decompressed by gzread().
+   If something other than a gzip stream is encountered after a gzip stream,
+   that remaining trailing garbage is ignored (and no error is returned).
+
+     gzread can be used to read a gzip file that is being concurrently written.
+   Upon reaching the end of the input, gzread will return with the available
+   data.  If the error code returned by gzerror is Z_OK or Z_BUF_ERROR, then
+   gzclearerr can be used to clear the end of file indicator in order to permit
+   gzread to be tried again.  Z_OK indicates that a gzip stream was completed
+   on the last gzread.  Z_BUF_ERROR indicates that the input file ended in the
+   middle of a gzip stream.  Note that gzread does not return -1 in the event
+   of an incomplete gzip stream.  This error is deferred until gzclose(), which
+   will return Z_BUF_ERROR if the last gzread ended in the middle of a gzip
+   stream.  Alternatively, gzerror can be used before gzclose to detect this
+   case.
+
+     gzread returns the number of uncompressed bytes actually read, less than
+   len for end of file, or -1 for error.  If len is too large to fit in an int,
+   then nothing is read, -1 is returned, and the error state is set to
+   Z_STREAM_ERROR.
+*/
+
+ZEXTERN z_size_t ZEXPORT gzfread(voidp buf, z_size_t size, z_size_t nitems,
+                                 gzFile file);
+/*
+     Read and decompress up to nitems items of size size from file into buf,
+   otherwise operating as gzread() does.  This duplicates the interface of
+   stdio's fread(), with size_t request and return types.  If the library
+   defines size_t, then z_size_t is identical to size_t.  If not, then z_size_t
+   is an unsigned integer type that can contain a pointer.
+
+     gzfread() returns the number of full items read of size size, or zero if
+   the end of the file was reached and a full item could not be read, or if
+   there was an error.  gzerror() must be consulted if zero is returned in
+   order to determine if there was an error.  If the multiplication of size and
+   nitems overflows, i.e. the product does not fit in a z_size_t, then nothing
+   is read, zero is returned, and the error state is set to Z_STREAM_ERROR.
+
+     In the event that the end of file is reached and only a partial item is
+   available at the end, i.e. the remaining uncompressed data length is not a
+   multiple of size, then the final partial item is nevertheless read into buf
+   and the end-of-file flag is set.  The length of the partial item read is not
+   provided, but could be inferred from the result of gztell().  This behavior
+   is the same as the behavior of fread() implementations in common libraries,
+   but it prevents the direct use of gzfread() to read a concurrently written
+   file, resetting and retrying on end-of-file, when size is not 1.
+*/
+
+ZEXTERN int ZEXPORT gzwrite(gzFile file, voidpc buf, unsigned len);
+/*
+     Compress and write the len uncompressed bytes at buf to file. gzwrite
+   returns the number of uncompressed bytes written or 0 in case of error.
+*/
+
+ZEXTERN z_size_t ZEXPORT gzfwrite(voidpc buf, z_size_t size,
+                                  z_size_t nitems, gzFile file);
+/*
+     Compress and write nitems items of size size from buf to file, duplicating
+   the interface of stdio's fwrite(), with size_t request and return types.  If
+   the library defines size_t, then z_size_t is identical to size_t.  If not,
+   then z_size_t is an unsigned integer type that can contain a pointer.
+
+     gzfwrite() returns the number of full items written of size size, or zero
+   if there was an error.  If the multiplication of size and nitems overflows,
+   i.e. the product does not fit in a z_size_t, then nothing is written, zero
+   is returned, and the error state is set to Z_STREAM_ERROR.
+*/
+
+ZEXTERN int ZEXPORTVA gzprintf(gzFile file, const char *format, ...);
+/*
+     Convert, format, compress, and write the arguments (...) to file under
+   control of the string format, as in fprintf.  gzprintf returns the number of
+   uncompressed bytes actually written, or a negative zlib error code in case
+   of error.  The number of uncompressed bytes written is limited to 8191, or
+   one less than the buffer size given to gzbuffer().  The caller should assure
+   that this limit is not exceeded.  If it is exceeded, then gzprintf() will
+   return an error (0) with nothing written.  In this case, there may also be a
+   buffer overflow with unpredictable consequences, which is possible only if
+   zlib was compiled with the insecure functions sprintf() or vsprintf(),
+   because the secure snprintf() or vsnprintf() functions were not available.
+   This can be determined using zlibCompileFlags().
+*/
+
+ZEXTERN int ZEXPORT gzputs(gzFile file, const char *s);
+/*
+     Compress and write the given null-terminated string s to file, excluding
+   the terminating null character.
+
+     gzputs returns the number of characters written, or -1 in case of error.
+*/
+
+ZEXTERN char * ZEXPORT gzgets(gzFile file, char *buf, int len);
+/*
+     Read and decompress bytes from file into buf, until len-1 characters are
+   read, or until a newline character is read and transferred to buf, or an
+   end-of-file condition is encountered.  If any characters are read or if len
+   is one, the string is terminated with a null character.  If no characters
+   are read due to an end-of-file or len is less than one, then the buffer is
+   left untouched.
+
+     gzgets returns buf which is a null-terminated string, or it returns NULL
+   for end-of-file or in case of error.  If there was an error, the contents at
+   buf are indeterminate.
+*/
+
+ZEXTERN int ZEXPORT gzputc(gzFile file, int c);
+/*
+     Compress and write c, converted to an unsigned char, into file.  gzputc
+   returns the value that was written, or -1 in case of error.
+*/
+
+ZEXTERN int ZEXPORT gzgetc(gzFile file);
+/*
+     Read and decompress one byte from file.  gzgetc returns this byte or -1
+   in case of end of file or error.  This is implemented as a macro for speed.
+   As such, it does not do all of the checking the other functions do.  I.e.
+   it does not check to see if file is NULL, nor whether the structure file
+   points to has been clobbered or not.
+*/
+
+ZEXTERN int ZEXPORT gzungetc(int c, gzFile file);
+/*
+     Push c back onto the stream for file to be read as the first character on
+   the next read.  At least one character of push-back is always allowed.
+   gzungetc() returns the character pushed, or -1 on failure.  gzungetc() will
+   fail if c is -1, and may fail if a character has been pushed but not read
+   yet.  If gzungetc is used immediately after gzopen or gzdopen, at least the
+   output buffer size of pushed characters is allowed.  (See gzbuffer above.)
+   The pushed character will be discarded if the stream is repositioned with
+   gzseek() or gzrewind().
+*/
+
+ZEXTERN int ZEXPORT gzflush(gzFile file, int flush);
+/*
+     Flush all pending output to file.  The parameter flush is as in the
+   deflate() function.  The return value is the zlib error number (see function
+   gzerror below).  gzflush is only permitted when writing.
+
+     If the flush parameter is Z_FINISH, the remaining data is written and the
+   gzip stream is completed in the output.  If gzwrite() is called again, a new
+   gzip stream will be started in the output.  gzread() is able to read such
+   concatenated gzip streams.
+
+     gzflush should be called only when strictly necessary because it will
+   degrade compression if called too often.
+*/
+
+/*
+ZEXTERN z_off_t ZEXPORT gzseek(gzFile file,
+                               z_off_t offset, int whence);
+
+     Set the starting position to offset relative to whence for the next gzread
+   or gzwrite on file.  The offset represents a number of bytes in the
+   uncompressed data stream.  The whence parameter is defined as in lseek(2);
+   the value SEEK_END is not supported.
+
+     If the file is opened for reading, this function is emulated but can be
+   extremely slow.  If the file is opened for writing, only forward seeks are
+   supported; gzseek then compresses a sequence of zeroes up to the new
+   starting position.
+
+     gzseek returns the resulting offset location as measured in bytes from
+   the beginning of the uncompressed stream, or -1 in case of error, in
+   particular if the file is opened for writing and the new starting position
+   would be before the current position.
+*/
+
+ZEXTERN int ZEXPORT    gzrewind(gzFile file);
+/*
+     Rewind file. This function is supported only for reading.
+
+     gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET).
+*/
+
+/*
+ZEXTERN z_off_t ZEXPORT    gztell(gzFile file);
+
+     Return the starting position for the next gzread or gzwrite on file.
+   This position represents a number of bytes in the uncompressed data stream,
+   and is zero when starting, even if appending or reading a gzip stream from
+   the middle of a file using gzdopen().
+
+     gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR)
+*/
+
+/*
+ZEXTERN z_off_t ZEXPORT gzoffset(gzFile file);
+
+     Return the current compressed (actual) read or write offset of file.  This
+   offset includes the count of bytes that precede the gzip stream, for example
+   when appending or when using gzdopen() for reading.  When reading, the
+   offset does not include as yet unused buffered input.  This information can
+   be used for a progress indicator.  On error, gzoffset() returns -1.
+*/
+
+ZEXTERN int ZEXPORT gzeof(gzFile file);
+/*
+     Return true (1) if the end-of-file indicator for file has been set while
+   reading, false (0) otherwise.  Note that the end-of-file indicator is set
+   only if the read tried to go past the end of the input, but came up short.
+   Therefore, just like feof(), gzeof() may return false even if there is no
+   more data to read, in the event that the last read request was for the exact
+   number of bytes remaining in the input file.  This will happen if the input
+   file size is an exact multiple of the buffer size.
+
+     If gzeof() returns true, then the read functions will return no more data,
+   unless the end-of-file indicator is reset by gzclearerr() and the input file
+   has grown since the previous end of file was detected.
+*/
+
+ZEXTERN int ZEXPORT gzdirect(gzFile file);
+/*
+     Return true (1) if file is being copied directly while reading, or false
+   (0) if file is a gzip stream being decompressed.
+
+     If the input file is empty, gzdirect() will return true, since the input
+   does not contain a gzip stream.
+
+     If gzdirect() is used immediately after gzopen() or gzdopen() it will
+   cause buffers to be allocated to allow reading the file to determine if it
+   is a gzip file.  Therefore if gzbuffer() is used, it should be called before
+   gzdirect().
+
+     When writing, gzdirect() returns true (1) if transparent writing was
+   requested ("wT" for the gzopen() mode), or false (0) otherwise.  (Note:
+   gzdirect() is not needed when writing.  Transparent writing must be
+   explicitly requested, so the application already knows the answer.  When
+   linking statically, using gzdirect() will include all of the zlib code for
+   gzip file reading and decompression, which may not be desired.)
+*/
+
+ZEXTERN int ZEXPORT    gzclose(gzFile file);
+/*
+     Flush all pending output for file, if necessary, close file and
+   deallocate the (de)compression state.  Note that once file is closed, you
+   cannot call gzerror with file, since its structures have been deallocated.
+   gzclose must not be called more than once on the same file, just as free
+   must not be called more than once on the same allocation.
+
+     gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a
+   file operation error, Z_MEM_ERROR if out of memory, Z_BUF_ERROR if the
+   last read ended in the middle of a gzip stream, or Z_OK on success.
+*/
+
+ZEXTERN int ZEXPORT gzclose_r(gzFile file);
+ZEXTERN int ZEXPORT gzclose_w(gzFile file);
+/*
+     Same as gzclose(), but gzclose_r() is only for use when reading, and
+   gzclose_w() is only for use when writing or appending.  The advantage to
+   using these instead of gzclose() is that they avoid linking in zlib
+   compression or decompression code that is not used when only reading or only
+   writing respectively.  If gzclose() is used, then both compression and
+   decompression code will be included the application when linking to a static
+   zlib library.
+*/
+
+ZEXTERN const char * ZEXPORT gzerror(gzFile file, int *errnum);
+/*
+     Return the error message for the last error which occurred on file.
+   errnum is set to zlib error number.  If an error occurred in the file system
+   and not in the compression library, errnum is set to Z_ERRNO and the
+   application may consult errno to get the exact error code.
+
+     The application must not modify the returned string.  Future calls to
+   this function may invalidate the previously returned string.  If file is
+   closed, then the string previously returned by gzerror will no longer be
+   available.
+
+     gzerror() should be used to distinguish errors from end-of-file for those
+   functions above that do not distinguish those cases in their return values.
+*/
+
+ZEXTERN void ZEXPORT gzclearerr(gzFile file);
+/*
+     Clear the error and end-of-file flags for file.  This is analogous to the
+   clearerr() function in stdio.  This is useful for continuing to read a gzip
+   file that is being written concurrently.
+*/
+
+#endif /* !Z_SOLO */
+
+                        /* checksum functions */
+
+/*
+     These functions are not related to compression but are exported
+   anyway because they might be useful in applications using the compression
+   library.
+*/
+
+ZEXTERN uLong ZEXPORT adler32(uLong adler, const Bytef *buf, uInt len);
+/*
+     Update a running Adler-32 checksum with the bytes buf[0..len-1] and
+   return the updated checksum. An Adler-32 value is in the range of a 32-bit
+   unsigned integer. If buf is Z_NULL, this function returns the required
+   initial value for the checksum.
+
+     An Adler-32 checksum is almost as reliable as a CRC-32 but can be computed
+   much faster.
+
+   Usage example:
+
+     uLong adler = adler32(0L, Z_NULL, 0);
+
+     while (read_buffer(buffer, length) != EOF) {
+       adler = adler32(adler, buffer, length);
+     }
+     if (adler != original_adler) error();
+*/
+
+ZEXTERN uLong ZEXPORT adler32_z(uLong adler, const Bytef *buf,
+                                z_size_t len);
+/*
+     Same as adler32(), but with a size_t length.
+*/
+
+/*
+ZEXTERN uLong ZEXPORT adler32_combine(uLong adler1, uLong adler2,
+                                      z_off_t len2);
+
+     Combine two Adler-32 checksums into one.  For two sequences of bytes, seq1
+   and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for
+   each, adler1 and adler2.  adler32_combine() returns the Adler-32 checksum of
+   seq1 and seq2 concatenated, requiring only adler1, adler2, and len2.  Note
+   that the z_off_t type (like off_t) is a signed integer.  If len2 is
+   negative, the result has no meaning or utility.
+*/
+
+ZEXTERN uLong ZEXPORT crc32(uLong crc, const Bytef *buf, uInt len);
+/*
+     Update a running CRC-32 with the bytes buf[0..len-1] and return the
+   updated CRC-32. A CRC-32 value is in the range of a 32-bit unsigned integer.
+   If buf is Z_NULL, this function returns the required initial value for the
+   crc. Pre- and post-conditioning (one's complement) is performed within this
+   function so it shouldn't be done by the application.
+
+   Usage example:
+
+     uLong crc = crc32(0L, Z_NULL, 0);
+
+     while (read_buffer(buffer, length) != EOF) {
+       crc = crc32(crc, buffer, length);
+     }
+     if (crc != original_crc) error();
+*/
+
+ZEXTERN uLong ZEXPORT crc32_z(uLong crc, const Bytef *buf,
+                              z_size_t len);
+/*
+     Same as crc32(), but with a size_t length.
+*/
+
+/*
+ZEXTERN uLong ZEXPORT crc32_combine(uLong crc1, uLong crc2, z_off_t len2);
+
+     Combine two CRC-32 check values into one.  For two sequences of bytes,
+   seq1 and seq2 with lengths len1 and len2, CRC-32 check values were
+   calculated for each, crc1 and crc2.  crc32_combine() returns the CRC-32
+   check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and
+   len2. len2 must be non-negative.
+*/
+
+/*
+ZEXTERN uLong ZEXPORT crc32_combine_gen(z_off_t len2);
+
+     Return the operator corresponding to length len2, to be used with
+   crc32_combine_op(). len2 must be non-negative.
+*/
+
+ZEXTERN uLong ZEXPORT crc32_combine_op(uLong crc1, uLong crc2, uLong op);
+/*
+     Give the same result as crc32_combine(), using op in place of len2. op is
+   is generated from len2 by crc32_combine_gen(). This will be faster than
+   crc32_combine() if the generated op is used more than once.
+*/
+
+
+                        /* various hacks, don't look :) */
+
+/* deflateInit and inflateInit are macros to allow checking the zlib version
+ * and the compiler's view of z_stream:
+ */
+ZEXTERN int ZEXPORT deflateInit_(z_streamp strm, int level,
+                                 const char *version, int stream_size);
+ZEXTERN int ZEXPORT inflateInit_(z_streamp strm,
+                                 const char *version, int stream_size);
+ZEXTERN int ZEXPORT deflateInit2_(z_streamp strm, int  level, int  method,
+                                  int windowBits, int memLevel,
+                                  int strategy, const char *version,
+                                  int stream_size);
+ZEXTERN int ZEXPORT inflateInit2_(z_streamp strm, int  windowBits,
+                                  const char *version, int stream_size);
+ZEXTERN int ZEXPORT inflateBackInit_(z_streamp strm, int windowBits,
+                                     unsigned char FAR *window,
+                                     const char *version,
+                                     int stream_size);
+#ifdef Z_PREFIX_SET
+#  define z_deflateInit(strm, level) \
+          deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream))
+#  define z_inflateInit(strm) \
+          inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream))
+#  define z_deflateInit2(strm, level, method, windowBits, memLevel, strategy) \
+          deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\
+                        (strategy), ZLIB_VERSION, (int)sizeof(z_stream))
+#  define z_inflateInit2(strm, windowBits) \
+          inflateInit2_((strm), (windowBits), ZLIB_VERSION, \
+                        (int)sizeof(z_stream))
+#  define z_inflateBackInit(strm, windowBits, window) \
+          inflateBackInit_((strm), (windowBits), (window), \
+                           ZLIB_VERSION, (int)sizeof(z_stream))
+#else
+#  define deflateInit(strm, level) \
+          deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream))
+#  define inflateInit(strm) \
+          inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream))
+#  define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \
+          deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\
+                        (strategy), ZLIB_VERSION, (int)sizeof(z_stream))
+#  define inflateInit2(strm, windowBits) \
+          inflateInit2_((strm), (windowBits), ZLIB_VERSION, \
+                        (int)sizeof(z_stream))
+#  define inflateBackInit(strm, windowBits, window) \
+          inflateBackInit_((strm), (windowBits), (window), \
+                           ZLIB_VERSION, (int)sizeof(z_stream))
+#endif
+
+#ifndef Z_SOLO
+
+/* gzgetc() macro and its supporting function and exposed data structure.  Note
+ * that the real internal state is much larger than the exposed structure.
+ * This abbreviated structure exposes just enough for the gzgetc() macro.  The
+ * user should not mess with these exposed elements, since their names or
+ * behavior could change in the future, perhaps even capriciously.  They can
+ * only be used by the gzgetc() macro.  You have been warned.
+ */
+struct gzFile_s {
+    unsigned have;
+    unsigned char *next;
+    z_off64_t pos;
+};
+ZEXTERN int ZEXPORT gzgetc_(gzFile file);       /* backward compatibility */
+#ifdef Z_PREFIX_SET
+#  undef z_gzgetc
+#  define z_gzgetc(g) \
+          ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g))
+#else
+#  define gzgetc(g) \
+          ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g))
+#endif
+
+/* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or
+ * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if
+ * both are true, the application gets the *64 functions, and the regular
+ * functions are changed to 64 bits) -- in case these are set on systems
+ * without large file support, _LFS64_LARGEFILE must also be true
+ */
+#ifdef Z_LARGE64
+   ZEXTERN gzFile ZEXPORT gzopen64(const char *, const char *);
+   ZEXTERN z_off64_t ZEXPORT gzseek64(gzFile, z_off64_t, int);
+   ZEXTERN z_off64_t ZEXPORT gztell64(gzFile);
+   ZEXTERN z_off64_t ZEXPORT gzoffset64(gzFile);
+   ZEXTERN uLong ZEXPORT adler32_combine64(uLong, uLong, z_off64_t);
+   ZEXTERN uLong ZEXPORT crc32_combine64(uLong, uLong, z_off64_t);
+   ZEXTERN uLong ZEXPORT crc32_combine_gen64(z_off64_t);
+#endif
+
+#if !defined(ZLIB_INTERNAL) && defined(Z_WANT64)
+#  ifdef Z_PREFIX_SET
+#    define z_gzopen z_gzopen64
+#    define z_gzseek z_gzseek64
+#    define z_gztell z_gztell64
+#    define z_gzoffset z_gzoffset64
+#    define z_adler32_combine z_adler32_combine64
+#    define z_crc32_combine z_crc32_combine64
+#    define z_crc32_combine_gen z_crc32_combine_gen64
+#  else
+#    define gzopen gzopen64
+#    define gzseek gzseek64
+#    define gztell gztell64
+#    define gzoffset gzoffset64
+#    define adler32_combine adler32_combine64
+#    define crc32_combine crc32_combine64
+#    define crc32_combine_gen crc32_combine_gen64
+#  endif
+#  ifndef Z_LARGE64
+     ZEXTERN gzFile ZEXPORT gzopen64(const char *, const char *);
+     ZEXTERN z_off_t ZEXPORT gzseek64(gzFile, z_off_t, int);
+     ZEXTERN z_off_t ZEXPORT gztell64(gzFile);
+     ZEXTERN z_off_t ZEXPORT gzoffset64(gzFile);
+     ZEXTERN uLong ZEXPORT adler32_combine64(uLong, uLong, z_off_t);
+     ZEXTERN uLong ZEXPORT crc32_combine64(uLong, uLong, z_off_t);
+     ZEXTERN uLong ZEXPORT crc32_combine_gen64(z_off_t);
+#  endif
+#else
+   ZEXTERN gzFile ZEXPORT gzopen(const char *, const char *);
+   ZEXTERN z_off_t ZEXPORT gzseek(gzFile, z_off_t, int);
+   ZEXTERN z_off_t ZEXPORT gztell(gzFile);
+   ZEXTERN z_off_t ZEXPORT gzoffset(gzFile);
+   ZEXTERN uLong ZEXPORT adler32_combine(uLong, uLong, z_off_t);
+   ZEXTERN uLong ZEXPORT crc32_combine(uLong, uLong, z_off_t);
+   ZEXTERN uLong ZEXPORT crc32_combine_gen(z_off_t);
+#endif
+
+#else /* Z_SOLO */
+
+   ZEXTERN uLong ZEXPORT adler32_combine(uLong, uLong, z_off_t);
+   ZEXTERN uLong ZEXPORT crc32_combine(uLong, uLong, z_off_t);
+   ZEXTERN uLong ZEXPORT crc32_combine_gen(z_off_t);
+
+#endif /* !Z_SOLO */
+
+/* undocumented functions */
+ZEXTERN const char   * ZEXPORT zError(int);
+ZEXTERN int            ZEXPORT inflateSyncPoint(z_streamp);
+ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table(void);
+ZEXTERN int            ZEXPORT inflateUndermine(z_streamp, int);
+ZEXTERN int            ZEXPORT inflateValidate(z_streamp, int);
+ZEXTERN unsigned long  ZEXPORT inflateCodesUsed(z_streamp);
+ZEXTERN int            ZEXPORT inflateResetKeep(z_streamp);
+ZEXTERN int            ZEXPORT deflateResetKeep(z_streamp);
+#if defined(_WIN32) && !defined(Z_SOLO)
+ZEXTERN gzFile         ZEXPORT gzopen_w(const wchar_t *path,
+                                        const char *mode);
+#endif
+#if defined(STDC) || defined(Z_HAVE_STDARG_H)
+#  ifndef Z_SOLO
+ZEXTERN int            ZEXPORTVA gzvprintf(gzFile file,
+                                           const char *format,
+                                           va_list va);
+#  endif
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ZLIB_H */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/htmlparser.pxd b/.venv/lib/python3.12/site-packages/lxml/includes/htmlparser.pxd
new file mode 100644
index 00000000..31dcc406
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/htmlparser.pxd
@@ -0,0 +1,56 @@
+from libc.string cimport const_char
+
+from lxml.includes.tree cimport xmlDoc
+from lxml.includes.tree cimport xmlInputReadCallback, xmlInputCloseCallback
+from lxml.includes.xmlparser cimport xmlParserCtxt, xmlSAXHandler, xmlSAXHandlerV1
+
+cdef extern from "libxml/HTMLparser.h" nogil:
+    ctypedef enum htmlParserOption:
+        HTML_PARSE_NOERROR    # suppress error reports
+        HTML_PARSE_NOWARNING  # suppress warning reports
+        HTML_PARSE_PEDANTIC   # pedantic error reporting
+        HTML_PARSE_NOBLANKS   # remove blank nodes
+        HTML_PARSE_NONET      # Forbid network access
+        # libxml2 2.6.21+ only:
+        HTML_PARSE_RECOVER    # Relaxed parsing
+        HTML_PARSE_COMPACT    # compact small text nodes
+        # libxml2 2.7.7+ only:
+        HTML_PARSE_NOIMPLIED  # Do not add implied html/body... elements
+        # libxml2 2.7.8+ only:
+        HTML_PARSE_NODEFDTD   # do not default a doctype if not found
+        # libxml2 2.8.0+ only:
+        XML_PARSE_IGNORE_ENC  # ignore internal document encoding hint
+
+    xmlSAXHandlerV1 htmlDefaultSAXHandler
+
+    cdef xmlParserCtxt* htmlCreateMemoryParserCtxt(
+        char* buffer, int size)
+    cdef xmlParserCtxt* htmlCreateFileParserCtxt(
+        char* filename, char* encoding)
+    cdef xmlParserCtxt* htmlCreatePushParserCtxt(xmlSAXHandler* sax,
+                                                 void* user_data,
+                                                 char* chunk, int size,
+                                                 char* filename, int enc)
+    cdef void htmlFreeParserCtxt(xmlParserCtxt* ctxt)
+    cdef void htmlCtxtReset(xmlParserCtxt* ctxt)
+    cdef int htmlCtxtUseOptions(xmlParserCtxt* ctxt, int options)
+    cdef int htmlParseDocument(xmlParserCtxt* ctxt)
+    cdef int htmlParseChunk(xmlParserCtxt* ctxt, 
+                            char* chunk, int size, int terminate)
+
+    cdef xmlDoc* htmlCtxtReadFile(xmlParserCtxt* ctxt,
+                                  char* filename, const_char* encoding,
+                                  int options)
+    cdef xmlDoc* htmlCtxtReadDoc(xmlParserCtxt* ctxt,
+                                 char* buffer, char* URL, const_char* encoding,
+                                 int options)
+    cdef xmlDoc* htmlCtxtReadIO(xmlParserCtxt* ctxt, 
+                                xmlInputReadCallback ioread, 
+                                xmlInputCloseCallback ioclose, 
+                                void* ioctx,
+                                char* URL, const_char* encoding,
+                                int options)
+    cdef xmlDoc* htmlCtxtReadMemory(xmlParserCtxt* ctxt,
+                                    char* buffer, int size,
+                                    char* filename, const_char* encoding,
+                                    int options)
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libexslt/__init__.py b/.venv/lib/python3.12/site-packages/lxml/includes/libexslt/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libexslt/__init__.py
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libexslt/exslt.h b/.venv/lib/python3.12/site-packages/lxml/includes/libexslt/exslt.h
new file mode 100644
index 00000000..dfbd09be
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libexslt/exslt.h
@@ -0,0 +1,108 @@
+/*
+ * Summary: main header file
+ *
+ * Copy: See Copyright for the status of this software.
+ */
+
+
+#ifndef __EXSLT_H__
+#define __EXSLT_H__
+
+#include <libxml/tree.h>
+#include <libxml/xpath.h>
+#include "exsltexports.h"
+#include <libexslt/exsltconfig.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+EXSLTPUBVAR const char *exsltLibraryVersion;
+EXSLTPUBVAR const int exsltLibexsltVersion;
+EXSLTPUBVAR const int exsltLibxsltVersion;
+EXSLTPUBVAR const int exsltLibxmlVersion;
+
+/**
+ * EXSLT_COMMON_NAMESPACE:
+ *
+ * Namespace for EXSLT common functions
+ */
+#define EXSLT_COMMON_NAMESPACE ((const xmlChar *) "http://exslt.org/common")
+/**
+ * EXSLT_CRYPTO_NAMESPACE:
+ *
+ * Namespace for EXSLT crypto functions
+ */
+#define EXSLT_CRYPTO_NAMESPACE ((const xmlChar *) "http://exslt.org/crypto")
+/**
+ * EXSLT_MATH_NAMESPACE:
+ *
+ * Namespace for EXSLT math functions
+ */
+#define EXSLT_MATH_NAMESPACE ((const xmlChar *) "http://exslt.org/math")
+/**
+ * EXSLT_SETS_NAMESPACE:
+ *
+ * Namespace for EXSLT set functions
+ */
+#define EXSLT_SETS_NAMESPACE ((const xmlChar *) "http://exslt.org/sets")
+/**
+ * EXSLT_FUNCTIONS_NAMESPACE:
+ *
+ * Namespace for EXSLT functions extension functions
+ */
+#define EXSLT_FUNCTIONS_NAMESPACE ((const xmlChar *) "http://exslt.org/functions")
+/**
+ * EXSLT_STRINGS_NAMESPACE:
+ *
+ * Namespace for EXSLT strings functions
+ */
+#define EXSLT_STRINGS_NAMESPACE ((const xmlChar *) "http://exslt.org/strings")
+/**
+ * EXSLT_DATE_NAMESPACE:
+ *
+ * Namespace for EXSLT date functions
+ */
+#define EXSLT_DATE_NAMESPACE ((const xmlChar *) "http://exslt.org/dates-and-times")
+/**
+ * EXSLT_DYNAMIC_NAMESPACE:
+ *
+ * Namespace for EXSLT dynamic functions
+ */
+#define EXSLT_DYNAMIC_NAMESPACE ((const xmlChar *) "http://exslt.org/dynamic")
+
+/**
+ * SAXON_NAMESPACE:
+ *
+ * Namespace for SAXON extensions functions
+ */
+#define SAXON_NAMESPACE ((const xmlChar *) "http://icl.com/saxon")
+
+EXSLTPUBFUN void EXSLTCALL exsltCommonRegister (void);
+#ifdef EXSLT_CRYPTO_ENABLED
+EXSLTPUBFUN void EXSLTCALL exsltCryptoRegister (void);
+#endif
+EXSLTPUBFUN void EXSLTCALL exsltMathRegister (void);
+EXSLTPUBFUN void EXSLTCALL exsltSetsRegister (void);
+EXSLTPUBFUN void EXSLTCALL exsltFuncRegister (void);
+EXSLTPUBFUN void EXSLTCALL exsltStrRegister (void);
+EXSLTPUBFUN void EXSLTCALL exsltDateRegister (void);
+EXSLTPUBFUN void EXSLTCALL exsltSaxonRegister (void);
+EXSLTPUBFUN void EXSLTCALL exsltDynRegister(void);
+
+EXSLTPUBFUN void EXSLTCALL exsltRegisterAll (void);
+
+EXSLTPUBFUN int EXSLTCALL exsltDateXpathCtxtRegister (xmlXPathContextPtr ctxt,
+                                                      const xmlChar *prefix);
+EXSLTPUBFUN int EXSLTCALL exsltMathXpathCtxtRegister (xmlXPathContextPtr ctxt,
+                                                      const xmlChar *prefix);
+EXSLTPUBFUN int EXSLTCALL exsltSetsXpathCtxtRegister (xmlXPathContextPtr ctxt,
+                                                      const xmlChar *prefix);
+EXSLTPUBFUN int EXSLTCALL exsltStrXpathCtxtRegister (xmlXPathContextPtr ctxt,
+                                                     const xmlChar *prefix);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __EXSLT_H__ */
+
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libexslt/exsltconfig.h b/.venv/lib/python3.12/site-packages/lxml/includes/libexslt/exsltconfig.h
new file mode 100644
index 00000000..62aa98cb
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libexslt/exsltconfig.h
@@ -0,0 +1,70 @@
+/*
+ * exsltconfig.h: compile-time version information for the EXSLT library
+ *
+ * See Copyright for the status of this software.
+ *
+ * daniel@veillard.com
+ */
+
+#ifndef __XML_EXSLTCONFIG_H__
+#define __XML_EXSLTCONFIG_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * LIBEXSLT_DOTTED_VERSION:
+ *
+ * the version string like "1.2.3"
+ */
+#define LIBEXSLT_DOTTED_VERSION "0.8.23"
+
+/**
+ * LIBEXSLT_VERSION:
+ *
+ * the version number: 1.2.3 value is 10203
+ */
+#define LIBEXSLT_VERSION 823
+
+/**
+ * LIBEXSLT_VERSION_STRING:
+ *
+ * the version number string, 1.2.3 value is "10203"
+ */
+#define LIBEXSLT_VERSION_STRING "823"
+
+/**
+ * LIBEXSLT_VERSION_EXTRA:
+ *
+ * extra version information, used to show a Git commit description
+ */
+#define	LIBEXSLT_VERSION_EXTRA ""
+
+/**
+ * WITH_CRYPTO:
+ *
+ * Whether crypto support is configured into exslt
+ */
+#if 0
+#define EXSLT_CRYPTO_ENABLED
+#endif
+
+/**
+ * ATTRIBUTE_UNUSED:
+ *
+ * This macro is used to flag unused function parameters to GCC
+ */
+#ifdef __GNUC__
+#ifndef ATTRIBUTE_UNUSED
+#define ATTRIBUTE_UNUSED __attribute__((unused))
+#endif
+#else
+#define ATTRIBUTE_UNUSED
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_EXSLTCONFIG_H__ */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libexslt/exsltexports.h b/.venv/lib/python3.12/site-packages/lxml/includes/libexslt/exsltexports.h
new file mode 100644
index 00000000..ee79ec7a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libexslt/exsltexports.h
@@ -0,0 +1,63 @@
+/*
+ * Summary: macros for marking symbols as exportable/importable.
+ *
+ * Copy: See Copyright for the status of this software.
+ */
+
+#ifndef __EXSLT_EXPORTS_H__
+#define __EXSLT_EXPORTS_H__
+
+#if defined(_WIN32) || defined(__CYGWIN__)
+/** DOC_DISABLE */
+
+#ifdef LIBEXSLT_STATIC
+  #define EXSLTPUBLIC
+#elif defined(IN_LIBEXSLT)
+  #define EXSLTPUBLIC __declspec(dllexport)
+#else
+  #define EXSLTPUBLIC __declspec(dllimport)
+#endif
+
+#define EXSLTCALL __cdecl
+
+/** DOC_ENABLE */
+#else /* not Windows */
+
+/**
+ * EXSLTPUBLIC:
+ *
+ * Macro which declares a public symbol
+ */
+#define EXSLTPUBLIC
+
+/**
+ * EXSLTCALL:
+ *
+ * Macro which declares the calling convention for exported functions
+ */
+#define EXSLTCALL
+
+#endif /* platform switch */
+
+/*
+ * EXSLTPUBFUN:
+ *
+ * Macro which declares an exportable function
+ */
+#define EXSLTPUBFUN EXSLTPUBLIC
+
+/**
+ * EXSLTPUBVAR:
+ *
+ * Macro which declares an exportable variable
+ */
+#define EXSLTPUBVAR EXSLTPUBLIC extern
+
+/* Compatibility */
+#if !defined(LIBEXSLT_PUBLIC)
+#define LIBEXSLT_PUBLIC EXSLTPUBVAR
+#endif
+
+#endif /* __EXSLT_EXPORTS_H__ */
+
+
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/HTMLparser.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/HTMLparser.h
new file mode 100644
index 00000000..e16d7749
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/HTMLparser.h
@@ -0,0 +1,343 @@
+/*
+ * Summary: interface for an HTML 4.0 non-verifying parser
+ * Description: this module implements an HTML 4.0 non-verifying parser
+ *              with API compatible with the XML parser ones. It should
+ *              be able to parse "real world" HTML, even if severely
+ *              broken from a specification point of view.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __HTML_PARSER_H__
+#define __HTML_PARSER_H__
+#include <libxml/xmlversion.h>
+#include <libxml/parser.h>
+
+#ifdef LIBXML_HTML_ENABLED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Most of the back-end structures from XML and HTML are shared.
+ */
+typedef xmlParserCtxt htmlParserCtxt;
+typedef xmlParserCtxtPtr htmlParserCtxtPtr;
+typedef xmlParserNodeInfo htmlParserNodeInfo;
+typedef xmlSAXHandler htmlSAXHandler;
+typedef xmlSAXHandlerPtr htmlSAXHandlerPtr;
+typedef xmlParserInput htmlParserInput;
+typedef xmlParserInputPtr htmlParserInputPtr;
+typedef xmlDocPtr htmlDocPtr;
+typedef xmlNodePtr htmlNodePtr;
+
+/*
+ * Internal description of an HTML element, representing HTML 4.01
+ * and XHTML 1.0 (which share the same structure).
+ */
+typedef struct _htmlElemDesc htmlElemDesc;
+typedef htmlElemDesc *htmlElemDescPtr;
+struct _htmlElemDesc {
+    const char *name;	/* The tag name */
+    char startTag;      /* Whether the start tag can be implied */
+    char endTag;        /* Whether the end tag can be implied */
+    char saveEndTag;    /* Whether the end tag should be saved */
+    char empty;         /* Is this an empty element ? */
+    char depr;          /* Is this a deprecated element ? */
+    char dtd;           /* 1: only in Loose DTD, 2: only Frameset one */
+    char isinline;      /* is this a block 0 or inline 1 element */
+    const char *desc;   /* the description */
+
+/* NRK Jan.2003
+ * New fields encapsulating HTML structure
+ *
+ * Bugs:
+ *	This is a very limited representation.  It fails to tell us when
+ *	an element *requires* subelements (we only have whether they're
+ *	allowed or not), and it doesn't tell us where CDATA and PCDATA
+ *	are allowed.  Some element relationships are not fully represented:
+ *	these are flagged with the word MODIFIER
+ */
+    const char** subelts;		/* allowed sub-elements of this element */
+    const char* defaultsubelt;	/* subelement for suggested auto-repair
+					   if necessary or NULL */
+    const char** attrs_opt;		/* Optional Attributes */
+    const char** attrs_depr;		/* Additional deprecated attributes */
+    const char** attrs_req;		/* Required attributes */
+};
+
+/*
+ * Internal description of an HTML entity.
+ */
+typedef struct _htmlEntityDesc htmlEntityDesc;
+typedef htmlEntityDesc *htmlEntityDescPtr;
+struct _htmlEntityDesc {
+    unsigned int value;	/* the UNICODE value for the character */
+    const char *name;	/* The entity name */
+    const char *desc;   /* the description */
+};
+
+/** DOC_DISABLE */
+#ifdef LIBXML_SAX1_ENABLED
+  #define XML_GLOBALS_HTML \
+    XML_OP(htmlDefaultSAXHandler, xmlSAXHandlerV1, XML_DEPRECATED)
+#else
+  #define XML_GLOBALS_HTML
+#endif
+
+#define XML_OP XML_DECLARE_GLOBAL
+XML_GLOBALS_HTML
+#undef XML_OP
+
+#if defined(LIBXML_THREAD_ENABLED) && !defined(XML_GLOBALS_NO_REDEFINITION)
+  #define htmlDefaultSAXHandler XML_GLOBAL_MACRO(htmlDefaultSAXHandler)
+#endif
+/** DOC_ENABLE */
+
+/*
+ * There is only few public functions.
+ */
+XML_DEPRECATED
+XMLPUBFUN void
+			htmlInitAutoClose	(void);
+XMLPUBFUN const htmlElemDesc *
+			htmlTagLookup	(const xmlChar *tag);
+XMLPUBFUN const htmlEntityDesc *
+			htmlEntityLookup(const xmlChar *name);
+XMLPUBFUN const htmlEntityDesc *
+			htmlEntityValueLookup(unsigned int value);
+
+XMLPUBFUN int
+			htmlIsAutoClosed(htmlDocPtr doc,
+					 htmlNodePtr elem);
+XMLPUBFUN int
+			htmlAutoCloseTag(htmlDocPtr doc,
+					 const xmlChar *name,
+					 htmlNodePtr elem);
+XML_DEPRECATED
+XMLPUBFUN const htmlEntityDesc *
+			htmlParseEntityRef(htmlParserCtxtPtr ctxt,
+					 const xmlChar **str);
+XML_DEPRECATED
+XMLPUBFUN int
+			htmlParseCharRef(htmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN void
+			htmlParseElement(htmlParserCtxtPtr ctxt);
+
+XMLPUBFUN htmlParserCtxtPtr
+			htmlNewParserCtxt(void);
+XMLPUBFUN htmlParserCtxtPtr
+			htmlNewSAXParserCtxt(const htmlSAXHandler *sax,
+					     void *userData);
+
+XMLPUBFUN htmlParserCtxtPtr
+			htmlCreateMemoryParserCtxt(const char *buffer,
+						   int size);
+
+XMLPUBFUN int
+			htmlParseDocument(htmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN htmlDocPtr
+			htmlSAXParseDoc	(const xmlChar *cur,
+					 const char *encoding,
+					 htmlSAXHandlerPtr sax,
+					 void *userData);
+XMLPUBFUN htmlDocPtr
+			htmlParseDoc	(const xmlChar *cur,
+					 const char *encoding);
+XMLPUBFUN htmlParserCtxtPtr
+			htmlCreateFileParserCtxt(const char *filename,
+	                                         const char *encoding);
+XML_DEPRECATED
+XMLPUBFUN htmlDocPtr
+			htmlSAXParseFile(const char *filename,
+					 const char *encoding,
+					 htmlSAXHandlerPtr sax,
+					 void *userData);
+XMLPUBFUN htmlDocPtr
+			htmlParseFile	(const char *filename,
+					 const char *encoding);
+XMLPUBFUN int
+			UTF8ToHtml	(unsigned char *out,
+					 int *outlen,
+					 const unsigned char *in,
+					 int *inlen);
+XMLPUBFUN int
+			htmlEncodeEntities(unsigned char *out,
+					 int *outlen,
+					 const unsigned char *in,
+					 int *inlen, int quoteChar);
+XMLPUBFUN int
+			htmlIsScriptAttribute(const xmlChar *name);
+XMLPUBFUN int
+			htmlHandleOmittedElem(int val);
+
+#ifdef LIBXML_PUSH_ENABLED
+/**
+ * Interfaces for the Push mode.
+ */
+XMLPUBFUN htmlParserCtxtPtr
+			htmlCreatePushParserCtxt(htmlSAXHandlerPtr sax,
+						 void *user_data,
+						 const char *chunk,
+						 int size,
+						 const char *filename,
+						 xmlCharEncoding enc);
+XMLPUBFUN int
+			htmlParseChunk		(htmlParserCtxtPtr ctxt,
+						 const char *chunk,
+						 int size,
+						 int terminate);
+#endif /* LIBXML_PUSH_ENABLED */
+
+XMLPUBFUN void
+			htmlFreeParserCtxt	(htmlParserCtxtPtr ctxt);
+
+/*
+ * New set of simpler/more flexible APIs
+ */
+/**
+ * xmlParserOption:
+ *
+ * This is the set of XML parser options that can be passed down
+ * to the xmlReadDoc() and similar calls.
+ */
+typedef enum {
+    HTML_PARSE_RECOVER  = 1<<0, /* Relaxed parsing */
+    HTML_PARSE_NODEFDTD = 1<<2, /* do not default a doctype if not found */
+    HTML_PARSE_NOERROR	= 1<<5,	/* suppress error reports */
+    HTML_PARSE_NOWARNING= 1<<6,	/* suppress warning reports */
+    HTML_PARSE_PEDANTIC	= 1<<7,	/* pedantic error reporting */
+    HTML_PARSE_NOBLANKS	= 1<<8,	/* remove blank nodes */
+    HTML_PARSE_NONET	= 1<<11,/* Forbid network access */
+    HTML_PARSE_NOIMPLIED= 1<<13,/* Do not add implied html/body... elements */
+    HTML_PARSE_COMPACT  = 1<<16,/* compact small text nodes */
+    HTML_PARSE_IGNORE_ENC=1<<21 /* ignore internal document encoding hint */
+} htmlParserOption;
+
+XMLPUBFUN void
+		htmlCtxtReset		(htmlParserCtxtPtr ctxt);
+XMLPUBFUN int
+		htmlCtxtUseOptions	(htmlParserCtxtPtr ctxt,
+					 int options);
+XMLPUBFUN htmlDocPtr
+		htmlReadDoc		(const xmlChar *cur,
+					 const char *URL,
+					 const char *encoding,
+					 int options);
+XMLPUBFUN htmlDocPtr
+		htmlReadFile		(const char *URL,
+					 const char *encoding,
+					 int options);
+XMLPUBFUN htmlDocPtr
+		htmlReadMemory		(const char *buffer,
+					 int size,
+					 const char *URL,
+					 const char *encoding,
+					 int options);
+XMLPUBFUN htmlDocPtr
+		htmlReadFd		(int fd,
+					 const char *URL,
+					 const char *encoding,
+					 int options);
+XMLPUBFUN htmlDocPtr
+		htmlReadIO		(xmlInputReadCallback ioread,
+					 xmlInputCloseCallback ioclose,
+					 void *ioctx,
+					 const char *URL,
+					 const char *encoding,
+					 int options);
+XMLPUBFUN htmlDocPtr
+		htmlCtxtReadDoc		(xmlParserCtxtPtr ctxt,
+					 const xmlChar *cur,
+					 const char *URL,
+					 const char *encoding,
+					 int options);
+XMLPUBFUN htmlDocPtr
+		htmlCtxtReadFile		(xmlParserCtxtPtr ctxt,
+					 const char *filename,
+					 const char *encoding,
+					 int options);
+XMLPUBFUN htmlDocPtr
+		htmlCtxtReadMemory		(xmlParserCtxtPtr ctxt,
+					 const char *buffer,
+					 int size,
+					 const char *URL,
+					 const char *encoding,
+					 int options);
+XMLPUBFUN htmlDocPtr
+		htmlCtxtReadFd		(xmlParserCtxtPtr ctxt,
+					 int fd,
+					 const char *URL,
+					 const char *encoding,
+					 int options);
+XMLPUBFUN htmlDocPtr
+		htmlCtxtReadIO		(xmlParserCtxtPtr ctxt,
+					 xmlInputReadCallback ioread,
+					 xmlInputCloseCallback ioclose,
+					 void *ioctx,
+					 const char *URL,
+					 const char *encoding,
+					 int options);
+
+/* NRK/Jan2003: further knowledge of HTML structure
+ */
+typedef enum {
+  HTML_NA = 0 ,		/* something we don't check at all */
+  HTML_INVALID = 0x1 ,
+  HTML_DEPRECATED = 0x2 ,
+  HTML_VALID = 0x4 ,
+  HTML_REQUIRED = 0xc /* VALID bit set so ( & HTML_VALID ) is TRUE */
+} htmlStatus ;
+
+/* Using htmlElemDesc rather than name here, to emphasise the fact
+   that otherwise there's a lookup overhead
+*/
+XMLPUBFUN htmlStatus htmlAttrAllowed(const htmlElemDesc*, const xmlChar*, int) ;
+XMLPUBFUN int htmlElementAllowedHere(const htmlElemDesc*, const xmlChar*) ;
+XMLPUBFUN htmlStatus htmlElementStatusHere(const htmlElemDesc*, const htmlElemDesc*) ;
+XMLPUBFUN htmlStatus htmlNodeStatus(const htmlNodePtr, int) ;
+/**
+ * htmlDefaultSubelement:
+ * @elt: HTML element
+ *
+ * Returns the default subelement for this element
+ */
+#define htmlDefaultSubelement(elt) elt->defaultsubelt
+/**
+ * htmlElementAllowedHereDesc:
+ * @parent: HTML parent element
+ * @elt: HTML element
+ *
+ * Checks whether an HTML element description may be a
+ * direct child of the specified element.
+ *
+ * Returns 1 if allowed; 0 otherwise.
+ */
+#define htmlElementAllowedHereDesc(parent,elt) \
+	htmlElementAllowedHere((parent), (elt)->name)
+/**
+ * htmlRequiredAttrs:
+ * @elt: HTML element
+ *
+ * Returns the attributes required for the specified element.
+ */
+#define htmlRequiredAttrs(elt) (elt)->attrs_req
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#else /* LIBXML_HTML_ENABLED */
+
+/** DOC_DISABLE */
+#define XML_GLOBALS_HTML
+/** DOC_ENABLE */
+
+#endif /* LIBXML_HTML_ENABLED */
+#endif /* __HTML_PARSER_H__ */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/HTMLtree.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/HTMLtree.h
new file mode 100644
index 00000000..8e1ba90e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/HTMLtree.h
@@ -0,0 +1,147 @@
+/*
+ * Summary: specific APIs to process HTML tree, especially serialization
+ * Description: this module implements a few function needed to process
+ *              tree in an HTML specific way.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __HTML_TREE_H__
+#define __HTML_TREE_H__
+
+#include <stdio.h>
+#include <libxml/xmlversion.h>
+#include <libxml/tree.h>
+#include <libxml/HTMLparser.h>
+
+#ifdef LIBXML_HTML_ENABLED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * HTML_TEXT_NODE:
+ *
+ * Macro. A text node in a HTML document is really implemented
+ * the same way as a text node in an XML document.
+ */
+#define HTML_TEXT_NODE		XML_TEXT_NODE
+/**
+ * HTML_ENTITY_REF_NODE:
+ *
+ * Macro. An entity reference in a HTML document is really implemented
+ * the same way as an entity reference in an XML document.
+ */
+#define HTML_ENTITY_REF_NODE	XML_ENTITY_REF_NODE
+/**
+ * HTML_COMMENT_NODE:
+ *
+ * Macro. A comment in a HTML document is really implemented
+ * the same way as a comment in an XML document.
+ */
+#define HTML_COMMENT_NODE	XML_COMMENT_NODE
+/**
+ * HTML_PRESERVE_NODE:
+ *
+ * Macro. A preserved node in a HTML document is really implemented
+ * the same way as a CDATA section in an XML document.
+ */
+#define HTML_PRESERVE_NODE	XML_CDATA_SECTION_NODE
+/**
+ * HTML_PI_NODE:
+ *
+ * Macro. A processing instruction in a HTML document is really implemented
+ * the same way as a processing instruction in an XML document.
+ */
+#define HTML_PI_NODE		XML_PI_NODE
+
+XMLPUBFUN htmlDocPtr
+		htmlNewDoc		(const xmlChar *URI,
+					 const xmlChar *ExternalID);
+XMLPUBFUN htmlDocPtr
+		htmlNewDocNoDtD		(const xmlChar *URI,
+					 const xmlChar *ExternalID);
+XMLPUBFUN const xmlChar *
+		htmlGetMetaEncoding	(htmlDocPtr doc);
+XMLPUBFUN int
+		htmlSetMetaEncoding	(htmlDocPtr doc,
+					 const xmlChar *encoding);
+#ifdef LIBXML_OUTPUT_ENABLED
+XMLPUBFUN void
+		htmlDocDumpMemory	(xmlDocPtr cur,
+					 xmlChar **mem,
+					 int *size);
+XMLPUBFUN void
+		htmlDocDumpMemoryFormat	(xmlDocPtr cur,
+					 xmlChar **mem,
+					 int *size,
+					 int format);
+XMLPUBFUN int
+		htmlDocDump		(FILE *f,
+					 xmlDocPtr cur);
+XMLPUBFUN int
+		htmlSaveFile		(const char *filename,
+					 xmlDocPtr cur);
+XMLPUBFUN int
+		htmlNodeDump		(xmlBufferPtr buf,
+					 xmlDocPtr doc,
+					 xmlNodePtr cur);
+XMLPUBFUN void
+		htmlNodeDumpFile	(FILE *out,
+					 xmlDocPtr doc,
+					 xmlNodePtr cur);
+XMLPUBFUN int
+		htmlNodeDumpFileFormat	(FILE *out,
+					 xmlDocPtr doc,
+					 xmlNodePtr cur,
+					 const char *encoding,
+					 int format);
+XMLPUBFUN int
+		htmlSaveFileEnc		(const char *filename,
+					 xmlDocPtr cur,
+					 const char *encoding);
+XMLPUBFUN int
+		htmlSaveFileFormat	(const char *filename,
+					 xmlDocPtr cur,
+					 const char *encoding,
+					 int format);
+
+XMLPUBFUN void
+		htmlNodeDumpFormatOutput(xmlOutputBufferPtr buf,
+					 xmlDocPtr doc,
+					 xmlNodePtr cur,
+					 const char *encoding,
+					 int format);
+XMLPUBFUN void
+		htmlDocContentDumpOutput(xmlOutputBufferPtr buf,
+					 xmlDocPtr cur,
+					 const char *encoding);
+XMLPUBFUN void
+		htmlDocContentDumpFormatOutput(xmlOutputBufferPtr buf,
+					 xmlDocPtr cur,
+					 const char *encoding,
+					 int format);
+XMLPUBFUN void
+		htmlNodeDumpOutput	(xmlOutputBufferPtr buf,
+					 xmlDocPtr doc,
+					 xmlNodePtr cur,
+					 const char *encoding);
+
+#endif /* LIBXML_OUTPUT_ENABLED */
+
+XMLPUBFUN int
+		htmlIsBooleanAttr	(const xmlChar *name);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBXML_HTML_ENABLED */
+
+#endif /* __HTML_TREE_H__ */
+
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/SAX.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/SAX.h
new file mode 100644
index 00000000..eea1057b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/SAX.h
@@ -0,0 +1,202 @@
+/*
+ * Summary: Old SAX version 1 handler, deprecated
+ * Description: DEPRECATED set of SAX version 1 interfaces used to
+ *              build the DOM tree.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+
+#ifndef __XML_SAX_H__
+#define __XML_SAX_H__
+
+#include <libxml/xmlversion.h>
+#include <libxml/parser.h>
+
+#ifdef LIBXML_LEGACY_ENABLED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+XML_DEPRECATED
+XMLPUBFUN const xmlChar *
+		getPublicId			(void *ctx);
+XML_DEPRECATED
+XMLPUBFUN const xmlChar *
+		getSystemId			(void *ctx);
+XML_DEPRECATED
+XMLPUBFUN void
+		setDocumentLocator		(void *ctx,
+						 xmlSAXLocatorPtr loc);
+
+XML_DEPRECATED
+XMLPUBFUN int
+		getLineNumber			(void *ctx);
+XML_DEPRECATED
+XMLPUBFUN int
+		getColumnNumber			(void *ctx);
+
+XML_DEPRECATED
+XMLPUBFUN int
+		isStandalone			(void *ctx);
+XML_DEPRECATED
+XMLPUBFUN int
+		hasInternalSubset		(void *ctx);
+XML_DEPRECATED
+XMLPUBFUN int
+		hasExternalSubset		(void *ctx);
+
+XML_DEPRECATED
+XMLPUBFUN void
+		internalSubset			(void *ctx,
+						 const xmlChar *name,
+						 const xmlChar *ExternalID,
+						 const xmlChar *SystemID);
+XML_DEPRECATED
+XMLPUBFUN void
+		externalSubset			(void *ctx,
+						 const xmlChar *name,
+						 const xmlChar *ExternalID,
+						 const xmlChar *SystemID);
+XML_DEPRECATED
+XMLPUBFUN xmlEntityPtr
+		getEntity			(void *ctx,
+						 const xmlChar *name);
+XML_DEPRECATED
+XMLPUBFUN xmlEntityPtr
+		getParameterEntity		(void *ctx,
+						 const xmlChar *name);
+XML_DEPRECATED
+XMLPUBFUN xmlParserInputPtr
+		resolveEntity			(void *ctx,
+						 const xmlChar *publicId,
+						 const xmlChar *systemId);
+
+XML_DEPRECATED
+XMLPUBFUN void
+		entityDecl			(void *ctx,
+						 const xmlChar *name,
+						 int type,
+						 const xmlChar *publicId,
+						 const xmlChar *systemId,
+						 xmlChar *content);
+XML_DEPRECATED
+XMLPUBFUN void
+		attributeDecl			(void *ctx,
+						 const xmlChar *elem,
+						 const xmlChar *fullname,
+						 int type,
+						 int def,
+						 const xmlChar *defaultValue,
+						 xmlEnumerationPtr tree);
+XML_DEPRECATED
+XMLPUBFUN void
+		elementDecl			(void *ctx,
+						 const xmlChar *name,
+						 int type,
+						 xmlElementContentPtr content);
+XML_DEPRECATED
+XMLPUBFUN void
+		notationDecl			(void *ctx,
+						 const xmlChar *name,
+						 const xmlChar *publicId,
+						 const xmlChar *systemId);
+XML_DEPRECATED
+XMLPUBFUN void
+		unparsedEntityDecl		(void *ctx,
+						 const xmlChar *name,
+						 const xmlChar *publicId,
+						 const xmlChar *systemId,
+						 const xmlChar *notationName);
+
+XML_DEPRECATED
+XMLPUBFUN void
+		startDocument			(void *ctx);
+XML_DEPRECATED
+XMLPUBFUN void
+		endDocument			(void *ctx);
+XML_DEPRECATED
+XMLPUBFUN void
+		attribute			(void *ctx,
+						 const xmlChar *fullname,
+						 const xmlChar *value);
+XML_DEPRECATED
+XMLPUBFUN void
+		startElement			(void *ctx,
+						 const xmlChar *fullname,
+						 const xmlChar **atts);
+XML_DEPRECATED
+XMLPUBFUN void
+		endElement			(void *ctx,
+						 const xmlChar *name);
+XML_DEPRECATED
+XMLPUBFUN void
+		reference			(void *ctx,
+						 const xmlChar *name);
+XML_DEPRECATED
+XMLPUBFUN void
+		characters			(void *ctx,
+						 const xmlChar *ch,
+						 int len);
+XML_DEPRECATED
+XMLPUBFUN void
+		ignorableWhitespace		(void *ctx,
+						 const xmlChar *ch,
+						 int len);
+XML_DEPRECATED
+XMLPUBFUN void
+		processingInstruction		(void *ctx,
+						 const xmlChar *target,
+						 const xmlChar *data);
+XML_DEPRECATED
+XMLPUBFUN void
+		globalNamespace			(void *ctx,
+						 const xmlChar *href,
+						 const xmlChar *prefix);
+XML_DEPRECATED
+XMLPUBFUN void
+		setNamespace			(void *ctx,
+						 const xmlChar *name);
+XML_DEPRECATED
+XMLPUBFUN xmlNsPtr
+		getNamespace			(void *ctx);
+XML_DEPRECATED
+XMLPUBFUN int
+		checkNamespace			(void *ctx,
+						 xmlChar *nameSpace);
+XML_DEPRECATED
+XMLPUBFUN void
+		namespaceDecl			(void *ctx,
+						 const xmlChar *href,
+						 const xmlChar *prefix);
+XML_DEPRECATED
+XMLPUBFUN void
+		comment				(void *ctx,
+						 const xmlChar *value);
+XML_DEPRECATED
+XMLPUBFUN void
+		cdataBlock			(void *ctx,
+						 const xmlChar *value,
+						 int len);
+
+#ifdef LIBXML_SAX1_ENABLED
+XML_DEPRECATED
+XMLPUBFUN void
+		initxmlDefaultSAXHandler	(xmlSAXHandlerV1 *hdlr,
+						 int warning);
+#ifdef LIBXML_HTML_ENABLED
+XML_DEPRECATED
+XMLPUBFUN void
+		inithtmlDefaultSAXHandler	(xmlSAXHandlerV1 *hdlr);
+#endif
+#endif /* LIBXML_SAX1_ENABLED */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBXML_LEGACY_ENABLED */
+
+#endif /* __XML_SAX_H__ */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/SAX2.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/SAX2.h
new file mode 100644
index 00000000..4c4ecce8
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/SAX2.h
@@ -0,0 +1,171 @@
+/*
+ * Summary: SAX2 parser interface used to build the DOM tree
+ * Description: those are the default SAX2 interfaces used by
+ *              the library when building DOM tree.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+
+#ifndef __XML_SAX2_H__
+#define __XML_SAX2_H__
+
+#include <libxml/xmlversion.h>
+#include <libxml/parser.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+XMLPUBFUN const xmlChar *
+		xmlSAX2GetPublicId		(void *ctx);
+XMLPUBFUN const xmlChar *
+		xmlSAX2GetSystemId		(void *ctx);
+XMLPUBFUN void
+		xmlSAX2SetDocumentLocator	(void *ctx,
+						 xmlSAXLocatorPtr loc);
+
+XMLPUBFUN int
+		xmlSAX2GetLineNumber		(void *ctx);
+XMLPUBFUN int
+		xmlSAX2GetColumnNumber		(void *ctx);
+
+XMLPUBFUN int
+		xmlSAX2IsStandalone		(void *ctx);
+XMLPUBFUN int
+		xmlSAX2HasInternalSubset	(void *ctx);
+XMLPUBFUN int
+		xmlSAX2HasExternalSubset	(void *ctx);
+
+XMLPUBFUN void
+		xmlSAX2InternalSubset		(void *ctx,
+						 const xmlChar *name,
+						 const xmlChar *ExternalID,
+						 const xmlChar *SystemID);
+XMLPUBFUN void
+		xmlSAX2ExternalSubset		(void *ctx,
+						 const xmlChar *name,
+						 const xmlChar *ExternalID,
+						 const xmlChar *SystemID);
+XMLPUBFUN xmlEntityPtr
+		xmlSAX2GetEntity		(void *ctx,
+						 const xmlChar *name);
+XMLPUBFUN xmlEntityPtr
+		xmlSAX2GetParameterEntity	(void *ctx,
+						 const xmlChar *name);
+XMLPUBFUN xmlParserInputPtr
+		xmlSAX2ResolveEntity		(void *ctx,
+						 const xmlChar *publicId,
+						 const xmlChar *systemId);
+
+XMLPUBFUN void
+		xmlSAX2EntityDecl		(void *ctx,
+						 const xmlChar *name,
+						 int type,
+						 const xmlChar *publicId,
+						 const xmlChar *systemId,
+						 xmlChar *content);
+XMLPUBFUN void
+		xmlSAX2AttributeDecl		(void *ctx,
+						 const xmlChar *elem,
+						 const xmlChar *fullname,
+						 int type,
+						 int def,
+						 const xmlChar *defaultValue,
+						 xmlEnumerationPtr tree);
+XMLPUBFUN void
+		xmlSAX2ElementDecl		(void *ctx,
+						 const xmlChar *name,
+						 int type,
+						 xmlElementContentPtr content);
+XMLPUBFUN void
+		xmlSAX2NotationDecl		(void *ctx,
+						 const xmlChar *name,
+						 const xmlChar *publicId,
+						 const xmlChar *systemId);
+XMLPUBFUN void
+		xmlSAX2UnparsedEntityDecl	(void *ctx,
+						 const xmlChar *name,
+						 const xmlChar *publicId,
+						 const xmlChar *systemId,
+						 const xmlChar *notationName);
+
+XMLPUBFUN void
+		xmlSAX2StartDocument		(void *ctx);
+XMLPUBFUN void
+		xmlSAX2EndDocument		(void *ctx);
+#if defined(LIBXML_SAX1_ENABLED) || defined(LIBXML_HTML_ENABLED) || \
+    defined(LIBXML_WRITER_ENABLED) || defined(LIBXML_LEGACY_ENABLED)
+XMLPUBFUN void
+		xmlSAX2StartElement		(void *ctx,
+						 const xmlChar *fullname,
+						 const xmlChar **atts);
+XMLPUBFUN void
+		xmlSAX2EndElement		(void *ctx,
+						 const xmlChar *name);
+#endif /* LIBXML_SAX1_ENABLED or LIBXML_HTML_ENABLED or LIBXML_LEGACY_ENABLED */
+XMLPUBFUN void
+		xmlSAX2StartElementNs		(void *ctx,
+						 const xmlChar *localname,
+						 const xmlChar *prefix,
+						 const xmlChar *URI,
+						 int nb_namespaces,
+						 const xmlChar **namespaces,
+						 int nb_attributes,
+						 int nb_defaulted,
+						 const xmlChar **attributes);
+XMLPUBFUN void
+		xmlSAX2EndElementNs		(void *ctx,
+						 const xmlChar *localname,
+						 const xmlChar *prefix,
+						 const xmlChar *URI);
+XMLPUBFUN void
+		xmlSAX2Reference		(void *ctx,
+						 const xmlChar *name);
+XMLPUBFUN void
+		xmlSAX2Characters		(void *ctx,
+						 const xmlChar *ch,
+						 int len);
+XMLPUBFUN void
+		xmlSAX2IgnorableWhitespace	(void *ctx,
+						 const xmlChar *ch,
+						 int len);
+XMLPUBFUN void
+		xmlSAX2ProcessingInstruction	(void *ctx,
+						 const xmlChar *target,
+						 const xmlChar *data);
+XMLPUBFUN void
+		xmlSAX2Comment			(void *ctx,
+						 const xmlChar *value);
+XMLPUBFUN void
+		xmlSAX2CDataBlock		(void *ctx,
+						 const xmlChar *value,
+						 int len);
+
+#ifdef LIBXML_SAX1_ENABLED
+XML_DEPRECATED
+XMLPUBFUN int
+		xmlSAXDefaultVersion		(int version);
+#endif /* LIBXML_SAX1_ENABLED */
+
+XMLPUBFUN int
+		xmlSAXVersion			(xmlSAXHandler *hdlr,
+						 int version);
+XMLPUBFUN void
+		xmlSAX2InitDefaultSAXHandler    (xmlSAXHandler *hdlr,
+						 int warning);
+#ifdef LIBXML_HTML_ENABLED
+XMLPUBFUN void
+		xmlSAX2InitHtmlDefaultSAXHandler(xmlSAXHandler *hdlr);
+XML_DEPRECATED
+XMLPUBFUN void
+		htmlDefaultSAXHandlerInit	(void);
+#endif
+XML_DEPRECATED
+XMLPUBFUN void
+		xmlDefaultSAXHandlerInit	(void);
+#ifdef __cplusplus
+}
+#endif
+#endif /* __XML_SAX2_H__ */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/__init__.py b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/__init__.py
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/c14n.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/c14n.h
new file mode 100644
index 00000000..f9bdf9b5
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/c14n.h
@@ -0,0 +1,126 @@
+/*
+ * Summary: Provide Canonical XML and Exclusive XML Canonicalization
+ * Description: the c14n modules provides a
+ *
+ * "Canonical XML" implementation
+ * http://www.w3.org/TR/xml-c14n
+ *
+ * and an
+ *
+ * "Exclusive XML Canonicalization" implementation
+ * http://www.w3.org/TR/xml-exc-c14n
+
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Aleksey Sanin <aleksey@aleksey.com>
+ */
+#ifndef __XML_C14N_H__
+#define __XML_C14N_H__
+
+#include <libxml/xmlversion.h>
+
+#ifdef LIBXML_C14N_ENABLED
+
+#include <libxml/tree.h>
+#include <libxml/xpath.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * XML Canonicalization
+ * http://www.w3.org/TR/xml-c14n
+ *
+ * Exclusive XML Canonicalization
+ * http://www.w3.org/TR/xml-exc-c14n
+ *
+ * Canonical form of an XML document could be created if and only if
+ *  a) default attributes (if any) are added to all nodes
+ *  b) all character and parsed entity references are resolved
+ * In order to achieve this in libxml2 the document MUST be loaded with
+ * following global settings:
+ *
+ *    xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
+ *    xmlSubstituteEntitiesDefault(1);
+ *
+ * or corresponding parser context setting:
+ *    xmlParserCtxtPtr ctxt;
+ *
+ *    ...
+ *    ctxt->loadsubset = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
+ *    ctxt->replaceEntities = 1;
+ *    ...
+ */
+
+/*
+ * xmlC14NMode:
+ *
+ * Predefined values for C14N modes
+ *
+ */
+typedef enum {
+    XML_C14N_1_0            = 0,    /* Original C14N 1.0 spec */
+    XML_C14N_EXCLUSIVE_1_0  = 1,    /* Exclusive C14N 1.0 spec */
+    XML_C14N_1_1            = 2     /* C14N 1.1 spec */
+} xmlC14NMode;
+
+XMLPUBFUN int
+		xmlC14NDocSaveTo	(xmlDocPtr doc,
+					 xmlNodeSetPtr nodes,
+					 int mode, /* a xmlC14NMode */
+					 xmlChar **inclusive_ns_prefixes,
+					 int with_comments,
+					 xmlOutputBufferPtr buf);
+
+XMLPUBFUN int
+		xmlC14NDocDumpMemory	(xmlDocPtr doc,
+					 xmlNodeSetPtr nodes,
+					 int mode, /* a xmlC14NMode */
+					 xmlChar **inclusive_ns_prefixes,
+					 int with_comments,
+					 xmlChar **doc_txt_ptr);
+
+XMLPUBFUN int
+		xmlC14NDocSave		(xmlDocPtr doc,
+					 xmlNodeSetPtr nodes,
+					 int mode, /* a xmlC14NMode */
+					 xmlChar **inclusive_ns_prefixes,
+					 int with_comments,
+					 const char* filename,
+					 int compression);
+
+
+/**
+ * This is the core C14N function
+ */
+/**
+ * xmlC14NIsVisibleCallback:
+ * @user_data: user data
+ * @node: the current node
+ * @parent: the parent node
+ *
+ * Signature for a C14N callback on visible nodes
+ *
+ * Returns 1 if the node should be included
+ */
+typedef int (*xmlC14NIsVisibleCallback)	(void* user_data,
+					 xmlNodePtr node,
+					 xmlNodePtr parent);
+
+XMLPUBFUN int
+		xmlC14NExecute		(xmlDocPtr doc,
+					 xmlC14NIsVisibleCallback is_visible_callback,
+					 void* user_data,
+					 int mode, /* a xmlC14NMode */
+					 xmlChar **inclusive_ns_prefixes,
+					 int with_comments,
+					 xmlOutputBufferPtr buf);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* LIBXML_C14N_ENABLED */
+#endif /* __XML_C14N_H__ */
+
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/catalog.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/catalog.h
new file mode 100644
index 00000000..02fa7ab2
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/catalog.h
@@ -0,0 +1,182 @@
+/**
+ * Summary: interfaces to the Catalog handling system
+ * Description: the catalog module implements the support for
+ * XML Catalogs and SGML catalogs
+ *
+ * SGML Open Technical Resolution TR9401:1997.
+ * http://www.jclark.com/sp/catalog.htm
+ *
+ * XML Catalogs Working Draft 06 August 2001
+ * http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_CATALOG_H__
+#define __XML_CATALOG_H__
+
+#include <stdio.h>
+
+#include <libxml/xmlversion.h>
+#include <libxml/xmlstring.h>
+#include <libxml/tree.h>
+
+#ifdef LIBXML_CATALOG_ENABLED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * XML_CATALOGS_NAMESPACE:
+ *
+ * The namespace for the XML Catalogs elements.
+ */
+#define XML_CATALOGS_NAMESPACE					\
+    (const xmlChar *) "urn:oasis:names:tc:entity:xmlns:xml:catalog"
+/**
+ * XML_CATALOG_PI:
+ *
+ * The specific XML Catalog Processing Instruction name.
+ */
+#define XML_CATALOG_PI						\
+    (const xmlChar *) "oasis-xml-catalog"
+
+/*
+ * The API is voluntarily limited to general cataloging.
+ */
+typedef enum {
+    XML_CATA_PREFER_NONE = 0,
+    XML_CATA_PREFER_PUBLIC = 1,
+    XML_CATA_PREFER_SYSTEM
+} xmlCatalogPrefer;
+
+typedef enum {
+    XML_CATA_ALLOW_NONE = 0,
+    XML_CATA_ALLOW_GLOBAL = 1,
+    XML_CATA_ALLOW_DOCUMENT = 2,
+    XML_CATA_ALLOW_ALL = 3
+} xmlCatalogAllow;
+
+typedef struct _xmlCatalog xmlCatalog;
+typedef xmlCatalog *xmlCatalogPtr;
+
+/*
+ * Operations on a given catalog.
+ */
+XMLPUBFUN xmlCatalogPtr
+		xmlNewCatalog		(int sgml);
+XMLPUBFUN xmlCatalogPtr
+		xmlLoadACatalog		(const char *filename);
+XMLPUBFUN xmlCatalogPtr
+		xmlLoadSGMLSuperCatalog	(const char *filename);
+XMLPUBFUN int
+		xmlConvertSGMLCatalog	(xmlCatalogPtr catal);
+XMLPUBFUN int
+		xmlACatalogAdd		(xmlCatalogPtr catal,
+					 const xmlChar *type,
+					 const xmlChar *orig,
+					 const xmlChar *replace);
+XMLPUBFUN int
+		xmlACatalogRemove	(xmlCatalogPtr catal,
+					 const xmlChar *value);
+XMLPUBFUN xmlChar *
+		xmlACatalogResolve	(xmlCatalogPtr catal,
+					 const xmlChar *pubID,
+	                                 const xmlChar *sysID);
+XMLPUBFUN xmlChar *
+		xmlACatalogResolveSystem(xmlCatalogPtr catal,
+					 const xmlChar *sysID);
+XMLPUBFUN xmlChar *
+		xmlACatalogResolvePublic(xmlCatalogPtr catal,
+					 const xmlChar *pubID);
+XMLPUBFUN xmlChar *
+		xmlACatalogResolveURI	(xmlCatalogPtr catal,
+					 const xmlChar *URI);
+#ifdef LIBXML_OUTPUT_ENABLED
+XMLPUBFUN void
+		xmlACatalogDump		(xmlCatalogPtr catal,
+					 FILE *out);
+#endif /* LIBXML_OUTPUT_ENABLED */
+XMLPUBFUN void
+		xmlFreeCatalog		(xmlCatalogPtr catal);
+XMLPUBFUN int
+		xmlCatalogIsEmpty	(xmlCatalogPtr catal);
+
+/*
+ * Global operations.
+ */
+XMLPUBFUN void
+		xmlInitializeCatalog	(void);
+XMLPUBFUN int
+		xmlLoadCatalog		(const char *filename);
+XMLPUBFUN void
+		xmlLoadCatalogs		(const char *paths);
+XMLPUBFUN void
+		xmlCatalogCleanup	(void);
+#ifdef LIBXML_OUTPUT_ENABLED
+XMLPUBFUN void
+		xmlCatalogDump		(FILE *out);
+#endif /* LIBXML_OUTPUT_ENABLED */
+XMLPUBFUN xmlChar *
+		xmlCatalogResolve	(const xmlChar *pubID,
+	                                 const xmlChar *sysID);
+XMLPUBFUN xmlChar *
+		xmlCatalogResolveSystem	(const xmlChar *sysID);
+XMLPUBFUN xmlChar *
+		xmlCatalogResolvePublic	(const xmlChar *pubID);
+XMLPUBFUN xmlChar *
+		xmlCatalogResolveURI	(const xmlChar *URI);
+XMLPUBFUN int
+		xmlCatalogAdd		(const xmlChar *type,
+					 const xmlChar *orig,
+					 const xmlChar *replace);
+XMLPUBFUN int
+		xmlCatalogRemove	(const xmlChar *value);
+XMLPUBFUN xmlDocPtr
+		xmlParseCatalogFile	(const char *filename);
+XMLPUBFUN int
+		xmlCatalogConvert	(void);
+
+/*
+ * Strictly minimal interfaces for per-document catalogs used
+ * by the parser.
+ */
+XMLPUBFUN void
+		xmlCatalogFreeLocal	(void *catalogs);
+XMLPUBFUN void *
+		xmlCatalogAddLocal	(void *catalogs,
+					 const xmlChar *URL);
+XMLPUBFUN xmlChar *
+		xmlCatalogLocalResolve	(void *catalogs,
+					 const xmlChar *pubID,
+	                                 const xmlChar *sysID);
+XMLPUBFUN xmlChar *
+		xmlCatalogLocalResolveURI(void *catalogs,
+					 const xmlChar *URI);
+/*
+ * Preference settings.
+ */
+XMLPUBFUN int
+		xmlCatalogSetDebug	(int level);
+XMLPUBFUN xmlCatalogPrefer
+		xmlCatalogSetDefaultPrefer(xmlCatalogPrefer prefer);
+XMLPUBFUN void
+		xmlCatalogSetDefaults	(xmlCatalogAllow allow);
+XMLPUBFUN xmlCatalogAllow
+		xmlCatalogGetDefaults	(void);
+
+
+/* DEPRECATED interfaces */
+XMLPUBFUN const xmlChar *
+		xmlCatalogGetSystem	(const xmlChar *sysID);
+XMLPUBFUN const xmlChar *
+		xmlCatalogGetPublic	(const xmlChar *pubID);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* LIBXML_CATALOG_ENABLED */
+#endif /* __XML_CATALOG_H__ */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/chvalid.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/chvalid.h
new file mode 100644
index 00000000..8225c95e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/chvalid.h
@@ -0,0 +1,230 @@
+/*
+ * Summary: Unicode character range checking
+ * Description: this module exports interfaces for the character
+ *               range validation APIs
+ *
+ * This file is automatically generated from the cvs source
+ * definition files using the genChRanges.py Python script
+ *
+ * Generation date: Mon Mar 27 11:09:48 2006
+ * Sources: chvalid.def
+ * Author: William Brack <wbrack@mmm.com.hk>
+ */
+
+#ifndef __XML_CHVALID_H__
+#define __XML_CHVALID_H__
+
+#include <libxml/xmlversion.h>
+#include <libxml/xmlstring.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Define our typedefs and structures
+ *
+ */
+typedef struct _xmlChSRange xmlChSRange;
+typedef xmlChSRange *xmlChSRangePtr;
+struct _xmlChSRange {
+    unsigned short	low;
+    unsigned short	high;
+};
+
+typedef struct _xmlChLRange xmlChLRange;
+typedef xmlChLRange *xmlChLRangePtr;
+struct _xmlChLRange {
+    unsigned int	low;
+    unsigned int	high;
+};
+
+typedef struct _xmlChRangeGroup xmlChRangeGroup;
+typedef xmlChRangeGroup *xmlChRangeGroupPtr;
+struct _xmlChRangeGroup {
+    int			nbShortRange;
+    int			nbLongRange;
+    const xmlChSRange	*shortRange;	/* points to an array of ranges */
+    const xmlChLRange	*longRange;
+};
+
+/**
+ * Range checking routine
+ */
+XMLPUBFUN int
+		xmlCharInRange(unsigned int val, const xmlChRangeGroup *group);
+
+
+/**
+ * xmlIsBaseChar_ch:
+ * @c: char to validate
+ *
+ * Automatically generated by genChRanges.py
+ */
+#define xmlIsBaseChar_ch(c)	(((0x41 <= (c)) && ((c) <= 0x5a)) || \
+				 ((0x61 <= (c)) && ((c) <= 0x7a)) || \
+				 ((0xc0 <= (c)) && ((c) <= 0xd6)) || \
+				 ((0xd8 <= (c)) && ((c) <= 0xf6)) || \
+				  (0xf8 <= (c)))
+
+/**
+ * xmlIsBaseCharQ:
+ * @c: char to validate
+ *
+ * Automatically generated by genChRanges.py
+ */
+#define xmlIsBaseCharQ(c)	(((c) < 0x100) ? \
+				 xmlIsBaseChar_ch((c)) : \
+				 xmlCharInRange((c), &xmlIsBaseCharGroup))
+
+XMLPUBVAR const xmlChRangeGroup xmlIsBaseCharGroup;
+
+/**
+ * xmlIsBlank_ch:
+ * @c: char to validate
+ *
+ * Automatically generated by genChRanges.py
+ */
+#define xmlIsBlank_ch(c)	(((c) == 0x20) || \
+				 ((0x9 <= (c)) && ((c) <= 0xa)) || \
+				 ((c) == 0xd))
+
+/**
+ * xmlIsBlankQ:
+ * @c: char to validate
+ *
+ * Automatically generated by genChRanges.py
+ */
+#define xmlIsBlankQ(c)		(((c) < 0x100) ? \
+				 xmlIsBlank_ch((c)) : 0)
+
+
+/**
+ * xmlIsChar_ch:
+ * @c: char to validate
+ *
+ * Automatically generated by genChRanges.py
+ */
+#define xmlIsChar_ch(c)		(((0x9 <= (c)) && ((c) <= 0xa)) || \
+				 ((c) == 0xd) || \
+				  (0x20 <= (c)))
+
+/**
+ * xmlIsCharQ:
+ * @c: char to validate
+ *
+ * Automatically generated by genChRanges.py
+ */
+#define xmlIsCharQ(c)		(((c) < 0x100) ? \
+				 xmlIsChar_ch((c)) :\
+				(((0x100 <= (c)) && ((c) <= 0xd7ff)) || \
+				 ((0xe000 <= (c)) && ((c) <= 0xfffd)) || \
+				 ((0x10000 <= (c)) && ((c) <= 0x10ffff))))
+
+XMLPUBVAR const xmlChRangeGroup xmlIsCharGroup;
+
+/**
+ * xmlIsCombiningQ:
+ * @c: char to validate
+ *
+ * Automatically generated by genChRanges.py
+ */
+#define xmlIsCombiningQ(c)	(((c) < 0x100) ? \
+				 0 : \
+				 xmlCharInRange((c), &xmlIsCombiningGroup))
+
+XMLPUBVAR const xmlChRangeGroup xmlIsCombiningGroup;
+
+/**
+ * xmlIsDigit_ch:
+ * @c: char to validate
+ *
+ * Automatically generated by genChRanges.py
+ */
+#define xmlIsDigit_ch(c)	(((0x30 <= (c)) && ((c) <= 0x39)))
+
+/**
+ * xmlIsDigitQ:
+ * @c: char to validate
+ *
+ * Automatically generated by genChRanges.py
+ */
+#define xmlIsDigitQ(c)		(((c) < 0x100) ? \
+				 xmlIsDigit_ch((c)) : \
+				 xmlCharInRange((c), &xmlIsDigitGroup))
+
+XMLPUBVAR const xmlChRangeGroup xmlIsDigitGroup;
+
+/**
+ * xmlIsExtender_ch:
+ * @c: char to validate
+ *
+ * Automatically generated by genChRanges.py
+ */
+#define xmlIsExtender_ch(c)	(((c) == 0xb7))
+
+/**
+ * xmlIsExtenderQ:
+ * @c: char to validate
+ *
+ * Automatically generated by genChRanges.py
+ */
+#define xmlIsExtenderQ(c)	(((c) < 0x100) ? \
+				 xmlIsExtender_ch((c)) : \
+				 xmlCharInRange((c), &xmlIsExtenderGroup))
+
+XMLPUBVAR const xmlChRangeGroup xmlIsExtenderGroup;
+
+/**
+ * xmlIsIdeographicQ:
+ * @c: char to validate
+ *
+ * Automatically generated by genChRanges.py
+ */
+#define xmlIsIdeographicQ(c)	(((c) < 0x100) ? \
+				 0 :\
+				(((0x4e00 <= (c)) && ((c) <= 0x9fa5)) || \
+				 ((c) == 0x3007) || \
+				 ((0x3021 <= (c)) && ((c) <= 0x3029))))
+
+XMLPUBVAR const xmlChRangeGroup xmlIsIdeographicGroup;
+XMLPUBVAR const unsigned char xmlIsPubidChar_tab[256];
+
+/**
+ * xmlIsPubidChar_ch:
+ * @c: char to validate
+ *
+ * Automatically generated by genChRanges.py
+ */
+#define xmlIsPubidChar_ch(c)	(xmlIsPubidChar_tab[(c)])
+
+/**
+ * xmlIsPubidCharQ:
+ * @c: char to validate
+ *
+ * Automatically generated by genChRanges.py
+ */
+#define xmlIsPubidCharQ(c)	(((c) < 0x100) ? \
+				 xmlIsPubidChar_ch((c)) : 0)
+
+XMLPUBFUN int
+		xmlIsBaseChar(unsigned int ch);
+XMLPUBFUN int
+		xmlIsBlank(unsigned int ch);
+XMLPUBFUN int
+		xmlIsChar(unsigned int ch);
+XMLPUBFUN int
+		xmlIsCombining(unsigned int ch);
+XMLPUBFUN int
+		xmlIsDigit(unsigned int ch);
+XMLPUBFUN int
+		xmlIsExtender(unsigned int ch);
+XMLPUBFUN int
+		xmlIsIdeographic(unsigned int ch);
+XMLPUBFUN int
+		xmlIsPubidChar(unsigned int ch);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __XML_CHVALID_H__ */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/debugXML.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/debugXML.h
new file mode 100644
index 00000000..82746873
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/debugXML.h
@@ -0,0 +1,217 @@
+/*
+ * Summary: Tree debugging APIs
+ * Description: Interfaces to a set of routines used for debugging the tree
+ *              produced by the XML parser.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __DEBUG_XML__
+#define __DEBUG_XML__
+#include <stdio.h>
+#include <libxml/xmlversion.h>
+#include <libxml/tree.h>
+
+#ifdef LIBXML_DEBUG_ENABLED
+
+#include <libxml/xpath.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * The standard Dump routines.
+ */
+XMLPUBFUN void
+	xmlDebugDumpString	(FILE *output,
+				 const xmlChar *str);
+XMLPUBFUN void
+	xmlDebugDumpAttr	(FILE *output,
+				 xmlAttrPtr attr,
+				 int depth);
+XMLPUBFUN void
+	xmlDebugDumpAttrList	(FILE *output,
+				 xmlAttrPtr attr,
+				 int depth);
+XMLPUBFUN void
+	xmlDebugDumpOneNode	(FILE *output,
+				 xmlNodePtr node,
+				 int depth);
+XMLPUBFUN void
+	xmlDebugDumpNode	(FILE *output,
+				 xmlNodePtr node,
+				 int depth);
+XMLPUBFUN void
+	xmlDebugDumpNodeList	(FILE *output,
+				 xmlNodePtr node,
+				 int depth);
+XMLPUBFUN void
+	xmlDebugDumpDocumentHead(FILE *output,
+				 xmlDocPtr doc);
+XMLPUBFUN void
+	xmlDebugDumpDocument	(FILE *output,
+				 xmlDocPtr doc);
+XMLPUBFUN void
+	xmlDebugDumpDTD		(FILE *output,
+				 xmlDtdPtr dtd);
+XMLPUBFUN void
+	xmlDebugDumpEntities	(FILE *output,
+				 xmlDocPtr doc);
+
+/****************************************************************
+ *								*
+ *			Checking routines			*
+ *								*
+ ****************************************************************/
+
+XMLPUBFUN int
+	xmlDebugCheckDocument	(FILE * output,
+				 xmlDocPtr doc);
+
+/****************************************************************
+ *								*
+ *			XML shell helpers			*
+ *								*
+ ****************************************************************/
+
+XMLPUBFUN void
+	xmlLsOneNode		(FILE *output, xmlNodePtr node);
+XMLPUBFUN int
+	xmlLsCountNode		(xmlNodePtr node);
+
+XMLPUBFUN const char *
+	xmlBoolToText		(int boolval);
+
+/****************************************************************
+ *								*
+ *	 The XML shell related structures and functions		*
+ *								*
+ ****************************************************************/
+
+#ifdef LIBXML_XPATH_ENABLED
+/**
+ * xmlShellReadlineFunc:
+ * @prompt:  a string prompt
+ *
+ * This is a generic signature for the XML shell input function.
+ *
+ * Returns a string which will be freed by the Shell.
+ */
+typedef char * (* xmlShellReadlineFunc)(char *prompt);
+
+/**
+ * xmlShellCtxt:
+ *
+ * A debugging shell context.
+ * TODO: add the defined function tables.
+ */
+typedef struct _xmlShellCtxt xmlShellCtxt;
+typedef xmlShellCtxt *xmlShellCtxtPtr;
+struct _xmlShellCtxt {
+    char *filename;
+    xmlDocPtr doc;
+    xmlNodePtr node;
+    xmlXPathContextPtr pctxt;
+    int loaded;
+    FILE *output;
+    xmlShellReadlineFunc input;
+};
+
+/**
+ * xmlShellCmd:
+ * @ctxt:  a shell context
+ * @arg:  a string argument
+ * @node:  a first node
+ * @node2:  a second node
+ *
+ * This is a generic signature for the XML shell functions.
+ *
+ * Returns an int, negative returns indicating errors.
+ */
+typedef int (* xmlShellCmd) (xmlShellCtxtPtr ctxt,
+                             char *arg,
+			     xmlNodePtr node,
+			     xmlNodePtr node2);
+
+XMLPUBFUN void
+	xmlShellPrintXPathError	(int errorType,
+				 const char *arg);
+XMLPUBFUN void
+	xmlShellPrintXPathResult(xmlXPathObjectPtr list);
+XMLPUBFUN int
+	xmlShellList		(xmlShellCtxtPtr ctxt,
+				 char *arg,
+				 xmlNodePtr node,
+				 xmlNodePtr node2);
+XMLPUBFUN int
+	xmlShellBase		(xmlShellCtxtPtr ctxt,
+				 char *arg,
+				 xmlNodePtr node,
+				 xmlNodePtr node2);
+XMLPUBFUN int
+	xmlShellDir		(xmlShellCtxtPtr ctxt,
+				 char *arg,
+				 xmlNodePtr node,
+				 xmlNodePtr node2);
+XMLPUBFUN int
+	xmlShellLoad		(xmlShellCtxtPtr ctxt,
+				 char *filename,
+				 xmlNodePtr node,
+				 xmlNodePtr node2);
+#ifdef LIBXML_OUTPUT_ENABLED
+XMLPUBFUN void
+	xmlShellPrintNode	(xmlNodePtr node);
+XMLPUBFUN int
+	xmlShellCat		(xmlShellCtxtPtr ctxt,
+				 char *arg,
+				 xmlNodePtr node,
+				 xmlNodePtr node2);
+XMLPUBFUN int
+	xmlShellWrite		(xmlShellCtxtPtr ctxt,
+				 char *filename,
+				 xmlNodePtr node,
+				 xmlNodePtr node2);
+XMLPUBFUN int
+	xmlShellSave		(xmlShellCtxtPtr ctxt,
+				 char *filename,
+				 xmlNodePtr node,
+				 xmlNodePtr node2);
+#endif /* LIBXML_OUTPUT_ENABLED */
+#ifdef LIBXML_VALID_ENABLED
+XMLPUBFUN int
+	xmlShellValidate	(xmlShellCtxtPtr ctxt,
+				 char *dtd,
+				 xmlNodePtr node,
+				 xmlNodePtr node2);
+#endif /* LIBXML_VALID_ENABLED */
+XMLPUBFUN int
+	xmlShellDu		(xmlShellCtxtPtr ctxt,
+				 char *arg,
+				 xmlNodePtr tree,
+				 xmlNodePtr node2);
+XMLPUBFUN int
+	xmlShellPwd		(xmlShellCtxtPtr ctxt,
+				 char *buffer,
+				 xmlNodePtr node,
+				 xmlNodePtr node2);
+
+/*
+ * The Shell interface.
+ */
+XMLPUBFUN void
+	xmlShell		(xmlDocPtr doc,
+				 char *filename,
+				 xmlShellReadlineFunc input,
+				 FILE *output);
+
+#endif /* LIBXML_XPATH_ENABLED */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBXML_DEBUG_ENABLED */
+#endif /* __DEBUG_XML__ */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/dict.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/dict.h
new file mode 100644
index 00000000..22aa3d9d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/dict.h
@@ -0,0 +1,82 @@
+/*
+ * Summary: string dictionary
+ * Description: dictionary of reusable strings, just used to avoid allocation
+ *         and freeing operations.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_DICT_H__
+#define __XML_DICT_H__
+
+#include <stddef.h>
+#include <libxml/xmlversion.h>
+#include <libxml/xmlstring.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * The dictionary.
+ */
+typedef struct _xmlDict xmlDict;
+typedef xmlDict *xmlDictPtr;
+
+/*
+ * Initializer
+ */
+XML_DEPRECATED
+XMLPUBFUN int  xmlInitializeDict(void);
+
+/*
+ * Constructor and destructor.
+ */
+XMLPUBFUN xmlDictPtr
+			xmlDictCreate	(void);
+XMLPUBFUN size_t
+			xmlDictSetLimit	(xmlDictPtr dict,
+                                         size_t limit);
+XMLPUBFUN size_t
+			xmlDictGetUsage (xmlDictPtr dict);
+XMLPUBFUN xmlDictPtr
+			xmlDictCreateSub(xmlDictPtr sub);
+XMLPUBFUN int
+			xmlDictReference(xmlDictPtr dict);
+XMLPUBFUN void
+			xmlDictFree	(xmlDictPtr dict);
+
+/*
+ * Lookup of entry in the dictionary.
+ */
+XMLPUBFUN const xmlChar *
+			xmlDictLookup	(xmlDictPtr dict,
+		                         const xmlChar *name,
+		                         int len);
+XMLPUBFUN const xmlChar *
+			xmlDictExists	(xmlDictPtr dict,
+		                         const xmlChar *name,
+		                         int len);
+XMLPUBFUN const xmlChar *
+			xmlDictQLookup	(xmlDictPtr dict,
+		                         const xmlChar *prefix,
+		                         const xmlChar *name);
+XMLPUBFUN int
+			xmlDictOwns	(xmlDictPtr dict,
+					 const xmlChar *str);
+XMLPUBFUN int
+			xmlDictSize	(xmlDictPtr dict);
+
+/*
+ * Cleanup function
+ */
+XML_DEPRECATED
+XMLPUBFUN void
+                        xmlDictCleanup  (void);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* ! __XML_DICT_H__ */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/encoding.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/encoding.h
new file mode 100644
index 00000000..8594cffc
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/encoding.h
@@ -0,0 +1,235 @@
+/*
+ * Summary: interface for the encoding conversion functions
+ * Description: interface for the encoding conversion functions needed for
+ *              XML basic encoding and iconv() support.
+ *
+ * Related specs are
+ * rfc2044        (UTF-8 and UTF-16) F. Yergeau Alis Technologies
+ * [ISO-10646]    UTF-8 and UTF-16 in Annexes
+ * [ISO-8859-1]   ISO Latin-1 characters codes.
+ * [UNICODE]      The Unicode Consortium, "The Unicode Standard --
+ *                Worldwide Character Encoding -- Version 1.0", Addison-
+ *                Wesley, Volume 1, 1991, Volume 2, 1992.  UTF-8 is
+ *                described in Unicode Technical Report #4.
+ * [US-ASCII]     Coded Character Set--7-bit American Standard Code for
+ *                Information Interchange, ANSI X3.4-1986.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_CHAR_ENCODING_H__
+#define __XML_CHAR_ENCODING_H__
+
+#include <libxml/xmlversion.h>
+
+#ifdef LIBXML_ICONV_ENABLED
+#include <iconv.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+    XML_ENC_ERR_SUCCESS     =  0,
+    XML_ENC_ERR_SPACE       = -1,
+    XML_ENC_ERR_INPUT       = -2,
+    XML_ENC_ERR_PARTIAL     = -3,
+    XML_ENC_ERR_INTERNAL    = -4,
+    XML_ENC_ERR_MEMORY      = -5
+} xmlCharEncError;
+
+/*
+ * xmlCharEncoding:
+ *
+ * Predefined values for some standard encodings.
+ * Libxml does not do beforehand translation on UTF8 and ISOLatinX.
+ * It also supports ASCII, ISO-8859-1, and UTF16 (LE and BE) by default.
+ *
+ * Anything else would have to be translated to UTF8 before being
+ * given to the parser itself. The BOM for UTF16 and the encoding
+ * declaration are looked at and a converter is looked for at that
+ * point. If not found the parser stops here as asked by the XML REC. A
+ * converter can be registered by the user using xmlRegisterCharEncodingHandler
+ * but the current form doesn't allow stateful transcoding (a serious
+ * problem agreed !). If iconv has been found it will be used
+ * automatically and allow stateful transcoding, the simplest is then
+ * to be sure to enable iconv and to provide iconv libs for the encoding
+ * support needed.
+ *
+ * Note that the generic "UTF-16" is not a predefined value.  Instead, only
+ * the specific UTF-16LE and UTF-16BE are present.
+ */
+typedef enum {
+    XML_CHAR_ENCODING_ERROR=   -1, /* No char encoding detected */
+    XML_CHAR_ENCODING_NONE=	0, /* No char encoding detected */
+    XML_CHAR_ENCODING_UTF8=	1, /* UTF-8 */
+    XML_CHAR_ENCODING_UTF16LE=	2, /* UTF-16 little endian */
+    XML_CHAR_ENCODING_UTF16BE=	3, /* UTF-16 big endian */
+    XML_CHAR_ENCODING_UCS4LE=	4, /* UCS-4 little endian */
+    XML_CHAR_ENCODING_UCS4BE=	5, /* UCS-4 big endian */
+    XML_CHAR_ENCODING_EBCDIC=	6, /* EBCDIC uh! */
+    XML_CHAR_ENCODING_UCS4_2143=7, /* UCS-4 unusual ordering */
+    XML_CHAR_ENCODING_UCS4_3412=8, /* UCS-4 unusual ordering */
+    XML_CHAR_ENCODING_UCS2=	9, /* UCS-2 */
+    XML_CHAR_ENCODING_8859_1=	10,/* ISO-8859-1 ISO Latin 1 */
+    XML_CHAR_ENCODING_8859_2=	11,/* ISO-8859-2 ISO Latin 2 */
+    XML_CHAR_ENCODING_8859_3=	12,/* ISO-8859-3 */
+    XML_CHAR_ENCODING_8859_4=	13,/* ISO-8859-4 */
+    XML_CHAR_ENCODING_8859_5=	14,/* ISO-8859-5 */
+    XML_CHAR_ENCODING_8859_6=	15,/* ISO-8859-6 */
+    XML_CHAR_ENCODING_8859_7=	16,/* ISO-8859-7 */
+    XML_CHAR_ENCODING_8859_8=	17,/* ISO-8859-8 */
+    XML_CHAR_ENCODING_8859_9=	18,/* ISO-8859-9 */
+    XML_CHAR_ENCODING_2022_JP=  19,/* ISO-2022-JP */
+    XML_CHAR_ENCODING_SHIFT_JIS=20,/* Shift_JIS */
+    XML_CHAR_ENCODING_EUC_JP=   21,/* EUC-JP */
+    XML_CHAR_ENCODING_ASCII=    22 /* pure ASCII */
+} xmlCharEncoding;
+
+/**
+ * xmlCharEncodingInputFunc:
+ * @out:  a pointer to an array of bytes to store the UTF-8 result
+ * @outlen:  the length of @out
+ * @in:  a pointer to an array of chars in the original encoding
+ * @inlen:  the length of @in
+ *
+ * Take a block of chars in the original encoding and try to convert
+ * it to an UTF-8 block of chars out.
+ *
+ * Returns the number of bytes written, -1 if lack of space, or -2
+ *     if the transcoding failed.
+ * The value of @inlen after return is the number of octets consumed
+ *     if the return value is positive, else unpredictiable.
+ * The value of @outlen after return is the number of octets consumed.
+ */
+typedef int (* xmlCharEncodingInputFunc)(unsigned char *out, int *outlen,
+                                         const unsigned char *in, int *inlen);
+
+
+/**
+ * xmlCharEncodingOutputFunc:
+ * @out:  a pointer to an array of bytes to store the result
+ * @outlen:  the length of @out
+ * @in:  a pointer to an array of UTF-8 chars
+ * @inlen:  the length of @in
+ *
+ * Take a block of UTF-8 chars in and try to convert it to another
+ * encoding.
+ * Note: a first call designed to produce heading info is called with
+ * in = NULL. If stateful this should also initialize the encoder state.
+ *
+ * Returns the number of bytes written, -1 if lack of space, or -2
+ *     if the transcoding failed.
+ * The value of @inlen after return is the number of octets consumed
+ *     if the return value is positive, else unpredictiable.
+ * The value of @outlen after return is the number of octets produced.
+ */
+typedef int (* xmlCharEncodingOutputFunc)(unsigned char *out, int *outlen,
+                                          const unsigned char *in, int *inlen);
+
+
+/*
+ * Block defining the handlers for non UTF-8 encodings.
+ * If iconv is supported, there are two extra fields.
+ */
+typedef struct _xmlCharEncodingHandler xmlCharEncodingHandler;
+typedef xmlCharEncodingHandler *xmlCharEncodingHandlerPtr;
+struct _xmlCharEncodingHandler {
+    char                       *name;
+    xmlCharEncodingInputFunc   input;
+    xmlCharEncodingOutputFunc  output;
+#ifdef LIBXML_ICONV_ENABLED
+    iconv_t                    iconv_in;
+    iconv_t                    iconv_out;
+#endif /* LIBXML_ICONV_ENABLED */
+#ifdef LIBXML_ICU_ENABLED
+    struct _uconv_t            *uconv_in;
+    struct _uconv_t            *uconv_out;
+#endif /* LIBXML_ICU_ENABLED */
+};
+
+/*
+ * Interfaces for encoding handlers.
+ */
+XML_DEPRECATED
+XMLPUBFUN void
+	xmlInitCharEncodingHandlers	(void);
+XML_DEPRECATED
+XMLPUBFUN void
+	xmlCleanupCharEncodingHandlers	(void);
+XMLPUBFUN void
+	xmlRegisterCharEncodingHandler	(xmlCharEncodingHandlerPtr handler);
+XMLPUBFUN xmlCharEncodingHandlerPtr
+	xmlGetCharEncodingHandler	(xmlCharEncoding enc);
+XMLPUBFUN xmlCharEncodingHandlerPtr
+	xmlFindCharEncodingHandler	(const char *name);
+XMLPUBFUN xmlCharEncodingHandlerPtr
+	xmlNewCharEncodingHandler	(const char *name,
+					 xmlCharEncodingInputFunc input,
+					 xmlCharEncodingOutputFunc output);
+
+/*
+ * Interfaces for encoding names and aliases.
+ */
+XMLPUBFUN int
+	xmlAddEncodingAlias		(const char *name,
+					 const char *alias);
+XMLPUBFUN int
+	xmlDelEncodingAlias		(const char *alias);
+XMLPUBFUN const char *
+	xmlGetEncodingAlias		(const char *alias);
+XMLPUBFUN void
+	xmlCleanupEncodingAliases	(void);
+XMLPUBFUN xmlCharEncoding
+	xmlParseCharEncoding		(const char *name);
+XMLPUBFUN const char *
+	xmlGetCharEncodingName		(xmlCharEncoding enc);
+
+/*
+ * Interfaces directly used by the parsers.
+ */
+XMLPUBFUN xmlCharEncoding
+	xmlDetectCharEncoding		(const unsigned char *in,
+					 int len);
+
+struct _xmlBuffer;
+XMLPUBFUN int
+	xmlCharEncOutFunc		(xmlCharEncodingHandler *handler,
+					 struct _xmlBuffer *out,
+					 struct _xmlBuffer *in);
+
+XMLPUBFUN int
+	xmlCharEncInFunc		(xmlCharEncodingHandler *handler,
+					 struct _xmlBuffer *out,
+					 struct _xmlBuffer *in);
+XML_DEPRECATED
+XMLPUBFUN int
+	xmlCharEncFirstLine		(xmlCharEncodingHandler *handler,
+					 struct _xmlBuffer *out,
+					 struct _xmlBuffer *in);
+XMLPUBFUN int
+	xmlCharEncCloseFunc		(xmlCharEncodingHandler *handler);
+
+/*
+ * Export a few useful functions
+ */
+#ifdef LIBXML_OUTPUT_ENABLED
+XMLPUBFUN int
+	UTF8Toisolat1			(unsigned char *out,
+					 int *outlen,
+					 const unsigned char *in,
+					 int *inlen);
+#endif /* LIBXML_OUTPUT_ENABLED */
+XMLPUBFUN int
+	isolat1ToUTF8			(unsigned char *out,
+					 int *outlen,
+					 const unsigned char *in,
+					 int *inlen);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_CHAR_ENCODING_H__ */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/entities.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/entities.h
new file mode 100644
index 00000000..f6793753
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/entities.h
@@ -0,0 +1,155 @@
+/*
+ * Summary: interface for the XML entities handling
+ * Description: this module provides some of the entity API needed
+ *              for the parser and applications.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_ENTITIES_H__
+#define __XML_ENTITIES_H__
+
+#include <libxml/xmlversion.h>
+#define XML_TREE_INTERNALS
+#include <libxml/tree.h>
+#undef XML_TREE_INTERNALS
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * The different valid entity types.
+ */
+typedef enum {
+    XML_INTERNAL_GENERAL_ENTITY = 1,
+    XML_EXTERNAL_GENERAL_PARSED_ENTITY = 2,
+    XML_EXTERNAL_GENERAL_UNPARSED_ENTITY = 3,
+    XML_INTERNAL_PARAMETER_ENTITY = 4,
+    XML_EXTERNAL_PARAMETER_ENTITY = 5,
+    XML_INTERNAL_PREDEFINED_ENTITY = 6
+} xmlEntityType;
+
+/*
+ * An unit of storage for an entity, contains the string, the value
+ * and the linkind data needed for the linking in the hash table.
+ */
+
+struct _xmlEntity {
+    void           *_private;	        /* application data */
+    xmlElementType          type;       /* XML_ENTITY_DECL, must be second ! */
+    const xmlChar          *name;	/* Entity name */
+    struct _xmlNode    *children;	/* First child link */
+    struct _xmlNode        *last;	/* Last child link */
+    struct _xmlDtd       *parent;	/* -> DTD */
+    struct _xmlNode        *next;	/* next sibling link  */
+    struct _xmlNode        *prev;	/* previous sibling link  */
+    struct _xmlDoc          *doc;       /* the containing document */
+
+    xmlChar                *orig;	/* content without ref substitution */
+    xmlChar             *content;	/* content or ndata if unparsed */
+    int                   length;	/* the content length */
+    xmlEntityType          etype;	/* The entity type */
+    const xmlChar    *ExternalID;	/* External identifier for PUBLIC */
+    const xmlChar      *SystemID;	/* URI for a SYSTEM or PUBLIC Entity */
+
+    struct _xmlEntity     *nexte;	/* unused */
+    const xmlChar           *URI;	/* the full URI as computed */
+    int                    owner;	/* does the entity own the childrens */
+    int                    flags;       /* various flags */
+    unsigned long   expandedSize;       /* expanded size */
+};
+
+/*
+ * All entities are stored in an hash table.
+ * There is 2 separate hash tables for global and parameter entities.
+ */
+
+typedef struct _xmlHashTable xmlEntitiesTable;
+typedef xmlEntitiesTable *xmlEntitiesTablePtr;
+
+/*
+ * External functions:
+ */
+
+#ifdef LIBXML_LEGACY_ENABLED
+XML_DEPRECATED
+XMLPUBFUN void
+		xmlInitializePredefinedEntities	(void);
+#endif /* LIBXML_LEGACY_ENABLED */
+
+XMLPUBFUN xmlEntityPtr
+			xmlNewEntity		(xmlDocPtr doc,
+						 const xmlChar *name,
+						 int type,
+						 const xmlChar *ExternalID,
+						 const xmlChar *SystemID,
+						 const xmlChar *content);
+XMLPUBFUN void
+			xmlFreeEntity		(xmlEntityPtr entity);
+XMLPUBFUN xmlEntityPtr
+			xmlAddDocEntity		(xmlDocPtr doc,
+						 const xmlChar *name,
+						 int type,
+						 const xmlChar *ExternalID,
+						 const xmlChar *SystemID,
+						 const xmlChar *content);
+XMLPUBFUN xmlEntityPtr
+			xmlAddDtdEntity		(xmlDocPtr doc,
+						 const xmlChar *name,
+						 int type,
+						 const xmlChar *ExternalID,
+						 const xmlChar *SystemID,
+						 const xmlChar *content);
+XMLPUBFUN xmlEntityPtr
+			xmlGetPredefinedEntity	(const xmlChar *name);
+XMLPUBFUN xmlEntityPtr
+			xmlGetDocEntity		(const xmlDoc *doc,
+						 const xmlChar *name);
+XMLPUBFUN xmlEntityPtr
+			xmlGetDtdEntity		(xmlDocPtr doc,
+						 const xmlChar *name);
+XMLPUBFUN xmlEntityPtr
+			xmlGetParameterEntity	(xmlDocPtr doc,
+						 const xmlChar *name);
+#ifdef LIBXML_LEGACY_ENABLED
+XML_DEPRECATED
+XMLPUBFUN const xmlChar *
+			xmlEncodeEntities	(xmlDocPtr doc,
+						 const xmlChar *input);
+#endif /* LIBXML_LEGACY_ENABLED */
+XMLPUBFUN xmlChar *
+			xmlEncodeEntitiesReentrant(xmlDocPtr doc,
+						 const xmlChar *input);
+XMLPUBFUN xmlChar *
+			xmlEncodeSpecialChars	(const xmlDoc *doc,
+						 const xmlChar *input);
+XMLPUBFUN xmlEntitiesTablePtr
+			xmlCreateEntitiesTable	(void);
+#ifdef LIBXML_TREE_ENABLED
+XMLPUBFUN xmlEntitiesTablePtr
+			xmlCopyEntitiesTable	(xmlEntitiesTablePtr table);
+#endif /* LIBXML_TREE_ENABLED */
+XMLPUBFUN void
+			xmlFreeEntitiesTable	(xmlEntitiesTablePtr table);
+#ifdef LIBXML_OUTPUT_ENABLED
+XMLPUBFUN void
+			xmlDumpEntitiesTable	(xmlBufferPtr buf,
+						 xmlEntitiesTablePtr table);
+XMLPUBFUN void
+			xmlDumpEntityDecl	(xmlBufferPtr buf,
+						 xmlEntityPtr ent);
+#endif /* LIBXML_OUTPUT_ENABLED */
+#ifdef LIBXML_LEGACY_ENABLED
+XMLPUBFUN void
+			xmlCleanupPredefinedEntities(void);
+#endif /* LIBXML_LEGACY_ENABLED */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+# endif /* __XML_ENTITIES_H__ */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/globals.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/globals.h
new file mode 100644
index 00000000..92f41312
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/globals.h
@@ -0,0 +1,41 @@
+/*
+ * Summary: interface for all global variables of the library
+ * Description: Deprecated, don't use
+ *
+ * Copy: See Copyright for the status of this software.
+ */
+
+#ifndef __XML_GLOBALS_H
+#define __XML_GLOBALS_H
+
+#include <libxml/xmlversion.h>
+
+/*
+ * This file was required to access global variables until version v2.12.0.
+ *
+ * These includes are for backward compatibility.
+ */
+#include <libxml/HTMLparser.h>
+#include <libxml/parser.h>
+#include <libxml/xmlerror.h>
+#include <libxml/xmlIO.h>
+#include <libxml/xmlsave.h>
+#include <libxml/threads.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct _xmlGlobalState xmlGlobalState;
+typedef xmlGlobalState *xmlGlobalStatePtr;
+
+XML_DEPRECATED XMLPUBFUN void
+xmlInitializeGlobalState(xmlGlobalStatePtr gs);
+XML_DEPRECATED XMLPUBFUN
+xmlGlobalStatePtr xmlGetGlobalState(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_GLOBALS_H */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/hash.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/hash.h
new file mode 100644
index 00000000..f4af09ee
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/hash.h
@@ -0,0 +1,232 @@
+/*
+ * Summary: Chained hash tables
+ * Description: This module implements the hash table support used in
+ *		various places in the library.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Bjorn Reese <bjorn.reese@systematic.dk>
+ */
+
+#ifndef __XML_HASH_H__
+#define __XML_HASH_H__
+
+#include <libxml/xmlversion.h>
+#include <libxml/dict.h>
+#include <libxml/xmlstring.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * The hash table.
+ */
+typedef struct _xmlHashTable xmlHashTable;
+typedef xmlHashTable *xmlHashTablePtr;
+
+/*
+ * Recent version of gcc produce a warning when a function pointer is assigned
+ * to an object pointer, or vice versa.  The following macro is a dirty hack
+ * to allow suppression of the warning.  If your architecture has function
+ * pointers which are a different size than a void pointer, there may be some
+ * serious trouble within the library.
+ */
+/**
+ * XML_CAST_FPTR:
+ * @fptr:  pointer to a function
+ *
+ * Macro to do a casting from an object pointer to a
+ * function pointer without encountering a warning from
+ * gcc
+ *
+ * #define XML_CAST_FPTR(fptr) (*(void **)(&fptr))
+ * This macro violated ISO C aliasing rules (gcc4 on s390 broke)
+ * so it is disabled now
+ */
+
+#define XML_CAST_FPTR(fptr) fptr
+
+/*
+ * function types:
+ */
+/**
+ * xmlHashDeallocator:
+ * @payload:  the data in the hash
+ * @name:  the name associated
+ *
+ * Callback to free data from a hash.
+ */
+typedef void (*xmlHashDeallocator)(void *payload, const xmlChar *name);
+/**
+ * xmlHashCopier:
+ * @payload:  the data in the hash
+ * @name:  the name associated
+ *
+ * Callback to copy data from a hash.
+ *
+ * Returns a copy of the data or NULL in case of error.
+ */
+typedef void *(*xmlHashCopier)(void *payload, const xmlChar *name);
+/**
+ * xmlHashScanner:
+ * @payload:  the data in the hash
+ * @data:  extra scanner data
+ * @name:  the name associated
+ *
+ * Callback when scanning data in a hash with the simple scanner.
+ */
+typedef void (*xmlHashScanner)(void *payload, void *data, const xmlChar *name);
+/**
+ * xmlHashScannerFull:
+ * @payload:  the data in the hash
+ * @data:  extra scanner data
+ * @name:  the name associated
+ * @name2:  the second name associated
+ * @name3:  the third name associated
+ *
+ * Callback when scanning data in a hash with the full scanner.
+ */
+typedef void (*xmlHashScannerFull)(void *payload, void *data,
+				   const xmlChar *name, const xmlChar *name2,
+				   const xmlChar *name3);
+
+/*
+ * Constructor and destructor.
+ */
+XMLPUBFUN xmlHashTablePtr
+		xmlHashCreate		(int size);
+XMLPUBFUN xmlHashTablePtr
+		xmlHashCreateDict	(int size,
+					 xmlDictPtr dict);
+XMLPUBFUN void
+		xmlHashFree		(xmlHashTablePtr hash,
+					 xmlHashDeallocator dealloc);
+XMLPUBFUN void
+		xmlHashDefaultDeallocator(void *entry,
+					 const xmlChar *name);
+
+/*
+ * Add a new entry to the hash table.
+ */
+XMLPUBFUN int
+		xmlHashAddEntry		(xmlHashTablePtr hash,
+		                         const xmlChar *name,
+		                         void *userdata);
+XMLPUBFUN int
+		xmlHashUpdateEntry	(xmlHashTablePtr hash,
+		                         const xmlChar *name,
+		                         void *userdata,
+					 xmlHashDeallocator dealloc);
+XMLPUBFUN int
+		xmlHashAddEntry2	(xmlHashTablePtr hash,
+		                         const xmlChar *name,
+		                         const xmlChar *name2,
+		                         void *userdata);
+XMLPUBFUN int
+		xmlHashUpdateEntry2	(xmlHashTablePtr hash,
+		                         const xmlChar *name,
+		                         const xmlChar *name2,
+		                         void *userdata,
+					 xmlHashDeallocator dealloc);
+XMLPUBFUN int
+		xmlHashAddEntry3	(xmlHashTablePtr hash,
+		                         const xmlChar *name,
+		                         const xmlChar *name2,
+		                         const xmlChar *name3,
+		                         void *userdata);
+XMLPUBFUN int
+		xmlHashUpdateEntry3	(xmlHashTablePtr hash,
+		                         const xmlChar *name,
+		                         const xmlChar *name2,
+		                         const xmlChar *name3,
+		                         void *userdata,
+					 xmlHashDeallocator dealloc);
+
+/*
+ * Remove an entry from the hash table.
+ */
+XMLPUBFUN int
+		xmlHashRemoveEntry	(xmlHashTablePtr hash,
+					 const xmlChar *name,
+					 xmlHashDeallocator dealloc);
+XMLPUBFUN int
+		xmlHashRemoveEntry2	(xmlHashTablePtr hash,
+					 const xmlChar *name,
+					 const xmlChar *name2,
+					 xmlHashDeallocator dealloc);
+XMLPUBFUN int 
+		xmlHashRemoveEntry3	(xmlHashTablePtr hash,
+					 const xmlChar *name,
+					 const xmlChar *name2,
+					 const xmlChar *name3,
+					 xmlHashDeallocator dealloc);
+
+/*
+ * Retrieve the payload.
+ */
+XMLPUBFUN void *
+		xmlHashLookup		(xmlHashTablePtr hash,
+					 const xmlChar *name);
+XMLPUBFUN void *
+		xmlHashLookup2		(xmlHashTablePtr hash,
+					 const xmlChar *name,
+					 const xmlChar *name2);
+XMLPUBFUN void *
+		xmlHashLookup3		(xmlHashTablePtr hash,
+					 const xmlChar *name,
+					 const xmlChar *name2,
+					 const xmlChar *name3);
+XMLPUBFUN void *
+		xmlHashQLookup		(xmlHashTablePtr hash,
+					 const xmlChar *prefix,
+					 const xmlChar *name);
+XMLPUBFUN void *
+		xmlHashQLookup2		(xmlHashTablePtr hash,
+					 const xmlChar *prefix,
+					 const xmlChar *name,
+					 const xmlChar *prefix2,
+					 const xmlChar *name2);
+XMLPUBFUN void *
+		xmlHashQLookup3		(xmlHashTablePtr hash,
+					 const xmlChar *prefix,
+					 const xmlChar *name,
+					 const xmlChar *prefix2,
+					 const xmlChar *name2,
+					 const xmlChar *prefix3,
+					 const xmlChar *name3);
+
+/*
+ * Helpers.
+ */
+XMLPUBFUN xmlHashTablePtr
+		xmlHashCopy		(xmlHashTablePtr hash,
+					 xmlHashCopier copy);
+XMLPUBFUN int
+		xmlHashSize		(xmlHashTablePtr hash);
+XMLPUBFUN void
+		xmlHashScan		(xmlHashTablePtr hash,
+					 xmlHashScanner scan,
+					 void *data);
+XMLPUBFUN void
+		xmlHashScan3		(xmlHashTablePtr hash,
+					 const xmlChar *name,
+					 const xmlChar *name2,
+					 const xmlChar *name3,
+					 xmlHashScanner scan,
+					 void *data);
+XMLPUBFUN void
+		xmlHashScanFull		(xmlHashTablePtr hash,
+					 xmlHashScannerFull scan,
+					 void *data);
+XMLPUBFUN void
+		xmlHashScanFull3	(xmlHashTablePtr hash,
+					 const xmlChar *name,
+					 const xmlChar *name2,
+					 const xmlChar *name3,
+					 xmlHashScannerFull scan,
+					 void *data);
+#ifdef __cplusplus
+}
+#endif
+#endif /* ! __XML_HASH_H__ */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/list.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/list.h
new file mode 100644
index 00000000..5eab8f59
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/list.h
@@ -0,0 +1,137 @@
+/*
+ * Summary: lists interfaces
+ * Description: this module implement the list support used in
+ * various place in the library.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Gary Pennington <Gary.Pennington@uk.sun.com>
+ */
+
+#ifndef __XML_LINK_INCLUDE__
+#define __XML_LINK_INCLUDE__
+
+#include <libxml/xmlversion.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct _xmlLink xmlLink;
+typedef xmlLink *xmlLinkPtr;
+
+typedef struct _xmlList xmlList;
+typedef xmlList *xmlListPtr;
+
+/**
+ * xmlListDeallocator:
+ * @lk:  the data to deallocate
+ *
+ * Callback function used to free data from a list.
+ */
+typedef void (*xmlListDeallocator) (xmlLinkPtr lk);
+/**
+ * xmlListDataCompare:
+ * @data0: the first data
+ * @data1: the second data
+ *
+ * Callback function used to compare 2 data.
+ *
+ * Returns 0 is equality, -1 or 1 otherwise depending on the ordering.
+ */
+typedef int  (*xmlListDataCompare) (const void *data0, const void *data1);
+/**
+ * xmlListWalker:
+ * @data: the data found in the list
+ * @user: extra user provided data to the walker
+ *
+ * Callback function used when walking a list with xmlListWalk().
+ *
+ * Returns 0 to stop walking the list, 1 otherwise.
+ */
+typedef int (*xmlListWalker) (const void *data, void *user);
+
+/* Creation/Deletion */
+XMLPUBFUN xmlListPtr
+		xmlListCreate		(xmlListDeallocator deallocator,
+	                                 xmlListDataCompare compare);
+XMLPUBFUN void
+		xmlListDelete		(xmlListPtr l);
+
+/* Basic Operators */
+XMLPUBFUN void *
+		xmlListSearch		(xmlListPtr l,
+					 void *data);
+XMLPUBFUN void *
+		xmlListReverseSearch	(xmlListPtr l,
+					 void *data);
+XMLPUBFUN int
+		xmlListInsert		(xmlListPtr l,
+					 void *data) ;
+XMLPUBFUN int
+		xmlListAppend		(xmlListPtr l,
+					 void *data) ;
+XMLPUBFUN int
+		xmlListRemoveFirst	(xmlListPtr l,
+					 void *data);
+XMLPUBFUN int
+		xmlListRemoveLast	(xmlListPtr l,
+					 void *data);
+XMLPUBFUN int
+		xmlListRemoveAll	(xmlListPtr l,
+					 void *data);
+XMLPUBFUN void
+		xmlListClear		(xmlListPtr l);
+XMLPUBFUN int
+		xmlListEmpty		(xmlListPtr l);
+XMLPUBFUN xmlLinkPtr
+		xmlListFront		(xmlListPtr l);
+XMLPUBFUN xmlLinkPtr
+		xmlListEnd		(xmlListPtr l);
+XMLPUBFUN int
+		xmlListSize		(xmlListPtr l);
+
+XMLPUBFUN void
+		xmlListPopFront		(xmlListPtr l);
+XMLPUBFUN void
+		xmlListPopBack		(xmlListPtr l);
+XMLPUBFUN int
+		xmlListPushFront	(xmlListPtr l,
+					 void *data);
+XMLPUBFUN int
+		xmlListPushBack		(xmlListPtr l,
+					 void *data);
+
+/* Advanced Operators */
+XMLPUBFUN void
+		xmlListReverse		(xmlListPtr l);
+XMLPUBFUN void
+		xmlListSort		(xmlListPtr l);
+XMLPUBFUN void
+		xmlListWalk		(xmlListPtr l,
+					 xmlListWalker walker,
+					 void *user);
+XMLPUBFUN void
+		xmlListReverseWalk	(xmlListPtr l,
+					 xmlListWalker walker,
+					 void *user);
+XMLPUBFUN void
+		xmlListMerge		(xmlListPtr l1,
+					 xmlListPtr l2);
+XMLPUBFUN xmlListPtr
+		xmlListDup		(const xmlListPtr old);
+XMLPUBFUN int
+		xmlListCopy		(xmlListPtr cur,
+					 const xmlListPtr old);
+/* Link operators */
+XMLPUBFUN void *
+		xmlLinkGetData          (xmlLinkPtr lk);
+
+/* xmlListUnique() */
+/* xmlListSwap */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_LINK_INCLUDE__ */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/nanoftp.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/nanoftp.h
new file mode 100644
index 00000000..ed3ac4f1
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/nanoftp.h
@@ -0,0 +1,186 @@
+/*
+ * Summary: minimal FTP implementation
+ * Description: minimal FTP implementation allowing to fetch resources
+ *              like external subset. This module is DEPRECATED, do not
+ *              use any of its functions.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __NANO_FTP_H__
+#define __NANO_FTP_H__
+
+#include <libxml/xmlversion.h>
+
+#if defined(LIBXML_FTP_ENABLED)
+
+/* Needed for portability to Windows 64 bits */
+#if defined(_WIN32)
+#include <winsock2.h>
+#else
+/**
+ * SOCKET:
+ *
+ * macro used to provide portability of code to windows sockets
+ */
+#define SOCKET int
+/**
+ * INVALID_SOCKET:
+ *
+ * macro used to provide portability of code to windows sockets
+ * the value to be used when the socket is not valid
+ */
+#undef  INVALID_SOCKET
+#define INVALID_SOCKET (-1)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * ftpListCallback:
+ * @userData:  user provided data for the callback
+ * @filename:  the file name (including "->" when links are shown)
+ * @attrib:  the attribute string
+ * @owner:  the owner string
+ * @group:  the group string
+ * @size:  the file size
+ * @links:  the link count
+ * @year:  the year
+ * @month:  the month
+ * @day:  the day
+ * @hour:  the hour
+ * @minute:  the minute
+ *
+ * A callback for the xmlNanoFTPList command.
+ * Note that only one of year and day:minute are specified.
+ */
+typedef void (*ftpListCallback) (void *userData,
+	                         const char *filename, const char *attrib,
+	                         const char *owner, const char *group,
+				 unsigned long size, int links, int year,
+				 const char *month, int day, int hour,
+				 int minute);
+/**
+ * ftpDataCallback:
+ * @userData: the user provided context
+ * @data: the data received
+ * @len: its size in bytes
+ *
+ * A callback for the xmlNanoFTPGet command.
+ */
+typedef void (*ftpDataCallback) (void *userData,
+				 const char *data,
+				 int len);
+
+/*
+ * Init
+ */
+XML_DEPRECATED
+XMLPUBFUN void
+	xmlNanoFTPInit		(void);
+XML_DEPRECATED
+XMLPUBFUN void
+	xmlNanoFTPCleanup	(void);
+
+/*
+ * Creating/freeing contexts.
+ */
+XML_DEPRECATED
+XMLPUBFUN void *
+	xmlNanoFTPNewCtxt	(const char *URL);
+XML_DEPRECATED
+XMLPUBFUN void
+	xmlNanoFTPFreeCtxt	(void * ctx);
+XML_DEPRECATED
+XMLPUBFUN void *
+	xmlNanoFTPConnectTo	(const char *server,
+				 int port);
+/*
+ * Opening/closing session connections.
+ */
+XML_DEPRECATED
+XMLPUBFUN void *
+	xmlNanoFTPOpen		(const char *URL);
+XML_DEPRECATED
+XMLPUBFUN int
+	xmlNanoFTPConnect	(void *ctx);
+XML_DEPRECATED
+XMLPUBFUN int
+	xmlNanoFTPClose		(void *ctx);
+XML_DEPRECATED
+XMLPUBFUN int
+	xmlNanoFTPQuit		(void *ctx);
+XML_DEPRECATED
+XMLPUBFUN void
+	xmlNanoFTPScanProxy	(const char *URL);
+XML_DEPRECATED
+XMLPUBFUN void
+	xmlNanoFTPProxy		(const char *host,
+				 int port,
+				 const char *user,
+				 const char *passwd,
+				 int type);
+XML_DEPRECATED
+XMLPUBFUN int
+	xmlNanoFTPUpdateURL	(void *ctx,
+				 const char *URL);
+
+/*
+ * Rather internal commands.
+ */
+XML_DEPRECATED
+XMLPUBFUN int
+	xmlNanoFTPGetResponse	(void *ctx);
+XML_DEPRECATED
+XMLPUBFUN int
+	xmlNanoFTPCheckResponse	(void *ctx);
+
+/*
+ * CD/DIR/GET handlers.
+ */
+XML_DEPRECATED
+XMLPUBFUN int
+	xmlNanoFTPCwd		(void *ctx,
+				 const char *directory);
+XML_DEPRECATED
+XMLPUBFUN int
+	xmlNanoFTPDele		(void *ctx,
+				 const char *file);
+
+XML_DEPRECATED
+XMLPUBFUN SOCKET
+	xmlNanoFTPGetConnection	(void *ctx);
+XML_DEPRECATED
+XMLPUBFUN int
+	xmlNanoFTPCloseConnection(void *ctx);
+XML_DEPRECATED
+XMLPUBFUN int
+	xmlNanoFTPList		(void *ctx,
+				 ftpListCallback callback,
+				 void *userData,
+				 const char *filename);
+XML_DEPRECATED
+XMLPUBFUN SOCKET
+	xmlNanoFTPGetSocket	(void *ctx,
+				 const char *filename);
+XML_DEPRECATED
+XMLPUBFUN int
+	xmlNanoFTPGet		(void *ctx,
+				 ftpDataCallback callback,
+				 void *userData,
+				 const char *filename);
+XML_DEPRECATED
+XMLPUBFUN int
+	xmlNanoFTPRead		(void *ctx,
+				 void *dest,
+				 int len);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* defined(LIBXML_FTP_ENABLED) || defined(LIBXML_LEGACY_ENABLED) */
+#endif /* __NANO_FTP_H__ */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/nanohttp.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/nanohttp.h
new file mode 100644
index 00000000..3b5e037f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/nanohttp.h
@@ -0,0 +1,81 @@
+/*
+ * Summary: minimal HTTP implementation
+ * Description: minimal HTTP implementation allowing to fetch resources
+ *              like external subset.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __NANO_HTTP_H__
+#define __NANO_HTTP_H__
+
+#include <libxml/xmlversion.h>
+
+#ifdef LIBXML_HTTP_ENABLED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+XMLPUBFUN void
+	xmlNanoHTTPInit		(void);
+XMLPUBFUN void
+	xmlNanoHTTPCleanup	(void);
+XMLPUBFUN void
+	xmlNanoHTTPScanProxy	(const char *URL);
+XMLPUBFUN int
+	xmlNanoHTTPFetch	(const char *URL,
+				 const char *filename,
+				 char **contentType);
+XMLPUBFUN void *
+	xmlNanoHTTPMethod	(const char *URL,
+				 const char *method,
+				 const char *input,
+				 char **contentType,
+				 const char *headers,
+				 int   ilen);
+XMLPUBFUN void *
+	xmlNanoHTTPMethodRedir	(const char *URL,
+				 const char *method,
+				 const char *input,
+				 char **contentType,
+				 char **redir,
+				 const char *headers,
+				 int   ilen);
+XMLPUBFUN void *
+	xmlNanoHTTPOpen		(const char *URL,
+				 char **contentType);
+XMLPUBFUN void *
+	xmlNanoHTTPOpenRedir	(const char *URL,
+				 char **contentType,
+				 char **redir);
+XMLPUBFUN int
+	xmlNanoHTTPReturnCode	(void *ctx);
+XMLPUBFUN const char *
+	xmlNanoHTTPAuthHeader	(void *ctx);
+XMLPUBFUN const char *
+	xmlNanoHTTPRedir	(void *ctx);
+XMLPUBFUN int
+	xmlNanoHTTPContentLength( void * ctx );
+XMLPUBFUN const char *
+	xmlNanoHTTPEncoding	(void *ctx);
+XMLPUBFUN const char *
+	xmlNanoHTTPMimeType	(void *ctx);
+XMLPUBFUN int
+	xmlNanoHTTPRead		(void *ctx,
+				 void *dest,
+				 int len);
+#ifdef LIBXML_OUTPUT_ENABLED
+XMLPUBFUN int
+	xmlNanoHTTPSave		(void *ctxt,
+				 const char *filename);
+#endif /* LIBXML_OUTPUT_ENABLED */
+XMLPUBFUN void
+	xmlNanoHTTPClose	(void *ctx);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBXML_HTTP_ENABLED */
+#endif /* __NANO_HTTP_H__ */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/parser.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/parser.h
new file mode 100644
index 00000000..fd6d5207
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/parser.h
@@ -0,0 +1,1384 @@
+/*
+ * Summary: the core parser module
+ * Description: Interfaces, constants and types related to the XML parser
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_PARSER_H__
+#define __XML_PARSER_H__
+
+#include <libxml/xmlversion.h>
+#define XML_TREE_INTERNALS
+#include <libxml/tree.h>
+#undef XML_TREE_INTERNALS
+#include <libxml/dict.h>
+#include <libxml/hash.h>
+#include <libxml/valid.h>
+#include <libxml/entities.h>
+#include <libxml/xmlerror.h>
+#include <libxml/xmlstring.h>
+#include <libxml/xmlmemory.h>
+#include <libxml/encoding.h>
+#include <libxml/xmlIO.h>
+/* for compatibility */
+#include <libxml/SAX2.h>
+#include <libxml/threads.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * XML_DEFAULT_VERSION:
+ *
+ * The default version of XML used: 1.0
+ */
+#define XML_DEFAULT_VERSION	"1.0"
+
+/**
+ * xmlParserInput:
+ *
+ * An xmlParserInput is an input flow for the XML processor.
+ * Each entity parsed is associated an xmlParserInput (except the
+ * few predefined ones). This is the case both for internal entities
+ * - in which case the flow is already completely in memory - or
+ * external entities - in which case we use the buf structure for
+ * progressive reading and I18N conversions to the internal UTF-8 format.
+ */
+
+/**
+ * xmlParserInputDeallocate:
+ * @str:  the string to deallocate
+ *
+ * Callback for freeing some parser input allocations.
+ */
+typedef void (* xmlParserInputDeallocate)(xmlChar *str);
+
+struct _xmlParserInput {
+    /* Input buffer */
+    xmlParserInputBufferPtr buf;      /* UTF-8 encoded buffer */
+
+    const char *filename;             /* The file analyzed, if any */
+    const char *directory;            /* the directory/base of the file */
+    const xmlChar *base;              /* Base of the array to parse */
+    const xmlChar *cur;               /* Current char being parsed */
+    const xmlChar *end;               /* end of the array to parse */
+    int length;                       /* length if known */
+    int line;                         /* Current line */
+    int col;                          /* Current column */
+    unsigned long consumed;           /* How many xmlChars already consumed */
+    xmlParserInputDeallocate free;    /* function to deallocate the base */
+    const xmlChar *encoding;          /* unused */
+    const xmlChar *version;           /* the version string for entity */
+    int flags;                        /* Flags */
+    int id;                           /* an unique identifier for the entity */
+    unsigned long parentConsumed;     /* consumed bytes from parents */
+    xmlEntityPtr entity;              /* entity, if any */
+};
+
+/**
+ * xmlParserNodeInfo:
+ *
+ * The parser can be asked to collect Node information, i.e. at what
+ * place in the file they were detected.
+ * NOTE: This is off by default and not very well tested.
+ */
+typedef struct _xmlParserNodeInfo xmlParserNodeInfo;
+typedef xmlParserNodeInfo *xmlParserNodeInfoPtr;
+
+struct _xmlParserNodeInfo {
+  const struct _xmlNode* node;
+  /* Position & line # that text that created the node begins & ends on */
+  unsigned long begin_pos;
+  unsigned long begin_line;
+  unsigned long end_pos;
+  unsigned long end_line;
+};
+
+typedef struct _xmlParserNodeInfoSeq xmlParserNodeInfoSeq;
+typedef xmlParserNodeInfoSeq *xmlParserNodeInfoSeqPtr;
+struct _xmlParserNodeInfoSeq {
+  unsigned long maximum;
+  unsigned long length;
+  xmlParserNodeInfo* buffer;
+};
+
+/**
+ * xmlParserInputState:
+ *
+ * The parser is now working also as a state based parser.
+ * The recursive one use the state info for entities processing.
+ */
+typedef enum {
+    XML_PARSER_EOF = -1,	/* nothing is to be parsed */
+    XML_PARSER_START = 0,	/* nothing has been parsed */
+    XML_PARSER_MISC,		/* Misc* before int subset */
+    XML_PARSER_PI,		/* Within a processing instruction */
+    XML_PARSER_DTD,		/* within some DTD content */
+    XML_PARSER_PROLOG,		/* Misc* after internal subset */
+    XML_PARSER_COMMENT,		/* within a comment */
+    XML_PARSER_START_TAG,	/* within a start tag */
+    XML_PARSER_CONTENT,		/* within the content */
+    XML_PARSER_CDATA_SECTION,	/* within a CDATA section */
+    XML_PARSER_END_TAG,		/* within a closing tag */
+    XML_PARSER_ENTITY_DECL,	/* within an entity declaration */
+    XML_PARSER_ENTITY_VALUE,	/* within an entity value in a decl */
+    XML_PARSER_ATTRIBUTE_VALUE,	/* within an attribute value */
+    XML_PARSER_SYSTEM_LITERAL,	/* within a SYSTEM value */
+    XML_PARSER_EPILOG,		/* the Misc* after the last end tag */
+    XML_PARSER_IGNORE,		/* within an IGNORED section */
+    XML_PARSER_PUBLIC_LITERAL,	/* within a PUBLIC value */
+    XML_PARSER_XML_DECL         /* before XML decl (but after BOM) */
+} xmlParserInputState;
+
+/**
+ * XML_DETECT_IDS:
+ *
+ * Bit in the loadsubset context field to tell to do ID/REFs lookups.
+ * Use it to initialize xmlLoadExtDtdDefaultValue.
+ */
+#define XML_DETECT_IDS		2
+
+/**
+ * XML_COMPLETE_ATTRS:
+ *
+ * Bit in the loadsubset context field to tell to do complete the
+ * elements attributes lists with the ones defaulted from the DTDs.
+ * Use it to initialize xmlLoadExtDtdDefaultValue.
+ */
+#define XML_COMPLETE_ATTRS	4
+
+/**
+ * XML_SKIP_IDS:
+ *
+ * Bit in the loadsubset context field to tell to not do ID/REFs registration.
+ * Used to initialize xmlLoadExtDtdDefaultValue in some special cases.
+ */
+#define XML_SKIP_IDS		8
+
+/**
+ * xmlParserMode:
+ *
+ * A parser can operate in various modes
+ */
+typedef enum {
+    XML_PARSE_UNKNOWN = 0,
+    XML_PARSE_DOM = 1,
+    XML_PARSE_SAX = 2,
+    XML_PARSE_PUSH_DOM = 3,
+    XML_PARSE_PUSH_SAX = 4,
+    XML_PARSE_READER = 5
+} xmlParserMode;
+
+typedef struct _xmlStartTag xmlStartTag;
+typedef struct _xmlParserNsData xmlParserNsData;
+typedef struct _xmlAttrHashBucket xmlAttrHashBucket;
+
+/**
+ * xmlParserCtxt:
+ *
+ * The parser context.
+ * NOTE This doesn't completely define the parser state, the (current ?)
+ *      design of the parser uses recursive function calls since this allow
+ *      and easy mapping from the production rules of the specification
+ *      to the actual code. The drawback is that the actual function call
+ *      also reflect the parser state. However most of the parsing routines
+ *      takes as the only argument the parser context pointer, so migrating
+ *      to a state based parser for progressive parsing shouldn't be too hard.
+ */
+struct _xmlParserCtxt {
+    struct _xmlSAXHandler *sax;       /* The SAX handler */
+    void            *userData;        /* For SAX interface only, used by DOM build */
+    xmlDocPtr           myDoc;        /* the document being built */
+    int            wellFormed;        /* is the document well formed */
+    int       replaceEntities;        /* shall we replace entities ? */
+    const xmlChar    *version;        /* the XML version string */
+    const xmlChar   *encoding;        /* the declared encoding, if any */
+    int            standalone;        /* standalone document */
+    int                  html;        /* an HTML(1) document
+                                       * 3 is HTML after <head>
+                                       * 10 is HTML after <body>
+                                       */
+
+    /* Input stream stack */
+    xmlParserInputPtr  input;         /* Current input stream */
+    int                inputNr;       /* Number of current input streams */
+    int                inputMax;      /* Max number of input streams */
+    xmlParserInputPtr *inputTab;      /* stack of inputs */
+
+    /* Node analysis stack only used for DOM building */
+    xmlNodePtr         node;          /* Current parsed Node */
+    int                nodeNr;        /* Depth of the parsing stack */
+    int                nodeMax;       /* Max depth of the parsing stack */
+    xmlNodePtr        *nodeTab;       /* array of nodes */
+
+    int record_info;                  /* Whether node info should be kept */
+    xmlParserNodeInfoSeq node_seq;    /* info about each node parsed */
+
+    int errNo;                        /* error code */
+
+    int     hasExternalSubset;        /* reference and external subset */
+    int             hasPErefs;        /* the internal subset has PE refs */
+    int              external;        /* are we parsing an external entity */
+
+    int                 valid;        /* is the document valid */
+    int              validate;        /* shall we try to validate ? */
+    xmlValidCtxt        vctxt;        /* The validity context */
+
+    xmlParserInputState instate;      /* current type of input */
+    int                 token;        /* next char look-ahead */
+
+    char           *directory;        /* the data directory */
+
+    /* Node name stack */
+    const xmlChar     *name;          /* Current parsed Node */
+    int                nameNr;        /* Depth of the parsing stack */
+    int                nameMax;       /* Max depth of the parsing stack */
+    const xmlChar *   *nameTab;       /* array of nodes */
+
+    long               nbChars;       /* unused */
+    long            checkIndex;       /* used by progressive parsing lookup */
+    int             keepBlanks;       /* ugly but ... */
+    int             disableSAX;       /* SAX callbacks are disabled */
+    int               inSubset;       /* Parsing is in int 1/ext 2 subset */
+    const xmlChar *    intSubName;    /* name of subset */
+    xmlChar *          extSubURI;     /* URI of external subset */
+    xmlChar *          extSubSystem;  /* SYSTEM ID of external subset */
+
+    /* xml:space values */
+    int *              space;         /* Should the parser preserve spaces */
+    int                spaceNr;       /* Depth of the parsing stack */
+    int                spaceMax;      /* Max depth of the parsing stack */
+    int *              spaceTab;      /* array of space infos */
+
+    int                depth;         /* to prevent entity substitution loops */
+    xmlParserInputPtr  entity;        /* used to check entities boundaries */
+    int                charset;       /* unused */
+    int                nodelen;       /* Those two fields are there to */
+    int                nodemem;       /* Speed up large node parsing */
+    int                pedantic;      /* signal pedantic warnings */
+    void              *_private;      /* For user data, libxml won't touch it */
+
+    int                loadsubset;    /* should the external subset be loaded */
+    int                linenumbers;   /* set line number in element content */
+    void              *catalogs;      /* document's own catalog */
+    int                recovery;      /* run in recovery mode */
+    int                progressive;   /* is this a progressive parsing */
+    xmlDictPtr         dict;          /* dictionary for the parser */
+    const xmlChar *   *atts;          /* array for the attributes callbacks */
+    int                maxatts;       /* the size of the array */
+    int                docdict;       /* use strings from dict to build tree */
+
+    /*
+     * pre-interned strings
+     */
+    const xmlChar *str_xml;
+    const xmlChar *str_xmlns;
+    const xmlChar *str_xml_ns;
+
+    /*
+     * Everything below is used only by the new SAX mode
+     */
+    int                sax2;          /* operating in the new SAX mode */
+    int                nsNr;          /* the number of inherited namespaces */
+    int                nsMax;         /* the size of the arrays */
+    const xmlChar *   *nsTab;         /* the array of prefix/namespace name */
+    unsigned          *attallocs;     /* which attribute were allocated */
+    xmlStartTag       *pushTab;       /* array of data for push */
+    xmlHashTablePtr    attsDefault;   /* defaulted attributes if any */
+    xmlHashTablePtr    attsSpecial;   /* non-CDATA attributes if any */
+    int                nsWellFormed;  /* is the document XML Namespace okay */
+    int                options;       /* Extra options */
+
+    /*
+     * Those fields are needed only for streaming parsing so far
+     */
+    int               dictNames;    /* Use dictionary names for the tree */
+    int               freeElemsNr;  /* number of freed element nodes */
+    xmlNodePtr        freeElems;    /* List of freed element nodes */
+    int               freeAttrsNr;  /* number of freed attributes nodes */
+    xmlAttrPtr        freeAttrs;    /* List of freed attributes nodes */
+
+    /*
+     * the complete error information for the last error.
+     */
+    xmlError          lastError;
+    xmlParserMode     parseMode;    /* the parser mode */
+    unsigned long    nbentities;    /* unused */
+    unsigned long  sizeentities;    /* size of parsed entities */
+
+    /* for use by HTML non-recursive parser */
+    xmlParserNodeInfo *nodeInfo;      /* Current NodeInfo */
+    int                nodeInfoNr;    /* Depth of the parsing stack */
+    int                nodeInfoMax;   /* Max depth of the parsing stack */
+    xmlParserNodeInfo *nodeInfoTab;   /* array of nodeInfos */
+
+    int                input_id;      /* we need to label inputs */
+    unsigned long      sizeentcopy;   /* volume of entity copy */
+
+    int           endCheckState;    /* quote state for push parser */
+    unsigned short     nbErrors;    /* number of errors */
+    unsigned short   nbWarnings;    /* number of warnings */
+    unsigned            maxAmpl;    /* maximum amplification factor */
+
+    xmlParserNsData       *nsdb;    /* namespace database */
+    unsigned        attrHashMax;    /* allocated size */
+    xmlAttrHashBucket *attrHash;    /* atttribute hash table */
+};
+
+/**
+ * xmlSAXLocator:
+ *
+ * A SAX Locator.
+ */
+struct _xmlSAXLocator {
+    const xmlChar *(*getPublicId)(void *ctx);
+    const xmlChar *(*getSystemId)(void *ctx);
+    int (*getLineNumber)(void *ctx);
+    int (*getColumnNumber)(void *ctx);
+};
+
+/**
+ * xmlSAXHandler:
+ *
+ * A SAX handler is bunch of callbacks called by the parser when processing
+ * of the input generate data or structure information.
+ */
+
+/**
+ * resolveEntitySAXFunc:
+ * @ctx:  the user data (XML parser context)
+ * @publicId: The public ID of the entity
+ * @systemId: The system ID of the entity
+ *
+ * Callback:
+ * The entity loader, to control the loading of external entities,
+ * the application can either:
+ *    - override this resolveEntity() callback in the SAX block
+ *    - or better use the xmlSetExternalEntityLoader() function to
+ *      set up it's own entity resolution routine
+ *
+ * Returns the xmlParserInputPtr if inlined or NULL for DOM behaviour.
+ */
+typedef xmlParserInputPtr (*resolveEntitySAXFunc) (void *ctx,
+				const xmlChar *publicId,
+				const xmlChar *systemId);
+/**
+ * internalSubsetSAXFunc:
+ * @ctx:  the user data (XML parser context)
+ * @name:  the root element name
+ * @ExternalID:  the external ID
+ * @SystemID:  the SYSTEM ID (e.g. filename or URL)
+ *
+ * Callback on internal subset declaration.
+ */
+typedef void (*internalSubsetSAXFunc) (void *ctx,
+				const xmlChar *name,
+				const xmlChar *ExternalID,
+				const xmlChar *SystemID);
+/**
+ * externalSubsetSAXFunc:
+ * @ctx:  the user data (XML parser context)
+ * @name:  the root element name
+ * @ExternalID:  the external ID
+ * @SystemID:  the SYSTEM ID (e.g. filename or URL)
+ *
+ * Callback on external subset declaration.
+ */
+typedef void (*externalSubsetSAXFunc) (void *ctx,
+				const xmlChar *name,
+				const xmlChar *ExternalID,
+				const xmlChar *SystemID);
+/**
+ * getEntitySAXFunc:
+ * @ctx:  the user data (XML parser context)
+ * @name: The entity name
+ *
+ * Get an entity by name.
+ *
+ * Returns the xmlEntityPtr if found.
+ */
+typedef xmlEntityPtr (*getEntitySAXFunc) (void *ctx,
+				const xmlChar *name);
+/**
+ * getParameterEntitySAXFunc:
+ * @ctx:  the user data (XML parser context)
+ * @name: The entity name
+ *
+ * Get a parameter entity by name.
+ *
+ * Returns the xmlEntityPtr if found.
+ */
+typedef xmlEntityPtr (*getParameterEntitySAXFunc) (void *ctx,
+				const xmlChar *name);
+/**
+ * entityDeclSAXFunc:
+ * @ctx:  the user data (XML parser context)
+ * @name:  the entity name
+ * @type:  the entity type
+ * @publicId: The public ID of the entity
+ * @systemId: The system ID of the entity
+ * @content: the entity value (without processing).
+ *
+ * An entity definition has been parsed.
+ */
+typedef void (*entityDeclSAXFunc) (void *ctx,
+				const xmlChar *name,
+				int type,
+				const xmlChar *publicId,
+				const xmlChar *systemId,
+				xmlChar *content);
+/**
+ * notationDeclSAXFunc:
+ * @ctx:  the user data (XML parser context)
+ * @name: The name of the notation
+ * @publicId: The public ID of the entity
+ * @systemId: The system ID of the entity
+ *
+ * What to do when a notation declaration has been parsed.
+ */
+typedef void (*notationDeclSAXFunc)(void *ctx,
+				const xmlChar *name,
+				const xmlChar *publicId,
+				const xmlChar *systemId);
+/**
+ * attributeDeclSAXFunc:
+ * @ctx:  the user data (XML parser context)
+ * @elem:  the name of the element
+ * @fullname:  the attribute name
+ * @type:  the attribute type
+ * @def:  the type of default value
+ * @defaultValue: the attribute default value
+ * @tree:  the tree of enumerated value set
+ *
+ * An attribute definition has been parsed.
+ */
+typedef void (*attributeDeclSAXFunc)(void *ctx,
+				const xmlChar *elem,
+				const xmlChar *fullname,
+				int type,
+				int def,
+				const xmlChar *defaultValue,
+				xmlEnumerationPtr tree);
+/**
+ * elementDeclSAXFunc:
+ * @ctx:  the user data (XML parser context)
+ * @name:  the element name
+ * @type:  the element type
+ * @content: the element value tree
+ *
+ * An element definition has been parsed.
+ */
+typedef void (*elementDeclSAXFunc)(void *ctx,
+				const xmlChar *name,
+				int type,
+				xmlElementContentPtr content);
+/**
+ * unparsedEntityDeclSAXFunc:
+ * @ctx:  the user data (XML parser context)
+ * @name: The name of the entity
+ * @publicId: The public ID of the entity
+ * @systemId: The system ID of the entity
+ * @notationName: the name of the notation
+ *
+ * What to do when an unparsed entity declaration is parsed.
+ */
+typedef void (*unparsedEntityDeclSAXFunc)(void *ctx,
+				const xmlChar *name,
+				const xmlChar *publicId,
+				const xmlChar *systemId,
+				const xmlChar *notationName);
+/**
+ * setDocumentLocatorSAXFunc:
+ * @ctx:  the user data (XML parser context)
+ * @loc: A SAX Locator
+ *
+ * Receive the document locator at startup, actually xmlDefaultSAXLocator.
+ * Everything is available on the context, so this is useless in our case.
+ */
+typedef void (*setDocumentLocatorSAXFunc) (void *ctx,
+				xmlSAXLocatorPtr loc);
+/**
+ * startDocumentSAXFunc:
+ * @ctx:  the user data (XML parser context)
+ *
+ * Called when the document start being processed.
+ */
+typedef void (*startDocumentSAXFunc) (void *ctx);
+/**
+ * endDocumentSAXFunc:
+ * @ctx:  the user data (XML parser context)
+ *
+ * Called when the document end has been detected.
+ */
+typedef void (*endDocumentSAXFunc) (void *ctx);
+/**
+ * startElementSAXFunc:
+ * @ctx:  the user data (XML parser context)
+ * @name:  The element name, including namespace prefix
+ * @atts:  An array of name/value attributes pairs, NULL terminated
+ *
+ * Called when an opening tag has been processed.
+ */
+typedef void (*startElementSAXFunc) (void *ctx,
+				const xmlChar *name,
+				const xmlChar **atts);
+/**
+ * endElementSAXFunc:
+ * @ctx:  the user data (XML parser context)
+ * @name:  The element name
+ *
+ * Called when the end of an element has been detected.
+ */
+typedef void (*endElementSAXFunc) (void *ctx,
+				const xmlChar *name);
+/**
+ * attributeSAXFunc:
+ * @ctx:  the user data (XML parser context)
+ * @name:  The attribute name, including namespace prefix
+ * @value:  The attribute value
+ *
+ * Handle an attribute that has been read by the parser.
+ * The default handling is to convert the attribute into an
+ * DOM subtree and past it in a new xmlAttr element added to
+ * the element.
+ */
+typedef void (*attributeSAXFunc) (void *ctx,
+				const xmlChar *name,
+				const xmlChar *value);
+/**
+ * referenceSAXFunc:
+ * @ctx:  the user data (XML parser context)
+ * @name:  The entity name
+ *
+ * Called when an entity reference is detected.
+ */
+typedef void (*referenceSAXFunc) (void *ctx,
+				const xmlChar *name);
+/**
+ * charactersSAXFunc:
+ * @ctx:  the user data (XML parser context)
+ * @ch:  a xmlChar string
+ * @len: the number of xmlChar
+ *
+ * Receiving some chars from the parser.
+ */
+typedef void (*charactersSAXFunc) (void *ctx,
+				const xmlChar *ch,
+				int len);
+/**
+ * ignorableWhitespaceSAXFunc:
+ * @ctx:  the user data (XML parser context)
+ * @ch:  a xmlChar string
+ * @len: the number of xmlChar
+ *
+ * Receiving some ignorable whitespaces from the parser.
+ * UNUSED: by default the DOM building will use characters.
+ */
+typedef void (*ignorableWhitespaceSAXFunc) (void *ctx,
+				const xmlChar *ch,
+				int len);
+/**
+ * processingInstructionSAXFunc:
+ * @ctx:  the user data (XML parser context)
+ * @target:  the target name
+ * @data: the PI data's
+ *
+ * A processing instruction has been parsed.
+ */
+typedef void (*processingInstructionSAXFunc) (void *ctx,
+				const xmlChar *target,
+				const xmlChar *data);
+/**
+ * commentSAXFunc:
+ * @ctx:  the user data (XML parser context)
+ * @value:  the comment content
+ *
+ * A comment has been parsed.
+ */
+typedef void (*commentSAXFunc) (void *ctx,
+				const xmlChar *value);
+/**
+ * cdataBlockSAXFunc:
+ * @ctx:  the user data (XML parser context)
+ * @value:  The pcdata content
+ * @len:  the block length
+ *
+ * Called when a pcdata block has been parsed.
+ */
+typedef void (*cdataBlockSAXFunc) (
+	                        void *ctx,
+				const xmlChar *value,
+				int len);
+/**
+ * warningSAXFunc:
+ * @ctx:  an XML parser context
+ * @msg:  the message to display/transmit
+ * @...:  extra parameters for the message display
+ *
+ * Display and format a warning messages, callback.
+ */
+typedef void (*warningSAXFunc) (void *ctx,
+				const char *msg, ...) LIBXML_ATTR_FORMAT(2,3);
+/**
+ * errorSAXFunc:
+ * @ctx:  an XML parser context
+ * @msg:  the message to display/transmit
+ * @...:  extra parameters for the message display
+ *
+ * Display and format an error messages, callback.
+ */
+typedef void (*errorSAXFunc) (void *ctx,
+				const char *msg, ...) LIBXML_ATTR_FORMAT(2,3);
+/**
+ * fatalErrorSAXFunc:
+ * @ctx:  an XML parser context
+ * @msg:  the message to display/transmit
+ * @...:  extra parameters for the message display
+ *
+ * Display and format fatal error messages, callback.
+ * Note: so far fatalError() SAX callbacks are not used, error()
+ *       get all the callbacks for errors.
+ */
+typedef void (*fatalErrorSAXFunc) (void *ctx,
+				const char *msg, ...) LIBXML_ATTR_FORMAT(2,3);
+/**
+ * isStandaloneSAXFunc:
+ * @ctx:  the user data (XML parser context)
+ *
+ * Is this document tagged standalone?
+ *
+ * Returns 1 if true
+ */
+typedef int (*isStandaloneSAXFunc) (void *ctx);
+/**
+ * hasInternalSubsetSAXFunc:
+ * @ctx:  the user data (XML parser context)
+ *
+ * Does this document has an internal subset.
+ *
+ * Returns 1 if true
+ */
+typedef int (*hasInternalSubsetSAXFunc) (void *ctx);
+
+/**
+ * hasExternalSubsetSAXFunc:
+ * @ctx:  the user data (XML parser context)
+ *
+ * Does this document has an external subset?
+ *
+ * Returns 1 if true
+ */
+typedef int (*hasExternalSubsetSAXFunc) (void *ctx);
+
+/************************************************************************
+ *									*
+ *			The SAX version 2 API extensions		*
+ *									*
+ ************************************************************************/
+/**
+ * XML_SAX2_MAGIC:
+ *
+ * Special constant found in SAX2 blocks initialized fields
+ */
+#define XML_SAX2_MAGIC 0xDEEDBEAF
+
+/**
+ * startElementNsSAX2Func:
+ * @ctx:  the user data (XML parser context)
+ * @localname:  the local name of the element
+ * @prefix:  the element namespace prefix if available
+ * @URI:  the element namespace name if available
+ * @nb_namespaces:  number of namespace definitions on that node
+ * @namespaces:  pointer to the array of prefix/URI pairs namespace definitions
+ * @nb_attributes:  the number of attributes on that node
+ * @nb_defaulted:  the number of defaulted attributes. The defaulted
+ *                  ones are at the end of the array
+ * @attributes:  pointer to the array of (localname/prefix/URI/value/end)
+ *               attribute values.
+ *
+ * SAX2 callback when an element start has been detected by the parser.
+ * It provides the namespace information for the element, as well as
+ * the new namespace declarations on the element.
+ */
+
+typedef void (*startElementNsSAX2Func) (void *ctx,
+					const xmlChar *localname,
+					const xmlChar *prefix,
+					const xmlChar *URI,
+					int nb_namespaces,
+					const xmlChar **namespaces,
+					int nb_attributes,
+					int nb_defaulted,
+					const xmlChar **attributes);
+
+/**
+ * endElementNsSAX2Func:
+ * @ctx:  the user data (XML parser context)
+ * @localname:  the local name of the element
+ * @prefix:  the element namespace prefix if available
+ * @URI:  the element namespace name if available
+ *
+ * SAX2 callback when an element end has been detected by the parser.
+ * It provides the namespace information for the element.
+ */
+
+typedef void (*endElementNsSAX2Func)   (void *ctx,
+					const xmlChar *localname,
+					const xmlChar *prefix,
+					const xmlChar *URI);
+
+
+struct _xmlSAXHandler {
+    internalSubsetSAXFunc internalSubset;
+    isStandaloneSAXFunc isStandalone;
+    hasInternalSubsetSAXFunc hasInternalSubset;
+    hasExternalSubsetSAXFunc hasExternalSubset;
+    resolveEntitySAXFunc resolveEntity;
+    getEntitySAXFunc getEntity;
+    entityDeclSAXFunc entityDecl;
+    notationDeclSAXFunc notationDecl;
+    attributeDeclSAXFunc attributeDecl;
+    elementDeclSAXFunc elementDecl;
+    unparsedEntityDeclSAXFunc unparsedEntityDecl;
+    setDocumentLocatorSAXFunc setDocumentLocator;
+    startDocumentSAXFunc startDocument;
+    endDocumentSAXFunc endDocument;
+    /*
+     * `startElement` and `endElement` are only used by the legacy SAX1
+     * interface and should not be used in new software. If you really
+     * have to enable SAX1, the preferred way is set the `initialized`
+     * member to 1 instead of XML_SAX2_MAGIC.
+     *
+     * For backward compatibility, it's also possible to set the
+     * `startElementNs` and `endElementNs` handlers to NULL.
+     *
+     * You can also set the XML_PARSE_SAX1 parser option, but versions
+     * older than 2.12.0 will probably crash if this option is provided
+     * together with custom SAX callbacks.
+     */
+    startElementSAXFunc startElement;
+    endElementSAXFunc endElement;
+    referenceSAXFunc reference;
+    charactersSAXFunc characters;
+    ignorableWhitespaceSAXFunc ignorableWhitespace;
+    processingInstructionSAXFunc processingInstruction;
+    commentSAXFunc comment;
+    warningSAXFunc warning;
+    errorSAXFunc error;
+    fatalErrorSAXFunc fatalError; /* unused error() get all the errors */
+    getParameterEntitySAXFunc getParameterEntity;
+    cdataBlockSAXFunc cdataBlock;
+    externalSubsetSAXFunc externalSubset;
+    /*
+     * `initialized` should always be set to XML_SAX2_MAGIC to enable the
+     * modern SAX2 interface.
+     */
+    unsigned int initialized;
+    /*
+     * The following members are only used by the SAX2 interface.
+     */
+    void *_private;
+    startElementNsSAX2Func startElementNs;
+    endElementNsSAX2Func endElementNs;
+    xmlStructuredErrorFunc serror;
+};
+
+/*
+ * SAX Version 1
+ */
+typedef struct _xmlSAXHandlerV1 xmlSAXHandlerV1;
+typedef xmlSAXHandlerV1 *xmlSAXHandlerV1Ptr;
+struct _xmlSAXHandlerV1 {
+    internalSubsetSAXFunc internalSubset;
+    isStandaloneSAXFunc isStandalone;
+    hasInternalSubsetSAXFunc hasInternalSubset;
+    hasExternalSubsetSAXFunc hasExternalSubset;
+    resolveEntitySAXFunc resolveEntity;
+    getEntitySAXFunc getEntity;
+    entityDeclSAXFunc entityDecl;
+    notationDeclSAXFunc notationDecl;
+    attributeDeclSAXFunc attributeDecl;
+    elementDeclSAXFunc elementDecl;
+    unparsedEntityDeclSAXFunc unparsedEntityDecl;
+    setDocumentLocatorSAXFunc setDocumentLocator;
+    startDocumentSAXFunc startDocument;
+    endDocumentSAXFunc endDocument;
+    startElementSAXFunc startElement;
+    endElementSAXFunc endElement;
+    referenceSAXFunc reference;
+    charactersSAXFunc characters;
+    ignorableWhitespaceSAXFunc ignorableWhitespace;
+    processingInstructionSAXFunc processingInstruction;
+    commentSAXFunc comment;
+    warningSAXFunc warning;
+    errorSAXFunc error;
+    fatalErrorSAXFunc fatalError; /* unused error() get all the errors */
+    getParameterEntitySAXFunc getParameterEntity;
+    cdataBlockSAXFunc cdataBlock;
+    externalSubsetSAXFunc externalSubset;
+    unsigned int initialized;
+};
+
+
+/**
+ * xmlExternalEntityLoader:
+ * @URL: The System ID of the resource requested
+ * @ID: The Public ID of the resource requested
+ * @context: the XML parser context
+ *
+ * External entity loaders types.
+ *
+ * Returns the entity input parser.
+ */
+typedef xmlParserInputPtr (*xmlExternalEntityLoader) (const char *URL,
+					 const char *ID,
+					 xmlParserCtxtPtr context);
+
+/*
+ * Variables
+ */
+
+XMLPUBVAR const char *const xmlParserVersion;
+#ifdef LIBXML_THREAD_ENABLED
+/* backward compatibility */
+XMLPUBFUN const char *const *__xmlParserVersion(void);
+#endif
+
+/** DOC_DISABLE */
+#define XML_GLOBALS_PARSER_CORE \
+  XML_OP(oldXMLWDcompatibility, int, XML_DEPRECATED) \
+  XML_OP(xmlDefaultSAXLocator, xmlSAXLocator, XML_DEPRECATED) \
+  XML_OP(xmlDoValidityCheckingDefaultValue, int, XML_DEPRECATED) \
+  XML_OP(xmlGetWarningsDefaultValue, int, XML_DEPRECATED) \
+  XML_OP(xmlKeepBlanksDefaultValue, int, XML_DEPRECATED) \
+  XML_OP(xmlLineNumbersDefaultValue, int, XML_DEPRECATED) \
+  XML_OP(xmlLoadExtDtdDefaultValue, int, XML_DEPRECATED) \
+  XML_OP(xmlParserDebugEntities, int, XML_DEPRECATED) \
+  XML_OP(xmlPedanticParserDefaultValue, int, XML_DEPRECATED) \
+  XML_OP(xmlSubstituteEntitiesDefaultValue, int, XML_DEPRECATED)
+
+#ifdef LIBXML_OUTPUT_ENABLED
+  #define XML_GLOBALS_PARSER_OUTPUT \
+    XML_OP(xmlIndentTreeOutput, int, XML_NO_ATTR) \
+    XML_OP(xmlTreeIndentString, const char *, XML_NO_ATTR) \
+    XML_OP(xmlSaveNoEmptyTags, int, XML_NO_ATTR)
+#else
+  #define XML_GLOBALS_PARSER_OUTPUT
+#endif
+
+#ifdef LIBXML_SAX1_ENABLED
+  #define XML_GLOBALS_PARSER_SAX1 \
+    XML_OP(xmlDefaultSAXHandler, xmlSAXHandlerV1, XML_DEPRECATED)
+#else
+  #define XML_GLOBALS_PARSER_SAX1
+#endif
+
+#define XML_GLOBALS_PARSER \
+  XML_GLOBALS_PARSER_CORE \
+  XML_GLOBALS_PARSER_OUTPUT \
+  XML_GLOBALS_PARSER_SAX1
+
+#define XML_OP XML_DECLARE_GLOBAL
+XML_GLOBALS_PARSER
+#undef XML_OP
+
+#if defined(LIBXML_THREAD_ENABLED) && !defined(XML_GLOBALS_NO_REDEFINITION)
+  #define oldXMLWDcompatibility XML_GLOBAL_MACRO(oldXMLWDcompatibility)
+  #define xmlDefaultSAXHandler XML_GLOBAL_MACRO(xmlDefaultSAXHandler)
+  #define xmlDefaultSAXLocator XML_GLOBAL_MACRO(xmlDefaultSAXLocator)
+  #define xmlDoValidityCheckingDefaultValue \
+    XML_GLOBAL_MACRO(xmlDoValidityCheckingDefaultValue)
+  #define xmlGetWarningsDefaultValue \
+    XML_GLOBAL_MACRO(xmlGetWarningsDefaultValue)
+  #define xmlKeepBlanksDefaultValue XML_GLOBAL_MACRO(xmlKeepBlanksDefaultValue)
+  #define xmlLineNumbersDefaultValue \
+    XML_GLOBAL_MACRO(xmlLineNumbersDefaultValue)
+  #define xmlLoadExtDtdDefaultValue XML_GLOBAL_MACRO(xmlLoadExtDtdDefaultValue)
+  #define xmlParserDebugEntities XML_GLOBAL_MACRO(xmlParserDebugEntities)
+  #define xmlPedanticParserDefaultValue \
+    XML_GLOBAL_MACRO(xmlPedanticParserDefaultValue)
+  #define xmlSubstituteEntitiesDefaultValue \
+    XML_GLOBAL_MACRO(xmlSubstituteEntitiesDefaultValue)
+  #ifdef LIBXML_OUTPUT_ENABLED
+    #define xmlIndentTreeOutput XML_GLOBAL_MACRO(xmlIndentTreeOutput)
+    #define xmlTreeIndentString XML_GLOBAL_MACRO(xmlTreeIndentString)
+    #define xmlSaveNoEmptyTags XML_GLOBAL_MACRO(xmlSaveNoEmptyTags)
+  #endif
+#endif
+/** DOC_ENABLE */
+
+/*
+ * Init/Cleanup
+ */
+XMLPUBFUN void
+		xmlInitParser		(void);
+XMLPUBFUN void
+		xmlCleanupParser	(void);
+XML_DEPRECATED
+XMLPUBFUN void
+		xmlInitGlobals		(void);
+XML_DEPRECATED
+XMLPUBFUN void
+		xmlCleanupGlobals	(void);
+
+/*
+ * Input functions
+ */
+XML_DEPRECATED
+XMLPUBFUN int
+		xmlParserInputRead	(xmlParserInputPtr in,
+					 int len);
+XML_DEPRECATED
+XMLPUBFUN int
+		xmlParserInputGrow	(xmlParserInputPtr in,
+					 int len);
+
+/*
+ * Basic parsing Interfaces
+ */
+#ifdef LIBXML_SAX1_ENABLED
+XMLPUBFUN xmlDocPtr
+		xmlParseDoc		(const xmlChar *cur);
+XMLPUBFUN xmlDocPtr
+		xmlParseFile		(const char *filename);
+XMLPUBFUN xmlDocPtr
+		xmlParseMemory		(const char *buffer,
+					 int size);
+#endif /* LIBXML_SAX1_ENABLED */
+XML_DEPRECATED XMLPUBFUN int
+		xmlSubstituteEntitiesDefault(int val);
+XML_DEPRECATED XMLPUBFUN int
+                xmlThrDefSubstituteEntitiesDefaultValue(int v);
+XMLPUBFUN int
+		xmlKeepBlanksDefault	(int val);
+XML_DEPRECATED XMLPUBFUN int
+		xmlThrDefKeepBlanksDefaultValue(int v);
+XMLPUBFUN void
+		xmlStopParser		(xmlParserCtxtPtr ctxt);
+XML_DEPRECATED XMLPUBFUN int
+		xmlPedanticParserDefault(int val);
+XML_DEPRECATED XMLPUBFUN int
+                xmlThrDefPedanticParserDefaultValue(int v);
+XML_DEPRECATED XMLPUBFUN int
+		xmlLineNumbersDefault	(int val);
+XML_DEPRECATED XMLPUBFUN int
+                xmlThrDefLineNumbersDefaultValue(int v);
+XML_DEPRECATED XMLPUBFUN int
+                xmlThrDefDoValidityCheckingDefaultValue(int v);
+XML_DEPRECATED XMLPUBFUN int
+                xmlThrDefGetWarningsDefaultValue(int v);
+XML_DEPRECATED XMLPUBFUN int
+                xmlThrDefLoadExtDtdDefaultValue(int v);
+XML_DEPRECATED XMLPUBFUN int
+                xmlThrDefParserDebugEntities(int v);
+
+#ifdef LIBXML_SAX1_ENABLED
+/*
+ * Recovery mode
+ */
+XML_DEPRECATED
+XMLPUBFUN xmlDocPtr
+		xmlRecoverDoc		(const xmlChar *cur);
+XML_DEPRECATED
+XMLPUBFUN xmlDocPtr
+		xmlRecoverMemory	(const char *buffer,
+					 int size);
+XML_DEPRECATED
+XMLPUBFUN xmlDocPtr
+		xmlRecoverFile		(const char *filename);
+#endif /* LIBXML_SAX1_ENABLED */
+
+/*
+ * Less common routines and SAX interfaces
+ */
+XMLPUBFUN int
+		xmlParseDocument	(xmlParserCtxtPtr ctxt);
+XMLPUBFUN int
+		xmlParseExtParsedEnt	(xmlParserCtxtPtr ctxt);
+#ifdef LIBXML_SAX1_ENABLED
+XML_DEPRECATED
+XMLPUBFUN int
+		xmlSAXUserParseFile	(xmlSAXHandlerPtr sax,
+					 void *user_data,
+					 const char *filename);
+XML_DEPRECATED
+XMLPUBFUN int
+		xmlSAXUserParseMemory	(xmlSAXHandlerPtr sax,
+					 void *user_data,
+					 const char *buffer,
+					 int size);
+XML_DEPRECATED
+XMLPUBFUN xmlDocPtr
+		xmlSAXParseDoc		(xmlSAXHandlerPtr sax,
+					 const xmlChar *cur,
+					 int recovery);
+XML_DEPRECATED
+XMLPUBFUN xmlDocPtr
+		xmlSAXParseMemory	(xmlSAXHandlerPtr sax,
+					 const char *buffer,
+					 int size,
+					 int recovery);
+XML_DEPRECATED
+XMLPUBFUN xmlDocPtr
+		xmlSAXParseMemoryWithData (xmlSAXHandlerPtr sax,
+					 const char *buffer,
+					 int size,
+					 int recovery,
+					 void *data);
+XML_DEPRECATED
+XMLPUBFUN xmlDocPtr
+		xmlSAXParseFile		(xmlSAXHandlerPtr sax,
+					 const char *filename,
+					 int recovery);
+XML_DEPRECATED
+XMLPUBFUN xmlDocPtr
+		xmlSAXParseFileWithData	(xmlSAXHandlerPtr sax,
+					 const char *filename,
+					 int recovery,
+					 void *data);
+XML_DEPRECATED
+XMLPUBFUN xmlDocPtr
+		xmlSAXParseEntity	(xmlSAXHandlerPtr sax,
+					 const char *filename);
+XML_DEPRECATED
+XMLPUBFUN xmlDocPtr
+		xmlParseEntity		(const char *filename);
+#endif /* LIBXML_SAX1_ENABLED */
+
+#ifdef LIBXML_VALID_ENABLED
+XML_DEPRECATED
+XMLPUBFUN xmlDtdPtr
+		xmlSAXParseDTD		(xmlSAXHandlerPtr sax,
+					 const xmlChar *ExternalID,
+					 const xmlChar *SystemID);
+XMLPUBFUN xmlDtdPtr
+		xmlParseDTD		(const xmlChar *ExternalID,
+					 const xmlChar *SystemID);
+XMLPUBFUN xmlDtdPtr
+		xmlIOParseDTD		(xmlSAXHandlerPtr sax,
+					 xmlParserInputBufferPtr input,
+					 xmlCharEncoding enc);
+#endif /* LIBXML_VALID_ENABLE */
+#ifdef LIBXML_SAX1_ENABLED
+XMLPUBFUN int
+		xmlParseBalancedChunkMemory(xmlDocPtr doc,
+					 xmlSAXHandlerPtr sax,
+					 void *user_data,
+					 int depth,
+					 const xmlChar *string,
+					 xmlNodePtr *lst);
+#endif /* LIBXML_SAX1_ENABLED */
+XMLPUBFUN xmlParserErrors
+		xmlParseInNodeContext	(xmlNodePtr node,
+					 const char *data,
+					 int datalen,
+					 int options,
+					 xmlNodePtr *lst);
+#ifdef LIBXML_SAX1_ENABLED
+XMLPUBFUN int
+		xmlParseBalancedChunkMemoryRecover(xmlDocPtr doc,
+                     xmlSAXHandlerPtr sax,
+                     void *user_data,
+                     int depth,
+                     const xmlChar *string,
+                     xmlNodePtr *lst,
+                     int recover);
+XML_DEPRECATED
+XMLPUBFUN int
+		xmlParseExternalEntity	(xmlDocPtr doc,
+					 xmlSAXHandlerPtr sax,
+					 void *user_data,
+					 int depth,
+					 const xmlChar *URL,
+					 const xmlChar *ID,
+					 xmlNodePtr *lst);
+#endif /* LIBXML_SAX1_ENABLED */
+XMLPUBFUN int
+		xmlParseCtxtExternalEntity(xmlParserCtxtPtr ctx,
+					 const xmlChar *URL,
+					 const xmlChar *ID,
+					 xmlNodePtr *lst);
+
+/*
+ * Parser contexts handling.
+ */
+XMLPUBFUN xmlParserCtxtPtr
+		xmlNewParserCtxt	(void);
+XMLPUBFUN xmlParserCtxtPtr
+		xmlNewSAXParserCtxt	(const xmlSAXHandler *sax,
+					 void *userData);
+XMLPUBFUN int
+		xmlInitParserCtxt	(xmlParserCtxtPtr ctxt);
+XMLPUBFUN void
+		xmlClearParserCtxt	(xmlParserCtxtPtr ctxt);
+XMLPUBFUN void
+		xmlFreeParserCtxt	(xmlParserCtxtPtr ctxt);
+#ifdef LIBXML_SAX1_ENABLED
+XML_DEPRECATED
+XMLPUBFUN void
+		xmlSetupParserForBuffer	(xmlParserCtxtPtr ctxt,
+					 const xmlChar* buffer,
+					 const char *filename);
+#endif /* LIBXML_SAX1_ENABLED */
+XMLPUBFUN xmlParserCtxtPtr
+		xmlCreateDocParserCtxt	(const xmlChar *cur);
+
+#ifdef LIBXML_LEGACY_ENABLED
+/*
+ * Reading/setting optional parsing features.
+ */
+XML_DEPRECATED
+XMLPUBFUN int
+		xmlGetFeaturesList	(int *len,
+					 const char **result);
+XML_DEPRECATED
+XMLPUBFUN int
+		xmlGetFeature		(xmlParserCtxtPtr ctxt,
+					 const char *name,
+					 void *result);
+XML_DEPRECATED
+XMLPUBFUN int
+		xmlSetFeature		(xmlParserCtxtPtr ctxt,
+					 const char *name,
+					 void *value);
+#endif /* LIBXML_LEGACY_ENABLED */
+
+#ifdef LIBXML_PUSH_ENABLED
+/*
+ * Interfaces for the Push mode.
+ */
+XMLPUBFUN xmlParserCtxtPtr
+		xmlCreatePushParserCtxt(xmlSAXHandlerPtr sax,
+					 void *user_data,
+					 const char *chunk,
+					 int size,
+					 const char *filename);
+XMLPUBFUN int
+		xmlParseChunk		(xmlParserCtxtPtr ctxt,
+					 const char *chunk,
+					 int size,
+					 int terminate);
+#endif /* LIBXML_PUSH_ENABLED */
+
+/*
+ * Special I/O mode.
+ */
+
+XMLPUBFUN xmlParserCtxtPtr
+		xmlCreateIOParserCtxt	(xmlSAXHandlerPtr sax,
+					 void *user_data,
+					 xmlInputReadCallback   ioread,
+					 xmlInputCloseCallback  ioclose,
+					 void *ioctx,
+					 xmlCharEncoding enc);
+
+XMLPUBFUN xmlParserInputPtr
+		xmlNewIOInputStream	(xmlParserCtxtPtr ctxt,
+					 xmlParserInputBufferPtr input,
+					 xmlCharEncoding enc);
+
+/*
+ * Node infos.
+ */
+XMLPUBFUN const xmlParserNodeInfo*
+		xmlParserFindNodeInfo	(const xmlParserCtxtPtr ctxt,
+				         const xmlNodePtr node);
+XMLPUBFUN void
+		xmlInitNodeInfoSeq	(xmlParserNodeInfoSeqPtr seq);
+XMLPUBFUN void
+		xmlClearNodeInfoSeq	(xmlParserNodeInfoSeqPtr seq);
+XMLPUBFUN unsigned long
+		xmlParserFindNodeInfoIndex(const xmlParserNodeInfoSeqPtr seq,
+                                         const xmlNodePtr node);
+XMLPUBFUN void
+		xmlParserAddNodeInfo	(xmlParserCtxtPtr ctxt,
+					 const xmlParserNodeInfoPtr info);
+
+/*
+ * External entities handling actually implemented in xmlIO.
+ */
+
+XMLPUBFUN void
+		xmlSetExternalEntityLoader(xmlExternalEntityLoader f);
+XMLPUBFUN xmlExternalEntityLoader
+		xmlGetExternalEntityLoader(void);
+XMLPUBFUN xmlParserInputPtr
+		xmlLoadExternalEntity	(const char *URL,
+					 const char *ID,
+					 xmlParserCtxtPtr ctxt);
+
+/*
+ * Index lookup, actually implemented in the encoding module
+ */
+XMLPUBFUN long
+		xmlByteConsumed		(xmlParserCtxtPtr ctxt);
+
+/*
+ * New set of simpler/more flexible APIs
+ */
+/**
+ * xmlParserOption:
+ *
+ * This is the set of XML parser options that can be passed down
+ * to the xmlReadDoc() and similar calls.
+ */
+typedef enum {
+    XML_PARSE_RECOVER	= 1<<0,	/* recover on errors */
+    XML_PARSE_NOENT	= 1<<1,	/* substitute entities */
+    XML_PARSE_DTDLOAD	= 1<<2,	/* load the external subset */
+    XML_PARSE_DTDATTR	= 1<<3,	/* default DTD attributes */
+    XML_PARSE_DTDVALID	= 1<<4,	/* validate with the DTD */
+    XML_PARSE_NOERROR	= 1<<5,	/* suppress error reports */
+    XML_PARSE_NOWARNING	= 1<<6,	/* suppress warning reports */
+    XML_PARSE_PEDANTIC	= 1<<7,	/* pedantic error reporting */
+    XML_PARSE_NOBLANKS	= 1<<8,	/* remove blank nodes */
+    XML_PARSE_SAX1	= 1<<9,	/* use the SAX1 interface internally */
+    XML_PARSE_XINCLUDE	= 1<<10,/* Implement XInclude substitution  */
+    XML_PARSE_NONET	= 1<<11,/* Forbid network access */
+    XML_PARSE_NODICT	= 1<<12,/* Do not reuse the context dictionary */
+    XML_PARSE_NSCLEAN	= 1<<13,/* remove redundant namespaces declarations */
+    XML_PARSE_NOCDATA	= 1<<14,/* merge CDATA as text nodes */
+    XML_PARSE_NOXINCNODE= 1<<15,/* do not generate XINCLUDE START/END nodes */
+    XML_PARSE_COMPACT   = 1<<16,/* compact small text nodes; no modification of
+                                   the tree allowed afterwards (will possibly
+				   crash if you try to modify the tree) */
+    XML_PARSE_OLD10	= 1<<17,/* parse using XML-1.0 before update 5 */
+    XML_PARSE_NOBASEFIX = 1<<18,/* do not fixup XINCLUDE xml:base uris */
+    XML_PARSE_HUGE      = 1<<19,/* relax any hardcoded limit from the parser */
+    XML_PARSE_OLDSAX    = 1<<20,/* parse using SAX2 interface before 2.7.0 */
+    XML_PARSE_IGNORE_ENC= 1<<21,/* ignore internal document encoding hint */
+    XML_PARSE_BIG_LINES = 1<<22 /* Store big lines numbers in text PSVI field */
+} xmlParserOption;
+
+XMLPUBFUN void
+		xmlCtxtReset		(xmlParserCtxtPtr ctxt);
+XMLPUBFUN int
+		xmlCtxtResetPush	(xmlParserCtxtPtr ctxt,
+					 const char *chunk,
+					 int size,
+					 const char *filename,
+					 const char *encoding);
+XMLPUBFUN int
+		xmlCtxtUseOptions	(xmlParserCtxtPtr ctxt,
+					 int options);
+XMLPUBFUN void
+		xmlCtxtSetMaxAmplification(xmlParserCtxtPtr ctxt,
+					 unsigned maxAmpl);
+XMLPUBFUN xmlDocPtr
+		xmlReadDoc		(const xmlChar *cur,
+					 const char *URL,
+					 const char *encoding,
+					 int options);
+XMLPUBFUN xmlDocPtr
+		xmlReadFile		(const char *URL,
+					 const char *encoding,
+					 int options);
+XMLPUBFUN xmlDocPtr
+		xmlReadMemory		(const char *buffer,
+					 int size,
+					 const char *URL,
+					 const char *encoding,
+					 int options);
+XMLPUBFUN xmlDocPtr
+		xmlReadFd		(int fd,
+					 const char *URL,
+					 const char *encoding,
+					 int options);
+XMLPUBFUN xmlDocPtr
+		xmlReadIO		(xmlInputReadCallback ioread,
+					 xmlInputCloseCallback ioclose,
+					 void *ioctx,
+					 const char *URL,
+					 const char *encoding,
+					 int options);
+XMLPUBFUN xmlDocPtr
+		xmlCtxtReadDoc		(xmlParserCtxtPtr ctxt,
+					 const xmlChar *cur,
+					 const char *URL,
+					 const char *encoding,
+					 int options);
+XMLPUBFUN xmlDocPtr
+		xmlCtxtReadFile		(xmlParserCtxtPtr ctxt,
+					 const char *filename,
+					 const char *encoding,
+					 int options);
+XMLPUBFUN xmlDocPtr
+		xmlCtxtReadMemory		(xmlParserCtxtPtr ctxt,
+					 const char *buffer,
+					 int size,
+					 const char *URL,
+					 const char *encoding,
+					 int options);
+XMLPUBFUN xmlDocPtr
+		xmlCtxtReadFd		(xmlParserCtxtPtr ctxt,
+					 int fd,
+					 const char *URL,
+					 const char *encoding,
+					 int options);
+XMLPUBFUN xmlDocPtr
+		xmlCtxtReadIO		(xmlParserCtxtPtr ctxt,
+					 xmlInputReadCallback ioread,
+					 xmlInputCloseCallback ioclose,
+					 void *ioctx,
+					 const char *URL,
+					 const char *encoding,
+					 int options);
+
+/*
+ * Library wide options
+ */
+/**
+ * xmlFeature:
+ *
+ * Used to examine the existence of features that can be enabled
+ * or disabled at compile-time.
+ * They used to be called XML_FEATURE_xxx but this clashed with Expat
+ */
+typedef enum {
+    XML_WITH_THREAD = 1,
+    XML_WITH_TREE = 2,
+    XML_WITH_OUTPUT = 3,
+    XML_WITH_PUSH = 4,
+    XML_WITH_READER = 5,
+    XML_WITH_PATTERN = 6,
+    XML_WITH_WRITER = 7,
+    XML_WITH_SAX1 = 8,
+    XML_WITH_FTP = 9,
+    XML_WITH_HTTP = 10,
+    XML_WITH_VALID = 11,
+    XML_WITH_HTML = 12,
+    XML_WITH_LEGACY = 13,
+    XML_WITH_C14N = 14,
+    XML_WITH_CATALOG = 15,
+    XML_WITH_XPATH = 16,
+    XML_WITH_XPTR = 17,
+    XML_WITH_XINCLUDE = 18,
+    XML_WITH_ICONV = 19,
+    XML_WITH_ISO8859X = 20,
+    XML_WITH_UNICODE = 21,
+    XML_WITH_REGEXP = 22,
+    XML_WITH_AUTOMATA = 23,
+    XML_WITH_EXPR = 24,
+    XML_WITH_SCHEMAS = 25,
+    XML_WITH_SCHEMATRON = 26,
+    XML_WITH_MODULES = 27,
+    XML_WITH_DEBUG = 28,
+    XML_WITH_DEBUG_MEM = 29,
+    XML_WITH_DEBUG_RUN = 30,
+    XML_WITH_ZLIB = 31,
+    XML_WITH_ICU = 32,
+    XML_WITH_LZMA = 33,
+    XML_WITH_NONE = 99999 /* just to be sure of allocation size */
+} xmlFeature;
+
+XMLPUBFUN int
+		xmlHasFeature		(xmlFeature feature);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __XML_PARSER_H__ */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/parserInternals.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/parserInternals.h
new file mode 100644
index 00000000..017ed273
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/parserInternals.h
@@ -0,0 +1,663 @@
+/*
+ * Summary: internals routines and limits exported by the parser.
+ * Description: this module exports a number of internal parsing routines
+ *              they are not really all intended for applications but
+ *              can prove useful doing low level processing.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_PARSER_INTERNALS_H__
+#define __XML_PARSER_INTERNALS_H__
+
+#include <libxml/xmlversion.h>
+#include <libxml/parser.h>
+#include <libxml/HTMLparser.h>
+#include <libxml/chvalid.h>
+#include <libxml/SAX2.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * xmlParserMaxDepth:
+ *
+ * arbitrary depth limit for the XML documents that we allow to
+ * process. This is not a limitation of the parser but a safety
+ * boundary feature, use XML_PARSE_HUGE option to override it.
+ */
+XMLPUBVAR unsigned int xmlParserMaxDepth;
+
+/**
+ * XML_MAX_TEXT_LENGTH:
+ *
+ * Maximum size allowed for a single text node when building a tree.
+ * This is not a limitation of the parser but a safety boundary feature,
+ * use XML_PARSE_HUGE option to override it.
+ * Introduced in 2.9.0
+ */
+#define XML_MAX_TEXT_LENGTH 10000000
+
+/**
+ * XML_MAX_HUGE_LENGTH:
+ *
+ * Maximum size allowed when XML_PARSE_HUGE is set.
+ */
+#define XML_MAX_HUGE_LENGTH 1000000000
+
+/**
+ * XML_MAX_NAME_LENGTH:
+ *
+ * Maximum size allowed for a markup identifier.
+ * This is not a limitation of the parser but a safety boundary feature,
+ * use XML_PARSE_HUGE option to override it.
+ * Note that with the use of parsing dictionaries overriding the limit
+ * may result in more runtime memory usage in face of "unfriendly' content
+ * Introduced in 2.9.0
+ */
+#define XML_MAX_NAME_LENGTH 50000
+
+/**
+ * XML_MAX_DICTIONARY_LIMIT:
+ *
+ * Maximum size allowed by the parser for a dictionary by default
+ * This is not a limitation of the parser but a safety boundary feature,
+ * use XML_PARSE_HUGE option to override it.
+ * Introduced in 2.9.0
+ */
+#define XML_MAX_DICTIONARY_LIMIT 10000000
+
+/**
+ * XML_MAX_LOOKUP_LIMIT:
+ *
+ * Maximum size allowed by the parser for ahead lookup
+ * This is an upper boundary enforced by the parser to avoid bad
+ * behaviour on "unfriendly' content
+ * Introduced in 2.9.0
+ */
+#define XML_MAX_LOOKUP_LIMIT 10000000
+
+/**
+ * XML_MAX_NAMELEN:
+ *
+ * Identifiers can be longer, but this will be more costly
+ * at runtime.
+ */
+#define XML_MAX_NAMELEN 100
+
+/**
+ * INPUT_CHUNK:
+ *
+ * The parser tries to always have that amount of input ready.
+ * One of the point is providing context when reporting errors.
+ */
+#define INPUT_CHUNK	250
+
+/************************************************************************
+ *									*
+ * UNICODE version of the macros.					*
+ *									*
+ ************************************************************************/
+/**
+ * IS_BYTE_CHAR:
+ * @c:  an byte value (int)
+ *
+ * Macro to check the following production in the XML spec:
+ *
+ * [2] Char ::= #x9 | #xA | #xD | [#x20...]
+ * any byte character in the accepted range
+ */
+#define IS_BYTE_CHAR(c)	 xmlIsChar_ch(c)
+
+/**
+ * IS_CHAR:
+ * @c:  an UNICODE value (int)
+ *
+ * Macro to check the following production in the XML spec:
+ *
+ * [2] Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD]
+ *                  | [#x10000-#x10FFFF]
+ * any Unicode character, excluding the surrogate blocks, FFFE, and FFFF.
+ */
+#define IS_CHAR(c)   xmlIsCharQ(c)
+
+/**
+ * IS_CHAR_CH:
+ * @c: an xmlChar (usually an unsigned char)
+ *
+ * Behaves like IS_CHAR on single-byte value
+ */
+#define IS_CHAR_CH(c)  xmlIsChar_ch(c)
+
+/**
+ * IS_BLANK:
+ * @c:  an UNICODE value (int)
+ *
+ * Macro to check the following production in the XML spec:
+ *
+ * [3] S ::= (#x20 | #x9 | #xD | #xA)+
+ */
+#define IS_BLANK(c)  xmlIsBlankQ(c)
+
+/**
+ * IS_BLANK_CH:
+ * @c:  an xmlChar value (normally unsigned char)
+ *
+ * Behaviour same as IS_BLANK
+ */
+#define IS_BLANK_CH(c)  xmlIsBlank_ch(c)
+
+/**
+ * IS_BASECHAR:
+ * @c:  an UNICODE value (int)
+ *
+ * Macro to check the following production in the XML spec:
+ *
+ * [85] BaseChar ::= ... long list see REC ...
+ */
+#define IS_BASECHAR(c) xmlIsBaseCharQ(c)
+
+/**
+ * IS_DIGIT:
+ * @c:  an UNICODE value (int)
+ *
+ * Macro to check the following production in the XML spec:
+ *
+ * [88] Digit ::= ... long list see REC ...
+ */
+#define IS_DIGIT(c) xmlIsDigitQ(c)
+
+/**
+ * IS_DIGIT_CH:
+ * @c:  an xmlChar value (usually an unsigned char)
+ *
+ * Behaves like IS_DIGIT but with a single byte argument
+ */
+#define IS_DIGIT_CH(c)  xmlIsDigit_ch(c)
+
+/**
+ * IS_COMBINING:
+ * @c:  an UNICODE value (int)
+ *
+ * Macro to check the following production in the XML spec:
+ *
+ * [87] CombiningChar ::= ... long list see REC ...
+ */
+#define IS_COMBINING(c) xmlIsCombiningQ(c)
+
+/**
+ * IS_COMBINING_CH:
+ * @c:  an xmlChar (usually an unsigned char)
+ *
+ * Always false (all combining chars > 0xff)
+ */
+#define IS_COMBINING_CH(c) 0
+
+/**
+ * IS_EXTENDER:
+ * @c:  an UNICODE value (int)
+ *
+ * Macro to check the following production in the XML spec:
+ *
+ *
+ * [89] Extender ::= #x00B7 | #x02D0 | #x02D1 | #x0387 | #x0640 |
+ *                   #x0E46 | #x0EC6 | #x3005 | [#x3031-#x3035] |
+ *                   [#x309D-#x309E] | [#x30FC-#x30FE]
+ */
+#define IS_EXTENDER(c) xmlIsExtenderQ(c)
+
+/**
+ * IS_EXTENDER_CH:
+ * @c:  an xmlChar value (usually an unsigned char)
+ *
+ * Behaves like IS_EXTENDER but with a single-byte argument
+ */
+#define IS_EXTENDER_CH(c)  xmlIsExtender_ch(c)
+
+/**
+ * IS_IDEOGRAPHIC:
+ * @c:  an UNICODE value (int)
+ *
+ * Macro to check the following production in the XML spec:
+ *
+ *
+ * [86] Ideographic ::= [#x4E00-#x9FA5] | #x3007 | [#x3021-#x3029]
+ */
+#define IS_IDEOGRAPHIC(c) xmlIsIdeographicQ(c)
+
+/**
+ * IS_LETTER:
+ * @c:  an UNICODE value (int)
+ *
+ * Macro to check the following production in the XML spec:
+ *
+ *
+ * [84] Letter ::= BaseChar | Ideographic
+ */
+#define IS_LETTER(c) (IS_BASECHAR(c) || IS_IDEOGRAPHIC(c))
+
+/**
+ * IS_LETTER_CH:
+ * @c:  an xmlChar value (normally unsigned char)
+ *
+ * Macro behaves like IS_LETTER, but only check base chars
+ *
+ */
+#define IS_LETTER_CH(c) xmlIsBaseChar_ch(c)
+
+/**
+ * IS_ASCII_LETTER:
+ * @c: an xmlChar value
+ *
+ * Macro to check [a-zA-Z]
+ *
+ */
+#define IS_ASCII_LETTER(c)	(((0x41 <= (c)) && ((c) <= 0x5a)) || \
+				 ((0x61 <= (c)) && ((c) <= 0x7a)))
+
+/**
+ * IS_ASCII_DIGIT:
+ * @c: an xmlChar value
+ *
+ * Macro to check [0-9]
+ *
+ */
+#define IS_ASCII_DIGIT(c)	((0x30 <= (c)) && ((c) <= 0x39))
+
+/**
+ * IS_PUBIDCHAR:
+ * @c:  an UNICODE value (int)
+ *
+ * Macro to check the following production in the XML spec:
+ *
+ *
+ * [13] PubidChar ::= #x20 | #xD | #xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%]
+ */
+#define IS_PUBIDCHAR(c)	xmlIsPubidCharQ(c)
+
+/**
+ * IS_PUBIDCHAR_CH:
+ * @c:  an xmlChar value (normally unsigned char)
+ *
+ * Same as IS_PUBIDCHAR but for single-byte value
+ */
+#define IS_PUBIDCHAR_CH(c) xmlIsPubidChar_ch(c)
+
+/**
+ * Global variables used for predefined strings.
+ */
+XMLPUBVAR const xmlChar xmlStringText[];
+XMLPUBVAR const xmlChar xmlStringTextNoenc[];
+XMLPUBVAR const xmlChar xmlStringComment[];
+
+/*
+ * Function to finish the work of the macros where needed.
+ */
+XMLPUBFUN int                   xmlIsLetter     (int c);
+
+/**
+ * Parser context.
+ */
+XMLPUBFUN xmlParserCtxtPtr
+			xmlCreateFileParserCtxt	(const char *filename);
+XMLPUBFUN xmlParserCtxtPtr
+			xmlCreateURLParserCtxt	(const char *filename,
+						 int options);
+XMLPUBFUN xmlParserCtxtPtr
+			xmlCreateMemoryParserCtxt(const char *buffer,
+						 int size);
+XMLPUBFUN xmlParserCtxtPtr
+			xmlCreateEntityParserCtxt(const xmlChar *URL,
+						 const xmlChar *ID,
+						 const xmlChar *base);
+XMLPUBFUN int
+			xmlSwitchEncoding	(xmlParserCtxtPtr ctxt,
+						 xmlCharEncoding enc);
+XMLPUBFUN int
+			xmlSwitchToEncoding	(xmlParserCtxtPtr ctxt,
+					 xmlCharEncodingHandlerPtr handler);
+XML_DEPRECATED
+XMLPUBFUN int
+			xmlSwitchInputEncoding	(xmlParserCtxtPtr ctxt,
+						 xmlParserInputPtr input,
+					 xmlCharEncodingHandlerPtr handler);
+
+/**
+ * Input Streams.
+ */
+XMLPUBFUN xmlParserInputPtr
+			xmlNewStringInputStream	(xmlParserCtxtPtr ctxt,
+						 const xmlChar *buffer);
+XML_DEPRECATED
+XMLPUBFUN xmlParserInputPtr
+			xmlNewEntityInputStream	(xmlParserCtxtPtr ctxt,
+						 xmlEntityPtr entity);
+XMLPUBFUN int
+			xmlPushInput		(xmlParserCtxtPtr ctxt,
+						 xmlParserInputPtr input);
+XMLPUBFUN xmlChar
+			xmlPopInput		(xmlParserCtxtPtr ctxt);
+XMLPUBFUN void
+			xmlFreeInputStream	(xmlParserInputPtr input);
+XMLPUBFUN xmlParserInputPtr
+			xmlNewInputFromFile	(xmlParserCtxtPtr ctxt,
+						 const char *filename);
+XMLPUBFUN xmlParserInputPtr
+			xmlNewInputStream	(xmlParserCtxtPtr ctxt);
+
+/**
+ * Namespaces.
+ */
+XMLPUBFUN xmlChar *
+			xmlSplitQName		(xmlParserCtxtPtr ctxt,
+						 const xmlChar *name,
+						 xmlChar **prefix);
+
+/**
+ * Generic production rules.
+ */
+XML_DEPRECATED
+XMLPUBFUN const xmlChar *
+			xmlParseName		(xmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN xmlChar *
+			xmlParseNmtoken		(xmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN xmlChar *
+			xmlParseEntityValue	(xmlParserCtxtPtr ctxt,
+						 xmlChar **orig);
+XML_DEPRECATED
+XMLPUBFUN xmlChar *
+			xmlParseAttValue	(xmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN xmlChar *
+			xmlParseSystemLiteral	(xmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN xmlChar *
+			xmlParsePubidLiteral	(xmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN void
+			xmlParseCharData	(xmlParserCtxtPtr ctxt,
+						 int cdata);
+XML_DEPRECATED
+XMLPUBFUN xmlChar *
+			xmlParseExternalID	(xmlParserCtxtPtr ctxt,
+						 xmlChar **publicID,
+						 int strict);
+XML_DEPRECATED
+XMLPUBFUN void
+			xmlParseComment		(xmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN const xmlChar *
+			xmlParsePITarget	(xmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN void
+			xmlParsePI		(xmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN void
+			xmlParseNotationDecl	(xmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN void
+			xmlParseEntityDecl	(xmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN int
+			xmlParseDefaultDecl	(xmlParserCtxtPtr ctxt,
+						 xmlChar **value);
+XML_DEPRECATED
+XMLPUBFUN xmlEnumerationPtr
+			xmlParseNotationType	(xmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN xmlEnumerationPtr
+			xmlParseEnumerationType	(xmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN int
+			xmlParseEnumeratedType	(xmlParserCtxtPtr ctxt,
+						 xmlEnumerationPtr *tree);
+XML_DEPRECATED
+XMLPUBFUN int
+			xmlParseAttributeType	(xmlParserCtxtPtr ctxt,
+						 xmlEnumerationPtr *tree);
+XML_DEPRECATED
+XMLPUBFUN void
+			xmlParseAttributeListDecl(xmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN xmlElementContentPtr
+			xmlParseElementMixedContentDecl
+						(xmlParserCtxtPtr ctxt,
+						 int inputchk);
+XML_DEPRECATED
+XMLPUBFUN xmlElementContentPtr
+			xmlParseElementChildrenContentDecl
+						(xmlParserCtxtPtr ctxt,
+						 int inputchk);
+XML_DEPRECATED
+XMLPUBFUN int
+			xmlParseElementContentDecl(xmlParserCtxtPtr ctxt,
+						 const xmlChar *name,
+						 xmlElementContentPtr *result);
+XML_DEPRECATED
+XMLPUBFUN int
+			xmlParseElementDecl	(xmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN void
+			xmlParseMarkupDecl	(xmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN int
+			xmlParseCharRef		(xmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN xmlEntityPtr
+			xmlParseEntityRef	(xmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN void
+			xmlParseReference	(xmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN void
+			xmlParsePEReference	(xmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN void
+			xmlParseDocTypeDecl	(xmlParserCtxtPtr ctxt);
+#ifdef LIBXML_SAX1_ENABLED
+XML_DEPRECATED
+XMLPUBFUN const xmlChar *
+			xmlParseAttribute	(xmlParserCtxtPtr ctxt,
+						 xmlChar **value);
+XML_DEPRECATED
+XMLPUBFUN const xmlChar *
+			xmlParseStartTag	(xmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN void
+			xmlParseEndTag		(xmlParserCtxtPtr ctxt);
+#endif /* LIBXML_SAX1_ENABLED */
+XML_DEPRECATED
+XMLPUBFUN void
+			xmlParseCDSect		(xmlParserCtxtPtr ctxt);
+XMLPUBFUN void
+			xmlParseContent		(xmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN void
+			xmlParseElement		(xmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN xmlChar *
+			xmlParseVersionNum	(xmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN xmlChar *
+			xmlParseVersionInfo	(xmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN xmlChar *
+			xmlParseEncName		(xmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN const xmlChar *
+			xmlParseEncodingDecl	(xmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN int
+			xmlParseSDDecl		(xmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN void
+			xmlParseXMLDecl		(xmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN void
+			xmlParseTextDecl	(xmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN void
+			xmlParseMisc		(xmlParserCtxtPtr ctxt);
+XMLPUBFUN void
+			xmlParseExternalSubset	(xmlParserCtxtPtr ctxt,
+						 const xmlChar *ExternalID,
+						 const xmlChar *SystemID);
+/**
+ * XML_SUBSTITUTE_NONE:
+ *
+ * If no entities need to be substituted.
+ */
+#define XML_SUBSTITUTE_NONE	0
+/**
+ * XML_SUBSTITUTE_REF:
+ *
+ * Whether general entities need to be substituted.
+ */
+#define XML_SUBSTITUTE_REF	1
+/**
+ * XML_SUBSTITUTE_PEREF:
+ *
+ * Whether parameter entities need to be substituted.
+ */
+#define XML_SUBSTITUTE_PEREF	2
+/**
+ * XML_SUBSTITUTE_BOTH:
+ *
+ * Both general and parameter entities need to be substituted.
+ */
+#define XML_SUBSTITUTE_BOTH	3
+
+XML_DEPRECATED
+XMLPUBFUN xmlChar *
+		xmlStringDecodeEntities		(xmlParserCtxtPtr ctxt,
+						 const xmlChar *str,
+						 int what,
+						 xmlChar end,
+						 xmlChar  end2,
+						 xmlChar end3);
+XML_DEPRECATED
+XMLPUBFUN xmlChar *
+		xmlStringLenDecodeEntities	(xmlParserCtxtPtr ctxt,
+						 const xmlChar *str,
+						 int len,
+						 int what,
+						 xmlChar end,
+						 xmlChar  end2,
+						 xmlChar end3);
+
+/*
+ * Generated by MACROS on top of parser.c c.f. PUSH_AND_POP.
+ */
+XML_DEPRECATED
+XMLPUBFUN int			nodePush		(xmlParserCtxtPtr ctxt,
+						 xmlNodePtr value);
+XML_DEPRECATED
+XMLPUBFUN xmlNodePtr		nodePop			(xmlParserCtxtPtr ctxt);
+XMLPUBFUN int			inputPush		(xmlParserCtxtPtr ctxt,
+						 xmlParserInputPtr value);
+XMLPUBFUN xmlParserInputPtr	inputPop		(xmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN const xmlChar *	namePop			(xmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN int			namePush		(xmlParserCtxtPtr ctxt,
+						 const xmlChar *value);
+
+/*
+ * other commodities shared between parser.c and parserInternals.
+ */
+XML_DEPRECATED
+XMLPUBFUN int			xmlSkipBlankChars	(xmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN int			xmlStringCurrentChar	(xmlParserCtxtPtr ctxt,
+						 const xmlChar *cur,
+						 int *len);
+XML_DEPRECATED
+XMLPUBFUN void			xmlParserHandlePEReference(xmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN int			xmlCheckLanguageID	(const xmlChar *lang);
+
+/*
+ * Really core function shared with HTML parser.
+ */
+XML_DEPRECATED
+XMLPUBFUN int			xmlCurrentChar		(xmlParserCtxtPtr ctxt,
+						 int *len);
+XMLPUBFUN int		xmlCopyCharMultiByte	(xmlChar *out,
+						 int val);
+XMLPUBFUN int			xmlCopyChar		(int len,
+						 xmlChar *out,
+						 int val);
+XML_DEPRECATED
+XMLPUBFUN void			xmlNextChar		(xmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN void			xmlParserInputShrink	(xmlParserInputPtr in);
+
+/*
+ * Specific function to keep track of entities references
+ * and used by the XSLT debugger.
+ */
+#ifdef LIBXML_LEGACY_ENABLED
+/**
+ * xmlEntityReferenceFunc:
+ * @ent: the entity
+ * @firstNode:  the fist node in the chunk
+ * @lastNode:  the last nod in the chunk
+ *
+ * Callback function used when one needs to be able to track back the
+ * provenance of a chunk of nodes inherited from an entity replacement.
+ */
+typedef	void	(*xmlEntityReferenceFunc)	(xmlEntityPtr ent,
+						 xmlNodePtr firstNode,
+						 xmlNodePtr lastNode);
+
+XML_DEPRECATED
+XMLPUBFUN void		xmlSetEntityReferenceFunc	(xmlEntityReferenceFunc func);
+
+XML_DEPRECATED
+XMLPUBFUN xmlChar *
+			xmlParseQuotedString	(xmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN void
+                        xmlParseNamespace       (xmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN xmlChar *
+			xmlNamespaceParseNSDef	(xmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN xmlChar *
+			xmlScanName		(xmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN xmlChar *
+			xmlNamespaceParseNCName	(xmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN void	xmlParserHandleReference(xmlParserCtxtPtr ctxt);
+XML_DEPRECATED
+XMLPUBFUN xmlChar *
+			xmlNamespaceParseQName	(xmlParserCtxtPtr ctxt,
+						 xmlChar **prefix);
+/**
+ * Entities
+ */
+XML_DEPRECATED
+XMLPUBFUN xmlChar *
+		xmlDecodeEntities		(xmlParserCtxtPtr ctxt,
+						 int len,
+						 int what,
+						 xmlChar end,
+						 xmlChar  end2,
+						 xmlChar end3);
+XML_DEPRECATED
+XMLPUBFUN void
+			xmlHandleEntity		(xmlParserCtxtPtr ctxt,
+						 xmlEntityPtr entity);
+
+#endif /* LIBXML_LEGACY_ENABLED */
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __XML_PARSER_INTERNALS_H__ */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/relaxng.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/relaxng.h
new file mode 100644
index 00000000..079b7f12
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/relaxng.h
@@ -0,0 +1,219 @@
+/*
+ * Summary: implementation of the Relax-NG validation
+ * Description: implementation of the Relax-NG validation
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_RELAX_NG__
+#define __XML_RELAX_NG__
+
+#include <libxml/xmlversion.h>
+#include <libxml/xmlerror.h>
+#include <libxml/xmlstring.h>
+#include <libxml/tree.h>
+
+#ifdef LIBXML_SCHEMAS_ENABLED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct _xmlRelaxNG xmlRelaxNG;
+typedef xmlRelaxNG *xmlRelaxNGPtr;
+
+
+/**
+ * xmlRelaxNGValidityErrorFunc:
+ * @ctx: the validation context
+ * @msg: the message
+ * @...: extra arguments
+ *
+ * Signature of an error callback from a Relax-NG validation
+ */
+typedef void (*xmlRelaxNGValidityErrorFunc) (void *ctx,
+						      const char *msg,
+						      ...) LIBXML_ATTR_FORMAT(2,3);
+
+/**
+ * xmlRelaxNGValidityWarningFunc:
+ * @ctx: the validation context
+ * @msg: the message
+ * @...: extra arguments
+ *
+ * Signature of a warning callback from a Relax-NG validation
+ */
+typedef void (*xmlRelaxNGValidityWarningFunc) (void *ctx,
+							const char *msg,
+							...) LIBXML_ATTR_FORMAT(2,3);
+
+/**
+ * A schemas validation context
+ */
+typedef struct _xmlRelaxNGParserCtxt xmlRelaxNGParserCtxt;
+typedef xmlRelaxNGParserCtxt *xmlRelaxNGParserCtxtPtr;
+
+typedef struct _xmlRelaxNGValidCtxt xmlRelaxNGValidCtxt;
+typedef xmlRelaxNGValidCtxt *xmlRelaxNGValidCtxtPtr;
+
+/*
+ * xmlRelaxNGValidErr:
+ *
+ * List of possible Relax NG validation errors
+ */
+typedef enum {
+    XML_RELAXNG_OK = 0,
+    XML_RELAXNG_ERR_MEMORY,
+    XML_RELAXNG_ERR_TYPE,
+    XML_RELAXNG_ERR_TYPEVAL,
+    XML_RELAXNG_ERR_DUPID,
+    XML_RELAXNG_ERR_TYPECMP,
+    XML_RELAXNG_ERR_NOSTATE,
+    XML_RELAXNG_ERR_NODEFINE,
+    XML_RELAXNG_ERR_LISTEXTRA,
+    XML_RELAXNG_ERR_LISTEMPTY,
+    XML_RELAXNG_ERR_INTERNODATA,
+    XML_RELAXNG_ERR_INTERSEQ,
+    XML_RELAXNG_ERR_INTEREXTRA,
+    XML_RELAXNG_ERR_ELEMNAME,
+    XML_RELAXNG_ERR_ATTRNAME,
+    XML_RELAXNG_ERR_ELEMNONS,
+    XML_RELAXNG_ERR_ATTRNONS,
+    XML_RELAXNG_ERR_ELEMWRONGNS,
+    XML_RELAXNG_ERR_ATTRWRONGNS,
+    XML_RELAXNG_ERR_ELEMEXTRANS,
+    XML_RELAXNG_ERR_ATTREXTRANS,
+    XML_RELAXNG_ERR_ELEMNOTEMPTY,
+    XML_RELAXNG_ERR_NOELEM,
+    XML_RELAXNG_ERR_NOTELEM,
+    XML_RELAXNG_ERR_ATTRVALID,
+    XML_RELAXNG_ERR_CONTENTVALID,
+    XML_RELAXNG_ERR_EXTRACONTENT,
+    XML_RELAXNG_ERR_INVALIDATTR,
+    XML_RELAXNG_ERR_DATAELEM,
+    XML_RELAXNG_ERR_VALELEM,
+    XML_RELAXNG_ERR_LISTELEM,
+    XML_RELAXNG_ERR_DATATYPE,
+    XML_RELAXNG_ERR_VALUE,
+    XML_RELAXNG_ERR_LIST,
+    XML_RELAXNG_ERR_NOGRAMMAR,
+    XML_RELAXNG_ERR_EXTRADATA,
+    XML_RELAXNG_ERR_LACKDATA,
+    XML_RELAXNG_ERR_INTERNAL,
+    XML_RELAXNG_ERR_ELEMWRONG,
+    XML_RELAXNG_ERR_TEXTWRONG
+} xmlRelaxNGValidErr;
+
+/*
+ * xmlRelaxNGParserFlags:
+ *
+ * List of possible Relax NG Parser flags
+ */
+typedef enum {
+    XML_RELAXNGP_NONE = 0,
+    XML_RELAXNGP_FREE_DOC = 1,
+    XML_RELAXNGP_CRNG = 2
+} xmlRelaxNGParserFlag;
+
+XMLPUBFUN int
+		    xmlRelaxNGInitTypes		(void);
+XML_DEPRECATED
+XMLPUBFUN void
+		    xmlRelaxNGCleanupTypes	(void);
+
+/*
+ * Interfaces for parsing.
+ */
+XMLPUBFUN xmlRelaxNGParserCtxtPtr
+		    xmlRelaxNGNewParserCtxt	(const char *URL);
+XMLPUBFUN xmlRelaxNGParserCtxtPtr
+		    xmlRelaxNGNewMemParserCtxt	(const char *buffer,
+						 int size);
+XMLPUBFUN xmlRelaxNGParserCtxtPtr
+		    xmlRelaxNGNewDocParserCtxt	(xmlDocPtr doc);
+
+XMLPUBFUN int
+		    xmlRelaxParserSetFlag	(xmlRelaxNGParserCtxtPtr ctxt,
+						 int flag);
+
+XMLPUBFUN void
+		    xmlRelaxNGFreeParserCtxt	(xmlRelaxNGParserCtxtPtr ctxt);
+XMLPUBFUN void
+		    xmlRelaxNGSetParserErrors(xmlRelaxNGParserCtxtPtr ctxt,
+					 xmlRelaxNGValidityErrorFunc err,
+					 xmlRelaxNGValidityWarningFunc warn,
+					 void *ctx);
+XMLPUBFUN int
+		    xmlRelaxNGGetParserErrors(xmlRelaxNGParserCtxtPtr ctxt,
+					 xmlRelaxNGValidityErrorFunc *err,
+					 xmlRelaxNGValidityWarningFunc *warn,
+					 void **ctx);
+XMLPUBFUN void
+		    xmlRelaxNGSetParserStructuredErrors(
+					 xmlRelaxNGParserCtxtPtr ctxt,
+					 xmlStructuredErrorFunc serror,
+					 void *ctx);
+XMLPUBFUN xmlRelaxNGPtr
+		    xmlRelaxNGParse		(xmlRelaxNGParserCtxtPtr ctxt);
+XMLPUBFUN void
+		    xmlRelaxNGFree		(xmlRelaxNGPtr schema);
+#ifdef LIBXML_OUTPUT_ENABLED
+XMLPUBFUN void
+		    xmlRelaxNGDump		(FILE *output,
+					 xmlRelaxNGPtr schema);
+XMLPUBFUN void
+		    xmlRelaxNGDumpTree	(FILE * output,
+					 xmlRelaxNGPtr schema);
+#endif /* LIBXML_OUTPUT_ENABLED */
+/*
+ * Interfaces for validating
+ */
+XMLPUBFUN void
+		    xmlRelaxNGSetValidErrors(xmlRelaxNGValidCtxtPtr ctxt,
+					 xmlRelaxNGValidityErrorFunc err,
+					 xmlRelaxNGValidityWarningFunc warn,
+					 void *ctx);
+XMLPUBFUN int
+		    xmlRelaxNGGetValidErrors(xmlRelaxNGValidCtxtPtr ctxt,
+					 xmlRelaxNGValidityErrorFunc *err,
+					 xmlRelaxNGValidityWarningFunc *warn,
+					 void **ctx);
+XMLPUBFUN void
+			xmlRelaxNGSetValidStructuredErrors(xmlRelaxNGValidCtxtPtr ctxt,
+					  xmlStructuredErrorFunc serror, void *ctx);
+XMLPUBFUN xmlRelaxNGValidCtxtPtr
+		    xmlRelaxNGNewValidCtxt	(xmlRelaxNGPtr schema);
+XMLPUBFUN void
+		    xmlRelaxNGFreeValidCtxt	(xmlRelaxNGValidCtxtPtr ctxt);
+XMLPUBFUN int
+		    xmlRelaxNGValidateDoc	(xmlRelaxNGValidCtxtPtr ctxt,
+						 xmlDocPtr doc);
+/*
+ * Interfaces for progressive validation when possible
+ */
+XMLPUBFUN int
+		    xmlRelaxNGValidatePushElement	(xmlRelaxNGValidCtxtPtr ctxt,
+					 xmlDocPtr doc,
+					 xmlNodePtr elem);
+XMLPUBFUN int
+		    xmlRelaxNGValidatePushCData	(xmlRelaxNGValidCtxtPtr ctxt,
+					 const xmlChar *data,
+					 int len);
+XMLPUBFUN int
+		    xmlRelaxNGValidatePopElement	(xmlRelaxNGValidCtxtPtr ctxt,
+					 xmlDocPtr doc,
+					 xmlNodePtr elem);
+XMLPUBFUN int
+		    xmlRelaxNGValidateFullElement	(xmlRelaxNGValidCtxtPtr ctxt,
+					 xmlDocPtr doc,
+					 xmlNodePtr elem);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBXML_SCHEMAS_ENABLED */
+
+#endif /* __XML_RELAX_NG__ */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/schemasInternals.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/schemasInternals.h
new file mode 100644
index 00000000..e9d3b3c7
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/schemasInternals.h
@@ -0,0 +1,959 @@
+/*
+ * Summary: internal interfaces for XML Schemas
+ * Description: internal interfaces for the XML Schemas handling
+ *              and schema validity checking
+ *		The Schemas development is a Work In Progress.
+ *              Some of those interfaces are not guaranteed to be API or ABI stable !
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+
+#ifndef __XML_SCHEMA_INTERNALS_H__
+#define __XML_SCHEMA_INTERNALS_H__
+
+#include <libxml/xmlversion.h>
+
+#ifdef LIBXML_SCHEMAS_ENABLED
+
+#include <libxml/xmlregexp.h>
+#include <libxml/hash.h>
+#include <libxml/dict.h>
+#include <libxml/tree.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+    XML_SCHEMAS_UNKNOWN = 0,
+    XML_SCHEMAS_STRING = 1,
+    XML_SCHEMAS_NORMSTRING = 2,
+    XML_SCHEMAS_DECIMAL = 3,
+    XML_SCHEMAS_TIME = 4,
+    XML_SCHEMAS_GDAY = 5,
+    XML_SCHEMAS_GMONTH = 6,
+    XML_SCHEMAS_GMONTHDAY = 7,
+    XML_SCHEMAS_GYEAR = 8,
+    XML_SCHEMAS_GYEARMONTH = 9,
+    XML_SCHEMAS_DATE = 10,
+    XML_SCHEMAS_DATETIME = 11,
+    XML_SCHEMAS_DURATION = 12,
+    XML_SCHEMAS_FLOAT = 13,
+    XML_SCHEMAS_DOUBLE = 14,
+    XML_SCHEMAS_BOOLEAN = 15,
+    XML_SCHEMAS_TOKEN = 16,
+    XML_SCHEMAS_LANGUAGE = 17,
+    XML_SCHEMAS_NMTOKEN = 18,
+    XML_SCHEMAS_NMTOKENS = 19,
+    XML_SCHEMAS_NAME = 20,
+    XML_SCHEMAS_QNAME = 21,
+    XML_SCHEMAS_NCNAME = 22,
+    XML_SCHEMAS_ID = 23,
+    XML_SCHEMAS_IDREF = 24,
+    XML_SCHEMAS_IDREFS = 25,
+    XML_SCHEMAS_ENTITY = 26,
+    XML_SCHEMAS_ENTITIES = 27,
+    XML_SCHEMAS_NOTATION = 28,
+    XML_SCHEMAS_ANYURI = 29,
+    XML_SCHEMAS_INTEGER = 30,
+    XML_SCHEMAS_NPINTEGER = 31,
+    XML_SCHEMAS_NINTEGER = 32,
+    XML_SCHEMAS_NNINTEGER = 33,
+    XML_SCHEMAS_PINTEGER = 34,
+    XML_SCHEMAS_INT = 35,
+    XML_SCHEMAS_UINT = 36,
+    XML_SCHEMAS_LONG = 37,
+    XML_SCHEMAS_ULONG = 38,
+    XML_SCHEMAS_SHORT = 39,
+    XML_SCHEMAS_USHORT = 40,
+    XML_SCHEMAS_BYTE = 41,
+    XML_SCHEMAS_UBYTE = 42,
+    XML_SCHEMAS_HEXBINARY = 43,
+    XML_SCHEMAS_BASE64BINARY = 44,
+    XML_SCHEMAS_ANYTYPE = 45,
+    XML_SCHEMAS_ANYSIMPLETYPE = 46
+} xmlSchemaValType;
+
+/*
+ * XML Schemas defines multiple type of types.
+ */
+typedef enum {
+    XML_SCHEMA_TYPE_BASIC = 1, /* A built-in datatype */
+    XML_SCHEMA_TYPE_ANY,
+    XML_SCHEMA_TYPE_FACET,
+    XML_SCHEMA_TYPE_SIMPLE,
+    XML_SCHEMA_TYPE_COMPLEX,
+    XML_SCHEMA_TYPE_SEQUENCE = 6,
+    XML_SCHEMA_TYPE_CHOICE,
+    XML_SCHEMA_TYPE_ALL,
+    XML_SCHEMA_TYPE_SIMPLE_CONTENT,
+    XML_SCHEMA_TYPE_COMPLEX_CONTENT,
+    XML_SCHEMA_TYPE_UR,
+    XML_SCHEMA_TYPE_RESTRICTION,
+    XML_SCHEMA_TYPE_EXTENSION,
+    XML_SCHEMA_TYPE_ELEMENT,
+    XML_SCHEMA_TYPE_ATTRIBUTE,
+    XML_SCHEMA_TYPE_ATTRIBUTEGROUP,
+    XML_SCHEMA_TYPE_GROUP,
+    XML_SCHEMA_TYPE_NOTATION,
+    XML_SCHEMA_TYPE_LIST,
+    XML_SCHEMA_TYPE_UNION,
+    XML_SCHEMA_TYPE_ANY_ATTRIBUTE,
+    XML_SCHEMA_TYPE_IDC_UNIQUE,
+    XML_SCHEMA_TYPE_IDC_KEY,
+    XML_SCHEMA_TYPE_IDC_KEYREF,
+    XML_SCHEMA_TYPE_PARTICLE = 25,
+    XML_SCHEMA_TYPE_ATTRIBUTE_USE,
+    XML_SCHEMA_FACET_MININCLUSIVE = 1000,
+    XML_SCHEMA_FACET_MINEXCLUSIVE,
+    XML_SCHEMA_FACET_MAXINCLUSIVE,
+    XML_SCHEMA_FACET_MAXEXCLUSIVE,
+    XML_SCHEMA_FACET_TOTALDIGITS,
+    XML_SCHEMA_FACET_FRACTIONDIGITS,
+    XML_SCHEMA_FACET_PATTERN,
+    XML_SCHEMA_FACET_ENUMERATION,
+    XML_SCHEMA_FACET_WHITESPACE,
+    XML_SCHEMA_FACET_LENGTH,
+    XML_SCHEMA_FACET_MAXLENGTH,
+    XML_SCHEMA_FACET_MINLENGTH,
+    XML_SCHEMA_EXTRA_QNAMEREF = 2000,
+    XML_SCHEMA_EXTRA_ATTR_USE_PROHIB
+} xmlSchemaTypeType;
+
+typedef enum {
+    XML_SCHEMA_CONTENT_UNKNOWN = 0,
+    XML_SCHEMA_CONTENT_EMPTY = 1,
+    XML_SCHEMA_CONTENT_ELEMENTS,
+    XML_SCHEMA_CONTENT_MIXED,
+    XML_SCHEMA_CONTENT_SIMPLE,
+    XML_SCHEMA_CONTENT_MIXED_OR_ELEMENTS, /* Obsolete */
+    XML_SCHEMA_CONTENT_BASIC,
+    XML_SCHEMA_CONTENT_ANY
+} xmlSchemaContentType;
+
+typedef struct _xmlSchemaVal xmlSchemaVal;
+typedef xmlSchemaVal *xmlSchemaValPtr;
+
+typedef struct _xmlSchemaType xmlSchemaType;
+typedef xmlSchemaType *xmlSchemaTypePtr;
+
+typedef struct _xmlSchemaFacet xmlSchemaFacet;
+typedef xmlSchemaFacet *xmlSchemaFacetPtr;
+
+/**
+ * Annotation
+ */
+typedef struct _xmlSchemaAnnot xmlSchemaAnnot;
+typedef xmlSchemaAnnot *xmlSchemaAnnotPtr;
+struct _xmlSchemaAnnot {
+    struct _xmlSchemaAnnot *next;
+    xmlNodePtr content;         /* the annotation */
+};
+
+/**
+ * XML_SCHEMAS_ANYATTR_SKIP:
+ *
+ * Skip unknown attribute from validation
+ * Obsolete, not used anymore.
+ */
+#define XML_SCHEMAS_ANYATTR_SKIP        1
+/**
+ * XML_SCHEMAS_ANYATTR_LAX:
+ *
+ * Ignore validation non definition on attributes
+ * Obsolete, not used anymore.
+ */
+#define XML_SCHEMAS_ANYATTR_LAX                2
+/**
+ * XML_SCHEMAS_ANYATTR_STRICT:
+ *
+ * Apply strict validation rules on attributes
+ * Obsolete, not used anymore.
+ */
+#define XML_SCHEMAS_ANYATTR_STRICT        3
+/**
+ * XML_SCHEMAS_ANY_SKIP:
+ *
+ * Skip unknown attribute from validation
+ */
+#define XML_SCHEMAS_ANY_SKIP        1
+/**
+ * XML_SCHEMAS_ANY_LAX:
+ *
+ * Used by wildcards.
+ * Validate if type found, don't worry if not found
+ */
+#define XML_SCHEMAS_ANY_LAX                2
+/**
+ * XML_SCHEMAS_ANY_STRICT:
+ *
+ * Used by wildcards.
+ * Apply strict validation rules
+ */
+#define XML_SCHEMAS_ANY_STRICT        3
+/**
+ * XML_SCHEMAS_ATTR_USE_PROHIBITED:
+ *
+ * Used by wildcards.
+ * The attribute is prohibited.
+ */
+#define XML_SCHEMAS_ATTR_USE_PROHIBITED 0
+/**
+ * XML_SCHEMAS_ATTR_USE_REQUIRED:
+ *
+ * The attribute is required.
+ */
+#define XML_SCHEMAS_ATTR_USE_REQUIRED 1
+/**
+ * XML_SCHEMAS_ATTR_USE_OPTIONAL:
+ *
+ * The attribute is optional.
+ */
+#define XML_SCHEMAS_ATTR_USE_OPTIONAL 2
+/**
+ * XML_SCHEMAS_ATTR_GLOBAL:
+ *
+ * allow elements in no namespace
+ */
+#define XML_SCHEMAS_ATTR_GLOBAL        1 << 0
+/**
+ * XML_SCHEMAS_ATTR_NSDEFAULT:
+ *
+ * allow elements in no namespace
+ */
+#define XML_SCHEMAS_ATTR_NSDEFAULT        1 << 7
+/**
+ * XML_SCHEMAS_ATTR_INTERNAL_RESOLVED:
+ *
+ * this is set when the "type" and "ref" references
+ * have been resolved.
+ */
+#define XML_SCHEMAS_ATTR_INTERNAL_RESOLVED        1 << 8
+/**
+ * XML_SCHEMAS_ATTR_FIXED:
+ *
+ * the attribute has a fixed value
+ */
+#define XML_SCHEMAS_ATTR_FIXED        1 << 9
+
+/**
+ * xmlSchemaAttribute:
+ * An attribute definition.
+ */
+
+typedef struct _xmlSchemaAttribute xmlSchemaAttribute;
+typedef xmlSchemaAttribute *xmlSchemaAttributePtr;
+struct _xmlSchemaAttribute {
+    xmlSchemaTypeType type;
+    struct _xmlSchemaAttribute *next; /* the next attribute (not used?) */
+    const xmlChar *name; /* the name of the declaration */
+    const xmlChar *id; /* Deprecated; not used */
+    const xmlChar *ref; /* Deprecated; not used */
+    const xmlChar *refNs; /* Deprecated; not used */
+    const xmlChar *typeName; /* the local name of the type definition */
+    const xmlChar *typeNs; /* the ns URI of the type definition */
+    xmlSchemaAnnotPtr annot;
+
+    xmlSchemaTypePtr base; /* Deprecated; not used */
+    int occurs; /* Deprecated; not used */
+    const xmlChar *defValue; /* The initial value of the value constraint */
+    xmlSchemaTypePtr subtypes; /* the type definition */
+    xmlNodePtr node;
+    const xmlChar *targetNamespace;
+    int flags;
+    const xmlChar *refPrefix; /* Deprecated; not used */
+    xmlSchemaValPtr defVal; /* The compiled value constraint */
+    xmlSchemaAttributePtr refDecl; /* Deprecated; not used */
+};
+
+/**
+ * xmlSchemaAttributeLink:
+ * Used to build a list of attribute uses on complexType definitions.
+ * WARNING: Deprecated; not used.
+ */
+typedef struct _xmlSchemaAttributeLink xmlSchemaAttributeLink;
+typedef xmlSchemaAttributeLink *xmlSchemaAttributeLinkPtr;
+struct _xmlSchemaAttributeLink {
+    struct _xmlSchemaAttributeLink *next;/* the next attribute link ... */
+    struct _xmlSchemaAttribute *attr;/* the linked attribute */
+};
+
+/**
+ * XML_SCHEMAS_WILDCARD_COMPLETE:
+ *
+ * If the wildcard is complete.
+ */
+#define XML_SCHEMAS_WILDCARD_COMPLETE 1 << 0
+
+/**
+ * xmlSchemaCharValueLink:
+ * Used to build a list of namespaces on wildcards.
+ */
+typedef struct _xmlSchemaWildcardNs xmlSchemaWildcardNs;
+typedef xmlSchemaWildcardNs *xmlSchemaWildcardNsPtr;
+struct _xmlSchemaWildcardNs {
+    struct _xmlSchemaWildcardNs *next;/* the next constraint link ... */
+    const xmlChar *value;/* the value */
+};
+
+/**
+ * xmlSchemaWildcard.
+ * A wildcard.
+ */
+typedef struct _xmlSchemaWildcard xmlSchemaWildcard;
+typedef xmlSchemaWildcard *xmlSchemaWildcardPtr;
+struct _xmlSchemaWildcard {
+    xmlSchemaTypeType type;        /* The kind of type */
+    const xmlChar *id; /* Deprecated; not used */
+    xmlSchemaAnnotPtr annot;
+    xmlNodePtr node;
+    int minOccurs; /* Deprecated; not used */
+    int maxOccurs; /* Deprecated; not used */
+    int processContents;
+    int any; /* Indicates if the ns constraint is of ##any */
+    xmlSchemaWildcardNsPtr nsSet; /* The list of allowed namespaces */
+    xmlSchemaWildcardNsPtr negNsSet; /* The negated namespace */
+    int flags;
+};
+
+/**
+ * XML_SCHEMAS_ATTRGROUP_WILDCARD_BUILDED:
+ *
+ * The attribute wildcard has been built.
+ */
+#define XML_SCHEMAS_ATTRGROUP_WILDCARD_BUILDED 1 << 0
+/**
+ * XML_SCHEMAS_ATTRGROUP_GLOBAL:
+ *
+ * The attribute group has been defined.
+ */
+#define XML_SCHEMAS_ATTRGROUP_GLOBAL 1 << 1
+/**
+ * XML_SCHEMAS_ATTRGROUP_MARKED:
+ *
+ * Marks the attr group as marked; used for circular checks.
+ */
+#define XML_SCHEMAS_ATTRGROUP_MARKED 1 << 2
+
+/**
+ * XML_SCHEMAS_ATTRGROUP_REDEFINED:
+ *
+ * The attr group was redefined.
+ */
+#define XML_SCHEMAS_ATTRGROUP_REDEFINED 1 << 3
+/**
+ * XML_SCHEMAS_ATTRGROUP_HAS_REFS:
+ *
+ * Whether this attr. group contains attr. group references.
+ */
+#define XML_SCHEMAS_ATTRGROUP_HAS_REFS 1 << 4
+
+/**
+ * An attribute group definition.
+ *
+ * xmlSchemaAttribute and xmlSchemaAttributeGroup start of structures
+ * must be kept similar
+ */
+typedef struct _xmlSchemaAttributeGroup xmlSchemaAttributeGroup;
+typedef xmlSchemaAttributeGroup *xmlSchemaAttributeGroupPtr;
+struct _xmlSchemaAttributeGroup {
+    xmlSchemaTypeType type;        /* The kind of type */
+    struct _xmlSchemaAttribute *next;/* the next attribute if in a group ... */
+    const xmlChar *name;
+    const xmlChar *id;
+    const xmlChar *ref; /* Deprecated; not used */
+    const xmlChar *refNs; /* Deprecated; not used */
+    xmlSchemaAnnotPtr annot;
+
+    xmlSchemaAttributePtr attributes; /* Deprecated; not used */
+    xmlNodePtr node;
+    int flags;
+    xmlSchemaWildcardPtr attributeWildcard;
+    const xmlChar *refPrefix; /* Deprecated; not used */
+    xmlSchemaAttributeGroupPtr refItem; /* Deprecated; not used */
+    const xmlChar *targetNamespace;
+    void *attrUses;
+};
+
+/**
+ * xmlSchemaTypeLink:
+ * Used to build a list of types (e.g. member types of
+ * simpleType with variety "union").
+ */
+typedef struct _xmlSchemaTypeLink xmlSchemaTypeLink;
+typedef xmlSchemaTypeLink *xmlSchemaTypeLinkPtr;
+struct _xmlSchemaTypeLink {
+    struct _xmlSchemaTypeLink *next;/* the next type link ... */
+    xmlSchemaTypePtr type;/* the linked type */
+};
+
+/**
+ * xmlSchemaFacetLink:
+ * Used to build a list of facets.
+ */
+typedef struct _xmlSchemaFacetLink xmlSchemaFacetLink;
+typedef xmlSchemaFacetLink *xmlSchemaFacetLinkPtr;
+struct _xmlSchemaFacetLink {
+    struct _xmlSchemaFacetLink *next;/* the next facet link ... */
+    xmlSchemaFacetPtr facet;/* the linked facet */
+};
+
+/**
+ * XML_SCHEMAS_TYPE_MIXED:
+ *
+ * the element content type is mixed
+ */
+#define XML_SCHEMAS_TYPE_MIXED                1 << 0
+/**
+ * XML_SCHEMAS_TYPE_DERIVATION_METHOD_EXTENSION:
+ *
+ * the simple or complex type has a derivation method of "extension".
+ */
+#define XML_SCHEMAS_TYPE_DERIVATION_METHOD_EXTENSION                1 << 1
+/**
+ * XML_SCHEMAS_TYPE_DERIVATION_METHOD_RESTRICTION:
+ *
+ * the simple or complex type has a derivation method of "restriction".
+ */
+#define XML_SCHEMAS_TYPE_DERIVATION_METHOD_RESTRICTION                1 << 2
+/**
+ * XML_SCHEMAS_TYPE_GLOBAL:
+ *
+ * the type is global
+ */
+#define XML_SCHEMAS_TYPE_GLOBAL                1 << 3
+/**
+ * XML_SCHEMAS_TYPE_OWNED_ATTR_WILDCARD:
+ *
+ * the complexType owns an attribute wildcard, i.e.
+ * it can be freed by the complexType
+ */
+#define XML_SCHEMAS_TYPE_OWNED_ATTR_WILDCARD    1 << 4 /* Obsolete. */
+/**
+ * XML_SCHEMAS_TYPE_VARIETY_ABSENT:
+ *
+ * the simpleType has a variety of "absent".
+ * TODO: Actually not necessary :-/, since if
+ * none of the variety flags occur then it's
+ * automatically absent.
+ */
+#define XML_SCHEMAS_TYPE_VARIETY_ABSENT    1 << 5
+/**
+ * XML_SCHEMAS_TYPE_VARIETY_LIST:
+ *
+ * the simpleType has a variety of "list".
+ */
+#define XML_SCHEMAS_TYPE_VARIETY_LIST    1 << 6
+/**
+ * XML_SCHEMAS_TYPE_VARIETY_UNION:
+ *
+ * the simpleType has a variety of "union".
+ */
+#define XML_SCHEMAS_TYPE_VARIETY_UNION    1 << 7
+/**
+ * XML_SCHEMAS_TYPE_VARIETY_ATOMIC:
+ *
+ * the simpleType has a variety of "union".
+ */
+#define XML_SCHEMAS_TYPE_VARIETY_ATOMIC    1 << 8
+/**
+ * XML_SCHEMAS_TYPE_FINAL_EXTENSION:
+ *
+ * the complexType has a final of "extension".
+ */
+#define XML_SCHEMAS_TYPE_FINAL_EXTENSION    1 << 9
+/**
+ * XML_SCHEMAS_TYPE_FINAL_RESTRICTION:
+ *
+ * the simpleType/complexType has a final of "restriction".
+ */
+#define XML_SCHEMAS_TYPE_FINAL_RESTRICTION    1 << 10
+/**
+ * XML_SCHEMAS_TYPE_FINAL_LIST:
+ *
+ * the simpleType has a final of "list".
+ */
+#define XML_SCHEMAS_TYPE_FINAL_LIST    1 << 11
+/**
+ * XML_SCHEMAS_TYPE_FINAL_UNION:
+ *
+ * the simpleType has a final of "union".
+ */
+#define XML_SCHEMAS_TYPE_FINAL_UNION    1 << 12
+/**
+ * XML_SCHEMAS_TYPE_FINAL_DEFAULT:
+ *
+ * the simpleType has a final of "default".
+ */
+#define XML_SCHEMAS_TYPE_FINAL_DEFAULT    1 << 13
+/**
+ * XML_SCHEMAS_TYPE_BUILTIN_PRIMITIVE:
+ *
+ * Marks the item as a builtin primitive.
+ */
+#define XML_SCHEMAS_TYPE_BUILTIN_PRIMITIVE    1 << 14
+/**
+ * XML_SCHEMAS_TYPE_MARKED:
+ *
+ * Marks the item as marked; used for circular checks.
+ */
+#define XML_SCHEMAS_TYPE_MARKED        1 << 16
+/**
+ * XML_SCHEMAS_TYPE_BLOCK_DEFAULT:
+ *
+ * the complexType did not specify 'block' so use the default of the
+ * <schema> item.
+ */
+#define XML_SCHEMAS_TYPE_BLOCK_DEFAULT    1 << 17
+/**
+ * XML_SCHEMAS_TYPE_BLOCK_EXTENSION:
+ *
+ * the complexType has a 'block' of "extension".
+ */
+#define XML_SCHEMAS_TYPE_BLOCK_EXTENSION    1 << 18
+/**
+ * XML_SCHEMAS_TYPE_BLOCK_RESTRICTION:
+ *
+ * the complexType has a 'block' of "restriction".
+ */
+#define XML_SCHEMAS_TYPE_BLOCK_RESTRICTION    1 << 19
+/**
+ * XML_SCHEMAS_TYPE_ABSTRACT:
+ *
+ * the simple/complexType is abstract.
+ */
+#define XML_SCHEMAS_TYPE_ABSTRACT    1 << 20
+/**
+ * XML_SCHEMAS_TYPE_FACETSNEEDVALUE:
+ *
+ * indicates if the facets need a computed value
+ */
+#define XML_SCHEMAS_TYPE_FACETSNEEDVALUE    1 << 21
+/**
+ * XML_SCHEMAS_TYPE_INTERNAL_RESOLVED:
+ *
+ * indicates that the type was typefixed
+ */
+#define XML_SCHEMAS_TYPE_INTERNAL_RESOLVED    1 << 22
+/**
+ * XML_SCHEMAS_TYPE_INTERNAL_INVALID:
+ *
+ * indicates that the type is invalid
+ */
+#define XML_SCHEMAS_TYPE_INTERNAL_INVALID    1 << 23
+/**
+ * XML_SCHEMAS_TYPE_WHITESPACE_PRESERVE:
+ *
+ * a whitespace-facet value of "preserve"
+ */
+#define XML_SCHEMAS_TYPE_WHITESPACE_PRESERVE    1 << 24
+/**
+ * XML_SCHEMAS_TYPE_WHITESPACE_REPLACE:
+ *
+ * a whitespace-facet value of "replace"
+ */
+#define XML_SCHEMAS_TYPE_WHITESPACE_REPLACE    1 << 25
+/**
+ * XML_SCHEMAS_TYPE_WHITESPACE_COLLAPSE:
+ *
+ * a whitespace-facet value of "collapse"
+ */
+#define XML_SCHEMAS_TYPE_WHITESPACE_COLLAPSE    1 << 26
+/**
+ * XML_SCHEMAS_TYPE_HAS_FACETS:
+ *
+ * has facets
+ */
+#define XML_SCHEMAS_TYPE_HAS_FACETS    1 << 27
+/**
+ * XML_SCHEMAS_TYPE_NORMVALUENEEDED:
+ *
+ * indicates if the facets (pattern) need a normalized value
+ */
+#define XML_SCHEMAS_TYPE_NORMVALUENEEDED    1 << 28
+
+/**
+ * XML_SCHEMAS_TYPE_FIXUP_1:
+ *
+ * First stage of fixup was done.
+ */
+#define XML_SCHEMAS_TYPE_FIXUP_1    1 << 29
+
+/**
+ * XML_SCHEMAS_TYPE_REDEFINED:
+ *
+ * The type was redefined.
+ */
+#define XML_SCHEMAS_TYPE_REDEFINED    1 << 30
+/**
+ * XML_SCHEMAS_TYPE_REDEFINING:
+ *
+ * The type redefines an other type.
+ */
+/* #define XML_SCHEMAS_TYPE_REDEFINING    1 << 31 */
+
+/**
+ * _xmlSchemaType:
+ *
+ * Schemas type definition.
+ */
+struct _xmlSchemaType {
+    xmlSchemaTypeType type; /* The kind of type */
+    struct _xmlSchemaType *next; /* the next type if in a sequence ... */
+    const xmlChar *name;
+    const xmlChar *id ; /* Deprecated; not used */
+    const xmlChar *ref; /* Deprecated; not used */
+    const xmlChar *refNs; /* Deprecated; not used */
+    xmlSchemaAnnotPtr annot;
+    xmlSchemaTypePtr subtypes;
+    xmlSchemaAttributePtr attributes; /* Deprecated; not used */
+    xmlNodePtr node;
+    int minOccurs; /* Deprecated; not used */
+    int maxOccurs; /* Deprecated; not used */
+
+    int flags;
+    xmlSchemaContentType contentType;
+    const xmlChar *base; /* Base type's local name */
+    const xmlChar *baseNs; /* Base type's target namespace */
+    xmlSchemaTypePtr baseType; /* The base type component */
+    xmlSchemaFacetPtr facets; /* Local facets */
+    struct _xmlSchemaType *redef; /* Deprecated; not used */
+    int recurse; /* Obsolete */
+    xmlSchemaAttributeLinkPtr *attributeUses; /* Deprecated; not used */
+    xmlSchemaWildcardPtr attributeWildcard;
+    int builtInType; /* Type of built-in types. */
+    xmlSchemaTypeLinkPtr memberTypes; /* member-types if a union type. */
+    xmlSchemaFacetLinkPtr facetSet; /* All facets (incl. inherited) */
+    const xmlChar *refPrefix; /* Deprecated; not used */
+    xmlSchemaTypePtr contentTypeDef; /* Used for the simple content of complex types.
+                                        Could we use @subtypes for this? */
+    xmlRegexpPtr contModel; /* Holds the automaton of the content model */
+    const xmlChar *targetNamespace;
+    void *attrUses;
+};
+
+/*
+ * xmlSchemaElement:
+ * An element definition.
+ *
+ * xmlSchemaType, xmlSchemaFacet and xmlSchemaElement start of
+ * structures must be kept similar
+ */
+/**
+ * XML_SCHEMAS_ELEM_NILLABLE:
+ *
+ * the element is nillable
+ */
+#define XML_SCHEMAS_ELEM_NILLABLE        1 << 0
+/**
+ * XML_SCHEMAS_ELEM_GLOBAL:
+ *
+ * the element is global
+ */
+#define XML_SCHEMAS_ELEM_GLOBAL                1 << 1
+/**
+ * XML_SCHEMAS_ELEM_DEFAULT:
+ *
+ * the element has a default value
+ */
+#define XML_SCHEMAS_ELEM_DEFAULT        1 << 2
+/**
+ * XML_SCHEMAS_ELEM_FIXED:
+ *
+ * the element has a fixed value
+ */
+#define XML_SCHEMAS_ELEM_FIXED                1 << 3
+/**
+ * XML_SCHEMAS_ELEM_ABSTRACT:
+ *
+ * the element is abstract
+ */
+#define XML_SCHEMAS_ELEM_ABSTRACT        1 << 4
+/**
+ * XML_SCHEMAS_ELEM_TOPLEVEL:
+ *
+ * the element is top level
+ * obsolete: use XML_SCHEMAS_ELEM_GLOBAL instead
+ */
+#define XML_SCHEMAS_ELEM_TOPLEVEL        1 << 5
+/**
+ * XML_SCHEMAS_ELEM_REF:
+ *
+ * the element is a reference to a type
+ */
+#define XML_SCHEMAS_ELEM_REF                1 << 6
+/**
+ * XML_SCHEMAS_ELEM_NSDEFAULT:
+ *
+ * allow elements in no namespace
+ * Obsolete, not used anymore.
+ */
+#define XML_SCHEMAS_ELEM_NSDEFAULT        1 << 7
+/**
+ * XML_SCHEMAS_ELEM_INTERNAL_RESOLVED:
+ *
+ * this is set when "type", "ref", "substitutionGroup"
+ * references have been resolved.
+ */
+#define XML_SCHEMAS_ELEM_INTERNAL_RESOLVED        1 << 8
+ /**
+ * XML_SCHEMAS_ELEM_CIRCULAR:
+ *
+ * a helper flag for the search of circular references.
+ */
+#define XML_SCHEMAS_ELEM_CIRCULAR        1 << 9
+/**
+ * XML_SCHEMAS_ELEM_BLOCK_ABSENT:
+ *
+ * the "block" attribute is absent
+ */
+#define XML_SCHEMAS_ELEM_BLOCK_ABSENT        1 << 10
+/**
+ * XML_SCHEMAS_ELEM_BLOCK_EXTENSION:
+ *
+ * disallowed substitutions are absent
+ */
+#define XML_SCHEMAS_ELEM_BLOCK_EXTENSION        1 << 11
+/**
+ * XML_SCHEMAS_ELEM_BLOCK_RESTRICTION:
+ *
+ * disallowed substitutions: "restriction"
+ */
+#define XML_SCHEMAS_ELEM_BLOCK_RESTRICTION        1 << 12
+/**
+ * XML_SCHEMAS_ELEM_BLOCK_SUBSTITUTION:
+ *
+ * disallowed substitutions: "substitution"
+ */
+#define XML_SCHEMAS_ELEM_BLOCK_SUBSTITUTION        1 << 13
+/**
+ * XML_SCHEMAS_ELEM_FINAL_ABSENT:
+ *
+ * substitution group exclusions are absent
+ */
+#define XML_SCHEMAS_ELEM_FINAL_ABSENT        1 << 14
+/**
+ * XML_SCHEMAS_ELEM_FINAL_EXTENSION:
+ *
+ * substitution group exclusions: "extension"
+ */
+#define XML_SCHEMAS_ELEM_FINAL_EXTENSION        1 << 15
+/**
+ * XML_SCHEMAS_ELEM_FINAL_RESTRICTION:
+ *
+ * substitution group exclusions: "restriction"
+ */
+#define XML_SCHEMAS_ELEM_FINAL_RESTRICTION        1 << 16
+/**
+ * XML_SCHEMAS_ELEM_SUBST_GROUP_HEAD:
+ *
+ * the declaration is a substitution group head
+ */
+#define XML_SCHEMAS_ELEM_SUBST_GROUP_HEAD        1 << 17
+/**
+ * XML_SCHEMAS_ELEM_INTERNAL_CHECKED:
+ *
+ * this is set when the elem decl has been checked against
+ * all constraints
+ */
+#define XML_SCHEMAS_ELEM_INTERNAL_CHECKED        1 << 18
+
+typedef struct _xmlSchemaElement xmlSchemaElement;
+typedef xmlSchemaElement *xmlSchemaElementPtr;
+struct _xmlSchemaElement {
+    xmlSchemaTypeType type; /* The kind of type */
+    struct _xmlSchemaType *next; /* Not used? */
+    const xmlChar *name;
+    const xmlChar *id; /* Deprecated; not used */
+    const xmlChar *ref; /* Deprecated; not used */
+    const xmlChar *refNs; /* Deprecated; not used */
+    xmlSchemaAnnotPtr annot;
+    xmlSchemaTypePtr subtypes; /* the type definition */
+    xmlSchemaAttributePtr attributes;
+    xmlNodePtr node;
+    int minOccurs; /* Deprecated; not used */
+    int maxOccurs; /* Deprecated; not used */
+
+    int flags;
+    const xmlChar *targetNamespace;
+    const xmlChar *namedType;
+    const xmlChar *namedTypeNs;
+    const xmlChar *substGroup;
+    const xmlChar *substGroupNs;
+    const xmlChar *scope;
+    const xmlChar *value; /* The original value of the value constraint. */
+    struct _xmlSchemaElement *refDecl; /* This will now be used for the
+                                          substitution group affiliation */
+    xmlRegexpPtr contModel; /* Obsolete for WXS, maybe used for RelaxNG */
+    xmlSchemaContentType contentType;
+    const xmlChar *refPrefix; /* Deprecated; not used */
+    xmlSchemaValPtr defVal; /* The compiled value constraint. */
+    void *idcs; /* The identity-constraint defs */
+};
+
+/*
+ * XML_SCHEMAS_FACET_UNKNOWN:
+ *
+ * unknown facet handling
+ */
+#define XML_SCHEMAS_FACET_UNKNOWN        0
+/*
+ * XML_SCHEMAS_FACET_PRESERVE:
+ *
+ * preserve the type of the facet
+ */
+#define XML_SCHEMAS_FACET_PRESERVE        1
+/*
+ * XML_SCHEMAS_FACET_REPLACE:
+ *
+ * replace the type of the facet
+ */
+#define XML_SCHEMAS_FACET_REPLACE        2
+/*
+ * XML_SCHEMAS_FACET_COLLAPSE:
+ *
+ * collapse the types of the facet
+ */
+#define XML_SCHEMAS_FACET_COLLAPSE        3
+/**
+ * A facet definition.
+ */
+struct _xmlSchemaFacet {
+    xmlSchemaTypeType type;        /* The kind of type */
+    struct _xmlSchemaFacet *next;/* the next type if in a sequence ... */
+    const xmlChar *value; /* The original value */
+    const xmlChar *id; /* Obsolete */
+    xmlSchemaAnnotPtr annot;
+    xmlNodePtr node;
+    int fixed; /* XML_SCHEMAS_FACET_PRESERVE, etc. */
+    int whitespace;
+    xmlSchemaValPtr val; /* The compiled value */
+    xmlRegexpPtr    regexp; /* The regex for patterns */
+};
+
+/**
+ * A notation definition.
+ */
+typedef struct _xmlSchemaNotation xmlSchemaNotation;
+typedef xmlSchemaNotation *xmlSchemaNotationPtr;
+struct _xmlSchemaNotation {
+    xmlSchemaTypeType type; /* The kind of type */
+    const xmlChar *name;
+    xmlSchemaAnnotPtr annot;
+    const xmlChar *identifier;
+    const xmlChar *targetNamespace;
+};
+
+/*
+* TODO: Actually all those flags used for the schema should sit
+* on the schema parser context, since they are used only
+* during parsing an XML schema document, and not available
+* on the component level as per spec.
+*/
+/**
+ * XML_SCHEMAS_QUALIF_ELEM:
+ *
+ * Reflects elementFormDefault == qualified in
+ * an XML schema document.
+ */
+#define XML_SCHEMAS_QUALIF_ELEM                1 << 0
+/**
+ * XML_SCHEMAS_QUALIF_ATTR:
+ *
+ * Reflects attributeFormDefault == qualified in
+ * an XML schema document.
+ */
+#define XML_SCHEMAS_QUALIF_ATTR            1 << 1
+/**
+ * XML_SCHEMAS_FINAL_DEFAULT_EXTENSION:
+ *
+ * the schema has "extension" in the set of finalDefault.
+ */
+#define XML_SCHEMAS_FINAL_DEFAULT_EXTENSION        1 << 2
+/**
+ * XML_SCHEMAS_FINAL_DEFAULT_RESTRICTION:
+ *
+ * the schema has "restriction" in the set of finalDefault.
+ */
+#define XML_SCHEMAS_FINAL_DEFAULT_RESTRICTION            1 << 3
+/**
+ * XML_SCHEMAS_FINAL_DEFAULT_LIST:
+ *
+ * the schema has "list" in the set of finalDefault.
+ */
+#define XML_SCHEMAS_FINAL_DEFAULT_LIST            1 << 4
+/**
+ * XML_SCHEMAS_FINAL_DEFAULT_UNION:
+ *
+ * the schema has "union" in the set of finalDefault.
+ */
+#define XML_SCHEMAS_FINAL_DEFAULT_UNION            1 << 5
+/**
+ * XML_SCHEMAS_BLOCK_DEFAULT_EXTENSION:
+ *
+ * the schema has "extension" in the set of blockDefault.
+ */
+#define XML_SCHEMAS_BLOCK_DEFAULT_EXTENSION            1 << 6
+/**
+ * XML_SCHEMAS_BLOCK_DEFAULT_RESTRICTION:
+ *
+ * the schema has "restriction" in the set of blockDefault.
+ */
+#define XML_SCHEMAS_BLOCK_DEFAULT_RESTRICTION            1 << 7
+/**
+ * XML_SCHEMAS_BLOCK_DEFAULT_SUBSTITUTION:
+ *
+ * the schema has "substitution" in the set of blockDefault.
+ */
+#define XML_SCHEMAS_BLOCK_DEFAULT_SUBSTITUTION            1 << 8
+/**
+ * XML_SCHEMAS_INCLUDING_CONVERT_NS:
+ *
+ * the schema is currently including an other schema with
+ * no target namespace.
+ */
+#define XML_SCHEMAS_INCLUDING_CONVERT_NS            1 << 9
+/**
+ * _xmlSchema:
+ *
+ * A Schemas definition
+ */
+struct _xmlSchema {
+    const xmlChar *name; /* schema name */
+    const xmlChar *targetNamespace; /* the target namespace */
+    const xmlChar *version;
+    const xmlChar *id; /* Obsolete */
+    xmlDocPtr doc;
+    xmlSchemaAnnotPtr annot;
+    int flags;
+
+    xmlHashTablePtr typeDecl;
+    xmlHashTablePtr attrDecl;
+    xmlHashTablePtr attrgrpDecl;
+    xmlHashTablePtr elemDecl;
+    xmlHashTablePtr notaDecl;
+
+    xmlHashTablePtr schemasImports;
+
+    void *_private;        /* unused by the library for users or bindings */
+    xmlHashTablePtr groupDecl;
+    xmlDictPtr      dict;
+    void *includes;     /* the includes, this is opaque for now */
+    int preserve;        /* whether to free the document */
+    int counter; /* used to give anonymous components unique names */
+    xmlHashTablePtr idcDef; /* All identity-constraint defs. */
+    void *volatiles; /* Obsolete */
+};
+
+XMLPUBFUN void         xmlSchemaFreeType        (xmlSchemaTypePtr type);
+XMLPUBFUN void         xmlSchemaFreeWildcard(xmlSchemaWildcardPtr wildcard);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBXML_SCHEMAS_ENABLED */
+#endif /* __XML_SCHEMA_INTERNALS_H__ */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/schematron.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/schematron.h
new file mode 100644
index 00000000..8dd8d25c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/schematron.h
@@ -0,0 +1,143 @@
+/*
+ * Summary: XML Schematron implementation
+ * Description: interface to the XML Schematron validity checking.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+
+#ifndef __XML_SCHEMATRON_H__
+#define __XML_SCHEMATRON_H__
+
+#include <libxml/xmlversion.h>
+
+#ifdef LIBXML_SCHEMATRON_ENABLED
+
+#include <libxml/xmlerror.h>
+#include <libxml/tree.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+    XML_SCHEMATRON_OUT_QUIET = 1 << 0,	/* quiet no report */
+    XML_SCHEMATRON_OUT_TEXT = 1 << 1,	/* build a textual report */
+    XML_SCHEMATRON_OUT_XML = 1 << 2,	/* output SVRL */
+    XML_SCHEMATRON_OUT_ERROR = 1 << 3,  /* output via xmlStructuredErrorFunc */
+    XML_SCHEMATRON_OUT_FILE = 1 << 8,	/* output to a file descriptor */
+    XML_SCHEMATRON_OUT_BUFFER = 1 << 9,	/* output to a buffer */
+    XML_SCHEMATRON_OUT_IO = 1 << 10	/* output to I/O mechanism */
+} xmlSchematronValidOptions;
+
+/**
+ * The schemas related types are kept internal
+ */
+typedef struct _xmlSchematron xmlSchematron;
+typedef xmlSchematron *xmlSchematronPtr;
+
+/**
+ * xmlSchematronValidityErrorFunc:
+ * @ctx: the validation context
+ * @msg: the message
+ * @...: extra arguments
+ *
+ * Signature of an error callback from a Schematron validation
+ */
+typedef void (*xmlSchematronValidityErrorFunc) (void *ctx, const char *msg, ...);
+
+/**
+ * xmlSchematronValidityWarningFunc:
+ * @ctx: the validation context
+ * @msg: the message
+ * @...: extra arguments
+ *
+ * Signature of a warning callback from a Schematron validation
+ */
+typedef void (*xmlSchematronValidityWarningFunc) (void *ctx, const char *msg, ...);
+
+/**
+ * A schemas validation context
+ */
+typedef struct _xmlSchematronParserCtxt xmlSchematronParserCtxt;
+typedef xmlSchematronParserCtxt *xmlSchematronParserCtxtPtr;
+
+typedef struct _xmlSchematronValidCtxt xmlSchematronValidCtxt;
+typedef xmlSchematronValidCtxt *xmlSchematronValidCtxtPtr;
+
+/*
+ * Interfaces for parsing.
+ */
+XMLPUBFUN xmlSchematronParserCtxtPtr
+	    xmlSchematronNewParserCtxt	(const char *URL);
+XMLPUBFUN xmlSchematronParserCtxtPtr
+	    xmlSchematronNewMemParserCtxt(const char *buffer,
+					 int size);
+XMLPUBFUN xmlSchematronParserCtxtPtr
+	    xmlSchematronNewDocParserCtxt(xmlDocPtr doc);
+XMLPUBFUN void
+	    xmlSchematronFreeParserCtxt	(xmlSchematronParserCtxtPtr ctxt);
+/*****
+XMLPUBFUN void
+	    xmlSchematronSetParserErrors(xmlSchematronParserCtxtPtr ctxt,
+					 xmlSchematronValidityErrorFunc err,
+					 xmlSchematronValidityWarningFunc warn,
+					 void *ctx);
+XMLPUBFUN int
+		xmlSchematronGetParserErrors(xmlSchematronParserCtxtPtr ctxt,
+					xmlSchematronValidityErrorFunc * err,
+					xmlSchematronValidityWarningFunc * warn,
+					void **ctx);
+XMLPUBFUN int
+		xmlSchematronIsValid	(xmlSchematronValidCtxtPtr ctxt);
+ *****/
+XMLPUBFUN xmlSchematronPtr
+	    xmlSchematronParse		(xmlSchematronParserCtxtPtr ctxt);
+XMLPUBFUN void
+	    xmlSchematronFree		(xmlSchematronPtr schema);
+/*
+ * Interfaces for validating
+ */
+XMLPUBFUN void
+	    xmlSchematronSetValidStructuredErrors(
+	                                  xmlSchematronValidCtxtPtr ctxt,
+					  xmlStructuredErrorFunc serror,
+					  void *ctx);
+/******
+XMLPUBFUN void
+	    xmlSchematronSetValidErrors	(xmlSchematronValidCtxtPtr ctxt,
+					 xmlSchematronValidityErrorFunc err,
+					 xmlSchematronValidityWarningFunc warn,
+					 void *ctx);
+XMLPUBFUN int
+	    xmlSchematronGetValidErrors	(xmlSchematronValidCtxtPtr ctxt,
+					 xmlSchematronValidityErrorFunc *err,
+					 xmlSchematronValidityWarningFunc *warn,
+					 void **ctx);
+XMLPUBFUN int
+	    xmlSchematronSetValidOptions(xmlSchematronValidCtxtPtr ctxt,
+					 int options);
+XMLPUBFUN int
+	    xmlSchematronValidCtxtGetOptions(xmlSchematronValidCtxtPtr ctxt);
+XMLPUBFUN int
+            xmlSchematronValidateOneElement (xmlSchematronValidCtxtPtr ctxt,
+			                 xmlNodePtr elem);
+ *******/
+
+XMLPUBFUN xmlSchematronValidCtxtPtr
+	    xmlSchematronNewValidCtxt	(xmlSchematronPtr schema,
+					 int options);
+XMLPUBFUN void
+	    xmlSchematronFreeValidCtxt	(xmlSchematronValidCtxtPtr ctxt);
+XMLPUBFUN int
+	    xmlSchematronValidateDoc	(xmlSchematronValidCtxtPtr ctxt,
+					 xmlDocPtr instance);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBXML_SCHEMATRON_ENABLED */
+#endif /* __XML_SCHEMATRON_H__ */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/threads.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/threads.h
new file mode 100644
index 00000000..8f4b6e17
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/threads.h
@@ -0,0 +1,87 @@
+/**
+ * Summary: interfaces for thread handling
+ * Description: set of generic threading related routines
+ *              should work with pthreads, Windows native or TLS threads
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_THREADS_H__
+#define __XML_THREADS_H__
+
+#include <libxml/xmlversion.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * xmlMutex are a simple mutual exception locks.
+ */
+typedef struct _xmlMutex xmlMutex;
+typedef xmlMutex *xmlMutexPtr;
+
+/*
+ * xmlRMutex are reentrant mutual exception locks.
+ */
+typedef struct _xmlRMutex xmlRMutex;
+typedef xmlRMutex *xmlRMutexPtr;
+
+XMLPUBFUN int
+			xmlCheckThreadLocalStorage(void);
+
+XMLPUBFUN xmlMutexPtr
+			xmlNewMutex	(void);
+XMLPUBFUN void
+			xmlMutexLock	(xmlMutexPtr tok);
+XMLPUBFUN void
+			xmlMutexUnlock	(xmlMutexPtr tok);
+XMLPUBFUN void
+			xmlFreeMutex	(xmlMutexPtr tok);
+
+XMLPUBFUN xmlRMutexPtr
+			xmlNewRMutex	(void);
+XMLPUBFUN void
+			xmlRMutexLock	(xmlRMutexPtr tok);
+XMLPUBFUN void
+			xmlRMutexUnlock	(xmlRMutexPtr tok);
+XMLPUBFUN void
+			xmlFreeRMutex	(xmlRMutexPtr tok);
+
+/*
+ * Library wide APIs.
+ */
+XML_DEPRECATED
+XMLPUBFUN void
+			xmlInitThreads	(void);
+XMLPUBFUN void
+			xmlLockLibrary	(void);
+XMLPUBFUN void
+			xmlUnlockLibrary(void);
+XML_DEPRECATED
+XMLPUBFUN int
+			xmlGetThreadId	(void);
+XML_DEPRECATED
+XMLPUBFUN int
+			xmlIsMainThread	(void);
+XML_DEPRECATED
+XMLPUBFUN void
+			xmlCleanupThreads(void);
+
+/** DOC_DISABLE */
+#if defined(LIBXML_THREAD_ENABLED) && defined(_WIN32) && \
+    defined(LIBXML_STATIC_FOR_DLL)
+int
+xmlDllMain(void *hinstDLL, unsigned long fdwReason,
+           void *lpvReserved);
+#endif
+/** DOC_ENABLE */
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* __XML_THREADS_H__ */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/tree.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/tree.h
new file mode 100644
index 00000000..a90a174f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/tree.h
@@ -0,0 +1,1362 @@
+/*
+ * Summary: interfaces for tree manipulation
+ * Description: this module describes the structures found in an tree resulting
+ *              from an XML or HTML parsing, as well as the API provided for
+ *              various processing on that tree
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef XML_TREE_INTERNALS
+
+/*
+ * Emulate circular dependency for backward compatibility
+ */
+#include <libxml/parser.h>
+
+#else /* XML_TREE_INTERNALS */
+
+#ifndef __XML_TREE_H__
+#define __XML_TREE_H__
+
+#include <stdio.h>
+#include <limits.h>
+#include <libxml/xmlversion.h>
+#include <libxml/xmlstring.h>
+#include <libxml/xmlmemory.h>
+#include <libxml/xmlregexp.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Some of the basic types pointer to structures:
+ */
+/* xmlIO.h */
+typedef struct _xmlParserInputBuffer xmlParserInputBuffer;
+typedef xmlParserInputBuffer *xmlParserInputBufferPtr;
+
+typedef struct _xmlOutputBuffer xmlOutputBuffer;
+typedef xmlOutputBuffer *xmlOutputBufferPtr;
+
+/* parser.h */
+typedef struct _xmlParserInput xmlParserInput;
+typedef xmlParserInput *xmlParserInputPtr;
+
+typedef struct _xmlParserCtxt xmlParserCtxt;
+typedef xmlParserCtxt *xmlParserCtxtPtr;
+
+typedef struct _xmlSAXLocator xmlSAXLocator;
+typedef xmlSAXLocator *xmlSAXLocatorPtr;
+
+typedef struct _xmlSAXHandler xmlSAXHandler;
+typedef xmlSAXHandler *xmlSAXHandlerPtr;
+
+/* entities.h */
+typedef struct _xmlEntity xmlEntity;
+typedef xmlEntity *xmlEntityPtr;
+
+/**
+ * BASE_BUFFER_SIZE:
+ *
+ * default buffer size 4000.
+ */
+#define BASE_BUFFER_SIZE 4096
+
+/**
+ * LIBXML_NAMESPACE_DICT:
+ *
+ * Defines experimental behaviour:
+ * 1) xmlNs gets an additional field @context (a xmlDoc)
+ * 2) when creating a tree, xmlNs->href is stored in the dict of xmlDoc.
+ */
+/* #define LIBXML_NAMESPACE_DICT */
+
+/**
+ * xmlBufferAllocationScheme:
+ *
+ * A buffer allocation scheme can be defined to either match exactly the
+ * need or double it's allocated size each time it is found too small.
+ */
+
+typedef enum {
+    XML_BUFFER_ALLOC_DOUBLEIT,	/* double each time one need to grow */
+    XML_BUFFER_ALLOC_EXACT,	/* grow only to the minimal size */
+    XML_BUFFER_ALLOC_IMMUTABLE, /* immutable buffer, deprecated */
+    XML_BUFFER_ALLOC_IO,	/* special allocation scheme used for I/O */
+    XML_BUFFER_ALLOC_HYBRID,	/* exact up to a threshold, and doubleit thereafter */
+    XML_BUFFER_ALLOC_BOUNDED	/* limit the upper size of the buffer */
+} xmlBufferAllocationScheme;
+
+/**
+ * xmlBuffer:
+ *
+ * A buffer structure, this old construct is limited to 2GB and
+ * is being deprecated, use API with xmlBuf instead
+ */
+typedef struct _xmlBuffer xmlBuffer;
+typedef xmlBuffer *xmlBufferPtr;
+struct _xmlBuffer {
+    xmlChar *content;		/* The buffer content UTF8 */
+    unsigned int use;		/* The buffer size used */
+    unsigned int size;		/* The buffer size */
+    xmlBufferAllocationScheme alloc; /* The realloc method */
+    xmlChar *contentIO;		/* in IO mode we may have a different base */
+};
+
+/**
+ * xmlBuf:
+ *
+ * A buffer structure, new one, the actual structure internals are not public
+ */
+
+typedef struct _xmlBuf xmlBuf;
+
+/**
+ * xmlBufPtr:
+ *
+ * A pointer to a buffer structure, the actual structure internals are not
+ * public
+ */
+
+typedef xmlBuf *xmlBufPtr;
+
+/*
+ * A few public routines for xmlBuf. As those are expected to be used
+ * mostly internally the bulk of the routines are internal in buf.h
+ */
+XMLPUBFUN xmlChar*       xmlBufContent	(const xmlBuf* buf);
+XMLPUBFUN xmlChar*       xmlBufEnd      (xmlBufPtr buf);
+XMLPUBFUN size_t         xmlBufUse      (const xmlBufPtr buf);
+XMLPUBFUN size_t         xmlBufShrink	(xmlBufPtr buf, size_t len);
+
+/*
+ * LIBXML2_NEW_BUFFER:
+ *
+ * Macro used to express that the API use the new buffers for
+ * xmlParserInputBuffer and xmlOutputBuffer. The change was
+ * introduced in 2.9.0.
+ */
+#define LIBXML2_NEW_BUFFER
+
+/**
+ * XML_XML_NAMESPACE:
+ *
+ * This is the namespace for the special xml: prefix predefined in the
+ * XML Namespace specification.
+ */
+#define XML_XML_NAMESPACE \
+    (const xmlChar *) "http://www.w3.org/XML/1998/namespace"
+
+/**
+ * XML_XML_ID:
+ *
+ * This is the name for the special xml:id attribute
+ */
+#define XML_XML_ID (const xmlChar *) "xml:id"
+
+/*
+ * The different element types carried by an XML tree.
+ *
+ * NOTE: This is synchronized with DOM Level1 values
+ *       See http://www.w3.org/TR/REC-DOM-Level-1/
+ *
+ * Actually this had diverged a bit, and now XML_DOCUMENT_TYPE_NODE should
+ * be deprecated to use an XML_DTD_NODE.
+ */
+typedef enum {
+    XML_ELEMENT_NODE=		1,
+    XML_ATTRIBUTE_NODE=		2,
+    XML_TEXT_NODE=		3,
+    XML_CDATA_SECTION_NODE=	4,
+    XML_ENTITY_REF_NODE=	5,
+    XML_ENTITY_NODE=		6,
+    XML_PI_NODE=		7,
+    XML_COMMENT_NODE=		8,
+    XML_DOCUMENT_NODE=		9,
+    XML_DOCUMENT_TYPE_NODE=	10,
+    XML_DOCUMENT_FRAG_NODE=	11,
+    XML_NOTATION_NODE=		12,
+    XML_HTML_DOCUMENT_NODE=	13,
+    XML_DTD_NODE=		14,
+    XML_ELEMENT_DECL=		15,
+    XML_ATTRIBUTE_DECL=		16,
+    XML_ENTITY_DECL=		17,
+    XML_NAMESPACE_DECL=		18,
+    XML_XINCLUDE_START=		19,
+    XML_XINCLUDE_END=		20
+    /* XML_DOCB_DOCUMENT_NODE=	21 */ /* removed */
+} xmlElementType;
+
+/** DOC_DISABLE */
+/* For backward compatibility */
+#define XML_DOCB_DOCUMENT_NODE 21
+/** DOC_ENABLE */
+
+/**
+ * xmlNotation:
+ *
+ * A DTD Notation definition.
+ */
+
+typedef struct _xmlNotation xmlNotation;
+typedef xmlNotation *xmlNotationPtr;
+struct _xmlNotation {
+    const xmlChar               *name;	        /* Notation name */
+    const xmlChar               *PublicID;	/* Public identifier, if any */
+    const xmlChar               *SystemID;	/* System identifier, if any */
+};
+
+/**
+ * xmlAttributeType:
+ *
+ * A DTD Attribute type definition.
+ */
+
+typedef enum {
+    XML_ATTRIBUTE_CDATA = 1,
+    XML_ATTRIBUTE_ID,
+    XML_ATTRIBUTE_IDREF	,
+    XML_ATTRIBUTE_IDREFS,
+    XML_ATTRIBUTE_ENTITY,
+    XML_ATTRIBUTE_ENTITIES,
+    XML_ATTRIBUTE_NMTOKEN,
+    XML_ATTRIBUTE_NMTOKENS,
+    XML_ATTRIBUTE_ENUMERATION,
+    XML_ATTRIBUTE_NOTATION
+} xmlAttributeType;
+
+/**
+ * xmlAttributeDefault:
+ *
+ * A DTD Attribute default definition.
+ */
+
+typedef enum {
+    XML_ATTRIBUTE_NONE = 1,
+    XML_ATTRIBUTE_REQUIRED,
+    XML_ATTRIBUTE_IMPLIED,
+    XML_ATTRIBUTE_FIXED
+} xmlAttributeDefault;
+
+/**
+ * xmlEnumeration:
+ *
+ * List structure used when there is an enumeration in DTDs.
+ */
+
+typedef struct _xmlEnumeration xmlEnumeration;
+typedef xmlEnumeration *xmlEnumerationPtr;
+struct _xmlEnumeration {
+    struct _xmlEnumeration    *next;	/* next one */
+    const xmlChar            *name;	/* Enumeration name */
+};
+
+/**
+ * xmlAttribute:
+ *
+ * An Attribute declaration in a DTD.
+ */
+
+typedef struct _xmlAttribute xmlAttribute;
+typedef xmlAttribute *xmlAttributePtr;
+struct _xmlAttribute {
+    void           *_private;	        /* application data */
+    xmlElementType          type;       /* XML_ATTRIBUTE_DECL, must be second ! */
+    const xmlChar          *name;	/* Attribute name */
+    struct _xmlNode    *children;	/* NULL */
+    struct _xmlNode        *last;	/* NULL */
+    struct _xmlDtd       *parent;	/* -> DTD */
+    struct _xmlNode        *next;	/* next sibling link  */
+    struct _xmlNode        *prev;	/* previous sibling link  */
+    struct _xmlDoc          *doc;       /* the containing document */
+
+    struct _xmlAttribute  *nexth;	/* next in hash table */
+    xmlAttributeType       atype;	/* The attribute type */
+    xmlAttributeDefault      def;	/* the default */
+    const xmlChar  *defaultValue;	/* or the default value */
+    xmlEnumerationPtr       tree;       /* or the enumeration tree if any */
+    const xmlChar        *prefix;	/* the namespace prefix if any */
+    const xmlChar          *elem;	/* Element holding the attribute */
+};
+
+/**
+ * xmlElementContentType:
+ *
+ * Possible definitions of element content types.
+ */
+typedef enum {
+    XML_ELEMENT_CONTENT_PCDATA = 1,
+    XML_ELEMENT_CONTENT_ELEMENT,
+    XML_ELEMENT_CONTENT_SEQ,
+    XML_ELEMENT_CONTENT_OR
+} xmlElementContentType;
+
+/**
+ * xmlElementContentOccur:
+ *
+ * Possible definitions of element content occurrences.
+ */
+typedef enum {
+    XML_ELEMENT_CONTENT_ONCE = 1,
+    XML_ELEMENT_CONTENT_OPT,
+    XML_ELEMENT_CONTENT_MULT,
+    XML_ELEMENT_CONTENT_PLUS
+} xmlElementContentOccur;
+
+/**
+ * xmlElementContent:
+ *
+ * An XML Element content as stored after parsing an element definition
+ * in a DTD.
+ */
+
+typedef struct _xmlElementContent xmlElementContent;
+typedef xmlElementContent *xmlElementContentPtr;
+struct _xmlElementContent {
+    xmlElementContentType     type;	/* PCDATA, ELEMENT, SEQ or OR */
+    xmlElementContentOccur    ocur;	/* ONCE, OPT, MULT or PLUS */
+    const xmlChar             *name;	/* Element name */
+    struct _xmlElementContent *c1;	/* first child */
+    struct _xmlElementContent *c2;	/* second child */
+    struct _xmlElementContent *parent;	/* parent */
+    const xmlChar             *prefix;	/* Namespace prefix */
+};
+
+/**
+ * xmlElementTypeVal:
+ *
+ * The different possibilities for an element content type.
+ */
+
+typedef enum {
+    XML_ELEMENT_TYPE_UNDEFINED = 0,
+    XML_ELEMENT_TYPE_EMPTY = 1,
+    XML_ELEMENT_TYPE_ANY,
+    XML_ELEMENT_TYPE_MIXED,
+    XML_ELEMENT_TYPE_ELEMENT
+} xmlElementTypeVal;
+
+/**
+ * xmlElement:
+ *
+ * An XML Element declaration from a DTD.
+ */
+
+typedef struct _xmlElement xmlElement;
+typedef xmlElement *xmlElementPtr;
+struct _xmlElement {
+    void           *_private;	        /* application data */
+    xmlElementType          type;       /* XML_ELEMENT_DECL, must be second ! */
+    const xmlChar          *name;	/* Element name */
+    struct _xmlNode    *children;	/* NULL */
+    struct _xmlNode        *last;	/* NULL */
+    struct _xmlDtd       *parent;	/* -> DTD */
+    struct _xmlNode        *next;	/* next sibling link  */
+    struct _xmlNode        *prev;	/* previous sibling link  */
+    struct _xmlDoc          *doc;       /* the containing document */
+
+    xmlElementTypeVal      etype;	/* The type */
+    xmlElementContentPtr content;	/* the allowed element content */
+    xmlAttributePtr   attributes;	/* List of the declared attributes */
+    const xmlChar        *prefix;	/* the namespace prefix if any */
+#ifdef LIBXML_REGEXP_ENABLED
+    xmlRegexpPtr       contModel;	/* the validating regexp */
+#else
+    void	      *contModel;
+#endif
+};
+
+
+/**
+ * XML_LOCAL_NAMESPACE:
+ *
+ * A namespace declaration node.
+ */
+#define XML_LOCAL_NAMESPACE XML_NAMESPACE_DECL
+typedef xmlElementType xmlNsType;
+
+/**
+ * xmlNs:
+ *
+ * An XML namespace.
+ * Note that prefix == NULL is valid, it defines the default namespace
+ * within the subtree (until overridden).
+ *
+ * xmlNsType is unified with xmlElementType.
+ */
+
+typedef struct _xmlNs xmlNs;
+typedef xmlNs *xmlNsPtr;
+struct _xmlNs {
+    struct _xmlNs  *next;	/* next Ns link for this node  */
+    xmlNsType      type;	/* global or local */
+    const xmlChar *href;	/* URL for the namespace */
+    const xmlChar *prefix;	/* prefix for the namespace */
+    void           *_private;   /* application data */
+    struct _xmlDoc *context;		/* normally an xmlDoc */
+};
+
+/**
+ * xmlDtd:
+ *
+ * An XML DTD, as defined by <!DOCTYPE ... There is actually one for
+ * the internal subset and for the external subset.
+ */
+typedef struct _xmlDtd xmlDtd;
+typedef xmlDtd *xmlDtdPtr;
+struct _xmlDtd {
+    void           *_private;	/* application data */
+    xmlElementType  type;       /* XML_DTD_NODE, must be second ! */
+    const xmlChar *name;	/* Name of the DTD */
+    struct _xmlNode *children;	/* the value of the property link */
+    struct _xmlNode *last;	/* last child link */
+    struct _xmlDoc  *parent;	/* child->parent link */
+    struct _xmlNode *next;	/* next sibling link  */
+    struct _xmlNode *prev;	/* previous sibling link  */
+    struct _xmlDoc  *doc;	/* the containing document */
+
+    /* End of common part */
+    void          *notations;   /* Hash table for notations if any */
+    void          *elements;    /* Hash table for elements if any */
+    void          *attributes;  /* Hash table for attributes if any */
+    void          *entities;    /* Hash table for entities if any */
+    const xmlChar *ExternalID;	/* External identifier for PUBLIC DTD */
+    const xmlChar *SystemID;	/* URI for a SYSTEM or PUBLIC DTD */
+    void          *pentities;   /* Hash table for param entities if any */
+};
+
+/**
+ * xmlAttr:
+ *
+ * An attribute on an XML node.
+ */
+typedef struct _xmlAttr xmlAttr;
+typedef xmlAttr *xmlAttrPtr;
+struct _xmlAttr {
+    void           *_private;	/* application data */
+    xmlElementType   type;      /* XML_ATTRIBUTE_NODE, must be second ! */
+    const xmlChar   *name;      /* the name of the property */
+    struct _xmlNode *children;	/* the value of the property */
+    struct _xmlNode *last;	/* NULL */
+    struct _xmlNode *parent;	/* child->parent link */
+    struct _xmlAttr *next;	/* next sibling link  */
+    struct _xmlAttr *prev;	/* previous sibling link  */
+    struct _xmlDoc  *doc;	/* the containing document */
+    xmlNs           *ns;        /* pointer to the associated namespace */
+    xmlAttributeType atype;     /* the attribute type if validating */
+    void            *psvi;	/* for type/PSVI information */
+};
+
+/**
+ * xmlID:
+ *
+ * An XML ID instance.
+ */
+
+typedef struct _xmlID xmlID;
+typedef xmlID *xmlIDPtr;
+struct _xmlID {
+    struct _xmlID    *next;	/* next ID */
+    const xmlChar    *value;	/* The ID name */
+    xmlAttrPtr        attr;	/* The attribute holding it */
+    const xmlChar    *name;	/* The attribute if attr is not available */
+    int               lineno;	/* The line number if attr is not available */
+    struct _xmlDoc   *doc;	/* The document holding the ID */
+};
+
+/**
+ * xmlRef:
+ *
+ * An XML IDREF instance.
+ */
+
+typedef struct _xmlRef xmlRef;
+typedef xmlRef *xmlRefPtr;
+struct _xmlRef {
+    struct _xmlRef    *next;	/* next Ref */
+    const xmlChar     *value;	/* The Ref name */
+    xmlAttrPtr        attr;	/* The attribute holding it */
+    const xmlChar    *name;	/* The attribute if attr is not available */
+    int               lineno;	/* The line number if attr is not available */
+};
+
+/**
+ * xmlNode:
+ *
+ * A node in an XML tree.
+ */
+typedef struct _xmlNode xmlNode;
+typedef xmlNode *xmlNodePtr;
+struct _xmlNode {
+    void           *_private;	/* application data */
+    xmlElementType   type;	/* type number, must be second ! */
+    const xmlChar   *name;      /* the name of the node, or the entity */
+    struct _xmlNode *children;	/* parent->childs link */
+    struct _xmlNode *last;	/* last child link */
+    struct _xmlNode *parent;	/* child->parent link */
+    struct _xmlNode *next;	/* next sibling link  */
+    struct _xmlNode *prev;	/* previous sibling link  */
+    struct _xmlDoc  *doc;	/* the containing document */
+
+    /* End of common part */
+    xmlNs           *ns;        /* pointer to the associated namespace */
+    xmlChar         *content;   /* the content */
+    struct _xmlAttr *properties;/* properties list */
+    xmlNs           *nsDef;     /* namespace definitions on this node */
+    void            *psvi;	/* for type/PSVI information */
+    unsigned short   line;	/* line number */
+    unsigned short   extra;	/* extra data for XPath/XSLT */
+};
+
+/**
+ * XML_GET_CONTENT:
+ *
+ * Macro to extract the content pointer of a node.
+ */
+#define XML_GET_CONTENT(n)					\
+    ((n)->type == XML_ELEMENT_NODE ? NULL : (n)->content)
+
+/**
+ * XML_GET_LINE:
+ *
+ * Macro to extract the line number of an element node.
+ */
+#define XML_GET_LINE(n)						\
+    (xmlGetLineNo(n))
+
+/**
+ * xmlDocProperty
+ *
+ * Set of properties of the document as found by the parser
+ * Some of them are linked to similarly named xmlParserOption
+ */
+typedef enum {
+    XML_DOC_WELLFORMED		= 1<<0, /* document is XML well formed */
+    XML_DOC_NSVALID		= 1<<1, /* document is Namespace valid */
+    XML_DOC_OLD10		= 1<<2, /* parsed with old XML-1.0 parser */
+    XML_DOC_DTDVALID		= 1<<3, /* DTD validation was successful */
+    XML_DOC_XINCLUDE		= 1<<4, /* XInclude substitution was done */
+    XML_DOC_USERBUILT		= 1<<5, /* Document was built using the API
+                                           and not by parsing an instance */
+    XML_DOC_INTERNAL		= 1<<6, /* built for internal processing */
+    XML_DOC_HTML		= 1<<7  /* parsed or built HTML document */
+} xmlDocProperties;
+
+/**
+ * xmlDoc:
+ *
+ * An XML document.
+ */
+typedef struct _xmlDoc xmlDoc;
+typedef xmlDoc *xmlDocPtr;
+struct _xmlDoc {
+    void           *_private;	/* application data */
+    xmlElementType  type;       /* XML_DOCUMENT_NODE, must be second ! */
+    char           *name;	/* name/filename/URI of the document */
+    struct _xmlNode *children;	/* the document tree */
+    struct _xmlNode *last;	/* last child link */
+    struct _xmlNode *parent;	/* child->parent link */
+    struct _xmlNode *next;	/* next sibling link  */
+    struct _xmlNode *prev;	/* previous sibling link  */
+    struct _xmlDoc  *doc;	/* autoreference to itself */
+
+    /* End of common part */
+    int             compression;/* level of zlib compression */
+    int             standalone; /* standalone document (no external refs)
+				     1 if standalone="yes"
+				     0 if standalone="no"
+				    -1 if there is no XML declaration
+				    -2 if there is an XML declaration, but no
+					standalone attribute was specified */
+    struct _xmlDtd  *intSubset;	/* the document internal subset */
+    struct _xmlDtd  *extSubset;	/* the document external subset */
+    struct _xmlNs   *oldNs;	/* Global namespace, the old way */
+    const xmlChar  *version;	/* the XML version string */
+    const xmlChar  *encoding;   /* actual encoding, if any */
+    void           *ids;        /* Hash table for ID attributes if any */
+    void           *refs;       /* Hash table for IDREFs attributes if any */
+    const xmlChar  *URL;	/* The URI for that document */
+    int             charset;    /* unused */
+    struct _xmlDict *dict;      /* dict used to allocate names or NULL */
+    void           *psvi;	/* for type/PSVI information */
+    int             parseFlags;	/* set of xmlParserOption used to parse the
+				   document */
+    int             properties;	/* set of xmlDocProperties for this document
+				   set at the end of parsing */
+};
+
+
+typedef struct _xmlDOMWrapCtxt xmlDOMWrapCtxt;
+typedef xmlDOMWrapCtxt *xmlDOMWrapCtxtPtr;
+
+/**
+ * xmlDOMWrapAcquireNsFunction:
+ * @ctxt:  a DOM wrapper context
+ * @node:  the context node (element or attribute)
+ * @nsName:  the requested namespace name
+ * @nsPrefix:  the requested namespace prefix
+ *
+ * A function called to acquire namespaces (xmlNs) from the wrapper.
+ *
+ * Returns an xmlNsPtr or NULL in case of an error.
+ */
+typedef xmlNsPtr (*xmlDOMWrapAcquireNsFunction) (xmlDOMWrapCtxtPtr ctxt,
+						 xmlNodePtr node,
+						 const xmlChar *nsName,
+						 const xmlChar *nsPrefix);
+
+/**
+ * xmlDOMWrapCtxt:
+ *
+ * Context for DOM wrapper-operations.
+ */
+struct _xmlDOMWrapCtxt {
+    void * _private;
+    /*
+    * The type of this context, just in case we need specialized
+    * contexts in the future.
+    */
+    int type;
+    /*
+    * Internal namespace map used for various operations.
+    */
+    void * namespaceMap;
+    /*
+    * Use this one to acquire an xmlNsPtr intended for node->ns.
+    * (Note that this is not intended for elem->nsDef).
+    */
+    xmlDOMWrapAcquireNsFunction getNsForNodeFunc;
+};
+
+/**
+ * xmlRegisterNodeFunc:
+ * @node: the current node
+ *
+ * Signature for the registration callback of a created node
+ */
+typedef void (*xmlRegisterNodeFunc) (xmlNodePtr node);
+
+/**
+ * xmlDeregisterNodeFunc:
+ * @node: the current node
+ *
+ * Signature for the deregistration callback of a discarded node
+ */
+typedef void (*xmlDeregisterNodeFunc) (xmlNodePtr node);
+
+/**
+ * xmlChildrenNode:
+ *
+ * Macro for compatibility naming layer with libxml1. Maps
+ * to "children."
+ */
+#ifndef xmlChildrenNode
+#define xmlChildrenNode children
+#endif
+
+/**
+ * xmlRootNode:
+ *
+ * Macro for compatibility naming layer with libxml1. Maps
+ * to "children".
+ */
+#ifndef xmlRootNode
+#define xmlRootNode children
+#endif
+
+/*
+ * Variables.
+ */
+
+/** DOC_DISABLE */
+#define XML_GLOBALS_TREE \
+  XML_OP(xmlBufferAllocScheme, xmlBufferAllocationScheme, XML_DEPRECATED) \
+  XML_OP(xmlDefaultBufferSize, int, XML_DEPRECATED) \
+  XML_OP(xmlRegisterNodeDefaultValue, xmlRegisterNodeFunc, XML_DEPRECATED) \
+  XML_OP(xmlDeregisterNodeDefaultValue, xmlDeregisterNodeFunc, \
+         XML_DEPRECATED)
+
+#define XML_OP XML_DECLARE_GLOBAL
+XML_GLOBALS_TREE
+#undef XML_OP
+
+#if defined(LIBXML_THREAD_ENABLED) && !defined(XML_GLOBALS_NO_REDEFINITION)
+  #define xmlBufferAllocScheme XML_GLOBAL_MACRO(xmlBufferAllocScheme)
+  #define xmlDefaultBufferSize XML_GLOBAL_MACRO(xmlDefaultBufferSize)
+  #define xmlRegisterNodeDefaultValue \
+    XML_GLOBAL_MACRO(xmlRegisterNodeDefaultValue)
+  #define xmlDeregisterNodeDefaultValue \
+    XML_GLOBAL_MACRO(xmlDeregisterNodeDefaultValue)
+#endif
+/** DOC_ENABLE */
+
+/*
+ * Some helper functions
+ */
+XMLPUBFUN int
+		xmlValidateNCName	(const xmlChar *value,
+					 int space);
+
+#if defined(LIBXML_TREE_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED)
+XMLPUBFUN int
+		xmlValidateQName	(const xmlChar *value,
+					 int space);
+XMLPUBFUN int
+		xmlValidateName		(const xmlChar *value,
+					 int space);
+XMLPUBFUN int
+		xmlValidateNMToken	(const xmlChar *value,
+					 int space);
+#endif
+
+XMLPUBFUN xmlChar *
+		xmlBuildQName		(const xmlChar *ncname,
+					 const xmlChar *prefix,
+					 xmlChar *memory,
+					 int len);
+XMLPUBFUN xmlChar *
+		xmlSplitQName2		(const xmlChar *name,
+					 xmlChar **prefix);
+XMLPUBFUN const xmlChar *
+		xmlSplitQName3		(const xmlChar *name,
+					 int *len);
+
+/*
+ * Handling Buffers, the old ones see @xmlBuf for the new ones.
+ */
+
+XMLPUBFUN void
+		xmlSetBufferAllocationScheme(xmlBufferAllocationScheme scheme);
+XMLPUBFUN xmlBufferAllocationScheme
+		xmlGetBufferAllocationScheme(void);
+
+XMLPUBFUN xmlBufferPtr
+		xmlBufferCreate		(void);
+XMLPUBFUN xmlBufferPtr
+		xmlBufferCreateSize	(size_t size);
+XMLPUBFUN xmlBufferPtr
+		xmlBufferCreateStatic	(void *mem,
+					 size_t size);
+XMLPUBFUN int
+		xmlBufferResize		(xmlBufferPtr buf,
+					 unsigned int size);
+XMLPUBFUN void
+		xmlBufferFree		(xmlBufferPtr buf);
+XMLPUBFUN int
+		xmlBufferDump		(FILE *file,
+					 xmlBufferPtr buf);
+XMLPUBFUN int
+		xmlBufferAdd		(xmlBufferPtr buf,
+					 const xmlChar *str,
+					 int len);
+XMLPUBFUN int
+		xmlBufferAddHead	(xmlBufferPtr buf,
+					 const xmlChar *str,
+					 int len);
+XMLPUBFUN int
+		xmlBufferCat		(xmlBufferPtr buf,
+					 const xmlChar *str);
+XMLPUBFUN int
+		xmlBufferCCat		(xmlBufferPtr buf,
+					 const char *str);
+XMLPUBFUN int
+		xmlBufferShrink		(xmlBufferPtr buf,
+					 unsigned int len);
+XMLPUBFUN int
+		xmlBufferGrow		(xmlBufferPtr buf,
+					 unsigned int len);
+XMLPUBFUN void
+		xmlBufferEmpty		(xmlBufferPtr buf);
+XMLPUBFUN const xmlChar*
+		xmlBufferContent	(const xmlBuffer *buf);
+XMLPUBFUN xmlChar*
+		xmlBufferDetach         (xmlBufferPtr buf);
+XMLPUBFUN void
+		xmlBufferSetAllocationScheme(xmlBufferPtr buf,
+					 xmlBufferAllocationScheme scheme);
+XMLPUBFUN int
+		xmlBufferLength		(const xmlBuffer *buf);
+
+/*
+ * Creating/freeing new structures.
+ */
+XMLPUBFUN xmlDtdPtr
+		xmlCreateIntSubset	(xmlDocPtr doc,
+					 const xmlChar *name,
+					 const xmlChar *ExternalID,
+					 const xmlChar *SystemID);
+XMLPUBFUN xmlDtdPtr
+		xmlNewDtd		(xmlDocPtr doc,
+					 const xmlChar *name,
+					 const xmlChar *ExternalID,
+					 const xmlChar *SystemID);
+XMLPUBFUN xmlDtdPtr
+		xmlGetIntSubset		(const xmlDoc *doc);
+XMLPUBFUN void
+		xmlFreeDtd		(xmlDtdPtr cur);
+#ifdef LIBXML_LEGACY_ENABLED
+XML_DEPRECATED
+XMLPUBFUN xmlNsPtr
+		xmlNewGlobalNs		(xmlDocPtr doc,
+					 const xmlChar *href,
+					 const xmlChar *prefix);
+#endif /* LIBXML_LEGACY_ENABLED */
+XMLPUBFUN xmlNsPtr
+		xmlNewNs		(xmlNodePtr node,
+					 const xmlChar *href,
+					 const xmlChar *prefix);
+XMLPUBFUN void
+		xmlFreeNs		(xmlNsPtr cur);
+XMLPUBFUN void
+		xmlFreeNsList		(xmlNsPtr cur);
+XMLPUBFUN xmlDocPtr
+		xmlNewDoc		(const xmlChar *version);
+XMLPUBFUN void
+		xmlFreeDoc		(xmlDocPtr cur);
+XMLPUBFUN xmlAttrPtr
+		xmlNewDocProp		(xmlDocPtr doc,
+					 const xmlChar *name,
+					 const xmlChar *value);
+#if defined(LIBXML_TREE_ENABLED) || defined(LIBXML_HTML_ENABLED) || \
+    defined(LIBXML_SCHEMAS_ENABLED)
+XMLPUBFUN xmlAttrPtr
+		xmlNewProp		(xmlNodePtr node,
+					 const xmlChar *name,
+					 const xmlChar *value);
+#endif
+XMLPUBFUN xmlAttrPtr
+		xmlNewNsProp		(xmlNodePtr node,
+					 xmlNsPtr ns,
+					 const xmlChar *name,
+					 const xmlChar *value);
+XMLPUBFUN xmlAttrPtr
+		xmlNewNsPropEatName	(xmlNodePtr node,
+					 xmlNsPtr ns,
+					 xmlChar *name,
+					 const xmlChar *value);
+XMLPUBFUN void
+		xmlFreePropList		(xmlAttrPtr cur);
+XMLPUBFUN void
+		xmlFreeProp		(xmlAttrPtr cur);
+XMLPUBFUN xmlAttrPtr
+		xmlCopyProp		(xmlNodePtr target,
+					 xmlAttrPtr cur);
+XMLPUBFUN xmlAttrPtr
+		xmlCopyPropList		(xmlNodePtr target,
+					 xmlAttrPtr cur);
+#ifdef LIBXML_TREE_ENABLED
+XMLPUBFUN xmlDtdPtr
+		xmlCopyDtd		(xmlDtdPtr dtd);
+#endif /* LIBXML_TREE_ENABLED */
+#if defined(LIBXML_TREE_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED)
+XMLPUBFUN xmlDocPtr
+		xmlCopyDoc		(xmlDocPtr doc,
+					 int recursive);
+#endif /* defined(LIBXML_TREE_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED) */
+/*
+ * Creating new nodes.
+ */
+XMLPUBFUN xmlNodePtr
+		xmlNewDocNode		(xmlDocPtr doc,
+					 xmlNsPtr ns,
+					 const xmlChar *name,
+					 const xmlChar *content);
+XMLPUBFUN xmlNodePtr
+		xmlNewDocNodeEatName	(xmlDocPtr doc,
+					 xmlNsPtr ns,
+					 xmlChar *name,
+					 const xmlChar *content);
+XMLPUBFUN xmlNodePtr
+		xmlNewNode		(xmlNsPtr ns,
+					 const xmlChar *name);
+XMLPUBFUN xmlNodePtr
+		xmlNewNodeEatName	(xmlNsPtr ns,
+					 xmlChar *name);
+#if defined(LIBXML_TREE_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED)
+XMLPUBFUN xmlNodePtr
+		xmlNewChild		(xmlNodePtr parent,
+					 xmlNsPtr ns,
+					 const xmlChar *name,
+					 const xmlChar *content);
+#endif
+XMLPUBFUN xmlNodePtr
+		xmlNewDocText		(const xmlDoc *doc,
+					 const xmlChar *content);
+XMLPUBFUN xmlNodePtr
+		xmlNewText		(const xmlChar *content);
+XMLPUBFUN xmlNodePtr
+		xmlNewDocPI		(xmlDocPtr doc,
+					 const xmlChar *name,
+					 const xmlChar *content);
+XMLPUBFUN xmlNodePtr
+		xmlNewPI		(const xmlChar *name,
+					 const xmlChar *content);
+XMLPUBFUN xmlNodePtr
+		xmlNewDocTextLen	(xmlDocPtr doc,
+					 const xmlChar *content,
+					 int len);
+XMLPUBFUN xmlNodePtr
+		xmlNewTextLen		(const xmlChar *content,
+					 int len);
+XMLPUBFUN xmlNodePtr
+		xmlNewDocComment	(xmlDocPtr doc,
+					 const xmlChar *content);
+XMLPUBFUN xmlNodePtr
+		xmlNewComment		(const xmlChar *content);
+XMLPUBFUN xmlNodePtr
+		xmlNewCDataBlock	(xmlDocPtr doc,
+					 const xmlChar *content,
+					 int len);
+XMLPUBFUN xmlNodePtr
+		xmlNewCharRef		(xmlDocPtr doc,
+					 const xmlChar *name);
+XMLPUBFUN xmlNodePtr
+		xmlNewReference		(const xmlDoc *doc,
+					 const xmlChar *name);
+XMLPUBFUN xmlNodePtr
+		xmlCopyNode		(xmlNodePtr node,
+					 int recursive);
+XMLPUBFUN xmlNodePtr
+		xmlDocCopyNode		(xmlNodePtr node,
+					 xmlDocPtr doc,
+					 int recursive);
+XMLPUBFUN xmlNodePtr
+		xmlDocCopyNodeList	(xmlDocPtr doc,
+					 xmlNodePtr node);
+XMLPUBFUN xmlNodePtr
+		xmlCopyNodeList		(xmlNodePtr node);
+#ifdef LIBXML_TREE_ENABLED
+XMLPUBFUN xmlNodePtr
+		xmlNewTextChild		(xmlNodePtr parent,
+					 xmlNsPtr ns,
+					 const xmlChar *name,
+					 const xmlChar *content);
+XMLPUBFUN xmlNodePtr
+		xmlNewDocRawNode	(xmlDocPtr doc,
+					 xmlNsPtr ns,
+					 const xmlChar *name,
+					 const xmlChar *content);
+XMLPUBFUN xmlNodePtr
+		xmlNewDocFragment	(xmlDocPtr doc);
+#endif /* LIBXML_TREE_ENABLED */
+
+/*
+ * Navigating.
+ */
+XMLPUBFUN long
+		xmlGetLineNo		(const xmlNode *node);
+#if defined(LIBXML_TREE_ENABLED) || defined(LIBXML_DEBUG_ENABLED)
+XMLPUBFUN xmlChar *
+		xmlGetNodePath		(const xmlNode *node);
+#endif /* defined(LIBXML_TREE_ENABLED) || defined(LIBXML_DEBUG_ENABLED) */
+XMLPUBFUN xmlNodePtr
+		xmlDocGetRootElement	(const xmlDoc *doc);
+XMLPUBFUN xmlNodePtr
+		xmlGetLastChild		(const xmlNode *parent);
+XMLPUBFUN int
+		xmlNodeIsText		(const xmlNode *node);
+XMLPUBFUN int
+		xmlIsBlankNode		(const xmlNode *node);
+
+/*
+ * Changing the structure.
+ */
+#if defined(LIBXML_TREE_ENABLED) || defined(LIBXML_WRITER_ENABLED)
+XMLPUBFUN xmlNodePtr
+		xmlDocSetRootElement	(xmlDocPtr doc,
+					 xmlNodePtr root);
+#endif /* defined(LIBXML_TREE_ENABLED) || defined(LIBXML_WRITER_ENABLED) */
+#ifdef LIBXML_TREE_ENABLED
+XMLPUBFUN void
+		xmlNodeSetName		(xmlNodePtr cur,
+					 const xmlChar *name);
+#endif /* LIBXML_TREE_ENABLED */
+XMLPUBFUN xmlNodePtr
+		xmlAddChild		(xmlNodePtr parent,
+					 xmlNodePtr cur);
+XMLPUBFUN xmlNodePtr
+		xmlAddChildList		(xmlNodePtr parent,
+					 xmlNodePtr cur);
+#if defined(LIBXML_TREE_ENABLED) || defined(LIBXML_WRITER_ENABLED)
+XMLPUBFUN xmlNodePtr
+		xmlReplaceNode		(xmlNodePtr old,
+					 xmlNodePtr cur);
+#endif /* defined(LIBXML_TREE_ENABLED) || defined(LIBXML_WRITER_ENABLED) */
+#if defined(LIBXML_TREE_ENABLED) || defined(LIBXML_HTML_ENABLED) || \
+    defined(LIBXML_SCHEMAS_ENABLED) || defined(LIBXML_XINCLUDE_ENABLED)
+XMLPUBFUN xmlNodePtr
+		xmlAddPrevSibling	(xmlNodePtr cur,
+					 xmlNodePtr elem);
+#endif /* LIBXML_TREE_ENABLED || LIBXML_HTML_ENABLED || LIBXML_SCHEMAS_ENABLED */
+XMLPUBFUN xmlNodePtr
+		xmlAddSibling		(xmlNodePtr cur,
+					 xmlNodePtr elem);
+XMLPUBFUN xmlNodePtr
+		xmlAddNextSibling	(xmlNodePtr cur,
+					 xmlNodePtr elem);
+XMLPUBFUN void
+		xmlUnlinkNode		(xmlNodePtr cur);
+XMLPUBFUN xmlNodePtr
+		xmlTextMerge		(xmlNodePtr first,
+					 xmlNodePtr second);
+XMLPUBFUN int
+		xmlTextConcat		(xmlNodePtr node,
+					 const xmlChar *content,
+					 int len);
+XMLPUBFUN void
+		xmlFreeNodeList		(xmlNodePtr cur);
+XMLPUBFUN void
+		xmlFreeNode		(xmlNodePtr cur);
+XMLPUBFUN void
+		xmlSetTreeDoc		(xmlNodePtr tree,
+					 xmlDocPtr doc);
+XMLPUBFUN void
+		xmlSetListDoc		(xmlNodePtr list,
+					 xmlDocPtr doc);
+/*
+ * Namespaces.
+ */
+XMLPUBFUN xmlNsPtr
+		xmlSearchNs		(xmlDocPtr doc,
+					 xmlNodePtr node,
+					 const xmlChar *nameSpace);
+XMLPUBFUN xmlNsPtr
+		xmlSearchNsByHref	(xmlDocPtr doc,
+					 xmlNodePtr node,
+					 const xmlChar *href);
+#if defined(LIBXML_TREE_ENABLED) || defined(LIBXML_XPATH_ENABLED) || \
+    defined(LIBXML_SCHEMAS_ENABLED)
+XMLPUBFUN xmlNsPtr *
+		xmlGetNsList		(const xmlDoc *doc,
+					 const xmlNode *node);
+#endif /* defined(LIBXML_TREE_ENABLED) || defined(LIBXML_XPATH_ENABLED) */
+
+XMLPUBFUN void
+		xmlSetNs		(xmlNodePtr node,
+					 xmlNsPtr ns);
+XMLPUBFUN xmlNsPtr
+		xmlCopyNamespace	(xmlNsPtr cur);
+XMLPUBFUN xmlNsPtr
+		xmlCopyNamespaceList	(xmlNsPtr cur);
+
+/*
+ * Changing the content.
+ */
+#if defined(LIBXML_TREE_ENABLED) || defined(LIBXML_XINCLUDE_ENABLED) || \
+    defined(LIBXML_SCHEMAS_ENABLED) || defined(LIBXML_HTML_ENABLED)
+XMLPUBFUN xmlAttrPtr
+		xmlSetProp		(xmlNodePtr node,
+					 const xmlChar *name,
+					 const xmlChar *value);
+XMLPUBFUN xmlAttrPtr
+		xmlSetNsProp		(xmlNodePtr node,
+					 xmlNsPtr ns,
+					 const xmlChar *name,
+					 const xmlChar *value);
+#endif /* defined(LIBXML_TREE_ENABLED) || defined(LIBXML_XINCLUDE_ENABLED) || \
+	  defined(LIBXML_SCHEMAS_ENABLED) || defined(LIBXML_HTML_ENABLED) */
+XMLPUBFUN xmlChar *
+		xmlGetNoNsProp		(const xmlNode *node,
+					 const xmlChar *name);
+XMLPUBFUN xmlChar *
+		xmlGetProp		(const xmlNode *node,
+					 const xmlChar *name);
+XMLPUBFUN xmlAttrPtr
+		xmlHasProp		(const xmlNode *node,
+					 const xmlChar *name);
+XMLPUBFUN xmlAttrPtr
+		xmlHasNsProp		(const xmlNode *node,
+					 const xmlChar *name,
+					 const xmlChar *nameSpace);
+XMLPUBFUN xmlChar *
+		xmlGetNsProp		(const xmlNode *node,
+					 const xmlChar *name,
+					 const xmlChar *nameSpace);
+XMLPUBFUN xmlNodePtr
+		xmlStringGetNodeList	(const xmlDoc *doc,
+					 const xmlChar *value);
+XMLPUBFUN xmlNodePtr
+		xmlStringLenGetNodeList	(const xmlDoc *doc,
+					 const xmlChar *value,
+					 int len);
+XMLPUBFUN xmlChar *
+		xmlNodeListGetString	(xmlDocPtr doc,
+					 const xmlNode *list,
+					 int inLine);
+#ifdef LIBXML_TREE_ENABLED
+XMLPUBFUN xmlChar *
+		xmlNodeListGetRawString	(const xmlDoc *doc,
+					 const xmlNode *list,
+					 int inLine);
+#endif /* LIBXML_TREE_ENABLED */
+XMLPUBFUN void
+		xmlNodeSetContent	(xmlNodePtr cur,
+					 const xmlChar *content);
+#ifdef LIBXML_TREE_ENABLED
+XMLPUBFUN void
+		xmlNodeSetContentLen	(xmlNodePtr cur,
+					 const xmlChar *content,
+					 int len);
+#endif /* LIBXML_TREE_ENABLED */
+XMLPUBFUN void
+		xmlNodeAddContent	(xmlNodePtr cur,
+					 const xmlChar *content);
+XMLPUBFUN void
+		xmlNodeAddContentLen	(xmlNodePtr cur,
+					 const xmlChar *content,
+					 int len);
+XMLPUBFUN xmlChar *
+		xmlNodeGetContent	(const xmlNode *cur);
+
+XMLPUBFUN int
+		xmlNodeBufGetContent	(xmlBufferPtr buffer,
+					 const xmlNode *cur);
+XMLPUBFUN int
+		xmlBufGetNodeContent	(xmlBufPtr buf,
+					 const xmlNode *cur);
+
+XMLPUBFUN xmlChar *
+		xmlNodeGetLang		(const xmlNode *cur);
+XMLPUBFUN int
+		xmlNodeGetSpacePreserve	(const xmlNode *cur);
+#ifdef LIBXML_TREE_ENABLED
+XMLPUBFUN void
+		xmlNodeSetLang		(xmlNodePtr cur,
+					 const xmlChar *lang);
+XMLPUBFUN void
+		xmlNodeSetSpacePreserve (xmlNodePtr cur,
+					 int val);
+#endif /* LIBXML_TREE_ENABLED */
+XMLPUBFUN xmlChar *
+		xmlNodeGetBase		(const xmlDoc *doc,
+					 const xmlNode *cur);
+#if defined(LIBXML_TREE_ENABLED) || defined(LIBXML_XINCLUDE_ENABLED)
+XMLPUBFUN void
+		xmlNodeSetBase		(xmlNodePtr cur,
+					 const xmlChar *uri);
+#endif
+
+/*
+ * Removing content.
+ */
+XMLPUBFUN int
+		xmlRemoveProp		(xmlAttrPtr cur);
+#if defined(LIBXML_TREE_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED)
+XMLPUBFUN int
+		xmlUnsetNsProp		(xmlNodePtr node,
+					 xmlNsPtr ns,
+					 const xmlChar *name);
+XMLPUBFUN int
+		xmlUnsetProp		(xmlNodePtr node,
+					 const xmlChar *name);
+#endif /* defined(LIBXML_TREE_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED) */
+
+/*
+ * Internal, don't use.
+ */
+XMLPUBFUN void
+		xmlBufferWriteCHAR	(xmlBufferPtr buf,
+					 const xmlChar *string);
+XMLPUBFUN void
+		xmlBufferWriteChar	(xmlBufferPtr buf,
+					 const char *string);
+XMLPUBFUN void
+		xmlBufferWriteQuotedString(xmlBufferPtr buf,
+					 const xmlChar *string);
+
+#ifdef LIBXML_OUTPUT_ENABLED
+XMLPUBFUN void xmlAttrSerializeTxtContent(xmlBufferPtr buf,
+					 xmlDocPtr doc,
+					 xmlAttrPtr attr,
+					 const xmlChar *string);
+#endif /* LIBXML_OUTPUT_ENABLED */
+
+#ifdef LIBXML_TREE_ENABLED
+/*
+ * Namespace handling.
+ */
+XMLPUBFUN int
+		xmlReconciliateNs	(xmlDocPtr doc,
+					 xmlNodePtr tree);
+#endif
+
+#ifdef LIBXML_OUTPUT_ENABLED
+/*
+ * Saving.
+ */
+XMLPUBFUN void
+		xmlDocDumpFormatMemory	(xmlDocPtr cur,
+					 xmlChar **mem,
+					 int *size,
+					 int format);
+XMLPUBFUN void
+		xmlDocDumpMemory	(xmlDocPtr cur,
+					 xmlChar **mem,
+					 int *size);
+XMLPUBFUN void
+		xmlDocDumpMemoryEnc	(xmlDocPtr out_doc,
+					 xmlChar **doc_txt_ptr,
+					 int * doc_txt_len,
+					 const char *txt_encoding);
+XMLPUBFUN void
+		xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc,
+					 xmlChar **doc_txt_ptr,
+					 int * doc_txt_len,
+					 const char *txt_encoding,
+					 int format);
+XMLPUBFUN int
+		xmlDocFormatDump	(FILE *f,
+					 xmlDocPtr cur,
+					 int format);
+XMLPUBFUN int
+		xmlDocDump		(FILE *f,
+					 xmlDocPtr cur);
+XMLPUBFUN void
+		xmlElemDump		(FILE *f,
+					 xmlDocPtr doc,
+					 xmlNodePtr cur);
+XMLPUBFUN int
+		xmlSaveFile		(const char *filename,
+					 xmlDocPtr cur);
+XMLPUBFUN int
+		xmlSaveFormatFile	(const char *filename,
+					 xmlDocPtr cur,
+					 int format);
+XMLPUBFUN size_t
+		xmlBufNodeDump		(xmlBufPtr buf,
+					 xmlDocPtr doc,
+					 xmlNodePtr cur,
+					 int level,
+					 int format);
+XMLPUBFUN int
+		xmlNodeDump		(xmlBufferPtr buf,
+					 xmlDocPtr doc,
+					 xmlNodePtr cur,
+					 int level,
+					 int format);
+
+XMLPUBFUN int
+		xmlSaveFileTo		(xmlOutputBufferPtr buf,
+					 xmlDocPtr cur,
+					 const char *encoding);
+XMLPUBFUN int
+		xmlSaveFormatFileTo     (xmlOutputBufferPtr buf,
+					 xmlDocPtr cur,
+				         const char *encoding,
+				         int format);
+XMLPUBFUN void
+		xmlNodeDumpOutput	(xmlOutputBufferPtr buf,
+					 xmlDocPtr doc,
+					 xmlNodePtr cur,
+					 int level,
+					 int format,
+					 const char *encoding);
+
+XMLPUBFUN int
+		xmlSaveFormatFileEnc    (const char *filename,
+					 xmlDocPtr cur,
+					 const char *encoding,
+					 int format);
+
+XMLPUBFUN int
+		xmlSaveFileEnc		(const char *filename,
+					 xmlDocPtr cur,
+					 const char *encoding);
+
+#endif /* LIBXML_OUTPUT_ENABLED */
+/*
+ * XHTML
+ */
+XMLPUBFUN int
+		xmlIsXHTML		(const xmlChar *systemID,
+					 const xmlChar *publicID);
+
+/*
+ * Compression.
+ */
+XMLPUBFUN int
+		xmlGetDocCompressMode	(const xmlDoc *doc);
+XMLPUBFUN void
+		xmlSetDocCompressMode	(xmlDocPtr doc,
+					 int mode);
+XMLPUBFUN int
+		xmlGetCompressMode	(void);
+XMLPUBFUN void
+		xmlSetCompressMode	(int mode);
+
+/*
+* DOM-wrapper helper functions.
+*/
+XMLPUBFUN xmlDOMWrapCtxtPtr
+		xmlDOMWrapNewCtxt	(void);
+XMLPUBFUN void
+		xmlDOMWrapFreeCtxt	(xmlDOMWrapCtxtPtr ctxt);
+XMLPUBFUN int
+	    xmlDOMWrapReconcileNamespaces(xmlDOMWrapCtxtPtr ctxt,
+					 xmlNodePtr elem,
+					 int options);
+XMLPUBFUN int
+	    xmlDOMWrapAdoptNode		(xmlDOMWrapCtxtPtr ctxt,
+					 xmlDocPtr sourceDoc,
+					 xmlNodePtr node,
+					 xmlDocPtr destDoc,
+					 xmlNodePtr destParent,
+					 int options);
+XMLPUBFUN int
+	    xmlDOMWrapRemoveNode	(xmlDOMWrapCtxtPtr ctxt,
+					 xmlDocPtr doc,
+					 xmlNodePtr node,
+					 int options);
+XMLPUBFUN int
+	    xmlDOMWrapCloneNode		(xmlDOMWrapCtxtPtr ctxt,
+					 xmlDocPtr sourceDoc,
+					 xmlNodePtr node,
+					 xmlNodePtr *clonedNode,
+					 xmlDocPtr destDoc,
+					 xmlNodePtr destParent,
+					 int deep,
+					 int options);
+
+#ifdef LIBXML_TREE_ENABLED
+/*
+ * 5 interfaces from DOM ElementTraversal, but different in entities
+ * traversal.
+ */
+XMLPUBFUN unsigned long
+            xmlChildElementCount        (xmlNodePtr parent);
+XMLPUBFUN xmlNodePtr
+            xmlNextElementSibling       (xmlNodePtr node);
+XMLPUBFUN xmlNodePtr
+            xmlFirstElementChild        (xmlNodePtr parent);
+XMLPUBFUN xmlNodePtr
+            xmlLastElementChild         (xmlNodePtr parent);
+XMLPUBFUN xmlNodePtr
+            xmlPreviousElementSibling   (xmlNodePtr node);
+#endif
+
+XMLPUBFUN xmlRegisterNodeFunc
+	    xmlRegisterNodeDefault	(xmlRegisterNodeFunc func);
+XMLPUBFUN xmlDeregisterNodeFunc
+	    xmlDeregisterNodeDefault	(xmlDeregisterNodeFunc func);
+XMLPUBFUN xmlRegisterNodeFunc
+            xmlThrDefRegisterNodeDefault(xmlRegisterNodeFunc func);
+XMLPUBFUN xmlDeregisterNodeFunc
+            xmlThrDefDeregisterNodeDefault(xmlDeregisterNodeFunc func);
+
+XML_DEPRECATED XMLPUBFUN xmlBufferAllocationScheme
+            xmlThrDefBufferAllocScheme  (xmlBufferAllocationScheme v);
+XML_DEPRECATED XMLPUBFUN int
+            xmlThrDefDefaultBufferSize  (int v);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_TREE_H__ */
+
+#endif /* XML_TREE_INTERNALS */
+
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/uri.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/uri.h
new file mode 100644
index 00000000..eb8631cf
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/uri.h
@@ -0,0 +1,95 @@
+/**
+ * Summary: library of generic URI related routines
+ * Description: library of generic URI related routines
+ *              Implements RFC 2396
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_URI_H__
+#define __XML_URI_H__
+
+#include <stdio.h>
+#include <libxml/xmlversion.h>
+#include <libxml/xmlstring.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * xmlURI:
+ *
+ * A parsed URI reference. This is a struct containing the various fields
+ * as described in RFC 2396 but separated for further processing.
+ *
+ * Note: query is a deprecated field which is incorrectly unescaped.
+ * query_raw takes precedence over query if the former is set.
+ * See: http://mail.gnome.org/archives/xml/2007-April/thread.html#00127
+ */
+typedef struct _xmlURI xmlURI;
+typedef xmlURI *xmlURIPtr;
+struct _xmlURI {
+    char *scheme;	/* the URI scheme */
+    char *opaque;	/* opaque part */
+    char *authority;	/* the authority part */
+    char *server;	/* the server part */
+    char *user;		/* the user part */
+    int port;		/* the port number */
+    char *path;		/* the path string */
+    char *query;	/* the query string (deprecated - use with caution) */
+    char *fragment;	/* the fragment identifier */
+    int  cleanup;	/* parsing potentially unclean URI */
+    char *query_raw;	/* the query string (as it appears in the URI) */
+};
+
+/*
+ * This function is in tree.h:
+ * xmlChar *	xmlNodeGetBase	(xmlDocPtr doc,
+ *                               xmlNodePtr cur);
+ */
+XMLPUBFUN xmlURIPtr
+		xmlCreateURI		(void);
+XMLPUBFUN xmlChar *
+		xmlBuildURI		(const xmlChar *URI,
+					 const xmlChar *base);
+XMLPUBFUN xmlChar *
+		xmlBuildRelativeURI	(const xmlChar *URI,
+					 const xmlChar *base);
+XMLPUBFUN xmlURIPtr
+		xmlParseURI		(const char *str);
+XMLPUBFUN xmlURIPtr
+		xmlParseURIRaw		(const char *str,
+					 int raw);
+XMLPUBFUN int
+		xmlParseURIReference	(xmlURIPtr uri,
+					 const char *str);
+XMLPUBFUN xmlChar *
+		xmlSaveUri		(xmlURIPtr uri);
+XMLPUBFUN void
+		xmlPrintURI		(FILE *stream,
+					 xmlURIPtr uri);
+XMLPUBFUN xmlChar *
+		xmlURIEscapeStr         (const xmlChar *str,
+					 const xmlChar *list);
+XMLPUBFUN char *
+		xmlURIUnescapeString	(const char *str,
+					 int len,
+					 char *target);
+XMLPUBFUN int
+		xmlNormalizeURIPath	(char *path);
+XMLPUBFUN xmlChar *
+		xmlURIEscape		(const xmlChar *str);
+XMLPUBFUN void
+		xmlFreeURI		(xmlURIPtr uri);
+XMLPUBFUN xmlChar*
+		xmlCanonicPath		(const xmlChar *path);
+XMLPUBFUN xmlChar*
+		xmlPathToURI		(const xmlChar *path);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __XML_URI_H__ */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/valid.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/valid.h
new file mode 100644
index 00000000..3e04b552
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/valid.h
@@ -0,0 +1,450 @@
+/*
+ * Summary: The DTD validation
+ * Description: API for the DTD handling and the validity checking
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+
+#ifndef __XML_VALID_H__
+#define __XML_VALID_H__
+
+#include <libxml/xmlversion.h>
+#include <libxml/xmlerror.h>
+#define XML_TREE_INTERNALS
+#include <libxml/tree.h>
+#undef XML_TREE_INTERNALS
+#include <libxml/list.h>
+#include <libxml/xmlautomata.h>
+#include <libxml/xmlregexp.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Validation state added for non-determinist content model.
+ */
+typedef struct _xmlValidState xmlValidState;
+typedef xmlValidState *xmlValidStatePtr;
+
+/**
+ * xmlValidityErrorFunc:
+ * @ctx:  usually an xmlValidCtxtPtr to a validity error context,
+ *        but comes from ctxt->userData (which normally contains such
+ *        a pointer); ctxt->userData can be changed by the user.
+ * @msg:  the string to format *printf like vararg
+ * @...:  remaining arguments to the format
+ *
+ * Callback called when a validity error is found. This is a message
+ * oriented function similar to an *printf function.
+ */
+typedef void (*xmlValidityErrorFunc) (void *ctx,
+			     const char *msg,
+			     ...) LIBXML_ATTR_FORMAT(2,3);
+
+/**
+ * xmlValidityWarningFunc:
+ * @ctx:  usually an xmlValidCtxtPtr to a validity error context,
+ *        but comes from ctxt->userData (which normally contains such
+ *        a pointer); ctxt->userData can be changed by the user.
+ * @msg:  the string to format *printf like vararg
+ * @...:  remaining arguments to the format
+ *
+ * Callback called when a validity warning is found. This is a message
+ * oriented function similar to an *printf function.
+ */
+typedef void (*xmlValidityWarningFunc) (void *ctx,
+			       const char *msg,
+			       ...) LIBXML_ATTR_FORMAT(2,3);
+
+/*
+ * xmlValidCtxt:
+ * An xmlValidCtxt is used for error reporting when validating.
+ */
+typedef struct _xmlValidCtxt xmlValidCtxt;
+typedef xmlValidCtxt *xmlValidCtxtPtr;
+struct _xmlValidCtxt {
+    void *userData;			/* user specific data block */
+    xmlValidityErrorFunc error;		/* the callback in case of errors */
+    xmlValidityWarningFunc warning;	/* the callback in case of warning */
+
+    /* Node analysis stack used when validating within entities */
+    xmlNodePtr         node;          /* Current parsed Node */
+    int                nodeNr;        /* Depth of the parsing stack */
+    int                nodeMax;       /* Max depth of the parsing stack */
+    xmlNodePtr        *nodeTab;       /* array of nodes */
+
+    unsigned int         flags;       /* internal flags */
+    xmlDocPtr              doc;       /* the document */
+    int                  valid;       /* temporary validity check result */
+
+    /* state state used for non-determinist content validation */
+    xmlValidState     *vstate;        /* current state */
+    int                vstateNr;      /* Depth of the validation stack */
+    int                vstateMax;     /* Max depth of the validation stack */
+    xmlValidState     *vstateTab;     /* array of validation states */
+
+#ifdef LIBXML_REGEXP_ENABLED
+    xmlAutomataPtr            am;     /* the automata */
+    xmlAutomataStatePtr    state;     /* used to build the automata */
+#else
+    void                     *am;
+    void                  *state;
+#endif
+};
+
+/*
+ * ALL notation declarations are stored in a table.
+ * There is one table per DTD.
+ */
+
+typedef struct _xmlHashTable xmlNotationTable;
+typedef xmlNotationTable *xmlNotationTablePtr;
+
+/*
+ * ALL element declarations are stored in a table.
+ * There is one table per DTD.
+ */
+
+typedef struct _xmlHashTable xmlElementTable;
+typedef xmlElementTable *xmlElementTablePtr;
+
+/*
+ * ALL attribute declarations are stored in a table.
+ * There is one table per DTD.
+ */
+
+typedef struct _xmlHashTable xmlAttributeTable;
+typedef xmlAttributeTable *xmlAttributeTablePtr;
+
+/*
+ * ALL IDs attributes are stored in a table.
+ * There is one table per document.
+ */
+
+typedef struct _xmlHashTable xmlIDTable;
+typedef xmlIDTable *xmlIDTablePtr;
+
+/*
+ * ALL Refs attributes are stored in a table.
+ * There is one table per document.
+ */
+
+typedef struct _xmlHashTable xmlRefTable;
+typedef xmlRefTable *xmlRefTablePtr;
+
+/* Notation */
+XMLPUBFUN xmlNotationPtr
+		xmlAddNotationDecl	(xmlValidCtxtPtr ctxt,
+					 xmlDtdPtr dtd,
+					 const xmlChar *name,
+					 const xmlChar *PublicID,
+					 const xmlChar *SystemID);
+#ifdef LIBXML_TREE_ENABLED
+XMLPUBFUN xmlNotationTablePtr
+		xmlCopyNotationTable	(xmlNotationTablePtr table);
+#endif /* LIBXML_TREE_ENABLED */
+XMLPUBFUN void
+		xmlFreeNotationTable	(xmlNotationTablePtr table);
+#ifdef LIBXML_OUTPUT_ENABLED
+XMLPUBFUN void
+		xmlDumpNotationDecl	(xmlBufferPtr buf,
+					 xmlNotationPtr nota);
+XMLPUBFUN void
+		xmlDumpNotationTable	(xmlBufferPtr buf,
+					 xmlNotationTablePtr table);
+#endif /* LIBXML_OUTPUT_ENABLED */
+
+/* Element Content */
+/* the non Doc version are being deprecated */
+XMLPUBFUN xmlElementContentPtr
+		xmlNewElementContent	(const xmlChar *name,
+					 xmlElementContentType type);
+XMLPUBFUN xmlElementContentPtr
+		xmlCopyElementContent	(xmlElementContentPtr content);
+XMLPUBFUN void
+		xmlFreeElementContent	(xmlElementContentPtr cur);
+/* the new versions with doc argument */
+XMLPUBFUN xmlElementContentPtr
+		xmlNewDocElementContent	(xmlDocPtr doc,
+					 const xmlChar *name,
+					 xmlElementContentType type);
+XMLPUBFUN xmlElementContentPtr
+		xmlCopyDocElementContent(xmlDocPtr doc,
+					 xmlElementContentPtr content);
+XMLPUBFUN void
+		xmlFreeDocElementContent(xmlDocPtr doc,
+					 xmlElementContentPtr cur);
+XMLPUBFUN void
+		xmlSnprintfElementContent(char *buf,
+					 int size,
+	                                 xmlElementContentPtr content,
+					 int englob);
+#ifdef LIBXML_OUTPUT_ENABLED
+/* DEPRECATED */
+XMLPUBFUN void
+		xmlSprintfElementContent(char *buf,
+	                                 xmlElementContentPtr content,
+					 int englob);
+#endif /* LIBXML_OUTPUT_ENABLED */
+/* DEPRECATED */
+
+/* Element */
+XMLPUBFUN xmlElementPtr
+		xmlAddElementDecl	(xmlValidCtxtPtr ctxt,
+					 xmlDtdPtr dtd,
+					 const xmlChar *name,
+					 xmlElementTypeVal type,
+					 xmlElementContentPtr content);
+#ifdef LIBXML_TREE_ENABLED
+XMLPUBFUN xmlElementTablePtr
+		xmlCopyElementTable	(xmlElementTablePtr table);
+#endif /* LIBXML_TREE_ENABLED */
+XMLPUBFUN void
+		xmlFreeElementTable	(xmlElementTablePtr table);
+#ifdef LIBXML_OUTPUT_ENABLED
+XMLPUBFUN void
+		xmlDumpElementTable	(xmlBufferPtr buf,
+					 xmlElementTablePtr table);
+XMLPUBFUN void
+		xmlDumpElementDecl	(xmlBufferPtr buf,
+					 xmlElementPtr elem);
+#endif /* LIBXML_OUTPUT_ENABLED */
+
+/* Enumeration */
+XMLPUBFUN xmlEnumerationPtr
+		xmlCreateEnumeration	(const xmlChar *name);
+XMLPUBFUN void
+		xmlFreeEnumeration	(xmlEnumerationPtr cur);
+#ifdef LIBXML_TREE_ENABLED
+XMLPUBFUN xmlEnumerationPtr
+		xmlCopyEnumeration	(xmlEnumerationPtr cur);
+#endif /* LIBXML_TREE_ENABLED */
+
+/* Attribute */
+XMLPUBFUN xmlAttributePtr
+		xmlAddAttributeDecl	(xmlValidCtxtPtr ctxt,
+					 xmlDtdPtr dtd,
+					 const xmlChar *elem,
+					 const xmlChar *name,
+					 const xmlChar *ns,
+					 xmlAttributeType type,
+					 xmlAttributeDefault def,
+					 const xmlChar *defaultValue,
+					 xmlEnumerationPtr tree);
+#ifdef LIBXML_TREE_ENABLED
+XMLPUBFUN xmlAttributeTablePtr
+		xmlCopyAttributeTable  (xmlAttributeTablePtr table);
+#endif /* LIBXML_TREE_ENABLED */
+XMLPUBFUN void
+		xmlFreeAttributeTable  (xmlAttributeTablePtr table);
+#ifdef LIBXML_OUTPUT_ENABLED
+XMLPUBFUN void
+		xmlDumpAttributeTable  (xmlBufferPtr buf,
+					xmlAttributeTablePtr table);
+XMLPUBFUN void
+		xmlDumpAttributeDecl   (xmlBufferPtr buf,
+					xmlAttributePtr attr);
+#endif /* LIBXML_OUTPUT_ENABLED */
+
+/* IDs */
+XMLPUBFUN xmlIDPtr
+		xmlAddID	       (xmlValidCtxtPtr ctxt,
+					xmlDocPtr doc,
+					const xmlChar *value,
+					xmlAttrPtr attr);
+XMLPUBFUN void
+		xmlFreeIDTable	       (xmlIDTablePtr table);
+XMLPUBFUN xmlAttrPtr
+		xmlGetID	       (xmlDocPtr doc,
+					const xmlChar *ID);
+XMLPUBFUN int
+		xmlIsID		       (xmlDocPtr doc,
+					xmlNodePtr elem,
+					xmlAttrPtr attr);
+XMLPUBFUN int
+		xmlRemoveID	       (xmlDocPtr doc,
+					xmlAttrPtr attr);
+
+/* IDREFs */
+XML_DEPRECATED
+XMLPUBFUN xmlRefPtr
+		xmlAddRef	       (xmlValidCtxtPtr ctxt,
+					xmlDocPtr doc,
+					const xmlChar *value,
+					xmlAttrPtr attr);
+XML_DEPRECATED
+XMLPUBFUN void
+		xmlFreeRefTable	       (xmlRefTablePtr table);
+XML_DEPRECATED
+XMLPUBFUN int
+		xmlIsRef	       (xmlDocPtr doc,
+					xmlNodePtr elem,
+					xmlAttrPtr attr);
+XML_DEPRECATED
+XMLPUBFUN int
+		xmlRemoveRef	       (xmlDocPtr doc,
+					xmlAttrPtr attr);
+XML_DEPRECATED
+XMLPUBFUN xmlListPtr
+		xmlGetRefs	       (xmlDocPtr doc,
+					const xmlChar *ID);
+
+/**
+ * The public function calls related to validity checking.
+ */
+#ifdef LIBXML_VALID_ENABLED
+/* Allocate/Release Validation Contexts */
+XMLPUBFUN xmlValidCtxtPtr
+		xmlNewValidCtxt(void);
+XMLPUBFUN void
+		xmlFreeValidCtxt(xmlValidCtxtPtr);
+
+XMLPUBFUN int
+		xmlValidateRoot		(xmlValidCtxtPtr ctxt,
+					 xmlDocPtr doc);
+XMLPUBFUN int
+		xmlValidateElementDecl	(xmlValidCtxtPtr ctxt,
+					 xmlDocPtr doc,
+		                         xmlElementPtr elem);
+XMLPUBFUN xmlChar *
+		xmlValidNormalizeAttributeValue(xmlDocPtr doc,
+					 xmlNodePtr elem,
+					 const xmlChar *name,
+					 const xmlChar *value);
+XMLPUBFUN xmlChar *
+		xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt,
+					 xmlDocPtr doc,
+					 xmlNodePtr elem,
+					 const xmlChar *name,
+					 const xmlChar *value);
+XMLPUBFUN int
+		xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt,
+					 xmlDocPtr doc,
+		                         xmlAttributePtr attr);
+XMLPUBFUN int
+		xmlValidateAttributeValue(xmlAttributeType type,
+					 const xmlChar *value);
+XMLPUBFUN int
+		xmlValidateNotationDecl	(xmlValidCtxtPtr ctxt,
+					 xmlDocPtr doc,
+		                         xmlNotationPtr nota);
+XMLPUBFUN int
+		xmlValidateDtd		(xmlValidCtxtPtr ctxt,
+					 xmlDocPtr doc,
+					 xmlDtdPtr dtd);
+XMLPUBFUN int
+		xmlValidateDtdFinal	(xmlValidCtxtPtr ctxt,
+					 xmlDocPtr doc);
+XMLPUBFUN int
+		xmlValidateDocument	(xmlValidCtxtPtr ctxt,
+					 xmlDocPtr doc);
+XMLPUBFUN int
+		xmlValidateElement	(xmlValidCtxtPtr ctxt,
+					 xmlDocPtr doc,
+					 xmlNodePtr elem);
+XMLPUBFUN int
+		xmlValidateOneElement	(xmlValidCtxtPtr ctxt,
+					 xmlDocPtr doc,
+		                         xmlNodePtr elem);
+XMLPUBFUN int
+		xmlValidateOneAttribute	(xmlValidCtxtPtr ctxt,
+					 xmlDocPtr doc,
+					 xmlNodePtr	elem,
+					 xmlAttrPtr attr,
+					 const xmlChar *value);
+XMLPUBFUN int
+		xmlValidateOneNamespace	(xmlValidCtxtPtr ctxt,
+					 xmlDocPtr doc,
+					 xmlNodePtr elem,
+					 const xmlChar *prefix,
+					 xmlNsPtr ns,
+					 const xmlChar *value);
+XMLPUBFUN int
+		xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt,
+					 xmlDocPtr doc);
+#endif /* LIBXML_VALID_ENABLED */
+
+#if defined(LIBXML_VALID_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED)
+XMLPUBFUN int
+		xmlValidateNotationUse	(xmlValidCtxtPtr ctxt,
+					 xmlDocPtr doc,
+					 const xmlChar *notationName);
+#endif /* LIBXML_VALID_ENABLED or LIBXML_SCHEMAS_ENABLED */
+
+XMLPUBFUN int
+		xmlIsMixedElement	(xmlDocPtr doc,
+					 const xmlChar *name);
+XMLPUBFUN xmlAttributePtr
+		xmlGetDtdAttrDesc	(xmlDtdPtr dtd,
+					 const xmlChar *elem,
+					 const xmlChar *name);
+XMLPUBFUN xmlAttributePtr
+		xmlGetDtdQAttrDesc	(xmlDtdPtr dtd,
+					 const xmlChar *elem,
+					 const xmlChar *name,
+					 const xmlChar *prefix);
+XMLPUBFUN xmlNotationPtr
+		xmlGetDtdNotationDesc	(xmlDtdPtr dtd,
+					 const xmlChar *name);
+XMLPUBFUN xmlElementPtr
+		xmlGetDtdQElementDesc	(xmlDtdPtr dtd,
+					 const xmlChar *name,
+					 const xmlChar *prefix);
+XMLPUBFUN xmlElementPtr
+		xmlGetDtdElementDesc	(xmlDtdPtr dtd,
+					 const xmlChar *name);
+
+#ifdef LIBXML_VALID_ENABLED
+
+XMLPUBFUN int
+		xmlValidGetPotentialChildren(xmlElementContent *ctree,
+					 const xmlChar **names,
+					 int *len,
+					 int max);
+
+XMLPUBFUN int
+		xmlValidGetValidElements(xmlNode *prev,
+					 xmlNode *next,
+					 const xmlChar **names,
+					 int max);
+XMLPUBFUN int
+		xmlValidateNameValue	(const xmlChar *value);
+XMLPUBFUN int
+		xmlValidateNamesValue	(const xmlChar *value);
+XMLPUBFUN int
+		xmlValidateNmtokenValue	(const xmlChar *value);
+XMLPUBFUN int
+		xmlValidateNmtokensValue(const xmlChar *value);
+
+#ifdef LIBXML_REGEXP_ENABLED
+/*
+ * Validation based on the regexp support
+ */
+XMLPUBFUN int
+		xmlValidBuildContentModel(xmlValidCtxtPtr ctxt,
+					 xmlElementPtr elem);
+
+XMLPUBFUN int
+		xmlValidatePushElement	(xmlValidCtxtPtr ctxt,
+					 xmlDocPtr doc,
+					 xmlNodePtr elem,
+					 const xmlChar *qname);
+XMLPUBFUN int
+		xmlValidatePushCData	(xmlValidCtxtPtr ctxt,
+					 const xmlChar *data,
+					 int len);
+XMLPUBFUN int
+		xmlValidatePopElement	(xmlValidCtxtPtr ctxt,
+					 xmlDocPtr doc,
+					 xmlNodePtr elem,
+					 const xmlChar *qname);
+#endif /* LIBXML_REGEXP_ENABLED */
+#endif /* LIBXML_VALID_ENABLED */
+#ifdef __cplusplus
+}
+#endif
+#endif /* __XML_VALID_H__ */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xinclude.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xinclude.h
new file mode 100644
index 00000000..e1d135b3
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xinclude.h
@@ -0,0 +1,129 @@
+/*
+ * Summary: implementation of XInclude
+ * Description: API to handle XInclude processing,
+ * implements the
+ * World Wide Web Consortium Last Call Working Draft 10 November 2003
+ * http://www.w3.org/TR/2003/WD-xinclude-20031110
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XINCLUDE_H__
+#define __XML_XINCLUDE_H__
+
+#include <libxml/xmlversion.h>
+#include <libxml/tree.h>
+
+#ifdef LIBXML_XINCLUDE_ENABLED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * XINCLUDE_NS:
+ *
+ * Macro defining the Xinclude namespace: http://www.w3.org/2003/XInclude
+ */
+#define XINCLUDE_NS (const xmlChar *) "http://www.w3.org/2003/XInclude"
+/**
+ * XINCLUDE_OLD_NS:
+ *
+ * Macro defining the draft Xinclude namespace: http://www.w3.org/2001/XInclude
+ */
+#define XINCLUDE_OLD_NS (const xmlChar *) "http://www.w3.org/2001/XInclude"
+/**
+ * XINCLUDE_NODE:
+ *
+ * Macro defining "include"
+ */
+#define XINCLUDE_NODE (const xmlChar *) "include"
+/**
+ * XINCLUDE_FALLBACK:
+ *
+ * Macro defining "fallback"
+ */
+#define XINCLUDE_FALLBACK (const xmlChar *) "fallback"
+/**
+ * XINCLUDE_HREF:
+ *
+ * Macro defining "href"
+ */
+#define XINCLUDE_HREF (const xmlChar *) "href"
+/**
+ * XINCLUDE_PARSE:
+ *
+ * Macro defining "parse"
+ */
+#define XINCLUDE_PARSE (const xmlChar *) "parse"
+/**
+ * XINCLUDE_PARSE_XML:
+ *
+ * Macro defining "xml"
+ */
+#define XINCLUDE_PARSE_XML (const xmlChar *) "xml"
+/**
+ * XINCLUDE_PARSE_TEXT:
+ *
+ * Macro defining "text"
+ */
+#define XINCLUDE_PARSE_TEXT (const xmlChar *) "text"
+/**
+ * XINCLUDE_PARSE_ENCODING:
+ *
+ * Macro defining "encoding"
+ */
+#define XINCLUDE_PARSE_ENCODING (const xmlChar *) "encoding"
+/**
+ * XINCLUDE_PARSE_XPOINTER:
+ *
+ * Macro defining "xpointer"
+ */
+#define XINCLUDE_PARSE_XPOINTER (const xmlChar *) "xpointer"
+
+typedef struct _xmlXIncludeCtxt xmlXIncludeCtxt;
+typedef xmlXIncludeCtxt *xmlXIncludeCtxtPtr;
+
+/*
+ * standalone processing
+ */
+XMLPUBFUN int
+		xmlXIncludeProcess	(xmlDocPtr doc);
+XMLPUBFUN int
+		xmlXIncludeProcessFlags	(xmlDocPtr doc,
+					 int flags);
+XMLPUBFUN int
+		xmlXIncludeProcessFlagsData(xmlDocPtr doc,
+					 int flags,
+					 void *data);
+XMLPUBFUN int
+                xmlXIncludeProcessTreeFlagsData(xmlNodePtr tree,
+                                         int flags,
+                                         void *data);
+XMLPUBFUN int
+		xmlXIncludeProcessTree	(xmlNodePtr tree);
+XMLPUBFUN int
+		xmlXIncludeProcessTreeFlags(xmlNodePtr tree,
+					 int flags);
+/*
+ * contextual processing
+ */
+XMLPUBFUN xmlXIncludeCtxtPtr
+		xmlXIncludeNewContext	(xmlDocPtr doc);
+XMLPUBFUN int
+		xmlXIncludeSetFlags	(xmlXIncludeCtxtPtr ctxt,
+					 int flags);
+XMLPUBFUN void
+		xmlXIncludeFreeContext	(xmlXIncludeCtxtPtr ctxt);
+XMLPUBFUN int
+		xmlXIncludeProcessNode	(xmlXIncludeCtxtPtr ctxt,
+					 xmlNodePtr tree);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBXML_XINCLUDE_ENABLED */
+
+#endif /* __XML_XINCLUDE_H__ */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xlink.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xlink.h
new file mode 100644
index 00000000..10657366
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xlink.h
@@ -0,0 +1,189 @@
+/*
+ * Summary: unfinished XLink detection module
+ * Description: unfinished XLink detection module
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XLINK_H__
+#define __XML_XLINK_H__
+
+#include <libxml/xmlversion.h>
+#include <libxml/tree.h>
+
+#ifdef LIBXML_XPTR_ENABLED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Various defines for the various Link properties.
+ *
+ * NOTE: the link detection layer will try to resolve QName expansion
+ *       of namespaces. If "foo" is the prefix for "http://foo.com/"
+ *       then the link detection layer will expand role="foo:myrole"
+ *       to "http://foo.com/:myrole".
+ * NOTE: the link detection layer will expand URI-References found on
+ *       href attributes by using the base mechanism if found.
+ */
+typedef xmlChar *xlinkHRef;
+typedef xmlChar *xlinkRole;
+typedef xmlChar *xlinkTitle;
+
+typedef enum {
+    XLINK_TYPE_NONE = 0,
+    XLINK_TYPE_SIMPLE,
+    XLINK_TYPE_EXTENDED,
+    XLINK_TYPE_EXTENDED_SET
+} xlinkType;
+
+typedef enum {
+    XLINK_SHOW_NONE = 0,
+    XLINK_SHOW_NEW,
+    XLINK_SHOW_EMBED,
+    XLINK_SHOW_REPLACE
+} xlinkShow;
+
+typedef enum {
+    XLINK_ACTUATE_NONE = 0,
+    XLINK_ACTUATE_AUTO,
+    XLINK_ACTUATE_ONREQUEST
+} xlinkActuate;
+
+/**
+ * xlinkNodeDetectFunc:
+ * @ctx:  user data pointer
+ * @node:  the node to check
+ *
+ * This is the prototype for the link detection routine.
+ * It calls the default link detection callbacks upon link detection.
+ */
+typedef void (*xlinkNodeDetectFunc) (void *ctx, xmlNodePtr node);
+
+/*
+ * The link detection module interact with the upper layers using
+ * a set of callback registered at parsing time.
+ */
+
+/**
+ * xlinkSimpleLinkFunk:
+ * @ctx:  user data pointer
+ * @node:  the node carrying the link
+ * @href:  the target of the link
+ * @role:  the role string
+ * @title:  the link title
+ *
+ * This is the prototype for a simple link detection callback.
+ */
+typedef void
+(*xlinkSimpleLinkFunk)	(void *ctx,
+			 xmlNodePtr node,
+			 const xlinkHRef href,
+			 const xlinkRole role,
+			 const xlinkTitle title);
+
+/**
+ * xlinkExtendedLinkFunk:
+ * @ctx:  user data pointer
+ * @node:  the node carrying the link
+ * @nbLocators: the number of locators detected on the link
+ * @hrefs:  pointer to the array of locator hrefs
+ * @roles:  pointer to the array of locator roles
+ * @nbArcs: the number of arcs detected on the link
+ * @from:  pointer to the array of source roles found on the arcs
+ * @to:  pointer to the array of target roles found on the arcs
+ * @show:  array of values for the show attributes found on the arcs
+ * @actuate:  array of values for the actuate attributes found on the arcs
+ * @nbTitles: the number of titles detected on the link
+ * @title:  array of titles detected on the link
+ * @langs:  array of xml:lang values for the titles
+ *
+ * This is the prototype for a extended link detection callback.
+ */
+typedef void
+(*xlinkExtendedLinkFunk)(void *ctx,
+			 xmlNodePtr node,
+			 int nbLocators,
+			 const xlinkHRef *hrefs,
+			 const xlinkRole *roles,
+			 int nbArcs,
+			 const xlinkRole *from,
+			 const xlinkRole *to,
+			 xlinkShow *show,
+			 xlinkActuate *actuate,
+			 int nbTitles,
+			 const xlinkTitle *titles,
+			 const xmlChar **langs);
+
+/**
+ * xlinkExtendedLinkSetFunk:
+ * @ctx:  user data pointer
+ * @node:  the node carrying the link
+ * @nbLocators: the number of locators detected on the link
+ * @hrefs:  pointer to the array of locator hrefs
+ * @roles:  pointer to the array of locator roles
+ * @nbTitles: the number of titles detected on the link
+ * @title:  array of titles detected on the link
+ * @langs:  array of xml:lang values for the titles
+ *
+ * This is the prototype for a extended link set detection callback.
+ */
+typedef void
+(*xlinkExtendedLinkSetFunk)	(void *ctx,
+				 xmlNodePtr node,
+				 int nbLocators,
+				 const xlinkHRef *hrefs,
+				 const xlinkRole *roles,
+				 int nbTitles,
+				 const xlinkTitle *titles,
+				 const xmlChar **langs);
+
+/**
+ * This is the structure containing a set of Links detection callbacks.
+ *
+ * There is no default xlink callbacks, if one want to get link
+ * recognition activated, those call backs must be provided before parsing.
+ */
+typedef struct _xlinkHandler xlinkHandler;
+typedef xlinkHandler *xlinkHandlerPtr;
+struct _xlinkHandler {
+    xlinkSimpleLinkFunk simple;
+    xlinkExtendedLinkFunk extended;
+    xlinkExtendedLinkSetFunk set;
+};
+
+/*
+ * The default detection routine, can be overridden, they call the default
+ * detection callbacks.
+ */
+
+XMLPUBFUN xlinkNodeDetectFunc
+		xlinkGetDefaultDetect	(void);
+XMLPUBFUN void
+		xlinkSetDefaultDetect	(xlinkNodeDetectFunc func);
+
+/*
+ * Routines to set/get the default handlers.
+ */
+XMLPUBFUN xlinkHandlerPtr
+		xlinkGetDefaultHandler	(void);
+XMLPUBFUN void
+		xlinkSetDefaultHandler	(xlinkHandlerPtr handler);
+
+/*
+ * Link detection module itself.
+ */
+XMLPUBFUN xlinkType
+		xlinkIsLink		(xmlDocPtr doc,
+					 xmlNodePtr node);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBXML_XPTR_ENABLED */
+
+#endif /* __XML_XLINK_H__ */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlIO.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlIO.h
new file mode 100644
index 00000000..2487be3b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlIO.h
@@ -0,0 +1,421 @@
+/*
+ * Summary: interface for the I/O interfaces used by the parser
+ * Description: interface for the I/O interfaces used by the parser
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_IO_H__
+#define __XML_IO_H__
+
+#include <stdio.h>
+#include <libxml/xmlversion.h>
+#include <libxml/encoding.h>
+#define XML_TREE_INTERNALS
+#include <libxml/tree.h>
+#undef XML_TREE_INTERNALS
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Those are the functions and datatypes for the parser input
+ * I/O structures.
+ */
+
+/**
+ * xmlInputMatchCallback:
+ * @filename: the filename or URI
+ *
+ * Callback used in the I/O Input API to detect if the current handler
+ * can provide input functionality for this resource.
+ *
+ * Returns 1 if yes and 0 if another Input module should be used
+ */
+typedef int (*xmlInputMatchCallback) (char const *filename);
+/**
+ * xmlInputOpenCallback:
+ * @filename: the filename or URI
+ *
+ * Callback used in the I/O Input API to open the resource
+ *
+ * Returns an Input context or NULL in case or error
+ */
+typedef void * (*xmlInputOpenCallback) (char const *filename);
+/**
+ * xmlInputReadCallback:
+ * @context:  an Input context
+ * @buffer:  the buffer to store data read
+ * @len:  the length of the buffer in bytes
+ *
+ * Callback used in the I/O Input API to read the resource
+ *
+ * Returns the number of bytes read or -1 in case of error
+ */
+typedef int (*xmlInputReadCallback) (void * context, char * buffer, int len);
+/**
+ * xmlInputCloseCallback:
+ * @context:  an Input context
+ *
+ * Callback used in the I/O Input API to close the resource
+ *
+ * Returns 0 or -1 in case of error
+ */
+typedef int (*xmlInputCloseCallback) (void * context);
+
+#ifdef LIBXML_OUTPUT_ENABLED
+/*
+ * Those are the functions and datatypes for the library output
+ * I/O structures.
+ */
+
+/**
+ * xmlOutputMatchCallback:
+ * @filename: the filename or URI
+ *
+ * Callback used in the I/O Output API to detect if the current handler
+ * can provide output functionality for this resource.
+ *
+ * Returns 1 if yes and 0 if another Output module should be used
+ */
+typedef int (*xmlOutputMatchCallback) (char const *filename);
+/**
+ * xmlOutputOpenCallback:
+ * @filename: the filename or URI
+ *
+ * Callback used in the I/O Output API to open the resource
+ *
+ * Returns an Output context or NULL in case or error
+ */
+typedef void * (*xmlOutputOpenCallback) (char const *filename);
+/**
+ * xmlOutputWriteCallback:
+ * @context:  an Output context
+ * @buffer:  the buffer of data to write
+ * @len:  the length of the buffer in bytes
+ *
+ * Callback used in the I/O Output API to write to the resource
+ *
+ * Returns the number of bytes written or -1 in case of error
+ */
+typedef int (*xmlOutputWriteCallback) (void * context, const char * buffer,
+                                       int len);
+/**
+ * xmlOutputCloseCallback:
+ * @context:  an Output context
+ *
+ * Callback used in the I/O Output API to close the resource
+ *
+ * Returns 0 or -1 in case of error
+ */
+typedef int (*xmlOutputCloseCallback) (void * context);
+#endif /* LIBXML_OUTPUT_ENABLED */
+
+/**
+ * xmlParserInputBufferCreateFilenameFunc:
+ * @URI: the URI to read from
+ * @enc: the requested source encoding
+ *
+ * Signature for the function doing the lookup for a suitable input method
+ * corresponding to an URI.
+ *
+ * Returns the new xmlParserInputBufferPtr in case of success or NULL if no
+ *         method was found.
+ */
+typedef xmlParserInputBufferPtr
+(*xmlParserInputBufferCreateFilenameFunc)(const char *URI, xmlCharEncoding enc);
+
+/**
+ * xmlOutputBufferCreateFilenameFunc:
+ * @URI: the URI to write to
+ * @enc: the requested target encoding
+ *
+ * Signature for the function doing the lookup for a suitable output method
+ * corresponding to an URI.
+ *
+ * Returns the new xmlOutputBufferPtr in case of success or NULL if no
+ *         method was found.
+ */
+typedef xmlOutputBufferPtr
+(*xmlOutputBufferCreateFilenameFunc)(const char *URI,
+        xmlCharEncodingHandlerPtr encoder, int compression);
+
+struct _xmlParserInputBuffer {
+    void*                  context;
+    xmlInputReadCallback   readcallback;
+    xmlInputCloseCallback  closecallback;
+
+    xmlCharEncodingHandlerPtr encoder; /* I18N conversions to UTF-8 */
+
+    xmlBufPtr buffer;    /* Local buffer encoded in UTF-8 */
+    xmlBufPtr raw;       /* if encoder != NULL buffer for raw input */
+    int	compressed;	    /* -1=unknown, 0=not compressed, 1=compressed */
+    int error;
+    unsigned long rawconsumed;/* amount consumed from raw */
+};
+
+
+#ifdef LIBXML_OUTPUT_ENABLED
+struct _xmlOutputBuffer {
+    void*                   context;
+    xmlOutputWriteCallback  writecallback;
+    xmlOutputCloseCallback  closecallback;
+
+    xmlCharEncodingHandlerPtr encoder; /* I18N conversions to UTF-8 */
+
+    xmlBufPtr buffer;    /* Local buffer encoded in UTF-8 or ISOLatin */
+    xmlBufPtr conv;      /* if encoder != NULL buffer for output */
+    int written;            /* total number of byte written */
+    int error;
+};
+#endif /* LIBXML_OUTPUT_ENABLED */
+
+/** DOC_DISABLE */
+#define XML_GLOBALS_IO \
+  XML_OP(xmlParserInputBufferCreateFilenameValue, \
+           xmlParserInputBufferCreateFilenameFunc, XML_DEPRECATED) \
+  XML_OP(xmlOutputBufferCreateFilenameValue, \
+           xmlOutputBufferCreateFilenameFunc, XML_DEPRECATED)
+
+#define XML_OP XML_DECLARE_GLOBAL
+XML_GLOBALS_IO
+#undef XML_OP
+
+#if defined(LIBXML_THREAD_ENABLED) && !defined(XML_GLOBALS_NO_REDEFINITION)
+  #define xmlParserInputBufferCreateFilenameValue \
+    XML_GLOBAL_MACRO(xmlParserInputBufferCreateFilenameValue)
+  #define xmlOutputBufferCreateFilenameValue \
+    XML_GLOBAL_MACRO(xmlOutputBufferCreateFilenameValue)
+#endif
+/** DOC_ENABLE */
+
+/*
+ * Interfaces for input
+ */
+XMLPUBFUN void
+	xmlCleanupInputCallbacks		(void);
+
+XMLPUBFUN int
+	xmlPopInputCallbacks			(void);
+
+XMLPUBFUN void
+	xmlRegisterDefaultInputCallbacks	(void);
+XMLPUBFUN xmlParserInputBufferPtr
+	xmlAllocParserInputBuffer		(xmlCharEncoding enc);
+
+XMLPUBFUN xmlParserInputBufferPtr
+	xmlParserInputBufferCreateFilename	(const char *URI,
+                                                 xmlCharEncoding enc);
+XMLPUBFUN xmlParserInputBufferPtr
+	xmlParserInputBufferCreateFile		(FILE *file,
+                                                 xmlCharEncoding enc);
+XMLPUBFUN xmlParserInputBufferPtr
+	xmlParserInputBufferCreateFd		(int fd,
+	                                         xmlCharEncoding enc);
+XMLPUBFUN xmlParserInputBufferPtr
+	xmlParserInputBufferCreateMem		(const char *mem, int size,
+	                                         xmlCharEncoding enc);
+XMLPUBFUN xmlParserInputBufferPtr
+	xmlParserInputBufferCreateStatic	(const char *mem, int size,
+	                                         xmlCharEncoding enc);
+XMLPUBFUN xmlParserInputBufferPtr
+	xmlParserInputBufferCreateIO		(xmlInputReadCallback   ioread,
+						 xmlInputCloseCallback  ioclose,
+						 void *ioctx,
+	                                         xmlCharEncoding enc);
+XMLPUBFUN int
+	xmlParserInputBufferRead		(xmlParserInputBufferPtr in,
+						 int len);
+XMLPUBFUN int
+	xmlParserInputBufferGrow		(xmlParserInputBufferPtr in,
+						 int len);
+XMLPUBFUN int
+	xmlParserInputBufferPush		(xmlParserInputBufferPtr in,
+						 int len,
+						 const char *buf);
+XMLPUBFUN void
+	xmlFreeParserInputBuffer		(xmlParserInputBufferPtr in);
+XMLPUBFUN char *
+	xmlParserGetDirectory			(const char *filename);
+
+XMLPUBFUN int
+	xmlRegisterInputCallbacks		(xmlInputMatchCallback matchFunc,
+						 xmlInputOpenCallback openFunc,
+						 xmlInputReadCallback readFunc,
+						 xmlInputCloseCallback closeFunc);
+
+xmlParserInputBufferPtr
+	__xmlParserInputBufferCreateFilename(const char *URI,
+						xmlCharEncoding enc);
+
+#ifdef LIBXML_OUTPUT_ENABLED
+/*
+ * Interfaces for output
+ */
+XMLPUBFUN void
+	xmlCleanupOutputCallbacks		(void);
+XMLPUBFUN int
+	xmlPopOutputCallbacks			(void);
+XMLPUBFUN void
+	xmlRegisterDefaultOutputCallbacks(void);
+XMLPUBFUN xmlOutputBufferPtr
+	xmlAllocOutputBuffer		(xmlCharEncodingHandlerPtr encoder);
+
+XMLPUBFUN xmlOutputBufferPtr
+	xmlOutputBufferCreateFilename	(const char *URI,
+					 xmlCharEncodingHandlerPtr encoder,
+					 int compression);
+
+XMLPUBFUN xmlOutputBufferPtr
+	xmlOutputBufferCreateFile	(FILE *file,
+					 xmlCharEncodingHandlerPtr encoder);
+
+XMLPUBFUN xmlOutputBufferPtr
+	xmlOutputBufferCreateBuffer	(xmlBufferPtr buffer,
+					 xmlCharEncodingHandlerPtr encoder);
+
+XMLPUBFUN xmlOutputBufferPtr
+	xmlOutputBufferCreateFd		(int fd,
+					 xmlCharEncodingHandlerPtr encoder);
+
+XMLPUBFUN xmlOutputBufferPtr
+	xmlOutputBufferCreateIO		(xmlOutputWriteCallback   iowrite,
+					 xmlOutputCloseCallback  ioclose,
+					 void *ioctx,
+					 xmlCharEncodingHandlerPtr encoder);
+
+/* Couple of APIs to get the output without digging into the buffers */
+XMLPUBFUN const xmlChar *
+        xmlOutputBufferGetContent       (xmlOutputBufferPtr out);
+XMLPUBFUN size_t
+        xmlOutputBufferGetSize          (xmlOutputBufferPtr out);
+
+XMLPUBFUN int
+	xmlOutputBufferWrite		(xmlOutputBufferPtr out,
+					 int len,
+					 const char *buf);
+XMLPUBFUN int
+	xmlOutputBufferWriteString	(xmlOutputBufferPtr out,
+					 const char *str);
+XMLPUBFUN int
+	xmlOutputBufferWriteEscape	(xmlOutputBufferPtr out,
+					 const xmlChar *str,
+					 xmlCharEncodingOutputFunc escaping);
+
+XMLPUBFUN int
+	xmlOutputBufferFlush		(xmlOutputBufferPtr out);
+XMLPUBFUN int
+	xmlOutputBufferClose		(xmlOutputBufferPtr out);
+
+XMLPUBFUN int
+	xmlRegisterOutputCallbacks	(xmlOutputMatchCallback matchFunc,
+					 xmlOutputOpenCallback openFunc,
+					 xmlOutputWriteCallback writeFunc,
+					 xmlOutputCloseCallback closeFunc);
+
+xmlOutputBufferPtr
+	__xmlOutputBufferCreateFilename(const char *URI,
+                              xmlCharEncodingHandlerPtr encoder,
+                              int compression);
+
+#ifdef LIBXML_HTTP_ENABLED
+/*  This function only exists if HTTP support built into the library  */
+XMLPUBFUN void
+	xmlRegisterHTTPPostCallbacks	(void );
+#endif /* LIBXML_HTTP_ENABLED */
+
+#endif /* LIBXML_OUTPUT_ENABLED */
+
+XMLPUBFUN xmlParserInputPtr
+	xmlCheckHTTPInput		(xmlParserCtxtPtr ctxt,
+					 xmlParserInputPtr ret);
+
+/*
+ * A predefined entity loader disabling network accesses
+ */
+XMLPUBFUN xmlParserInputPtr
+	xmlNoNetExternalEntityLoader	(const char *URL,
+					 const char *ID,
+					 xmlParserCtxtPtr ctxt);
+
+/*
+ * xmlNormalizeWindowsPath is obsolete, don't use it.
+ * Check xmlCanonicPath in uri.h for a better alternative.
+ */
+XMLPUBFUN xmlChar *
+	xmlNormalizeWindowsPath		(const xmlChar *path);
+
+XMLPUBFUN int
+	xmlCheckFilename		(const char *path);
+/**
+ * Default 'file://' protocol callbacks
+ */
+XMLPUBFUN int
+	xmlFileMatch			(const char *filename);
+XMLPUBFUN void *
+	xmlFileOpen			(const char *filename);
+XMLPUBFUN int
+	xmlFileRead			(void * context,
+					 char * buffer,
+					 int len);
+XMLPUBFUN int
+	xmlFileClose			(void * context);
+
+/**
+ * Default 'http://' protocol callbacks
+ */
+#ifdef LIBXML_HTTP_ENABLED
+XMLPUBFUN int
+	xmlIOHTTPMatch			(const char *filename);
+XMLPUBFUN void *
+	xmlIOHTTPOpen			(const char *filename);
+#ifdef LIBXML_OUTPUT_ENABLED
+XMLPUBFUN void *
+	xmlIOHTTPOpenW			(const char * post_uri,
+					 int   compression );
+#endif /* LIBXML_OUTPUT_ENABLED */
+XMLPUBFUN int
+	xmlIOHTTPRead			(void * context,
+					 char * buffer,
+					 int len);
+XMLPUBFUN int
+	xmlIOHTTPClose			(void * context);
+#endif /* LIBXML_HTTP_ENABLED */
+
+/**
+ * Default 'ftp://' protocol callbacks
+ */
+#if defined(LIBXML_FTP_ENABLED)
+XMLPUBFUN int
+	xmlIOFTPMatch			(const char *filename);
+XMLPUBFUN void *
+	xmlIOFTPOpen			(const char *filename);
+XMLPUBFUN int
+	xmlIOFTPRead			(void * context,
+					 char * buffer,
+					 int len);
+XMLPUBFUN int
+	xmlIOFTPClose			(void * context);
+#endif /* defined(LIBXML_FTP_ENABLED) */
+
+XMLPUBFUN xmlParserInputBufferCreateFilenameFunc
+	xmlParserInputBufferCreateFilenameDefault(
+		xmlParserInputBufferCreateFilenameFunc func);
+XMLPUBFUN xmlOutputBufferCreateFilenameFunc
+	xmlOutputBufferCreateFilenameDefault(
+		xmlOutputBufferCreateFilenameFunc func);
+XMLPUBFUN xmlOutputBufferCreateFilenameFunc
+	xmlThrDefOutputBufferCreateFilenameDefault(
+		xmlOutputBufferCreateFilenameFunc func);
+XMLPUBFUN xmlParserInputBufferCreateFilenameFunc
+	xmlThrDefParserInputBufferCreateFilenameDefault(
+		xmlParserInputBufferCreateFilenameFunc func);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_IO_H__ */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlautomata.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlautomata.h
new file mode 100644
index 00000000..ea38eb37
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlautomata.h
@@ -0,0 +1,146 @@
+/*
+ * Summary: API to build regexp automata
+ * Description: the API to build regexp automata
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_AUTOMATA_H__
+#define __XML_AUTOMATA_H__
+
+#include <libxml/xmlversion.h>
+
+#ifdef LIBXML_REGEXP_ENABLED
+#ifdef LIBXML_AUTOMATA_ENABLED
+
+#include <libxml/xmlstring.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * xmlAutomataPtr:
+ *
+ * A libxml automata description, It can be compiled into a regexp
+ */
+typedef struct _xmlAutomata xmlAutomata;
+typedef xmlAutomata *xmlAutomataPtr;
+
+/**
+ * xmlAutomataStatePtr:
+ *
+ * A state int the automata description,
+ */
+typedef struct _xmlAutomataState xmlAutomataState;
+typedef xmlAutomataState *xmlAutomataStatePtr;
+
+/*
+ * Building API
+ */
+XMLPUBFUN xmlAutomataPtr
+		    xmlNewAutomata		(void);
+XMLPUBFUN void
+		    xmlFreeAutomata		(xmlAutomataPtr am);
+
+XMLPUBFUN xmlAutomataStatePtr
+		    xmlAutomataGetInitState	(xmlAutomataPtr am);
+XMLPUBFUN int
+		    xmlAutomataSetFinalState	(xmlAutomataPtr am,
+						 xmlAutomataStatePtr state);
+XMLPUBFUN xmlAutomataStatePtr
+		    xmlAutomataNewState		(xmlAutomataPtr am);
+XMLPUBFUN xmlAutomataStatePtr
+		    xmlAutomataNewTransition	(xmlAutomataPtr am,
+						 xmlAutomataStatePtr from,
+						 xmlAutomataStatePtr to,
+						 const xmlChar *token,
+						 void *data);
+XMLPUBFUN xmlAutomataStatePtr
+		    xmlAutomataNewTransition2	(xmlAutomataPtr am,
+						 xmlAutomataStatePtr from,
+						 xmlAutomataStatePtr to,
+						 const xmlChar *token,
+						 const xmlChar *token2,
+						 void *data);
+XMLPUBFUN xmlAutomataStatePtr
+                    xmlAutomataNewNegTrans	(xmlAutomataPtr am,
+						 xmlAutomataStatePtr from,
+						 xmlAutomataStatePtr to,
+						 const xmlChar *token,
+						 const xmlChar *token2,
+						 void *data);
+
+XMLPUBFUN xmlAutomataStatePtr
+		    xmlAutomataNewCountTrans	(xmlAutomataPtr am,
+						 xmlAutomataStatePtr from,
+						 xmlAutomataStatePtr to,
+						 const xmlChar *token,
+						 int min,
+						 int max,
+						 void *data);
+XMLPUBFUN xmlAutomataStatePtr
+		    xmlAutomataNewCountTrans2	(xmlAutomataPtr am,
+						 xmlAutomataStatePtr from,
+						 xmlAutomataStatePtr to,
+						 const xmlChar *token,
+						 const xmlChar *token2,
+						 int min,
+						 int max,
+						 void *data);
+XMLPUBFUN xmlAutomataStatePtr
+		    xmlAutomataNewOnceTrans	(xmlAutomataPtr am,
+						 xmlAutomataStatePtr from,
+						 xmlAutomataStatePtr to,
+						 const xmlChar *token,
+						 int min,
+						 int max,
+						 void *data);
+XMLPUBFUN xmlAutomataStatePtr
+		    xmlAutomataNewOnceTrans2	(xmlAutomataPtr am,
+						 xmlAutomataStatePtr from,
+						 xmlAutomataStatePtr to,
+						 const xmlChar *token,
+						 const xmlChar *token2,
+						 int min,
+						 int max,
+						 void *data);
+XMLPUBFUN xmlAutomataStatePtr
+		    xmlAutomataNewAllTrans	(xmlAutomataPtr am,
+						 xmlAutomataStatePtr from,
+						 xmlAutomataStatePtr to,
+						 int lax);
+XMLPUBFUN xmlAutomataStatePtr
+		    xmlAutomataNewEpsilon	(xmlAutomataPtr am,
+						 xmlAutomataStatePtr from,
+						 xmlAutomataStatePtr to);
+XMLPUBFUN xmlAutomataStatePtr
+		    xmlAutomataNewCountedTrans	(xmlAutomataPtr am,
+						 xmlAutomataStatePtr from,
+						 xmlAutomataStatePtr to,
+						 int counter);
+XMLPUBFUN xmlAutomataStatePtr
+		    xmlAutomataNewCounterTrans	(xmlAutomataPtr am,
+						 xmlAutomataStatePtr from,
+						 xmlAutomataStatePtr to,
+						 int counter);
+XMLPUBFUN int
+		    xmlAutomataNewCounter	(xmlAutomataPtr am,
+						 int min,
+						 int max);
+
+XMLPUBFUN struct _xmlRegexp *
+		    xmlAutomataCompile		(xmlAutomataPtr am);
+XMLPUBFUN int
+		    xmlAutomataIsDeterminist	(xmlAutomataPtr am);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBXML_AUTOMATA_ENABLED */
+#endif /* LIBXML_REGEXP_ENABLED */
+
+#endif /* __XML_AUTOMATA_H__ */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlerror.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlerror.h
new file mode 100644
index 00000000..1f0ab4a3
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlerror.h
@@ -0,0 +1,948 @@
+/*
+ * Summary: error handling
+ * Description: the API used to report errors
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_ERROR_H__
+#define __XML_ERROR_H__
+
+#include <libxml/xmlversion.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * xmlErrorLevel:
+ *
+ * Indicates the level of an error
+ */
+typedef enum {
+    XML_ERR_NONE = 0,
+    XML_ERR_WARNING = 1,	/* A simple warning */
+    XML_ERR_ERROR = 2,		/* A recoverable error */
+    XML_ERR_FATAL = 3		/* A fatal error */
+} xmlErrorLevel;
+
+/**
+ * xmlErrorDomain:
+ *
+ * Indicates where an error may have come from
+ */
+typedef enum {
+    XML_FROM_NONE = 0,
+    XML_FROM_PARSER,	/* The XML parser */
+    XML_FROM_TREE,	/* The tree module */
+    XML_FROM_NAMESPACE,	/* The XML Namespace module */
+    XML_FROM_DTD,	/* The XML DTD validation with parser context*/
+    XML_FROM_HTML,	/* The HTML parser */
+    XML_FROM_MEMORY,	/* The memory allocator */
+    XML_FROM_OUTPUT,	/* The serialization code */
+    XML_FROM_IO,	/* The Input/Output stack */
+    XML_FROM_FTP,	/* The FTP module */
+    XML_FROM_HTTP,	/* The HTTP module */
+    XML_FROM_XINCLUDE,	/* The XInclude processing */
+    XML_FROM_XPATH,	/* The XPath module */
+    XML_FROM_XPOINTER,	/* The XPointer module */
+    XML_FROM_REGEXP,	/* The regular expressions module */
+    XML_FROM_DATATYPE,	/* The W3C XML Schemas Datatype module */
+    XML_FROM_SCHEMASP,	/* The W3C XML Schemas parser module */
+    XML_FROM_SCHEMASV,	/* The W3C XML Schemas validation module */
+    XML_FROM_RELAXNGP,	/* The Relax-NG parser module */
+    XML_FROM_RELAXNGV,	/* The Relax-NG validator module */
+    XML_FROM_CATALOG,	/* The Catalog module */
+    XML_FROM_C14N,	/* The Canonicalization module */
+    XML_FROM_XSLT,	/* The XSLT engine from libxslt */
+    XML_FROM_VALID,	/* The XML DTD validation with valid context */
+    XML_FROM_CHECK,	/* The error checking module */
+    XML_FROM_WRITER,	/* The xmlwriter module */
+    XML_FROM_MODULE,	/* The dynamically loaded module module*/
+    XML_FROM_I18N,	/* The module handling character conversion */
+    XML_FROM_SCHEMATRONV,/* The Schematron validator module */
+    XML_FROM_BUFFER,    /* The buffers module */
+    XML_FROM_URI        /* The URI module */
+} xmlErrorDomain;
+
+/**
+ * xmlError:
+ *
+ * An XML Error instance.
+ */
+
+typedef struct _xmlError xmlError;
+typedef xmlError *xmlErrorPtr;
+struct _xmlError {
+    int		domain;	/* What part of the library raised this error */
+    int		code;	/* The error code, e.g. an xmlParserError */
+    char       *message;/* human-readable informative error message */
+    xmlErrorLevel level;/* how consequent is the error */
+    char       *file;	/* the filename */
+    int		line;	/* the line number if available */
+    char       *str1;	/* extra string information */
+    char       *str2;	/* extra string information */
+    char       *str3;	/* extra string information */
+    int		int1;	/* extra number information */
+    int		int2;	/* error column # or 0 if N/A (todo: rename field when we would brk ABI) */
+    void       *ctxt;   /* the parser context if available */
+    void       *node;   /* the node in the tree */
+};
+
+/**
+ * xmlParserError:
+ *
+ * This is an error that the XML (or HTML) parser can generate
+ */
+typedef enum {
+    XML_ERR_OK = 0,
+    XML_ERR_INTERNAL_ERROR, /* 1 */
+    XML_ERR_NO_MEMORY, /* 2 */
+    XML_ERR_DOCUMENT_START, /* 3 */
+    XML_ERR_DOCUMENT_EMPTY, /* 4 */
+    XML_ERR_DOCUMENT_END, /* 5 */
+    XML_ERR_INVALID_HEX_CHARREF, /* 6 */
+    XML_ERR_INVALID_DEC_CHARREF, /* 7 */
+    XML_ERR_INVALID_CHARREF, /* 8 */
+    XML_ERR_INVALID_CHAR, /* 9 */
+    XML_ERR_CHARREF_AT_EOF, /* 10 */
+    XML_ERR_CHARREF_IN_PROLOG, /* 11 */
+    XML_ERR_CHARREF_IN_EPILOG, /* 12 */
+    XML_ERR_CHARREF_IN_DTD, /* 13 */
+    XML_ERR_ENTITYREF_AT_EOF, /* 14 */
+    XML_ERR_ENTITYREF_IN_PROLOG, /* 15 */
+    XML_ERR_ENTITYREF_IN_EPILOG, /* 16 */
+    XML_ERR_ENTITYREF_IN_DTD, /* 17 */
+    XML_ERR_PEREF_AT_EOF, /* 18 */
+    XML_ERR_PEREF_IN_PROLOG, /* 19 */
+    XML_ERR_PEREF_IN_EPILOG, /* 20 */
+    XML_ERR_PEREF_IN_INT_SUBSET, /* 21 */
+    XML_ERR_ENTITYREF_NO_NAME, /* 22 */
+    XML_ERR_ENTITYREF_SEMICOL_MISSING, /* 23 */
+    XML_ERR_PEREF_NO_NAME, /* 24 */
+    XML_ERR_PEREF_SEMICOL_MISSING, /* 25 */
+    XML_ERR_UNDECLARED_ENTITY, /* 26 */
+    XML_WAR_UNDECLARED_ENTITY, /* 27 */
+    XML_ERR_UNPARSED_ENTITY, /* 28 */
+    XML_ERR_ENTITY_IS_EXTERNAL, /* 29 */
+    XML_ERR_ENTITY_IS_PARAMETER, /* 30 */
+    XML_ERR_UNKNOWN_ENCODING, /* 31 */
+    XML_ERR_UNSUPPORTED_ENCODING, /* 32 */
+    XML_ERR_STRING_NOT_STARTED, /* 33 */
+    XML_ERR_STRING_NOT_CLOSED, /* 34 */
+    XML_ERR_NS_DECL_ERROR, /* 35 */
+    XML_ERR_ENTITY_NOT_STARTED, /* 36 */
+    XML_ERR_ENTITY_NOT_FINISHED, /* 37 */
+    XML_ERR_LT_IN_ATTRIBUTE, /* 38 */
+    XML_ERR_ATTRIBUTE_NOT_STARTED, /* 39 */
+    XML_ERR_ATTRIBUTE_NOT_FINISHED, /* 40 */
+    XML_ERR_ATTRIBUTE_WITHOUT_VALUE, /* 41 */
+    XML_ERR_ATTRIBUTE_REDEFINED, /* 42 */
+    XML_ERR_LITERAL_NOT_STARTED, /* 43 */
+    XML_ERR_LITERAL_NOT_FINISHED, /* 44 */
+    XML_ERR_COMMENT_NOT_FINISHED, /* 45 */
+    XML_ERR_PI_NOT_STARTED, /* 46 */
+    XML_ERR_PI_NOT_FINISHED, /* 47 */
+    XML_ERR_NOTATION_NOT_STARTED, /* 48 */
+    XML_ERR_NOTATION_NOT_FINISHED, /* 49 */
+    XML_ERR_ATTLIST_NOT_STARTED, /* 50 */
+    XML_ERR_ATTLIST_NOT_FINISHED, /* 51 */
+    XML_ERR_MIXED_NOT_STARTED, /* 52 */
+    XML_ERR_MIXED_NOT_FINISHED, /* 53 */
+    XML_ERR_ELEMCONTENT_NOT_STARTED, /* 54 */
+    XML_ERR_ELEMCONTENT_NOT_FINISHED, /* 55 */
+    XML_ERR_XMLDECL_NOT_STARTED, /* 56 */
+    XML_ERR_XMLDECL_NOT_FINISHED, /* 57 */
+    XML_ERR_CONDSEC_NOT_STARTED, /* 58 */
+    XML_ERR_CONDSEC_NOT_FINISHED, /* 59 */
+    XML_ERR_EXT_SUBSET_NOT_FINISHED, /* 60 */
+    XML_ERR_DOCTYPE_NOT_FINISHED, /* 61 */
+    XML_ERR_MISPLACED_CDATA_END, /* 62 */
+    XML_ERR_CDATA_NOT_FINISHED, /* 63 */
+    XML_ERR_RESERVED_XML_NAME, /* 64 */
+    XML_ERR_SPACE_REQUIRED, /* 65 */
+    XML_ERR_SEPARATOR_REQUIRED, /* 66 */
+    XML_ERR_NMTOKEN_REQUIRED, /* 67 */
+    XML_ERR_NAME_REQUIRED, /* 68 */
+    XML_ERR_PCDATA_REQUIRED, /* 69 */
+    XML_ERR_URI_REQUIRED, /* 70 */
+    XML_ERR_PUBID_REQUIRED, /* 71 */
+    XML_ERR_LT_REQUIRED, /* 72 */
+    XML_ERR_GT_REQUIRED, /* 73 */
+    XML_ERR_LTSLASH_REQUIRED, /* 74 */
+    XML_ERR_EQUAL_REQUIRED, /* 75 */
+    XML_ERR_TAG_NAME_MISMATCH, /* 76 */
+    XML_ERR_TAG_NOT_FINISHED, /* 77 */
+    XML_ERR_STANDALONE_VALUE, /* 78 */
+    XML_ERR_ENCODING_NAME, /* 79 */
+    XML_ERR_HYPHEN_IN_COMMENT, /* 80 */
+    XML_ERR_INVALID_ENCODING, /* 81 */
+    XML_ERR_EXT_ENTITY_STANDALONE, /* 82 */
+    XML_ERR_CONDSEC_INVALID, /* 83 */
+    XML_ERR_VALUE_REQUIRED, /* 84 */
+    XML_ERR_NOT_WELL_BALANCED, /* 85 */
+    XML_ERR_EXTRA_CONTENT, /* 86 */
+    XML_ERR_ENTITY_CHAR_ERROR, /* 87 */
+    XML_ERR_ENTITY_PE_INTERNAL, /* 88 */
+    XML_ERR_ENTITY_LOOP, /* 89 */
+    XML_ERR_ENTITY_BOUNDARY, /* 90 */
+    XML_ERR_INVALID_URI, /* 91 */
+    XML_ERR_URI_FRAGMENT, /* 92 */
+    XML_WAR_CATALOG_PI, /* 93 */
+    XML_ERR_NO_DTD, /* 94 */
+    XML_ERR_CONDSEC_INVALID_KEYWORD, /* 95 */
+    XML_ERR_VERSION_MISSING, /* 96 */
+    XML_WAR_UNKNOWN_VERSION, /* 97 */
+    XML_WAR_LANG_VALUE, /* 98 */
+    XML_WAR_NS_URI, /* 99 */
+    XML_WAR_NS_URI_RELATIVE, /* 100 */
+    XML_ERR_MISSING_ENCODING, /* 101 */
+    XML_WAR_SPACE_VALUE, /* 102 */
+    XML_ERR_NOT_STANDALONE, /* 103 */
+    XML_ERR_ENTITY_PROCESSING, /* 104 */
+    XML_ERR_NOTATION_PROCESSING, /* 105 */
+    XML_WAR_NS_COLUMN, /* 106 */
+    XML_WAR_ENTITY_REDEFINED, /* 107 */
+    XML_ERR_UNKNOWN_VERSION, /* 108 */
+    XML_ERR_VERSION_MISMATCH, /* 109 */
+    XML_ERR_NAME_TOO_LONG, /* 110 */
+    XML_ERR_USER_STOP, /* 111 */
+    XML_ERR_COMMENT_ABRUPTLY_ENDED, /* 112 */
+    XML_WAR_ENCODING_MISMATCH, /* 113 */
+    XML_NS_ERR_XML_NAMESPACE = 200,
+    XML_NS_ERR_UNDEFINED_NAMESPACE, /* 201 */
+    XML_NS_ERR_QNAME, /* 202 */
+    XML_NS_ERR_ATTRIBUTE_REDEFINED, /* 203 */
+    XML_NS_ERR_EMPTY, /* 204 */
+    XML_NS_ERR_COLON, /* 205 */
+    XML_DTD_ATTRIBUTE_DEFAULT = 500,
+    XML_DTD_ATTRIBUTE_REDEFINED, /* 501 */
+    XML_DTD_ATTRIBUTE_VALUE, /* 502 */
+    XML_DTD_CONTENT_ERROR, /* 503 */
+    XML_DTD_CONTENT_MODEL, /* 504 */
+    XML_DTD_CONTENT_NOT_DETERMINIST, /* 505 */
+    XML_DTD_DIFFERENT_PREFIX, /* 506 */
+    XML_DTD_ELEM_DEFAULT_NAMESPACE, /* 507 */
+    XML_DTD_ELEM_NAMESPACE, /* 508 */
+    XML_DTD_ELEM_REDEFINED, /* 509 */
+    XML_DTD_EMPTY_NOTATION, /* 510 */
+    XML_DTD_ENTITY_TYPE, /* 511 */
+    XML_DTD_ID_FIXED, /* 512 */
+    XML_DTD_ID_REDEFINED, /* 513 */
+    XML_DTD_ID_SUBSET, /* 514 */
+    XML_DTD_INVALID_CHILD, /* 515 */
+    XML_DTD_INVALID_DEFAULT, /* 516 */
+    XML_DTD_LOAD_ERROR, /* 517 */
+    XML_DTD_MISSING_ATTRIBUTE, /* 518 */
+    XML_DTD_MIXED_CORRUPT, /* 519 */
+    XML_DTD_MULTIPLE_ID, /* 520 */
+    XML_DTD_NO_DOC, /* 521 */
+    XML_DTD_NO_DTD, /* 522 */
+    XML_DTD_NO_ELEM_NAME, /* 523 */
+    XML_DTD_NO_PREFIX, /* 524 */
+    XML_DTD_NO_ROOT, /* 525 */
+    XML_DTD_NOTATION_REDEFINED, /* 526 */
+    XML_DTD_NOTATION_VALUE, /* 527 */
+    XML_DTD_NOT_EMPTY, /* 528 */
+    XML_DTD_NOT_PCDATA, /* 529 */
+    XML_DTD_NOT_STANDALONE, /* 530 */
+    XML_DTD_ROOT_NAME, /* 531 */
+    XML_DTD_STANDALONE_WHITE_SPACE, /* 532 */
+    XML_DTD_UNKNOWN_ATTRIBUTE, /* 533 */
+    XML_DTD_UNKNOWN_ELEM, /* 534 */
+    XML_DTD_UNKNOWN_ENTITY, /* 535 */
+    XML_DTD_UNKNOWN_ID, /* 536 */
+    XML_DTD_UNKNOWN_NOTATION, /* 537 */
+    XML_DTD_STANDALONE_DEFAULTED, /* 538 */
+    XML_DTD_XMLID_VALUE, /* 539 */
+    XML_DTD_XMLID_TYPE, /* 540 */
+    XML_DTD_DUP_TOKEN, /* 541 */
+    XML_HTML_STRUCURE_ERROR = 800,
+    XML_HTML_UNKNOWN_TAG, /* 801 */
+    XML_HTML_INCORRECTLY_OPENED_COMMENT, /* 802 */
+    XML_RNGP_ANYNAME_ATTR_ANCESTOR = 1000,
+    XML_RNGP_ATTR_CONFLICT, /* 1001 */
+    XML_RNGP_ATTRIBUTE_CHILDREN, /* 1002 */
+    XML_RNGP_ATTRIBUTE_CONTENT, /* 1003 */
+    XML_RNGP_ATTRIBUTE_EMPTY, /* 1004 */
+    XML_RNGP_ATTRIBUTE_NOOP, /* 1005 */
+    XML_RNGP_CHOICE_CONTENT, /* 1006 */
+    XML_RNGP_CHOICE_EMPTY, /* 1007 */
+    XML_RNGP_CREATE_FAILURE, /* 1008 */
+    XML_RNGP_DATA_CONTENT, /* 1009 */
+    XML_RNGP_DEF_CHOICE_AND_INTERLEAVE, /* 1010 */
+    XML_RNGP_DEFINE_CREATE_FAILED, /* 1011 */
+    XML_RNGP_DEFINE_EMPTY, /* 1012 */
+    XML_RNGP_DEFINE_MISSING, /* 1013 */
+    XML_RNGP_DEFINE_NAME_MISSING, /* 1014 */
+    XML_RNGP_ELEM_CONTENT_EMPTY, /* 1015 */
+    XML_RNGP_ELEM_CONTENT_ERROR, /* 1016 */
+    XML_RNGP_ELEMENT_EMPTY, /* 1017 */
+    XML_RNGP_ELEMENT_CONTENT, /* 1018 */
+    XML_RNGP_ELEMENT_NAME, /* 1019 */
+    XML_RNGP_ELEMENT_NO_CONTENT, /* 1020 */
+    XML_RNGP_ELEM_TEXT_CONFLICT, /* 1021 */
+    XML_RNGP_EMPTY, /* 1022 */
+    XML_RNGP_EMPTY_CONSTRUCT, /* 1023 */
+    XML_RNGP_EMPTY_CONTENT, /* 1024 */
+    XML_RNGP_EMPTY_NOT_EMPTY, /* 1025 */
+    XML_RNGP_ERROR_TYPE_LIB, /* 1026 */
+    XML_RNGP_EXCEPT_EMPTY, /* 1027 */
+    XML_RNGP_EXCEPT_MISSING, /* 1028 */
+    XML_RNGP_EXCEPT_MULTIPLE, /* 1029 */
+    XML_RNGP_EXCEPT_NO_CONTENT, /* 1030 */
+    XML_RNGP_EXTERNALREF_EMTPY, /* 1031 */
+    XML_RNGP_EXTERNAL_REF_FAILURE, /* 1032 */
+    XML_RNGP_EXTERNALREF_RECURSE, /* 1033 */
+    XML_RNGP_FORBIDDEN_ATTRIBUTE, /* 1034 */
+    XML_RNGP_FOREIGN_ELEMENT, /* 1035 */
+    XML_RNGP_GRAMMAR_CONTENT, /* 1036 */
+    XML_RNGP_GRAMMAR_EMPTY, /* 1037 */
+    XML_RNGP_GRAMMAR_MISSING, /* 1038 */
+    XML_RNGP_GRAMMAR_NO_START, /* 1039 */
+    XML_RNGP_GROUP_ATTR_CONFLICT, /* 1040 */
+    XML_RNGP_HREF_ERROR, /* 1041 */
+    XML_RNGP_INCLUDE_EMPTY, /* 1042 */
+    XML_RNGP_INCLUDE_FAILURE, /* 1043 */
+    XML_RNGP_INCLUDE_RECURSE, /* 1044 */
+    XML_RNGP_INTERLEAVE_ADD, /* 1045 */
+    XML_RNGP_INTERLEAVE_CREATE_FAILED, /* 1046 */
+    XML_RNGP_INTERLEAVE_EMPTY, /* 1047 */
+    XML_RNGP_INTERLEAVE_NO_CONTENT, /* 1048 */
+    XML_RNGP_INVALID_DEFINE_NAME, /* 1049 */
+    XML_RNGP_INVALID_URI, /* 1050 */
+    XML_RNGP_INVALID_VALUE, /* 1051 */
+    XML_RNGP_MISSING_HREF, /* 1052 */
+    XML_RNGP_NAME_MISSING, /* 1053 */
+    XML_RNGP_NEED_COMBINE, /* 1054 */
+    XML_RNGP_NOTALLOWED_NOT_EMPTY, /* 1055 */
+    XML_RNGP_NSNAME_ATTR_ANCESTOR, /* 1056 */
+    XML_RNGP_NSNAME_NO_NS, /* 1057 */
+    XML_RNGP_PARAM_FORBIDDEN, /* 1058 */
+    XML_RNGP_PARAM_NAME_MISSING, /* 1059 */
+    XML_RNGP_PARENTREF_CREATE_FAILED, /* 1060 */
+    XML_RNGP_PARENTREF_NAME_INVALID, /* 1061 */
+    XML_RNGP_PARENTREF_NO_NAME, /* 1062 */
+    XML_RNGP_PARENTREF_NO_PARENT, /* 1063 */
+    XML_RNGP_PARENTREF_NOT_EMPTY, /* 1064 */
+    XML_RNGP_PARSE_ERROR, /* 1065 */
+    XML_RNGP_PAT_ANYNAME_EXCEPT_ANYNAME, /* 1066 */
+    XML_RNGP_PAT_ATTR_ATTR, /* 1067 */
+    XML_RNGP_PAT_ATTR_ELEM, /* 1068 */
+    XML_RNGP_PAT_DATA_EXCEPT_ATTR, /* 1069 */
+    XML_RNGP_PAT_DATA_EXCEPT_ELEM, /* 1070 */
+    XML_RNGP_PAT_DATA_EXCEPT_EMPTY, /* 1071 */
+    XML_RNGP_PAT_DATA_EXCEPT_GROUP, /* 1072 */
+    XML_RNGP_PAT_DATA_EXCEPT_INTERLEAVE, /* 1073 */
+    XML_RNGP_PAT_DATA_EXCEPT_LIST, /* 1074 */
+    XML_RNGP_PAT_DATA_EXCEPT_ONEMORE, /* 1075 */
+    XML_RNGP_PAT_DATA_EXCEPT_REF, /* 1076 */
+    XML_RNGP_PAT_DATA_EXCEPT_TEXT, /* 1077 */
+    XML_RNGP_PAT_LIST_ATTR, /* 1078 */
+    XML_RNGP_PAT_LIST_ELEM, /* 1079 */
+    XML_RNGP_PAT_LIST_INTERLEAVE, /* 1080 */
+    XML_RNGP_PAT_LIST_LIST, /* 1081 */
+    XML_RNGP_PAT_LIST_REF, /* 1082 */
+    XML_RNGP_PAT_LIST_TEXT, /* 1083 */
+    XML_RNGP_PAT_NSNAME_EXCEPT_ANYNAME, /* 1084 */
+    XML_RNGP_PAT_NSNAME_EXCEPT_NSNAME, /* 1085 */
+    XML_RNGP_PAT_ONEMORE_GROUP_ATTR, /* 1086 */
+    XML_RNGP_PAT_ONEMORE_INTERLEAVE_ATTR, /* 1087 */
+    XML_RNGP_PAT_START_ATTR, /* 1088 */
+    XML_RNGP_PAT_START_DATA, /* 1089 */
+    XML_RNGP_PAT_START_EMPTY, /* 1090 */
+    XML_RNGP_PAT_START_GROUP, /* 1091 */
+    XML_RNGP_PAT_START_INTERLEAVE, /* 1092 */
+    XML_RNGP_PAT_START_LIST, /* 1093 */
+    XML_RNGP_PAT_START_ONEMORE, /* 1094 */
+    XML_RNGP_PAT_START_TEXT, /* 1095 */
+    XML_RNGP_PAT_START_VALUE, /* 1096 */
+    XML_RNGP_PREFIX_UNDEFINED, /* 1097 */
+    XML_RNGP_REF_CREATE_FAILED, /* 1098 */
+    XML_RNGP_REF_CYCLE, /* 1099 */
+    XML_RNGP_REF_NAME_INVALID, /* 1100 */
+    XML_RNGP_REF_NO_DEF, /* 1101 */
+    XML_RNGP_REF_NO_NAME, /* 1102 */
+    XML_RNGP_REF_NOT_EMPTY, /* 1103 */
+    XML_RNGP_START_CHOICE_AND_INTERLEAVE, /* 1104 */
+    XML_RNGP_START_CONTENT, /* 1105 */
+    XML_RNGP_START_EMPTY, /* 1106 */
+    XML_RNGP_START_MISSING, /* 1107 */
+    XML_RNGP_TEXT_EXPECTED, /* 1108 */
+    XML_RNGP_TEXT_HAS_CHILD, /* 1109 */
+    XML_RNGP_TYPE_MISSING, /* 1110 */
+    XML_RNGP_TYPE_NOT_FOUND, /* 1111 */
+    XML_RNGP_TYPE_VALUE, /* 1112 */
+    XML_RNGP_UNKNOWN_ATTRIBUTE, /* 1113 */
+    XML_RNGP_UNKNOWN_COMBINE, /* 1114 */
+    XML_RNGP_UNKNOWN_CONSTRUCT, /* 1115 */
+    XML_RNGP_UNKNOWN_TYPE_LIB, /* 1116 */
+    XML_RNGP_URI_FRAGMENT, /* 1117 */
+    XML_RNGP_URI_NOT_ABSOLUTE, /* 1118 */
+    XML_RNGP_VALUE_EMPTY, /* 1119 */
+    XML_RNGP_VALUE_NO_CONTENT, /* 1120 */
+    XML_RNGP_XMLNS_NAME, /* 1121 */
+    XML_RNGP_XML_NS, /* 1122 */
+    XML_XPATH_EXPRESSION_OK = 1200,
+    XML_XPATH_NUMBER_ERROR, /* 1201 */
+    XML_XPATH_UNFINISHED_LITERAL_ERROR, /* 1202 */
+    XML_XPATH_START_LITERAL_ERROR, /* 1203 */
+    XML_XPATH_VARIABLE_REF_ERROR, /* 1204 */
+    XML_XPATH_UNDEF_VARIABLE_ERROR, /* 1205 */
+    XML_XPATH_INVALID_PREDICATE_ERROR, /* 1206 */
+    XML_XPATH_EXPR_ERROR, /* 1207 */
+    XML_XPATH_UNCLOSED_ERROR, /* 1208 */
+    XML_XPATH_UNKNOWN_FUNC_ERROR, /* 1209 */
+    XML_XPATH_INVALID_OPERAND, /* 1210 */
+    XML_XPATH_INVALID_TYPE, /* 1211 */
+    XML_XPATH_INVALID_ARITY, /* 1212 */
+    XML_XPATH_INVALID_CTXT_SIZE, /* 1213 */
+    XML_XPATH_INVALID_CTXT_POSITION, /* 1214 */
+    XML_XPATH_MEMORY_ERROR, /* 1215 */
+    XML_XPTR_SYNTAX_ERROR, /* 1216 */
+    XML_XPTR_RESOURCE_ERROR, /* 1217 */
+    XML_XPTR_SUB_RESOURCE_ERROR, /* 1218 */
+    XML_XPATH_UNDEF_PREFIX_ERROR, /* 1219 */
+    XML_XPATH_ENCODING_ERROR, /* 1220 */
+    XML_XPATH_INVALID_CHAR_ERROR, /* 1221 */
+    XML_TREE_INVALID_HEX = 1300,
+    XML_TREE_INVALID_DEC, /* 1301 */
+    XML_TREE_UNTERMINATED_ENTITY, /* 1302 */
+    XML_TREE_NOT_UTF8, /* 1303 */
+    XML_SAVE_NOT_UTF8 = 1400,
+    XML_SAVE_CHAR_INVALID, /* 1401 */
+    XML_SAVE_NO_DOCTYPE, /* 1402 */
+    XML_SAVE_UNKNOWN_ENCODING, /* 1403 */
+    XML_REGEXP_COMPILE_ERROR = 1450,
+    XML_IO_UNKNOWN = 1500,
+    XML_IO_EACCES, /* 1501 */
+    XML_IO_EAGAIN, /* 1502 */
+    XML_IO_EBADF, /* 1503 */
+    XML_IO_EBADMSG, /* 1504 */
+    XML_IO_EBUSY, /* 1505 */
+    XML_IO_ECANCELED, /* 1506 */
+    XML_IO_ECHILD, /* 1507 */
+    XML_IO_EDEADLK, /* 1508 */
+    XML_IO_EDOM, /* 1509 */
+    XML_IO_EEXIST, /* 1510 */
+    XML_IO_EFAULT, /* 1511 */
+    XML_IO_EFBIG, /* 1512 */
+    XML_IO_EINPROGRESS, /* 1513 */
+    XML_IO_EINTR, /* 1514 */
+    XML_IO_EINVAL, /* 1515 */
+    XML_IO_EIO, /* 1516 */
+    XML_IO_EISDIR, /* 1517 */
+    XML_IO_EMFILE, /* 1518 */
+    XML_IO_EMLINK, /* 1519 */
+    XML_IO_EMSGSIZE, /* 1520 */
+    XML_IO_ENAMETOOLONG, /* 1521 */
+    XML_IO_ENFILE, /* 1522 */
+    XML_IO_ENODEV, /* 1523 */
+    XML_IO_ENOENT, /* 1524 */
+    XML_IO_ENOEXEC, /* 1525 */
+    XML_IO_ENOLCK, /* 1526 */
+    XML_IO_ENOMEM, /* 1527 */
+    XML_IO_ENOSPC, /* 1528 */
+    XML_IO_ENOSYS, /* 1529 */
+    XML_IO_ENOTDIR, /* 1530 */
+    XML_IO_ENOTEMPTY, /* 1531 */
+    XML_IO_ENOTSUP, /* 1532 */
+    XML_IO_ENOTTY, /* 1533 */
+    XML_IO_ENXIO, /* 1534 */
+    XML_IO_EPERM, /* 1535 */
+    XML_IO_EPIPE, /* 1536 */
+    XML_IO_ERANGE, /* 1537 */
+    XML_IO_EROFS, /* 1538 */
+    XML_IO_ESPIPE, /* 1539 */
+    XML_IO_ESRCH, /* 1540 */
+    XML_IO_ETIMEDOUT, /* 1541 */
+    XML_IO_EXDEV, /* 1542 */
+    XML_IO_NETWORK_ATTEMPT, /* 1543 */
+    XML_IO_ENCODER, /* 1544 */
+    XML_IO_FLUSH, /* 1545 */
+    XML_IO_WRITE, /* 1546 */
+    XML_IO_NO_INPUT, /* 1547 */
+    XML_IO_BUFFER_FULL, /* 1548 */
+    XML_IO_LOAD_ERROR, /* 1549 */
+    XML_IO_ENOTSOCK, /* 1550 */
+    XML_IO_EISCONN, /* 1551 */
+    XML_IO_ECONNREFUSED, /* 1552 */
+    XML_IO_ENETUNREACH, /* 1553 */
+    XML_IO_EADDRINUSE, /* 1554 */
+    XML_IO_EALREADY, /* 1555 */
+    XML_IO_EAFNOSUPPORT, /* 1556 */
+    XML_XINCLUDE_RECURSION=1600,
+    XML_XINCLUDE_PARSE_VALUE, /* 1601 */
+    XML_XINCLUDE_ENTITY_DEF_MISMATCH, /* 1602 */
+    XML_XINCLUDE_NO_HREF, /* 1603 */
+    XML_XINCLUDE_NO_FALLBACK, /* 1604 */
+    XML_XINCLUDE_HREF_URI, /* 1605 */
+    XML_XINCLUDE_TEXT_FRAGMENT, /* 1606 */
+    XML_XINCLUDE_TEXT_DOCUMENT, /* 1607 */
+    XML_XINCLUDE_INVALID_CHAR, /* 1608 */
+    XML_XINCLUDE_BUILD_FAILED, /* 1609 */
+    XML_XINCLUDE_UNKNOWN_ENCODING, /* 1610 */
+    XML_XINCLUDE_MULTIPLE_ROOT, /* 1611 */
+    XML_XINCLUDE_XPTR_FAILED, /* 1612 */
+    XML_XINCLUDE_XPTR_RESULT, /* 1613 */
+    XML_XINCLUDE_INCLUDE_IN_INCLUDE, /* 1614 */
+    XML_XINCLUDE_FALLBACKS_IN_INCLUDE, /* 1615 */
+    XML_XINCLUDE_FALLBACK_NOT_IN_INCLUDE, /* 1616 */
+    XML_XINCLUDE_DEPRECATED_NS, /* 1617 */
+    XML_XINCLUDE_FRAGMENT_ID, /* 1618 */
+    XML_CATALOG_MISSING_ATTR = 1650,
+    XML_CATALOG_ENTRY_BROKEN, /* 1651 */
+    XML_CATALOG_PREFER_VALUE, /* 1652 */
+    XML_CATALOG_NOT_CATALOG, /* 1653 */
+    XML_CATALOG_RECURSION, /* 1654 */
+    XML_SCHEMAP_PREFIX_UNDEFINED = 1700,
+    XML_SCHEMAP_ATTRFORMDEFAULT_VALUE, /* 1701 */
+    XML_SCHEMAP_ATTRGRP_NONAME_NOREF, /* 1702 */
+    XML_SCHEMAP_ATTR_NONAME_NOREF, /* 1703 */
+    XML_SCHEMAP_COMPLEXTYPE_NONAME_NOREF, /* 1704 */
+    XML_SCHEMAP_ELEMFORMDEFAULT_VALUE, /* 1705 */
+    XML_SCHEMAP_ELEM_NONAME_NOREF, /* 1706 */
+    XML_SCHEMAP_EXTENSION_NO_BASE, /* 1707 */
+    XML_SCHEMAP_FACET_NO_VALUE, /* 1708 */
+    XML_SCHEMAP_FAILED_BUILD_IMPORT, /* 1709 */
+    XML_SCHEMAP_GROUP_NONAME_NOREF, /* 1710 */
+    XML_SCHEMAP_IMPORT_NAMESPACE_NOT_URI, /* 1711 */
+    XML_SCHEMAP_IMPORT_REDEFINE_NSNAME, /* 1712 */
+    XML_SCHEMAP_IMPORT_SCHEMA_NOT_URI, /* 1713 */
+    XML_SCHEMAP_INVALID_BOOLEAN, /* 1714 */
+    XML_SCHEMAP_INVALID_ENUM, /* 1715 */
+    XML_SCHEMAP_INVALID_FACET, /* 1716 */
+    XML_SCHEMAP_INVALID_FACET_VALUE, /* 1717 */
+    XML_SCHEMAP_INVALID_MAXOCCURS, /* 1718 */
+    XML_SCHEMAP_INVALID_MINOCCURS, /* 1719 */
+    XML_SCHEMAP_INVALID_REF_AND_SUBTYPE, /* 1720 */
+    XML_SCHEMAP_INVALID_WHITE_SPACE, /* 1721 */
+    XML_SCHEMAP_NOATTR_NOREF, /* 1722 */
+    XML_SCHEMAP_NOTATION_NO_NAME, /* 1723 */
+    XML_SCHEMAP_NOTYPE_NOREF, /* 1724 */
+    XML_SCHEMAP_REF_AND_SUBTYPE, /* 1725 */
+    XML_SCHEMAP_RESTRICTION_NONAME_NOREF, /* 1726 */
+    XML_SCHEMAP_SIMPLETYPE_NONAME, /* 1727 */
+    XML_SCHEMAP_TYPE_AND_SUBTYPE, /* 1728 */
+    XML_SCHEMAP_UNKNOWN_ALL_CHILD, /* 1729 */
+    XML_SCHEMAP_UNKNOWN_ANYATTRIBUTE_CHILD, /* 1730 */
+    XML_SCHEMAP_UNKNOWN_ATTR_CHILD, /* 1731 */
+    XML_SCHEMAP_UNKNOWN_ATTRGRP_CHILD, /* 1732 */
+    XML_SCHEMAP_UNKNOWN_ATTRIBUTE_GROUP, /* 1733 */
+    XML_SCHEMAP_UNKNOWN_BASE_TYPE, /* 1734 */
+    XML_SCHEMAP_UNKNOWN_CHOICE_CHILD, /* 1735 */
+    XML_SCHEMAP_UNKNOWN_COMPLEXCONTENT_CHILD, /* 1736 */
+    XML_SCHEMAP_UNKNOWN_COMPLEXTYPE_CHILD, /* 1737 */
+    XML_SCHEMAP_UNKNOWN_ELEM_CHILD, /* 1738 */
+    XML_SCHEMAP_UNKNOWN_EXTENSION_CHILD, /* 1739 */
+    XML_SCHEMAP_UNKNOWN_FACET_CHILD, /* 1740 */
+    XML_SCHEMAP_UNKNOWN_FACET_TYPE, /* 1741 */
+    XML_SCHEMAP_UNKNOWN_GROUP_CHILD, /* 1742 */
+    XML_SCHEMAP_UNKNOWN_IMPORT_CHILD, /* 1743 */
+    XML_SCHEMAP_UNKNOWN_LIST_CHILD, /* 1744 */
+    XML_SCHEMAP_UNKNOWN_NOTATION_CHILD, /* 1745 */
+    XML_SCHEMAP_UNKNOWN_PROCESSCONTENT_CHILD, /* 1746 */
+    XML_SCHEMAP_UNKNOWN_REF, /* 1747 */
+    XML_SCHEMAP_UNKNOWN_RESTRICTION_CHILD, /* 1748 */
+    XML_SCHEMAP_UNKNOWN_SCHEMAS_CHILD, /* 1749 */
+    XML_SCHEMAP_UNKNOWN_SEQUENCE_CHILD, /* 1750 */
+    XML_SCHEMAP_UNKNOWN_SIMPLECONTENT_CHILD, /* 1751 */
+    XML_SCHEMAP_UNKNOWN_SIMPLETYPE_CHILD, /* 1752 */
+    XML_SCHEMAP_UNKNOWN_TYPE, /* 1753 */
+    XML_SCHEMAP_UNKNOWN_UNION_CHILD, /* 1754 */
+    XML_SCHEMAP_ELEM_DEFAULT_FIXED, /* 1755 */
+    XML_SCHEMAP_REGEXP_INVALID, /* 1756 */
+    XML_SCHEMAP_FAILED_LOAD, /* 1757 */
+    XML_SCHEMAP_NOTHING_TO_PARSE, /* 1758 */
+    XML_SCHEMAP_NOROOT, /* 1759 */
+    XML_SCHEMAP_REDEFINED_GROUP, /* 1760 */
+    XML_SCHEMAP_REDEFINED_TYPE, /* 1761 */
+    XML_SCHEMAP_REDEFINED_ELEMENT, /* 1762 */
+    XML_SCHEMAP_REDEFINED_ATTRGROUP, /* 1763 */
+    XML_SCHEMAP_REDEFINED_ATTR, /* 1764 */
+    XML_SCHEMAP_REDEFINED_NOTATION, /* 1765 */
+    XML_SCHEMAP_FAILED_PARSE, /* 1766 */
+    XML_SCHEMAP_UNKNOWN_PREFIX, /* 1767 */
+    XML_SCHEMAP_DEF_AND_PREFIX, /* 1768 */
+    XML_SCHEMAP_UNKNOWN_INCLUDE_CHILD, /* 1769 */
+    XML_SCHEMAP_INCLUDE_SCHEMA_NOT_URI, /* 1770 */
+    XML_SCHEMAP_INCLUDE_SCHEMA_NO_URI, /* 1771 */
+    XML_SCHEMAP_NOT_SCHEMA, /* 1772 */
+    XML_SCHEMAP_UNKNOWN_MEMBER_TYPE, /* 1773 */
+    XML_SCHEMAP_INVALID_ATTR_USE, /* 1774 */
+    XML_SCHEMAP_RECURSIVE, /* 1775 */
+    XML_SCHEMAP_SUPERNUMEROUS_LIST_ITEM_TYPE, /* 1776 */
+    XML_SCHEMAP_INVALID_ATTR_COMBINATION, /* 1777 */
+    XML_SCHEMAP_INVALID_ATTR_INLINE_COMBINATION, /* 1778 */
+    XML_SCHEMAP_MISSING_SIMPLETYPE_CHILD, /* 1779 */
+    XML_SCHEMAP_INVALID_ATTR_NAME, /* 1780 */
+    XML_SCHEMAP_REF_AND_CONTENT, /* 1781 */
+    XML_SCHEMAP_CT_PROPS_CORRECT_1, /* 1782 */
+    XML_SCHEMAP_CT_PROPS_CORRECT_2, /* 1783 */
+    XML_SCHEMAP_CT_PROPS_CORRECT_3, /* 1784 */
+    XML_SCHEMAP_CT_PROPS_CORRECT_4, /* 1785 */
+    XML_SCHEMAP_CT_PROPS_CORRECT_5, /* 1786 */
+    XML_SCHEMAP_DERIVATION_OK_RESTRICTION_1, /* 1787 */
+    XML_SCHEMAP_DERIVATION_OK_RESTRICTION_2_1_1, /* 1788 */
+    XML_SCHEMAP_DERIVATION_OK_RESTRICTION_2_1_2, /* 1789 */
+    XML_SCHEMAP_DERIVATION_OK_RESTRICTION_2_2, /* 1790 */
+    XML_SCHEMAP_DERIVATION_OK_RESTRICTION_3, /* 1791 */
+    XML_SCHEMAP_WILDCARD_INVALID_NS_MEMBER, /* 1792 */
+    XML_SCHEMAP_INTERSECTION_NOT_EXPRESSIBLE, /* 1793 */
+    XML_SCHEMAP_UNION_NOT_EXPRESSIBLE, /* 1794 */
+    XML_SCHEMAP_SRC_IMPORT_3_1, /* 1795 */
+    XML_SCHEMAP_SRC_IMPORT_3_2, /* 1796 */
+    XML_SCHEMAP_DERIVATION_OK_RESTRICTION_4_1, /* 1797 */
+    XML_SCHEMAP_DERIVATION_OK_RESTRICTION_4_2, /* 1798 */
+    XML_SCHEMAP_DERIVATION_OK_RESTRICTION_4_3, /* 1799 */
+    XML_SCHEMAP_COS_CT_EXTENDS_1_3, /* 1800 */
+    XML_SCHEMAV_NOROOT = 1801,
+    XML_SCHEMAV_UNDECLAREDELEM, /* 1802 */
+    XML_SCHEMAV_NOTTOPLEVEL, /* 1803 */
+    XML_SCHEMAV_MISSING, /* 1804 */
+    XML_SCHEMAV_WRONGELEM, /* 1805 */
+    XML_SCHEMAV_NOTYPE, /* 1806 */
+    XML_SCHEMAV_NOROLLBACK, /* 1807 */
+    XML_SCHEMAV_ISABSTRACT, /* 1808 */
+    XML_SCHEMAV_NOTEMPTY, /* 1809 */
+    XML_SCHEMAV_ELEMCONT, /* 1810 */
+    XML_SCHEMAV_HAVEDEFAULT, /* 1811 */
+    XML_SCHEMAV_NOTNILLABLE, /* 1812 */
+    XML_SCHEMAV_EXTRACONTENT, /* 1813 */
+    XML_SCHEMAV_INVALIDATTR, /* 1814 */
+    XML_SCHEMAV_INVALIDELEM, /* 1815 */
+    XML_SCHEMAV_NOTDETERMINIST, /* 1816 */
+    XML_SCHEMAV_CONSTRUCT, /* 1817 */
+    XML_SCHEMAV_INTERNAL, /* 1818 */
+    XML_SCHEMAV_NOTSIMPLE, /* 1819 */
+    XML_SCHEMAV_ATTRUNKNOWN, /* 1820 */
+    XML_SCHEMAV_ATTRINVALID, /* 1821 */
+    XML_SCHEMAV_VALUE, /* 1822 */
+    XML_SCHEMAV_FACET, /* 1823 */
+    XML_SCHEMAV_CVC_DATATYPE_VALID_1_2_1, /* 1824 */
+    XML_SCHEMAV_CVC_DATATYPE_VALID_1_2_2, /* 1825 */
+    XML_SCHEMAV_CVC_DATATYPE_VALID_1_2_3, /* 1826 */
+    XML_SCHEMAV_CVC_TYPE_3_1_1, /* 1827 */
+    XML_SCHEMAV_CVC_TYPE_3_1_2, /* 1828 */
+    XML_SCHEMAV_CVC_FACET_VALID, /* 1829 */
+    XML_SCHEMAV_CVC_LENGTH_VALID, /* 1830 */
+    XML_SCHEMAV_CVC_MINLENGTH_VALID, /* 1831 */
+    XML_SCHEMAV_CVC_MAXLENGTH_VALID, /* 1832 */
+    XML_SCHEMAV_CVC_MININCLUSIVE_VALID, /* 1833 */
+    XML_SCHEMAV_CVC_MAXINCLUSIVE_VALID, /* 1834 */
+    XML_SCHEMAV_CVC_MINEXCLUSIVE_VALID, /* 1835 */
+    XML_SCHEMAV_CVC_MAXEXCLUSIVE_VALID, /* 1836 */
+    XML_SCHEMAV_CVC_TOTALDIGITS_VALID, /* 1837 */
+    XML_SCHEMAV_CVC_FRACTIONDIGITS_VALID, /* 1838 */
+    XML_SCHEMAV_CVC_PATTERN_VALID, /* 1839 */
+    XML_SCHEMAV_CVC_ENUMERATION_VALID, /* 1840 */
+    XML_SCHEMAV_CVC_COMPLEX_TYPE_2_1, /* 1841 */
+    XML_SCHEMAV_CVC_COMPLEX_TYPE_2_2, /* 1842 */
+    XML_SCHEMAV_CVC_COMPLEX_TYPE_2_3, /* 1843 */
+    XML_SCHEMAV_CVC_COMPLEX_TYPE_2_4, /* 1844 */
+    XML_SCHEMAV_CVC_ELT_1, /* 1845 */
+    XML_SCHEMAV_CVC_ELT_2, /* 1846 */
+    XML_SCHEMAV_CVC_ELT_3_1, /* 1847 */
+    XML_SCHEMAV_CVC_ELT_3_2_1, /* 1848 */
+    XML_SCHEMAV_CVC_ELT_3_2_2, /* 1849 */
+    XML_SCHEMAV_CVC_ELT_4_1, /* 1850 */
+    XML_SCHEMAV_CVC_ELT_4_2, /* 1851 */
+    XML_SCHEMAV_CVC_ELT_4_3, /* 1852 */
+    XML_SCHEMAV_CVC_ELT_5_1_1, /* 1853 */
+    XML_SCHEMAV_CVC_ELT_5_1_2, /* 1854 */
+    XML_SCHEMAV_CVC_ELT_5_2_1, /* 1855 */
+    XML_SCHEMAV_CVC_ELT_5_2_2_1, /* 1856 */
+    XML_SCHEMAV_CVC_ELT_5_2_2_2_1, /* 1857 */
+    XML_SCHEMAV_CVC_ELT_5_2_2_2_2, /* 1858 */
+    XML_SCHEMAV_CVC_ELT_6, /* 1859 */
+    XML_SCHEMAV_CVC_ELT_7, /* 1860 */
+    XML_SCHEMAV_CVC_ATTRIBUTE_1, /* 1861 */
+    XML_SCHEMAV_CVC_ATTRIBUTE_2, /* 1862 */
+    XML_SCHEMAV_CVC_ATTRIBUTE_3, /* 1863 */
+    XML_SCHEMAV_CVC_ATTRIBUTE_4, /* 1864 */
+    XML_SCHEMAV_CVC_COMPLEX_TYPE_3_1, /* 1865 */
+    XML_SCHEMAV_CVC_COMPLEX_TYPE_3_2_1, /* 1866 */
+    XML_SCHEMAV_CVC_COMPLEX_TYPE_3_2_2, /* 1867 */
+    XML_SCHEMAV_CVC_COMPLEX_TYPE_4, /* 1868 */
+    XML_SCHEMAV_CVC_COMPLEX_TYPE_5_1, /* 1869 */
+    XML_SCHEMAV_CVC_COMPLEX_TYPE_5_2, /* 1870 */
+    XML_SCHEMAV_ELEMENT_CONTENT, /* 1871 */
+    XML_SCHEMAV_DOCUMENT_ELEMENT_MISSING, /* 1872 */
+    XML_SCHEMAV_CVC_COMPLEX_TYPE_1, /* 1873 */
+    XML_SCHEMAV_CVC_AU, /* 1874 */
+    XML_SCHEMAV_CVC_TYPE_1, /* 1875 */
+    XML_SCHEMAV_CVC_TYPE_2, /* 1876 */
+    XML_SCHEMAV_CVC_IDC, /* 1877 */
+    XML_SCHEMAV_CVC_WILDCARD, /* 1878 */
+    XML_SCHEMAV_MISC, /* 1879 */
+    XML_XPTR_UNKNOWN_SCHEME = 1900,
+    XML_XPTR_CHILDSEQ_START, /* 1901 */
+    XML_XPTR_EVAL_FAILED, /* 1902 */
+    XML_XPTR_EXTRA_OBJECTS, /* 1903 */
+    XML_C14N_CREATE_CTXT = 1950,
+    XML_C14N_REQUIRES_UTF8, /* 1951 */
+    XML_C14N_CREATE_STACK, /* 1952 */
+    XML_C14N_INVALID_NODE, /* 1953 */
+    XML_C14N_UNKNOW_NODE, /* 1954 */
+    XML_C14N_RELATIVE_NAMESPACE, /* 1955 */
+    XML_FTP_PASV_ANSWER = 2000,
+    XML_FTP_EPSV_ANSWER, /* 2001 */
+    XML_FTP_ACCNT, /* 2002 */
+    XML_FTP_URL_SYNTAX, /* 2003 */
+    XML_HTTP_URL_SYNTAX = 2020,
+    XML_HTTP_USE_IP, /* 2021 */
+    XML_HTTP_UNKNOWN_HOST, /* 2022 */
+    XML_SCHEMAP_SRC_SIMPLE_TYPE_1 = 3000,
+    XML_SCHEMAP_SRC_SIMPLE_TYPE_2, /* 3001 */
+    XML_SCHEMAP_SRC_SIMPLE_TYPE_3, /* 3002 */
+    XML_SCHEMAP_SRC_SIMPLE_TYPE_4, /* 3003 */
+    XML_SCHEMAP_SRC_RESOLVE, /* 3004 */
+    XML_SCHEMAP_SRC_RESTRICTION_BASE_OR_SIMPLETYPE, /* 3005 */
+    XML_SCHEMAP_SRC_LIST_ITEMTYPE_OR_SIMPLETYPE, /* 3006 */
+    XML_SCHEMAP_SRC_UNION_MEMBERTYPES_OR_SIMPLETYPES, /* 3007 */
+    XML_SCHEMAP_ST_PROPS_CORRECT_1, /* 3008 */
+    XML_SCHEMAP_ST_PROPS_CORRECT_2, /* 3009 */
+    XML_SCHEMAP_ST_PROPS_CORRECT_3, /* 3010 */
+    XML_SCHEMAP_COS_ST_RESTRICTS_1_1, /* 3011 */
+    XML_SCHEMAP_COS_ST_RESTRICTS_1_2, /* 3012 */
+    XML_SCHEMAP_COS_ST_RESTRICTS_1_3_1, /* 3013 */
+    XML_SCHEMAP_COS_ST_RESTRICTS_1_3_2, /* 3014 */
+    XML_SCHEMAP_COS_ST_RESTRICTS_2_1, /* 3015 */
+    XML_SCHEMAP_COS_ST_RESTRICTS_2_3_1_1, /* 3016 */
+    XML_SCHEMAP_COS_ST_RESTRICTS_2_3_1_2, /* 3017 */
+    XML_SCHEMAP_COS_ST_RESTRICTS_2_3_2_1, /* 3018 */
+    XML_SCHEMAP_COS_ST_RESTRICTS_2_3_2_2, /* 3019 */
+    XML_SCHEMAP_COS_ST_RESTRICTS_2_3_2_3, /* 3020 */
+    XML_SCHEMAP_COS_ST_RESTRICTS_2_3_2_4, /* 3021 */
+    XML_SCHEMAP_COS_ST_RESTRICTS_2_3_2_5, /* 3022 */
+    XML_SCHEMAP_COS_ST_RESTRICTS_3_1, /* 3023 */
+    XML_SCHEMAP_COS_ST_RESTRICTS_3_3_1, /* 3024 */
+    XML_SCHEMAP_COS_ST_RESTRICTS_3_3_1_2, /* 3025 */
+    XML_SCHEMAP_COS_ST_RESTRICTS_3_3_2_2, /* 3026 */
+    XML_SCHEMAP_COS_ST_RESTRICTS_3_3_2_1, /* 3027 */
+    XML_SCHEMAP_COS_ST_RESTRICTS_3_3_2_3, /* 3028 */
+    XML_SCHEMAP_COS_ST_RESTRICTS_3_3_2_4, /* 3029 */
+    XML_SCHEMAP_COS_ST_RESTRICTS_3_3_2_5, /* 3030 */
+    XML_SCHEMAP_COS_ST_DERIVED_OK_2_1, /* 3031 */
+    XML_SCHEMAP_COS_ST_DERIVED_OK_2_2, /* 3032 */
+    XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED, /* 3033 */
+    XML_SCHEMAP_S4S_ELEM_MISSING, /* 3034 */
+    XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED, /* 3035 */
+    XML_SCHEMAP_S4S_ATTR_MISSING, /* 3036 */
+    XML_SCHEMAP_S4S_ATTR_INVALID_VALUE, /* 3037 */
+    XML_SCHEMAP_SRC_ELEMENT_1, /* 3038 */
+    XML_SCHEMAP_SRC_ELEMENT_2_1, /* 3039 */
+    XML_SCHEMAP_SRC_ELEMENT_2_2, /* 3040 */
+    XML_SCHEMAP_SRC_ELEMENT_3, /* 3041 */
+    XML_SCHEMAP_P_PROPS_CORRECT_1, /* 3042 */
+    XML_SCHEMAP_P_PROPS_CORRECT_2_1, /* 3043 */
+    XML_SCHEMAP_P_PROPS_CORRECT_2_2, /* 3044 */
+    XML_SCHEMAP_E_PROPS_CORRECT_2, /* 3045 */
+    XML_SCHEMAP_E_PROPS_CORRECT_3, /* 3046 */
+    XML_SCHEMAP_E_PROPS_CORRECT_4, /* 3047 */
+    XML_SCHEMAP_E_PROPS_CORRECT_5, /* 3048 */
+    XML_SCHEMAP_E_PROPS_CORRECT_6, /* 3049 */
+    XML_SCHEMAP_SRC_INCLUDE, /* 3050 */
+    XML_SCHEMAP_SRC_ATTRIBUTE_1, /* 3051 */
+    XML_SCHEMAP_SRC_ATTRIBUTE_2, /* 3052 */
+    XML_SCHEMAP_SRC_ATTRIBUTE_3_1, /* 3053 */
+    XML_SCHEMAP_SRC_ATTRIBUTE_3_2, /* 3054 */
+    XML_SCHEMAP_SRC_ATTRIBUTE_4, /* 3055 */
+    XML_SCHEMAP_NO_XMLNS, /* 3056 */
+    XML_SCHEMAP_NO_XSI, /* 3057 */
+    XML_SCHEMAP_COS_VALID_DEFAULT_1, /* 3058 */
+    XML_SCHEMAP_COS_VALID_DEFAULT_2_1, /* 3059 */
+    XML_SCHEMAP_COS_VALID_DEFAULT_2_2_1, /* 3060 */
+    XML_SCHEMAP_COS_VALID_DEFAULT_2_2_2, /* 3061 */
+    XML_SCHEMAP_CVC_SIMPLE_TYPE, /* 3062 */
+    XML_SCHEMAP_COS_CT_EXTENDS_1_1, /* 3063 */
+    XML_SCHEMAP_SRC_IMPORT_1_1, /* 3064 */
+    XML_SCHEMAP_SRC_IMPORT_1_2, /* 3065 */
+    XML_SCHEMAP_SRC_IMPORT_2, /* 3066 */
+    XML_SCHEMAP_SRC_IMPORT_2_1, /* 3067 */
+    XML_SCHEMAP_SRC_IMPORT_2_2, /* 3068 */
+    XML_SCHEMAP_INTERNAL, /* 3069 non-W3C */
+    XML_SCHEMAP_NOT_DETERMINISTIC, /* 3070 non-W3C */
+    XML_SCHEMAP_SRC_ATTRIBUTE_GROUP_1, /* 3071 */
+    XML_SCHEMAP_SRC_ATTRIBUTE_GROUP_2, /* 3072 */
+    XML_SCHEMAP_SRC_ATTRIBUTE_GROUP_3, /* 3073 */
+    XML_SCHEMAP_MG_PROPS_CORRECT_1, /* 3074 */
+    XML_SCHEMAP_MG_PROPS_CORRECT_2, /* 3075 */
+    XML_SCHEMAP_SRC_CT_1, /* 3076 */
+    XML_SCHEMAP_DERIVATION_OK_RESTRICTION_2_1_3, /* 3077 */
+    XML_SCHEMAP_AU_PROPS_CORRECT_2, /* 3078 */
+    XML_SCHEMAP_A_PROPS_CORRECT_2, /* 3079 */
+    XML_SCHEMAP_C_PROPS_CORRECT, /* 3080 */
+    XML_SCHEMAP_SRC_REDEFINE, /* 3081 */
+    XML_SCHEMAP_SRC_IMPORT, /* 3082 */
+    XML_SCHEMAP_WARN_SKIP_SCHEMA, /* 3083 */
+    XML_SCHEMAP_WARN_UNLOCATED_SCHEMA, /* 3084 */
+    XML_SCHEMAP_WARN_ATTR_REDECL_PROH, /* 3085 */
+    XML_SCHEMAP_WARN_ATTR_POINTLESS_PROH, /* 3085 */
+    XML_SCHEMAP_AG_PROPS_CORRECT, /* 3086 */
+    XML_SCHEMAP_COS_CT_EXTENDS_1_2, /* 3087 */
+    XML_SCHEMAP_AU_PROPS_CORRECT, /* 3088 */
+    XML_SCHEMAP_A_PROPS_CORRECT_3, /* 3089 */
+    XML_SCHEMAP_COS_ALL_LIMITED, /* 3090 */
+    XML_SCHEMATRONV_ASSERT = 4000, /* 4000 */
+    XML_SCHEMATRONV_REPORT,
+    XML_MODULE_OPEN = 4900, /* 4900 */
+    XML_MODULE_CLOSE, /* 4901 */
+    XML_CHECK_FOUND_ELEMENT = 5000,
+    XML_CHECK_FOUND_ATTRIBUTE, /* 5001 */
+    XML_CHECK_FOUND_TEXT, /* 5002 */
+    XML_CHECK_FOUND_CDATA, /* 5003 */
+    XML_CHECK_FOUND_ENTITYREF, /* 5004 */
+    XML_CHECK_FOUND_ENTITY, /* 5005 */
+    XML_CHECK_FOUND_PI, /* 5006 */
+    XML_CHECK_FOUND_COMMENT, /* 5007 */
+    XML_CHECK_FOUND_DOCTYPE, /* 5008 */
+    XML_CHECK_FOUND_FRAGMENT, /* 5009 */
+    XML_CHECK_FOUND_NOTATION, /* 5010 */
+    XML_CHECK_UNKNOWN_NODE, /* 5011 */
+    XML_CHECK_ENTITY_TYPE, /* 5012 */
+    XML_CHECK_NO_PARENT, /* 5013 */
+    XML_CHECK_NO_DOC, /* 5014 */
+    XML_CHECK_NO_NAME, /* 5015 */
+    XML_CHECK_NO_ELEM, /* 5016 */
+    XML_CHECK_WRONG_DOC, /* 5017 */
+    XML_CHECK_NO_PREV, /* 5018 */
+    XML_CHECK_WRONG_PREV, /* 5019 */
+    XML_CHECK_NO_NEXT, /* 5020 */
+    XML_CHECK_WRONG_NEXT, /* 5021 */
+    XML_CHECK_NOT_DTD, /* 5022 */
+    XML_CHECK_NOT_ATTR, /* 5023 */
+    XML_CHECK_NOT_ATTR_DECL, /* 5024 */
+    XML_CHECK_NOT_ELEM_DECL, /* 5025 */
+    XML_CHECK_NOT_ENTITY_DECL, /* 5026 */
+    XML_CHECK_NOT_NS_DECL, /* 5027 */
+    XML_CHECK_NO_HREF, /* 5028 */
+    XML_CHECK_WRONG_PARENT,/* 5029 */
+    XML_CHECK_NS_SCOPE, /* 5030 */
+    XML_CHECK_NS_ANCESTOR, /* 5031 */
+    XML_CHECK_NOT_UTF8, /* 5032 */
+    XML_CHECK_NO_DICT, /* 5033 */
+    XML_CHECK_NOT_NCNAME, /* 5034 */
+    XML_CHECK_OUTSIDE_DICT, /* 5035 */
+    XML_CHECK_WRONG_NAME, /* 5036 */
+    XML_CHECK_NAME_NOT_NULL, /* 5037 */
+    XML_I18N_NO_NAME = 6000,
+    XML_I18N_NO_HANDLER, /* 6001 */
+    XML_I18N_EXCESS_HANDLER, /* 6002 */
+    XML_I18N_CONV_FAILED, /* 6003 */
+    XML_I18N_NO_OUTPUT, /* 6004 */
+    XML_BUF_OVERFLOW = 7000
+} xmlParserErrors;
+
+/**
+ * xmlGenericErrorFunc:
+ * @ctx:  a parsing context
+ * @msg:  the message
+ * @...:  the extra arguments of the varargs to format the message
+ *
+ * Signature of the function to use when there is an error and
+ * no parsing or validity context available .
+ */
+typedef void (*xmlGenericErrorFunc) (void *ctx,
+				 const char *msg,
+				 ...) LIBXML_ATTR_FORMAT(2,3);
+/**
+ * xmlStructuredErrorFunc:
+ * @userData:  user provided data for the error callback
+ * @error:  the error being raised.
+ *
+ * Signature of the function to use when there is an error and
+ * the module handles the new error reporting mechanism.
+ */
+typedef void (*xmlStructuredErrorFunc) (void *userData, const xmlError *error);
+
+/** DOC_DISABLE */
+#define XML_GLOBALS_ERROR \
+  XML_OP(xmlLastError, xmlError, XML_DEPRECATED) \
+  XML_OP(xmlGenericError, xmlGenericErrorFunc, XML_NO_ATTR) \
+  XML_OP(xmlGenericErrorContext, void *, XML_NO_ATTR) \
+  XML_OP(xmlStructuredError, xmlStructuredErrorFunc, XML_NO_ATTR) \
+  XML_OP(xmlStructuredErrorContext, void *, XML_NO_ATTR)
+
+#define XML_OP XML_DECLARE_GLOBAL
+XML_GLOBALS_ERROR
+#undef XML_OP
+
+#if defined(LIBXML_THREAD_ENABLED) && !defined(XML_GLOBALS_NO_REDEFINITION)
+  #define xmlLastError XML_GLOBAL_MACRO(xmlLastError)
+  #define xmlGenericError XML_GLOBAL_MACRO(xmlGenericError)
+  #define xmlGenericErrorContext XML_GLOBAL_MACRO(xmlGenericErrorContext)
+  #define xmlStructuredError XML_GLOBAL_MACRO(xmlStructuredError)
+  #define xmlStructuredErrorContext XML_GLOBAL_MACRO(xmlStructuredErrorContext)
+#endif
+/** DOC_ENABLE */
+
+/*
+ * Use the following function to reset the two global variables
+ * xmlGenericError and xmlGenericErrorContext.
+ */
+XMLPUBFUN void
+    xmlSetGenericErrorFunc	(void *ctx,
+				 xmlGenericErrorFunc handler);
+XMLPUBFUN void
+    xmlThrDefSetGenericErrorFunc(void *ctx,
+                                 xmlGenericErrorFunc handler);
+XML_DEPRECATED
+XMLPUBFUN void
+    initGenericErrorDefaultFunc	(xmlGenericErrorFunc *handler);
+
+XMLPUBFUN void
+    xmlSetStructuredErrorFunc	(void *ctx,
+				 xmlStructuredErrorFunc handler);
+XMLPUBFUN void
+    xmlThrDefSetStructuredErrorFunc(void *ctx,
+                                 xmlStructuredErrorFunc handler);
+/*
+ * Default message routines used by SAX and Valid context for error
+ * and warning reporting.
+ */
+XMLPUBFUN void
+    xmlParserError		(void *ctx,
+				 const char *msg,
+				 ...) LIBXML_ATTR_FORMAT(2,3);
+XMLPUBFUN void
+    xmlParserWarning		(void *ctx,
+				 const char *msg,
+				 ...) LIBXML_ATTR_FORMAT(2,3);
+XMLPUBFUN void
+    xmlParserValidityError	(void *ctx,
+				 const char *msg,
+				 ...) LIBXML_ATTR_FORMAT(2,3);
+XMLPUBFUN void
+    xmlParserValidityWarning	(void *ctx,
+				 const char *msg,
+				 ...) LIBXML_ATTR_FORMAT(2,3);
+struct _xmlParserInput;
+XMLPUBFUN void
+    xmlParserPrintFileInfo	(struct _xmlParserInput *input);
+XMLPUBFUN void
+    xmlParserPrintFileContext	(struct _xmlParserInput *input);
+
+/*
+ * Extended error information routines
+ */
+XMLPUBFUN const xmlError *
+    xmlGetLastError		(void);
+XMLPUBFUN void
+    xmlResetLastError		(void);
+XMLPUBFUN const xmlError *
+    xmlCtxtGetLastError		(void *ctx);
+XMLPUBFUN void
+    xmlCtxtResetLastError	(void *ctx);
+XMLPUBFUN void
+    xmlResetError		(xmlErrorPtr err);
+XMLPUBFUN int
+    xmlCopyError		(const xmlError *from,
+				 xmlErrorPtr to);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __XML_ERROR_H__ */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlexports.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlexports.h
new file mode 100644
index 00000000..3b063e7d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlexports.h
@@ -0,0 +1,50 @@
+/*
+ * Summary: macros for marking symbols as exportable/importable.
+ * Description: macros for marking symbols as exportable/importable.
+ *
+ * Copy: See Copyright for the status of this software.
+ */
+
+#ifndef __XML_EXPORTS_H__
+#define __XML_EXPORTS_H__
+
+/** DOC_DISABLE */
+#if defined(_WIN32) || defined(__CYGWIN__)
+  #ifdef LIBXML_STATIC
+    #define XMLPUBLIC
+  #elif defined(IN_LIBXML)
+    #define XMLPUBLIC __declspec(dllexport)
+  #else
+    #define XMLPUBLIC __declspec(dllimport)
+  #endif
+#else /* not Windows */
+  #define XMLPUBLIC
+#endif /* platform switch */
+/** DOC_ENABLE */
+
+/*
+ * XMLPUBFUN:
+ *
+ * Macro which declares an exportable function
+ */
+#define XMLPUBFUN XMLPUBLIC
+
+/**
+ * XMLPUBVAR:
+ *
+ * Macro which declares an exportable variable
+ */
+#define XMLPUBVAR XMLPUBLIC extern
+
+/** DOC_DISABLE */
+/* Compatibility */
+#define XMLCALL
+#define XMLCDECL
+#if !defined(LIBXML_DLL_IMPORT)
+#define LIBXML_DLL_IMPORT XMLPUBVAR
+#endif
+/** DOC_ENABLE */
+
+#endif /* __XML_EXPORTS_H__ */
+
+
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlmemory.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlmemory.h
new file mode 100644
index 00000000..097e3c8f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlmemory.h
@@ -0,0 +1,225 @@
+/*
+ * Summary: interface for the memory allocator
+ * Description: provides interfaces for the memory allocator,
+ *              including debugging capabilities.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+
+#ifndef __DEBUG_MEMORY_ALLOC__
+#define __DEBUG_MEMORY_ALLOC__
+
+#include <stdio.h>
+#include <libxml/xmlversion.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * The XML memory wrapper support 4 basic overloadable functions.
+ */
+/**
+ * xmlFreeFunc:
+ * @mem: an already allocated block of memory
+ *
+ * Signature for a free() implementation.
+ */
+typedef void (*xmlFreeFunc)(void *mem);
+/**
+ * xmlMallocFunc:
+ * @size:  the size requested in bytes
+ *
+ * Signature for a malloc() implementation.
+ *
+ * Returns a pointer to the newly allocated block or NULL in case of error.
+ */
+typedef void *(LIBXML_ATTR_ALLOC_SIZE(1) *xmlMallocFunc)(size_t size);
+
+/**
+ * xmlReallocFunc:
+ * @mem: an already allocated block of memory
+ * @size:  the new size requested in bytes
+ *
+ * Signature for a realloc() implementation.
+ *
+ * Returns a pointer to the newly reallocated block or NULL in case of error.
+ */
+typedef void *(*xmlReallocFunc)(void *mem, size_t size);
+
+/**
+ * xmlStrdupFunc:
+ * @str: a zero terminated string
+ *
+ * Signature for an strdup() implementation.
+ *
+ * Returns the copy of the string or NULL in case of error.
+ */
+typedef char *(*xmlStrdupFunc)(const char *str);
+
+/*
+ * In general the memory allocation entry points are not kept
+ * thread specific but this can be overridden by LIBXML_THREAD_ALLOC_ENABLED
+ *    - xmlMalloc
+ *    - xmlMallocAtomic
+ *    - xmlRealloc
+ *    - xmlMemStrdup
+ *    - xmlFree
+ */
+/** DOC_DISABLE */
+#ifdef LIBXML_THREAD_ALLOC_ENABLED
+  #define XML_GLOBALS_ALLOC \
+    XML_OP(xmlMalloc, xmlMallocFunc, XML_NO_ATTR) \
+    XML_OP(xmlMallocAtomic, xmlMallocFunc, XML_NO_ATTR) \
+    XML_OP(xmlRealloc, xmlReallocFunc, XML_NO_ATTR) \
+    XML_OP(xmlFree, xmlFreeFunc, XML_NO_ATTR) \
+    XML_OP(xmlMemStrdup, xmlStrdupFunc, XML_NO_ATTR)
+  #define XML_OP XML_DECLARE_GLOBAL
+    XML_GLOBALS_ALLOC
+  #undef XML_OP
+  #if defined(LIBXML_THREAD_ENABLED) && !defined(XML_GLOBALS_NO_REDEFINITION)
+    #define xmlMalloc XML_GLOBAL_MACRO(xmlMalloc)
+    #define xmlMallocAtomic XML_GLOBAL_MACRO(xmlMallocAtomic)
+    #define xmlRealloc XML_GLOBAL_MACRO(xmlRealloc)
+    #define xmlFree XML_GLOBAL_MACRO(xmlFree)
+    #define xmlMemStrdup XML_GLOBAL_MACRO(xmlMemStrdup)
+  #endif
+#else
+  #define XML_GLOBALS_ALLOC
+/** DOC_ENABLE */
+  XMLPUBVAR xmlMallocFunc xmlMalloc;
+  XMLPUBVAR xmlMallocFunc xmlMallocAtomic;
+  XMLPUBVAR xmlReallocFunc xmlRealloc;
+  XMLPUBVAR xmlFreeFunc xmlFree;
+  XMLPUBVAR xmlStrdupFunc xmlMemStrdup;
+#endif
+
+/*
+ * The way to overload the existing functions.
+ * The xmlGc function have an extra entry for atomic block
+ * allocations useful for garbage collected memory allocators
+ */
+XMLPUBFUN int
+	xmlMemSetup	(xmlFreeFunc freeFunc,
+			 xmlMallocFunc mallocFunc,
+			 xmlReallocFunc reallocFunc,
+			 xmlStrdupFunc strdupFunc);
+XMLPUBFUN int
+	xmlMemGet	(xmlFreeFunc *freeFunc,
+			 xmlMallocFunc *mallocFunc,
+			 xmlReallocFunc *reallocFunc,
+			 xmlStrdupFunc *strdupFunc);
+XMLPUBFUN int
+	xmlGcMemSetup	(xmlFreeFunc freeFunc,
+			 xmlMallocFunc mallocFunc,
+			 xmlMallocFunc mallocAtomicFunc,
+			 xmlReallocFunc reallocFunc,
+			 xmlStrdupFunc strdupFunc);
+XMLPUBFUN int
+	xmlGcMemGet	(xmlFreeFunc *freeFunc,
+			 xmlMallocFunc *mallocFunc,
+			 xmlMallocFunc *mallocAtomicFunc,
+			 xmlReallocFunc *reallocFunc,
+			 xmlStrdupFunc *strdupFunc);
+
+/*
+ * Initialization of the memory layer.
+ */
+XML_DEPRECATED
+XMLPUBFUN int
+	xmlInitMemory	(void);
+
+/*
+ * Cleanup of the memory layer.
+ */
+XML_DEPRECATED
+XMLPUBFUN void
+                xmlCleanupMemory        (void);
+/*
+ * These are specific to the XML debug memory wrapper.
+ */
+XMLPUBFUN size_t
+	xmlMemSize	(void *ptr);
+XMLPUBFUN int
+	xmlMemUsed	(void);
+XMLPUBFUN int
+	xmlMemBlocks	(void);
+XMLPUBFUN void
+	xmlMemDisplay	(FILE *fp);
+XMLPUBFUN void
+	xmlMemDisplayLast(FILE *fp, long nbBytes);
+XMLPUBFUN void
+	xmlMemShow	(FILE *fp, int nr);
+XMLPUBFUN void
+	xmlMemoryDump	(void);
+XMLPUBFUN void *
+	xmlMemMalloc	(size_t size) LIBXML_ATTR_ALLOC_SIZE(1);
+XMLPUBFUN void *
+	xmlMemRealloc	(void *ptr,size_t size);
+XMLPUBFUN void
+	xmlMemFree	(void *ptr);
+XMLPUBFUN char *
+	xmlMemoryStrdup	(const char *str);
+XMLPUBFUN void *
+	xmlMallocLoc	(size_t size, const char *file, int line) LIBXML_ATTR_ALLOC_SIZE(1);
+XMLPUBFUN void *
+	xmlReallocLoc	(void *ptr, size_t size, const char *file, int line);
+XMLPUBFUN void *
+	xmlMallocAtomicLoc (size_t size, const char *file, int line) LIBXML_ATTR_ALLOC_SIZE(1);
+XMLPUBFUN char *
+	xmlMemStrdupLoc	(const char *str, const char *file, int line);
+
+
+/** DOC_DISABLE */
+#ifdef DEBUG_MEMORY_LOCATION
+/**
+ * xmlMalloc:
+ * @size:  number of bytes to allocate
+ *
+ * Wrapper for the malloc() function used in the XML library.
+ *
+ * Returns the pointer to the allocated area or NULL in case of error.
+ */
+#define xmlMalloc(size) xmlMallocLoc((size), __FILE__, __LINE__)
+/**
+ * xmlMallocAtomic:
+ * @size:  number of bytes to allocate
+ *
+ * Wrapper for the malloc() function used in the XML library for allocation
+ * of block not containing pointers to other areas.
+ *
+ * Returns the pointer to the allocated area or NULL in case of error.
+ */
+#define xmlMallocAtomic(size) xmlMallocAtomicLoc((size), __FILE__, __LINE__)
+/**
+ * xmlRealloc:
+ * @ptr:  pointer to the existing allocated area
+ * @size:  number of bytes to allocate
+ *
+ * Wrapper for the realloc() function used in the XML library.
+ *
+ * Returns the pointer to the allocated area or NULL in case of error.
+ */
+#define xmlRealloc(ptr, size) xmlReallocLoc((ptr), (size), __FILE__, __LINE__)
+/**
+ * xmlMemStrdup:
+ * @str:  pointer to the existing string
+ *
+ * Wrapper for the strdup() function, xmlStrdup() is usually preferred.
+ *
+ * Returns the pointer to the allocated area or NULL in case of error.
+ */
+#define xmlMemStrdup(str) xmlMemStrdupLoc((str), __FILE__, __LINE__)
+
+#endif /* DEBUG_MEMORY_LOCATION */
+/** DOC_ENABLE */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif  /* __DEBUG_MEMORY_ALLOC__ */
+
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlmodule.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlmodule.h
new file mode 100644
index 00000000..279986c1
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlmodule.h
@@ -0,0 +1,57 @@
+/*
+ * Summary: dynamic module loading
+ * Description: basic API for dynamic module loading, used by
+ *              libexslt added in 2.6.17
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Joel W. Reed
+ */
+
+#ifndef __XML_MODULE_H__
+#define __XML_MODULE_H__
+
+#include <libxml/xmlversion.h>
+
+#ifdef LIBXML_MODULES_ENABLED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * xmlModulePtr:
+ *
+ * A handle to a dynamically loaded module
+ */
+typedef struct _xmlModule xmlModule;
+typedef xmlModule *xmlModulePtr;
+
+/**
+ * xmlModuleOption:
+ *
+ * enumeration of options that can be passed down to xmlModuleOpen()
+ */
+typedef enum {
+    XML_MODULE_LAZY = 1,	/* lazy binding */
+    XML_MODULE_LOCAL= 2		/* local binding */
+} xmlModuleOption;
+
+XMLPUBFUN xmlModulePtr xmlModuleOpen	(const char *filename,
+						 int options);
+
+XMLPUBFUN int xmlModuleSymbol		(xmlModulePtr module,
+						 const char* name,
+						 void **result);
+
+XMLPUBFUN int xmlModuleClose		(xmlModulePtr module);
+
+XMLPUBFUN int xmlModuleFree		(xmlModulePtr module);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBXML_MODULES_ENABLED */
+
+#endif /*__XML_MODULE_H__ */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlreader.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlreader.h
new file mode 100644
index 00000000..b9f69897
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlreader.h
@@ -0,0 +1,434 @@
+/*
+ * Summary: the XMLReader implementation
+ * Description: API of the XML streaming API based on C# interfaces.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XMLREADER_H__
+#define __XML_XMLREADER_H__
+
+#include <libxml/xmlversion.h>
+#include <libxml/tree.h>
+#include <libxml/xmlerror.h>
+#include <libxml/xmlIO.h>
+#ifdef LIBXML_SCHEMAS_ENABLED
+#include <libxml/relaxng.h>
+#include <libxml/xmlschemas.h>
+#endif
+/* for compatibility */
+#include <libxml/parser.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * xmlParserSeverities:
+ *
+ * How severe an error callback is when the per-reader error callback API
+ * is used.
+ */
+typedef enum {
+    XML_PARSER_SEVERITY_VALIDITY_WARNING = 1,
+    XML_PARSER_SEVERITY_VALIDITY_ERROR = 2,
+    XML_PARSER_SEVERITY_WARNING = 3,
+    XML_PARSER_SEVERITY_ERROR = 4
+} xmlParserSeverities;
+
+#ifdef LIBXML_READER_ENABLED
+
+/**
+ * xmlTextReaderMode:
+ *
+ * Internal state values for the reader.
+ */
+typedef enum {
+    XML_TEXTREADER_MODE_INITIAL = 0,
+    XML_TEXTREADER_MODE_INTERACTIVE = 1,
+    XML_TEXTREADER_MODE_ERROR = 2,
+    XML_TEXTREADER_MODE_EOF =3,
+    XML_TEXTREADER_MODE_CLOSED = 4,
+    XML_TEXTREADER_MODE_READING = 5
+} xmlTextReaderMode;
+
+/**
+ * xmlParserProperties:
+ *
+ * Some common options to use with xmlTextReaderSetParserProp, but it
+ * is better to use xmlParserOption and the xmlReaderNewxxx and
+ * xmlReaderForxxx APIs now.
+ */
+typedef enum {
+    XML_PARSER_LOADDTD = 1,
+    XML_PARSER_DEFAULTATTRS = 2,
+    XML_PARSER_VALIDATE = 3,
+    XML_PARSER_SUBST_ENTITIES = 4
+} xmlParserProperties;
+
+/**
+ * xmlReaderTypes:
+ *
+ * Predefined constants for the different types of nodes.
+ */
+typedef enum {
+    XML_READER_TYPE_NONE = 0,
+    XML_READER_TYPE_ELEMENT = 1,
+    XML_READER_TYPE_ATTRIBUTE = 2,
+    XML_READER_TYPE_TEXT = 3,
+    XML_READER_TYPE_CDATA = 4,
+    XML_READER_TYPE_ENTITY_REFERENCE = 5,
+    XML_READER_TYPE_ENTITY = 6,
+    XML_READER_TYPE_PROCESSING_INSTRUCTION = 7,
+    XML_READER_TYPE_COMMENT = 8,
+    XML_READER_TYPE_DOCUMENT = 9,
+    XML_READER_TYPE_DOCUMENT_TYPE = 10,
+    XML_READER_TYPE_DOCUMENT_FRAGMENT = 11,
+    XML_READER_TYPE_NOTATION = 12,
+    XML_READER_TYPE_WHITESPACE = 13,
+    XML_READER_TYPE_SIGNIFICANT_WHITESPACE = 14,
+    XML_READER_TYPE_END_ELEMENT = 15,
+    XML_READER_TYPE_END_ENTITY = 16,
+    XML_READER_TYPE_XML_DECLARATION = 17
+} xmlReaderTypes;
+
+/**
+ * xmlTextReader:
+ *
+ * Structure for an xmlReader context.
+ */
+typedef struct _xmlTextReader xmlTextReader;
+
+/**
+ * xmlTextReaderPtr:
+ *
+ * Pointer to an xmlReader context.
+ */
+typedef xmlTextReader *xmlTextReaderPtr;
+
+/*
+ * Constructors & Destructor
+ */
+XMLPUBFUN xmlTextReaderPtr
+			xmlNewTextReader	(xmlParserInputBufferPtr input,
+	                                         const char *URI);
+XMLPUBFUN xmlTextReaderPtr
+			xmlNewTextReaderFilename(const char *URI);
+
+XMLPUBFUN void
+			xmlFreeTextReader	(xmlTextReaderPtr reader);
+
+XMLPUBFUN int
+            xmlTextReaderSetup(xmlTextReaderPtr reader,
+                   xmlParserInputBufferPtr input, const char *URL,
+                   const char *encoding, int options);
+XMLPUBFUN void
+            xmlTextReaderSetMaxAmplification(xmlTextReaderPtr reader,
+                   unsigned maxAmpl);
+
+/*
+ * Iterators
+ */
+XMLPUBFUN int
+			xmlTextReaderRead	(xmlTextReaderPtr reader);
+
+#ifdef LIBXML_WRITER_ENABLED
+XMLPUBFUN xmlChar *
+			xmlTextReaderReadInnerXml(xmlTextReaderPtr reader);
+
+XMLPUBFUN xmlChar *
+			xmlTextReaderReadOuterXml(xmlTextReaderPtr reader);
+#endif
+
+XMLPUBFUN xmlChar *
+			xmlTextReaderReadString	(xmlTextReaderPtr reader);
+XMLPUBFUN int
+			xmlTextReaderReadAttributeValue(xmlTextReaderPtr reader);
+
+/*
+ * Attributes of the node
+ */
+XMLPUBFUN int
+			xmlTextReaderAttributeCount(xmlTextReaderPtr reader);
+XMLPUBFUN int
+			xmlTextReaderDepth	(xmlTextReaderPtr reader);
+XMLPUBFUN int
+			xmlTextReaderHasAttributes(xmlTextReaderPtr reader);
+XMLPUBFUN int
+			xmlTextReaderHasValue(xmlTextReaderPtr reader);
+XMLPUBFUN int
+			xmlTextReaderIsDefault	(xmlTextReaderPtr reader);
+XMLPUBFUN int
+			xmlTextReaderIsEmptyElement(xmlTextReaderPtr reader);
+XMLPUBFUN int
+			xmlTextReaderNodeType	(xmlTextReaderPtr reader);
+XMLPUBFUN int
+			xmlTextReaderQuoteChar	(xmlTextReaderPtr reader);
+XMLPUBFUN int
+			xmlTextReaderReadState	(xmlTextReaderPtr reader);
+XMLPUBFUN int
+                        xmlTextReaderIsNamespaceDecl(xmlTextReaderPtr reader);
+
+XMLPUBFUN const xmlChar *
+		    xmlTextReaderConstBaseUri	(xmlTextReaderPtr reader);
+XMLPUBFUN const xmlChar *
+		    xmlTextReaderConstLocalName	(xmlTextReaderPtr reader);
+XMLPUBFUN const xmlChar *
+		    xmlTextReaderConstName	(xmlTextReaderPtr reader);
+XMLPUBFUN const xmlChar *
+		    xmlTextReaderConstNamespaceUri(xmlTextReaderPtr reader);
+XMLPUBFUN const xmlChar *
+		    xmlTextReaderConstPrefix	(xmlTextReaderPtr reader);
+XMLPUBFUN const xmlChar *
+		    xmlTextReaderConstXmlLang	(xmlTextReaderPtr reader);
+XMLPUBFUN const xmlChar *
+		    xmlTextReaderConstString	(xmlTextReaderPtr reader,
+						 const xmlChar *str);
+XMLPUBFUN const xmlChar *
+		    xmlTextReaderConstValue	(xmlTextReaderPtr reader);
+
+/*
+ * use the Const version of the routine for
+ * better performance and simpler code
+ */
+XMLPUBFUN xmlChar *
+			xmlTextReaderBaseUri	(xmlTextReaderPtr reader);
+XMLPUBFUN xmlChar *
+			xmlTextReaderLocalName	(xmlTextReaderPtr reader);
+XMLPUBFUN xmlChar *
+			xmlTextReaderName	(xmlTextReaderPtr reader);
+XMLPUBFUN xmlChar *
+			xmlTextReaderNamespaceUri(xmlTextReaderPtr reader);
+XMLPUBFUN xmlChar *
+			xmlTextReaderPrefix	(xmlTextReaderPtr reader);
+XMLPUBFUN xmlChar *
+			xmlTextReaderXmlLang	(xmlTextReaderPtr reader);
+XMLPUBFUN xmlChar *
+			xmlTextReaderValue	(xmlTextReaderPtr reader);
+
+/*
+ * Methods of the XmlTextReader
+ */
+XMLPUBFUN int
+		    xmlTextReaderClose		(xmlTextReaderPtr reader);
+XMLPUBFUN xmlChar *
+		    xmlTextReaderGetAttributeNo	(xmlTextReaderPtr reader,
+						 int no);
+XMLPUBFUN xmlChar *
+		    xmlTextReaderGetAttribute	(xmlTextReaderPtr reader,
+						 const xmlChar *name);
+XMLPUBFUN xmlChar *
+		    xmlTextReaderGetAttributeNs	(xmlTextReaderPtr reader,
+						 const xmlChar *localName,
+						 const xmlChar *namespaceURI);
+XMLPUBFUN xmlParserInputBufferPtr
+		    xmlTextReaderGetRemainder	(xmlTextReaderPtr reader);
+XMLPUBFUN xmlChar *
+		    xmlTextReaderLookupNamespace(xmlTextReaderPtr reader,
+						 const xmlChar *prefix);
+XMLPUBFUN int
+		    xmlTextReaderMoveToAttributeNo(xmlTextReaderPtr reader,
+						 int no);
+XMLPUBFUN int
+		    xmlTextReaderMoveToAttribute(xmlTextReaderPtr reader,
+						 const xmlChar *name);
+XMLPUBFUN int
+		    xmlTextReaderMoveToAttributeNs(xmlTextReaderPtr reader,
+						 const xmlChar *localName,
+						 const xmlChar *namespaceURI);
+XMLPUBFUN int
+		    xmlTextReaderMoveToFirstAttribute(xmlTextReaderPtr reader);
+XMLPUBFUN int
+		    xmlTextReaderMoveToNextAttribute(xmlTextReaderPtr reader);
+XMLPUBFUN int
+		    xmlTextReaderMoveToElement	(xmlTextReaderPtr reader);
+XMLPUBFUN int
+		    xmlTextReaderNormalization	(xmlTextReaderPtr reader);
+XMLPUBFUN const xmlChar *
+		    xmlTextReaderConstEncoding  (xmlTextReaderPtr reader);
+
+/*
+ * Extensions
+ */
+XMLPUBFUN int
+		    xmlTextReaderSetParserProp	(xmlTextReaderPtr reader,
+						 int prop,
+						 int value);
+XMLPUBFUN int
+		    xmlTextReaderGetParserProp	(xmlTextReaderPtr reader,
+						 int prop);
+XMLPUBFUN xmlNodePtr
+		    xmlTextReaderCurrentNode	(xmlTextReaderPtr reader);
+
+XMLPUBFUN int
+            xmlTextReaderGetParserLineNumber(xmlTextReaderPtr reader);
+
+XMLPUBFUN int
+            xmlTextReaderGetParserColumnNumber(xmlTextReaderPtr reader);
+
+XMLPUBFUN xmlNodePtr
+		    xmlTextReaderPreserve	(xmlTextReaderPtr reader);
+#ifdef LIBXML_PATTERN_ENABLED
+XMLPUBFUN int
+		    xmlTextReaderPreservePattern(xmlTextReaderPtr reader,
+						 const xmlChar *pattern,
+						 const xmlChar **namespaces);
+#endif /* LIBXML_PATTERN_ENABLED */
+XMLPUBFUN xmlDocPtr
+		    xmlTextReaderCurrentDoc	(xmlTextReaderPtr reader);
+XMLPUBFUN xmlNodePtr
+		    xmlTextReaderExpand		(xmlTextReaderPtr reader);
+XMLPUBFUN int
+		    xmlTextReaderNext		(xmlTextReaderPtr reader);
+XMLPUBFUN int
+		    xmlTextReaderNextSibling	(xmlTextReaderPtr reader);
+XMLPUBFUN int
+		    xmlTextReaderIsValid	(xmlTextReaderPtr reader);
+#ifdef LIBXML_SCHEMAS_ENABLED
+XMLPUBFUN int
+		    xmlTextReaderRelaxNGValidate(xmlTextReaderPtr reader,
+						 const char *rng);
+XMLPUBFUN int
+		    xmlTextReaderRelaxNGValidateCtxt(xmlTextReaderPtr reader,
+						 xmlRelaxNGValidCtxtPtr ctxt,
+						 int options);
+
+XMLPUBFUN int
+		    xmlTextReaderRelaxNGSetSchema(xmlTextReaderPtr reader,
+						 xmlRelaxNGPtr schema);
+XMLPUBFUN int
+		    xmlTextReaderSchemaValidate	(xmlTextReaderPtr reader,
+						 const char *xsd);
+XMLPUBFUN int
+		    xmlTextReaderSchemaValidateCtxt(xmlTextReaderPtr reader,
+						 xmlSchemaValidCtxtPtr ctxt,
+						 int options);
+XMLPUBFUN int
+		    xmlTextReaderSetSchema	(xmlTextReaderPtr reader,
+						 xmlSchemaPtr schema);
+#endif
+XMLPUBFUN const xmlChar *
+		    xmlTextReaderConstXmlVersion(xmlTextReaderPtr reader);
+XMLPUBFUN int
+		    xmlTextReaderStandalone     (xmlTextReaderPtr reader);
+
+
+/*
+ * Index lookup
+ */
+XMLPUBFUN long
+		xmlTextReaderByteConsumed	(xmlTextReaderPtr reader);
+
+/*
+ * New more complete APIs for simpler creation and reuse of readers
+ */
+XMLPUBFUN xmlTextReaderPtr
+		xmlReaderWalker		(xmlDocPtr doc);
+XMLPUBFUN xmlTextReaderPtr
+		xmlReaderForDoc		(const xmlChar * cur,
+					 const char *URL,
+					 const char *encoding,
+					 int options);
+XMLPUBFUN xmlTextReaderPtr
+		xmlReaderForFile	(const char *filename,
+					 const char *encoding,
+					 int options);
+XMLPUBFUN xmlTextReaderPtr
+		xmlReaderForMemory	(const char *buffer,
+					 int size,
+					 const char *URL,
+					 const char *encoding,
+					 int options);
+XMLPUBFUN xmlTextReaderPtr
+		xmlReaderForFd		(int fd,
+					 const char *URL,
+					 const char *encoding,
+					 int options);
+XMLPUBFUN xmlTextReaderPtr
+		xmlReaderForIO		(xmlInputReadCallback ioread,
+					 xmlInputCloseCallback ioclose,
+					 void *ioctx,
+					 const char *URL,
+					 const char *encoding,
+					 int options);
+
+XMLPUBFUN int
+		xmlReaderNewWalker	(xmlTextReaderPtr reader,
+					 xmlDocPtr doc);
+XMLPUBFUN int
+		xmlReaderNewDoc		(xmlTextReaderPtr reader,
+					 const xmlChar * cur,
+					 const char *URL,
+					 const char *encoding,
+					 int options);
+XMLPUBFUN int
+		xmlReaderNewFile	(xmlTextReaderPtr reader,
+					 const char *filename,
+					 const char *encoding,
+					 int options);
+XMLPUBFUN int
+		xmlReaderNewMemory	(xmlTextReaderPtr reader,
+					 const char *buffer,
+					 int size,
+					 const char *URL,
+					 const char *encoding,
+					 int options);
+XMLPUBFUN int
+		xmlReaderNewFd		(xmlTextReaderPtr reader,
+					 int fd,
+					 const char *URL,
+					 const char *encoding,
+					 int options);
+XMLPUBFUN int
+		xmlReaderNewIO		(xmlTextReaderPtr reader,
+					 xmlInputReadCallback ioread,
+					 xmlInputCloseCallback ioclose,
+					 void *ioctx,
+					 const char *URL,
+					 const char *encoding,
+					 int options);
+/*
+ * Error handling extensions
+ */
+typedef void *  xmlTextReaderLocatorPtr;
+
+/**
+ * xmlTextReaderErrorFunc:
+ * @arg: the user argument
+ * @msg: the message
+ * @severity: the severity of the error
+ * @locator: a locator indicating where the error occurred
+ *
+ * Signature of an error callback from a reader parser
+ */
+typedef void (*xmlTextReaderErrorFunc)(void *arg,
+					       const char *msg,
+					       xmlParserSeverities severity,
+					       xmlTextReaderLocatorPtr locator);
+XMLPUBFUN int
+	    xmlTextReaderLocatorLineNumber(xmlTextReaderLocatorPtr locator);
+XMLPUBFUN xmlChar *
+	    xmlTextReaderLocatorBaseURI (xmlTextReaderLocatorPtr locator);
+XMLPUBFUN void
+	    xmlTextReaderSetErrorHandler(xmlTextReaderPtr reader,
+					 xmlTextReaderErrorFunc f,
+					 void *arg);
+XMLPUBFUN void
+	    xmlTextReaderSetStructuredErrorHandler(xmlTextReaderPtr reader,
+						   xmlStructuredErrorFunc f,
+						   void *arg);
+XMLPUBFUN void
+	    xmlTextReaderGetErrorHandler(xmlTextReaderPtr reader,
+					 xmlTextReaderErrorFunc *f,
+					 void **arg);
+
+#endif /* LIBXML_READER_ENABLED */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_XMLREADER_H__ */
+
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlregexp.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlregexp.h
new file mode 100644
index 00000000..2d66437a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlregexp.h
@@ -0,0 +1,215 @@
+/*
+ * Summary: regular expressions handling
+ * Description: basic API for libxml regular expressions handling used
+ *              for XML Schemas and validation.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_REGEXP_H__
+#define __XML_REGEXP_H__
+
+#include <stdio.h>
+#include <libxml/xmlversion.h>
+#include <libxml/xmlstring.h>
+
+#ifdef LIBXML_REGEXP_ENABLED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * xmlRegexpPtr:
+ *
+ * A libxml regular expression, they can actually be far more complex
+ * thank the POSIX regex expressions.
+ */
+typedef struct _xmlRegexp xmlRegexp;
+typedef xmlRegexp *xmlRegexpPtr;
+
+/**
+ * xmlRegExecCtxtPtr:
+ *
+ * A libxml progressive regular expression evaluation context
+ */
+typedef struct _xmlRegExecCtxt xmlRegExecCtxt;
+typedef xmlRegExecCtxt *xmlRegExecCtxtPtr;
+
+/*
+ * The POSIX like API
+ */
+XMLPUBFUN xmlRegexpPtr
+		    xmlRegexpCompile	(const xmlChar *regexp);
+XMLPUBFUN void			 xmlRegFreeRegexp(xmlRegexpPtr regexp);
+XMLPUBFUN int
+		    xmlRegexpExec	(xmlRegexpPtr comp,
+					 const xmlChar *value);
+XMLPUBFUN void
+		    xmlRegexpPrint	(FILE *output,
+					 xmlRegexpPtr regexp);
+XMLPUBFUN int
+		    xmlRegexpIsDeterminist(xmlRegexpPtr comp);
+
+/**
+ * xmlRegExecCallbacks:
+ * @exec: the regular expression context
+ * @token: the current token string
+ * @transdata: transition data
+ * @inputdata: input data
+ *
+ * Callback function when doing a transition in the automata
+ */
+typedef void (*xmlRegExecCallbacks) (xmlRegExecCtxtPtr exec,
+	                             const xmlChar *token,
+				     void *transdata,
+				     void *inputdata);
+
+/*
+ * The progressive API
+ */
+XMLPUBFUN xmlRegExecCtxtPtr
+		    xmlRegNewExecCtxt	(xmlRegexpPtr comp,
+					 xmlRegExecCallbacks callback,
+					 void *data);
+XMLPUBFUN void
+		    xmlRegFreeExecCtxt	(xmlRegExecCtxtPtr exec);
+XMLPUBFUN int
+		    xmlRegExecPushString(xmlRegExecCtxtPtr exec,
+					 const xmlChar *value,
+					 void *data);
+XMLPUBFUN int
+		    xmlRegExecPushString2(xmlRegExecCtxtPtr exec,
+					 const xmlChar *value,
+					 const xmlChar *value2,
+					 void *data);
+
+XMLPUBFUN int
+		    xmlRegExecNextValues(xmlRegExecCtxtPtr exec,
+					 int *nbval,
+					 int *nbneg,
+					 xmlChar **values,
+					 int *terminal);
+XMLPUBFUN int
+		    xmlRegExecErrInfo	(xmlRegExecCtxtPtr exec,
+					 const xmlChar **string,
+					 int *nbval,
+					 int *nbneg,
+					 xmlChar **values,
+					 int *terminal);
+#ifdef LIBXML_EXPR_ENABLED
+/*
+ * Formal regular expression handling
+ * Its goal is to do some formal work on content models
+ */
+
+/* expressions are used within a context */
+typedef struct _xmlExpCtxt xmlExpCtxt;
+typedef xmlExpCtxt *xmlExpCtxtPtr;
+
+XMLPUBFUN void
+			xmlExpFreeCtxt	(xmlExpCtxtPtr ctxt);
+XMLPUBFUN xmlExpCtxtPtr
+			xmlExpNewCtxt	(int maxNodes,
+					 xmlDictPtr dict);
+
+XMLPUBFUN int
+			xmlExpCtxtNbNodes(xmlExpCtxtPtr ctxt);
+XMLPUBFUN int
+			xmlExpCtxtNbCons(xmlExpCtxtPtr ctxt);
+
+/* Expressions are trees but the tree is opaque */
+typedef struct _xmlExpNode xmlExpNode;
+typedef xmlExpNode *xmlExpNodePtr;
+
+typedef enum {
+    XML_EXP_EMPTY = 0,
+    XML_EXP_FORBID = 1,
+    XML_EXP_ATOM = 2,
+    XML_EXP_SEQ = 3,
+    XML_EXP_OR = 4,
+    XML_EXP_COUNT = 5
+} xmlExpNodeType;
+
+/*
+ * 2 core expressions shared by all for the empty language set
+ * and for the set with just the empty token
+ */
+XMLPUBVAR xmlExpNodePtr forbiddenExp;
+XMLPUBVAR xmlExpNodePtr emptyExp;
+
+/*
+ * Expressions are reference counted internally
+ */
+XMLPUBFUN void
+			xmlExpFree	(xmlExpCtxtPtr ctxt,
+					 xmlExpNodePtr expr);
+XMLPUBFUN void
+			xmlExpRef	(xmlExpNodePtr expr);
+
+/*
+ * constructors can be either manual or from a string
+ */
+XMLPUBFUN xmlExpNodePtr
+			xmlExpParse	(xmlExpCtxtPtr ctxt,
+					 const char *expr);
+XMLPUBFUN xmlExpNodePtr
+			xmlExpNewAtom	(xmlExpCtxtPtr ctxt,
+					 const xmlChar *name,
+					 int len);
+XMLPUBFUN xmlExpNodePtr
+			xmlExpNewOr	(xmlExpCtxtPtr ctxt,
+					 xmlExpNodePtr left,
+					 xmlExpNodePtr right);
+XMLPUBFUN xmlExpNodePtr
+			xmlExpNewSeq	(xmlExpCtxtPtr ctxt,
+					 xmlExpNodePtr left,
+					 xmlExpNodePtr right);
+XMLPUBFUN xmlExpNodePtr
+			xmlExpNewRange	(xmlExpCtxtPtr ctxt,
+					 xmlExpNodePtr subset,
+					 int min,
+					 int max);
+/*
+ * The really interesting APIs
+ */
+XMLPUBFUN int
+			xmlExpIsNillable(xmlExpNodePtr expr);
+XMLPUBFUN int
+			xmlExpMaxToken	(xmlExpNodePtr expr);
+XMLPUBFUN int
+			xmlExpGetLanguage(xmlExpCtxtPtr ctxt,
+					 xmlExpNodePtr expr,
+					 const xmlChar**langList,
+					 int len);
+XMLPUBFUN int
+			xmlExpGetStart	(xmlExpCtxtPtr ctxt,
+					 xmlExpNodePtr expr,
+					 const xmlChar**tokList,
+					 int len);
+XMLPUBFUN xmlExpNodePtr
+			xmlExpStringDerive(xmlExpCtxtPtr ctxt,
+					 xmlExpNodePtr expr,
+					 const xmlChar *str,
+					 int len);
+XMLPUBFUN xmlExpNodePtr
+			xmlExpExpDerive	(xmlExpCtxtPtr ctxt,
+					 xmlExpNodePtr expr,
+					 xmlExpNodePtr sub);
+XMLPUBFUN int
+			xmlExpSubsume	(xmlExpCtxtPtr ctxt,
+					 xmlExpNodePtr expr,
+					 xmlExpNodePtr sub);
+XMLPUBFUN void
+			xmlExpDump	(xmlBufferPtr buf,
+					 xmlExpNodePtr expr);
+#endif /* LIBXML_EXPR_ENABLED */
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBXML_REGEXP_ENABLED */
+
+#endif /*__XML_REGEXP_H__ */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlsave.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlsave.h
new file mode 100644
index 00000000..fbf293a2
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlsave.h
@@ -0,0 +1,97 @@
+/*
+ * Summary: the XML document serializer
+ * Description: API to save document or subtree of document
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XMLSAVE_H__
+#define __XML_XMLSAVE_H__
+
+#include <libxml/xmlversion.h>
+#include <libxml/tree.h>
+#include <libxml/encoding.h>
+#include <libxml/xmlIO.h>
+
+#ifdef LIBXML_OUTPUT_ENABLED
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * xmlSaveOption:
+ *
+ * This is the set of XML save options that can be passed down
+ * to the xmlSaveToFd() and similar calls.
+ */
+typedef enum {
+    XML_SAVE_FORMAT     = 1<<0,	/* format save output */
+    XML_SAVE_NO_DECL    = 1<<1,	/* drop the xml declaration */
+    XML_SAVE_NO_EMPTY	= 1<<2, /* no empty tags */
+    XML_SAVE_NO_XHTML	= 1<<3, /* disable XHTML1 specific rules */
+    XML_SAVE_XHTML	= 1<<4, /* force XHTML1 specific rules */
+    XML_SAVE_AS_XML     = 1<<5, /* force XML serialization on HTML doc */
+    XML_SAVE_AS_HTML    = 1<<6, /* force HTML serialization on XML doc */
+    XML_SAVE_WSNONSIG   = 1<<7  /* format with non-significant whitespace */
+} xmlSaveOption;
+
+
+typedef struct _xmlSaveCtxt xmlSaveCtxt;
+typedef xmlSaveCtxt *xmlSaveCtxtPtr;
+
+XMLPUBFUN xmlSaveCtxtPtr
+		xmlSaveToFd		(int fd,
+					 const char *encoding,
+					 int options);
+XMLPUBFUN xmlSaveCtxtPtr
+		xmlSaveToFilename	(const char *filename,
+					 const char *encoding,
+					 int options);
+
+XMLPUBFUN xmlSaveCtxtPtr
+		xmlSaveToBuffer		(xmlBufferPtr buffer,
+					 const char *encoding,
+					 int options);
+
+XMLPUBFUN xmlSaveCtxtPtr
+		xmlSaveToIO		(xmlOutputWriteCallback iowrite,
+					 xmlOutputCloseCallback ioclose,
+					 void *ioctx,
+					 const char *encoding,
+					 int options);
+
+XMLPUBFUN long
+		xmlSaveDoc		(xmlSaveCtxtPtr ctxt,
+					 xmlDocPtr doc);
+XMLPUBFUN long
+		xmlSaveTree		(xmlSaveCtxtPtr ctxt,
+					 xmlNodePtr node);
+
+XMLPUBFUN int
+		xmlSaveFlush		(xmlSaveCtxtPtr ctxt);
+XMLPUBFUN int
+		xmlSaveClose		(xmlSaveCtxtPtr ctxt);
+XMLPUBFUN int
+		xmlSaveSetEscape	(xmlSaveCtxtPtr ctxt,
+					 xmlCharEncodingOutputFunc escape);
+XMLPUBFUN int
+		xmlSaveSetAttrEscape	(xmlSaveCtxtPtr ctxt,
+					 xmlCharEncodingOutputFunc escape);
+
+XMLPUBFUN int
+                xmlThrDefIndentTreeOutput(int v);
+XMLPUBFUN const char *
+                xmlThrDefTreeIndentString(const char * v);
+XMLPUBFUN int
+                xmlThrDefSaveNoEmptyTags(int v);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBXML_OUTPUT_ENABLED */
+#endif /* __XML_XMLSAVE_H__ */
+
+
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlschemas.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlschemas.h
new file mode 100644
index 00000000..c2af3d70
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlschemas.h
@@ -0,0 +1,249 @@
+/*
+ * Summary: incomplete XML Schemas structure implementation
+ * Description: interface to the XML Schemas handling and schema validity
+ *              checking, it is incomplete right now.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+
+#ifndef __XML_SCHEMA_H__
+#define __XML_SCHEMA_H__
+
+#include <libxml/xmlversion.h>
+
+#ifdef LIBXML_SCHEMAS_ENABLED
+
+#include <stdio.h>
+#include <libxml/encoding.h>
+#include <libxml/tree.h>
+#include <libxml/xmlerror.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * This error codes are obsolete; not used any more.
+ */
+typedef enum {
+    XML_SCHEMAS_ERR_OK		= 0,
+    XML_SCHEMAS_ERR_NOROOT	= 1,
+    XML_SCHEMAS_ERR_UNDECLAREDELEM,
+    XML_SCHEMAS_ERR_NOTTOPLEVEL,
+    XML_SCHEMAS_ERR_MISSING,
+    XML_SCHEMAS_ERR_WRONGELEM,
+    XML_SCHEMAS_ERR_NOTYPE,
+    XML_SCHEMAS_ERR_NOROLLBACK,
+    XML_SCHEMAS_ERR_ISABSTRACT,
+    XML_SCHEMAS_ERR_NOTEMPTY,
+    XML_SCHEMAS_ERR_ELEMCONT,
+    XML_SCHEMAS_ERR_HAVEDEFAULT,
+    XML_SCHEMAS_ERR_NOTNILLABLE,
+    XML_SCHEMAS_ERR_EXTRACONTENT,
+    XML_SCHEMAS_ERR_INVALIDATTR,
+    XML_SCHEMAS_ERR_INVALIDELEM,
+    XML_SCHEMAS_ERR_NOTDETERMINIST,
+    XML_SCHEMAS_ERR_CONSTRUCT,
+    XML_SCHEMAS_ERR_INTERNAL,
+    XML_SCHEMAS_ERR_NOTSIMPLE,
+    XML_SCHEMAS_ERR_ATTRUNKNOWN,
+    XML_SCHEMAS_ERR_ATTRINVALID,
+    XML_SCHEMAS_ERR_VALUE,
+    XML_SCHEMAS_ERR_FACET,
+    XML_SCHEMAS_ERR_,
+    XML_SCHEMAS_ERR_XXX
+} xmlSchemaValidError;
+
+/*
+* ATTENTION: Change xmlSchemaSetValidOptions's check
+* for invalid values, if adding to the validation
+* options below.
+*/
+/**
+ * xmlSchemaValidOption:
+ *
+ * This is the set of XML Schema validation options.
+ */
+typedef enum {
+    XML_SCHEMA_VAL_VC_I_CREATE			= 1<<0
+	/* Default/fixed: create an attribute node
+	* or an element's text node on the instance.
+	*/
+} xmlSchemaValidOption;
+
+/*
+    XML_SCHEMA_VAL_XSI_ASSEMBLE			= 1<<1,
+	* assemble schemata using
+	* xsi:schemaLocation and
+	* xsi:noNamespaceSchemaLocation
+*/
+
+/**
+ * The schemas related types are kept internal
+ */
+typedef struct _xmlSchema xmlSchema;
+typedef xmlSchema *xmlSchemaPtr;
+
+/**
+ * xmlSchemaValidityErrorFunc:
+ * @ctx: the validation context
+ * @msg: the message
+ * @...: extra arguments
+ *
+ * Signature of an error callback from an XSD validation
+ */
+typedef void (*xmlSchemaValidityErrorFunc)
+                 (void *ctx, const char *msg, ...) LIBXML_ATTR_FORMAT(2,3);
+
+/**
+ * xmlSchemaValidityWarningFunc:
+ * @ctx: the validation context
+ * @msg: the message
+ * @...: extra arguments
+ *
+ * Signature of a warning callback from an XSD validation
+ */
+typedef void (*xmlSchemaValidityWarningFunc)
+                 (void *ctx, const char *msg, ...) LIBXML_ATTR_FORMAT(2,3);
+
+/**
+ * A schemas validation context
+ */
+typedef struct _xmlSchemaParserCtxt xmlSchemaParserCtxt;
+typedef xmlSchemaParserCtxt *xmlSchemaParserCtxtPtr;
+
+typedef struct _xmlSchemaValidCtxt xmlSchemaValidCtxt;
+typedef xmlSchemaValidCtxt *xmlSchemaValidCtxtPtr;
+
+/**
+ * xmlSchemaValidityLocatorFunc:
+ * @ctx: user provided context
+ * @file: returned file information
+ * @line: returned line information
+ *
+ * A schemas validation locator, a callback called by the validator.
+ * This is used when file or node information are not available
+ * to find out what file and line number are affected
+ *
+ * Returns: 0 in case of success and -1 in case of error
+ */
+
+typedef int (*xmlSchemaValidityLocatorFunc) (void *ctx,
+                           const char **file, unsigned long *line);
+
+/*
+ * Interfaces for parsing.
+ */
+XMLPUBFUN xmlSchemaParserCtxtPtr
+	    xmlSchemaNewParserCtxt	(const char *URL);
+XMLPUBFUN xmlSchemaParserCtxtPtr
+	    xmlSchemaNewMemParserCtxt	(const char *buffer,
+					 int size);
+XMLPUBFUN xmlSchemaParserCtxtPtr
+	    xmlSchemaNewDocParserCtxt	(xmlDocPtr doc);
+XMLPUBFUN void
+	    xmlSchemaFreeParserCtxt	(xmlSchemaParserCtxtPtr ctxt);
+XMLPUBFUN void
+	    xmlSchemaSetParserErrors	(xmlSchemaParserCtxtPtr ctxt,
+					 xmlSchemaValidityErrorFunc err,
+					 xmlSchemaValidityWarningFunc warn,
+					 void *ctx);
+XMLPUBFUN void
+	    xmlSchemaSetParserStructuredErrors(xmlSchemaParserCtxtPtr ctxt,
+					 xmlStructuredErrorFunc serror,
+					 void *ctx);
+XMLPUBFUN int
+		xmlSchemaGetParserErrors(xmlSchemaParserCtxtPtr ctxt,
+					xmlSchemaValidityErrorFunc * err,
+					xmlSchemaValidityWarningFunc * warn,
+					void **ctx);
+XMLPUBFUN int
+		xmlSchemaIsValid	(xmlSchemaValidCtxtPtr ctxt);
+
+XMLPUBFUN xmlSchemaPtr
+	    xmlSchemaParse		(xmlSchemaParserCtxtPtr ctxt);
+XMLPUBFUN void
+	    xmlSchemaFree		(xmlSchemaPtr schema);
+#ifdef LIBXML_OUTPUT_ENABLED
+XMLPUBFUN void
+	    xmlSchemaDump		(FILE *output,
+					 xmlSchemaPtr schema);
+#endif /* LIBXML_OUTPUT_ENABLED */
+/*
+ * Interfaces for validating
+ */
+XMLPUBFUN void
+	    xmlSchemaSetValidErrors	(xmlSchemaValidCtxtPtr ctxt,
+					 xmlSchemaValidityErrorFunc err,
+					 xmlSchemaValidityWarningFunc warn,
+					 void *ctx);
+XMLPUBFUN void
+	    xmlSchemaSetValidStructuredErrors(xmlSchemaValidCtxtPtr ctxt,
+					 xmlStructuredErrorFunc serror,
+					 void *ctx);
+XMLPUBFUN int
+	    xmlSchemaGetValidErrors	(xmlSchemaValidCtxtPtr ctxt,
+					 xmlSchemaValidityErrorFunc *err,
+					 xmlSchemaValidityWarningFunc *warn,
+					 void **ctx);
+XMLPUBFUN int
+	    xmlSchemaSetValidOptions	(xmlSchemaValidCtxtPtr ctxt,
+					 int options);
+XMLPUBFUN void
+            xmlSchemaValidateSetFilename(xmlSchemaValidCtxtPtr vctxt,
+	                                 const char *filename);
+XMLPUBFUN int
+	    xmlSchemaValidCtxtGetOptions(xmlSchemaValidCtxtPtr ctxt);
+
+XMLPUBFUN xmlSchemaValidCtxtPtr
+	    xmlSchemaNewValidCtxt	(xmlSchemaPtr schema);
+XMLPUBFUN void
+	    xmlSchemaFreeValidCtxt	(xmlSchemaValidCtxtPtr ctxt);
+XMLPUBFUN int
+	    xmlSchemaValidateDoc	(xmlSchemaValidCtxtPtr ctxt,
+					 xmlDocPtr instance);
+XMLPUBFUN int
+            xmlSchemaValidateOneElement (xmlSchemaValidCtxtPtr ctxt,
+			                 xmlNodePtr elem);
+XMLPUBFUN int
+	    xmlSchemaValidateStream	(xmlSchemaValidCtxtPtr ctxt,
+					 xmlParserInputBufferPtr input,
+					 xmlCharEncoding enc,
+					 xmlSAXHandlerPtr sax,
+					 void *user_data);
+XMLPUBFUN int
+	    xmlSchemaValidateFile	(xmlSchemaValidCtxtPtr ctxt,
+					 const char * filename,
+					 int options);
+
+XMLPUBFUN xmlParserCtxtPtr
+	    xmlSchemaValidCtxtGetParserCtxt(xmlSchemaValidCtxtPtr ctxt);
+
+/*
+ * Interface to insert Schemas SAX validation in a SAX stream
+ */
+typedef struct _xmlSchemaSAXPlug xmlSchemaSAXPlugStruct;
+typedef xmlSchemaSAXPlugStruct *xmlSchemaSAXPlugPtr;
+
+XMLPUBFUN xmlSchemaSAXPlugPtr
+            xmlSchemaSAXPlug		(xmlSchemaValidCtxtPtr ctxt,
+					 xmlSAXHandlerPtr *sax,
+					 void **user_data);
+XMLPUBFUN int
+            xmlSchemaSAXUnplug		(xmlSchemaSAXPlugPtr plug);
+
+
+XMLPUBFUN void
+            xmlSchemaValidateSetLocator	(xmlSchemaValidCtxtPtr vctxt,
+					 xmlSchemaValidityLocatorFunc f,
+					 void *ctxt);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBXML_SCHEMAS_ENABLED */
+#endif /* __XML_SCHEMA_H__ */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlschemastypes.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlschemastypes.h
new file mode 100644
index 00000000..e2cde357
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlschemastypes.h
@@ -0,0 +1,152 @@
+/*
+ * Summary: implementation of XML Schema Datatypes
+ * Description: module providing the XML Schema Datatypes implementation
+ *              both definition and validity checking
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+
+#ifndef __XML_SCHEMA_TYPES_H__
+#define __XML_SCHEMA_TYPES_H__
+
+#include <libxml/xmlversion.h>
+
+#ifdef LIBXML_SCHEMAS_ENABLED
+
+#include <libxml/schemasInternals.h>
+#include <libxml/xmlschemas.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+    XML_SCHEMA_WHITESPACE_UNKNOWN = 0,
+    XML_SCHEMA_WHITESPACE_PRESERVE = 1,
+    XML_SCHEMA_WHITESPACE_REPLACE = 2,
+    XML_SCHEMA_WHITESPACE_COLLAPSE = 3
+} xmlSchemaWhitespaceValueType;
+
+XMLPUBFUN int
+		xmlSchemaInitTypes		(void);
+XML_DEPRECATED
+XMLPUBFUN void
+		xmlSchemaCleanupTypes		(void);
+XMLPUBFUN xmlSchemaTypePtr
+		xmlSchemaGetPredefinedType	(const xmlChar *name,
+						 const xmlChar *ns);
+XMLPUBFUN int
+		xmlSchemaValidatePredefinedType	(xmlSchemaTypePtr type,
+						 const xmlChar *value,
+						 xmlSchemaValPtr *val);
+XMLPUBFUN int
+		xmlSchemaValPredefTypeNode	(xmlSchemaTypePtr type,
+						 const xmlChar *value,
+						 xmlSchemaValPtr *val,
+						 xmlNodePtr node);
+XMLPUBFUN int
+		xmlSchemaValidateFacet		(xmlSchemaTypePtr base,
+						 xmlSchemaFacetPtr facet,
+						 const xmlChar *value,
+						 xmlSchemaValPtr val);
+XMLPUBFUN int
+		xmlSchemaValidateFacetWhtsp	(xmlSchemaFacetPtr facet,
+						 xmlSchemaWhitespaceValueType fws,
+						 xmlSchemaValType valType,
+						 const xmlChar *value,
+						 xmlSchemaValPtr val,
+						 xmlSchemaWhitespaceValueType ws);
+XMLPUBFUN void
+		xmlSchemaFreeValue		(xmlSchemaValPtr val);
+XMLPUBFUN xmlSchemaFacetPtr
+		xmlSchemaNewFacet		(void);
+XMLPUBFUN int
+		xmlSchemaCheckFacet		(xmlSchemaFacetPtr facet,
+						 xmlSchemaTypePtr typeDecl,
+						 xmlSchemaParserCtxtPtr ctxt,
+						 const xmlChar *name);
+XMLPUBFUN void
+		xmlSchemaFreeFacet		(xmlSchemaFacetPtr facet);
+XMLPUBFUN int
+		xmlSchemaCompareValues		(xmlSchemaValPtr x,
+						 xmlSchemaValPtr y);
+XMLPUBFUN xmlSchemaTypePtr
+    xmlSchemaGetBuiltInListSimpleTypeItemType	(xmlSchemaTypePtr type);
+XMLPUBFUN int
+    xmlSchemaValidateListSimpleTypeFacet	(xmlSchemaFacetPtr facet,
+						 const xmlChar *value,
+						 unsigned long actualLen,
+						 unsigned long *expectedLen);
+XMLPUBFUN xmlSchemaTypePtr
+		xmlSchemaGetBuiltInType		(xmlSchemaValType type);
+XMLPUBFUN int
+		xmlSchemaIsBuiltInTypeFacet	(xmlSchemaTypePtr type,
+						 int facetType);
+XMLPUBFUN xmlChar *
+		xmlSchemaCollapseString		(const xmlChar *value);
+XMLPUBFUN xmlChar *
+		xmlSchemaWhiteSpaceReplace	(const xmlChar *value);
+XMLPUBFUN unsigned long 
+		xmlSchemaGetFacetValueAsULong	(xmlSchemaFacetPtr facet);
+XMLPUBFUN int
+		xmlSchemaValidateLengthFacet	(xmlSchemaTypePtr type,
+						 xmlSchemaFacetPtr facet,
+						 const xmlChar *value,
+						 xmlSchemaValPtr val,
+						 unsigned long *length);
+XMLPUBFUN int
+		xmlSchemaValidateLengthFacetWhtsp(xmlSchemaFacetPtr facet,
+						  xmlSchemaValType valType,
+						  const xmlChar *value,
+						  xmlSchemaValPtr val,
+						  unsigned long *length,
+						  xmlSchemaWhitespaceValueType ws);
+XMLPUBFUN int
+		xmlSchemaValPredefTypeNodeNoNorm(xmlSchemaTypePtr type,
+						 const xmlChar *value,
+						 xmlSchemaValPtr *val,
+						 xmlNodePtr node);
+XMLPUBFUN int
+		xmlSchemaGetCanonValue		(xmlSchemaValPtr val,
+						 const xmlChar **retValue);
+XMLPUBFUN int
+		xmlSchemaGetCanonValueWhtsp	(xmlSchemaValPtr val,
+						 const xmlChar **retValue,
+						 xmlSchemaWhitespaceValueType ws);
+XMLPUBFUN int
+		xmlSchemaValueAppend		(xmlSchemaValPtr prev,
+						 xmlSchemaValPtr cur);
+XMLPUBFUN xmlSchemaValPtr
+		xmlSchemaValueGetNext		(xmlSchemaValPtr cur);
+XMLPUBFUN const xmlChar *
+		xmlSchemaValueGetAsString	(xmlSchemaValPtr val);
+XMLPUBFUN int
+		xmlSchemaValueGetAsBoolean	(xmlSchemaValPtr val);
+XMLPUBFUN xmlSchemaValPtr
+		xmlSchemaNewStringValue		(xmlSchemaValType type,
+						 const xmlChar *value);
+XMLPUBFUN xmlSchemaValPtr
+		xmlSchemaNewNOTATIONValue	(const xmlChar *name,
+						 const xmlChar *ns);
+XMLPUBFUN xmlSchemaValPtr
+		xmlSchemaNewQNameValue		(const xmlChar *namespaceName,
+						 const xmlChar *localName);
+XMLPUBFUN int
+		xmlSchemaCompareValuesWhtsp	(xmlSchemaValPtr x,
+						 xmlSchemaWhitespaceValueType xws,
+						 xmlSchemaValPtr y,
+						 xmlSchemaWhitespaceValueType yws);
+XMLPUBFUN xmlSchemaValPtr
+		xmlSchemaCopyValue		(xmlSchemaValPtr val);
+XMLPUBFUN xmlSchemaValType
+		xmlSchemaGetValType		(xmlSchemaValPtr val);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBXML_SCHEMAS_ENABLED */
+#endif /* __XML_SCHEMA_TYPES_H__ */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlstring.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlstring.h
new file mode 100644
index 00000000..db11a0b0
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlstring.h
@@ -0,0 +1,140 @@
+/*
+ * Summary: set of routines to process strings
+ * Description: type and interfaces needed for the internal string handling
+ *              of the library, especially UTF8 processing.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_STRING_H__
+#define __XML_STRING_H__
+
+#include <stdarg.h>
+#include <libxml/xmlversion.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * xmlChar:
+ *
+ * This is a basic byte in an UTF-8 encoded string.
+ * It's unsigned allowing to pinpoint case where char * are assigned
+ * to xmlChar * (possibly making serialization back impossible).
+ */
+typedef unsigned char xmlChar;
+
+/**
+ * BAD_CAST:
+ *
+ * Macro to cast a string to an xmlChar * when one know its safe.
+ */
+#define BAD_CAST (xmlChar *)
+
+/*
+ * xmlChar handling
+ */
+XMLPUBFUN xmlChar *
+                xmlStrdup                (const xmlChar *cur);
+XMLPUBFUN xmlChar *
+                xmlStrndup               (const xmlChar *cur,
+                                         int len);
+XMLPUBFUN xmlChar *
+                xmlCharStrndup           (const char *cur,
+                                         int len);
+XMLPUBFUN xmlChar *
+                xmlCharStrdup            (const char *cur);
+XMLPUBFUN xmlChar *
+                xmlStrsub                (const xmlChar *str,
+                                         int start,
+                                         int len);
+XMLPUBFUN const xmlChar *
+                xmlStrchr                (const xmlChar *str,
+                                         xmlChar val);
+XMLPUBFUN const xmlChar *
+                xmlStrstr                (const xmlChar *str,
+                                         const xmlChar *val);
+XMLPUBFUN const xmlChar *
+                xmlStrcasestr            (const xmlChar *str,
+                                         const xmlChar *val);
+XMLPUBFUN int
+                xmlStrcmp                (const xmlChar *str1,
+                                         const xmlChar *str2);
+XMLPUBFUN int
+                xmlStrncmp               (const xmlChar *str1,
+                                         const xmlChar *str2,
+                                         int len);
+XMLPUBFUN int
+                xmlStrcasecmp            (const xmlChar *str1,
+                                         const xmlChar *str2);
+XMLPUBFUN int
+                xmlStrncasecmp           (const xmlChar *str1,
+                                         const xmlChar *str2,
+                                         int len);
+XMLPUBFUN int
+                xmlStrEqual              (const xmlChar *str1,
+                                         const xmlChar *str2);
+XMLPUBFUN int
+                xmlStrQEqual             (const xmlChar *pref,
+                                         const xmlChar *name,
+                                         const xmlChar *str);
+XMLPUBFUN int
+                xmlStrlen                (const xmlChar *str);
+XMLPUBFUN xmlChar *
+                xmlStrcat                (xmlChar *cur,
+                                         const xmlChar *add);
+XMLPUBFUN xmlChar *
+                xmlStrncat               (xmlChar *cur,
+                                         const xmlChar *add,
+                                         int len);
+XMLPUBFUN xmlChar *
+                xmlStrncatNew            (const xmlChar *str1,
+                                         const xmlChar *str2,
+                                         int len);
+XMLPUBFUN int
+                xmlStrPrintf             (xmlChar *buf,
+                                         int len,
+                                         const char *msg,
+                                         ...) LIBXML_ATTR_FORMAT(3,4);
+XMLPUBFUN int
+                xmlStrVPrintf                (xmlChar *buf,
+                                         int len,
+                                         const char *msg,
+                                         va_list ap) LIBXML_ATTR_FORMAT(3,0);
+
+XMLPUBFUN int
+        xmlGetUTF8Char                   (const unsigned char *utf,
+                                         int *len);
+XMLPUBFUN int
+        xmlCheckUTF8                     (const unsigned char *utf);
+XMLPUBFUN int
+        xmlUTF8Strsize                   (const xmlChar *utf,
+                                         int len);
+XMLPUBFUN xmlChar *
+        xmlUTF8Strndup                   (const xmlChar *utf,
+                                         int len);
+XMLPUBFUN const xmlChar *
+        xmlUTF8Strpos                    (const xmlChar *utf,
+                                         int pos);
+XMLPUBFUN int
+        xmlUTF8Strloc                    (const xmlChar *utf,
+                                         const xmlChar *utfchar);
+XMLPUBFUN xmlChar *
+        xmlUTF8Strsub                    (const xmlChar *utf,
+                                         int start,
+                                         int len);
+XMLPUBFUN int
+        xmlUTF8Strlen                    (const xmlChar *utf);
+XMLPUBFUN int
+        xmlUTF8Size                      (const xmlChar *utf);
+XMLPUBFUN int
+        xmlUTF8Charcmp                   (const xmlChar *utf1,
+                                         const xmlChar *utf2);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __XML_STRING_H__ */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlunicode.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlunicode.h
new file mode 100644
index 00000000..2e50a49f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlunicode.h
@@ -0,0 +1,202 @@
+/*
+ * Summary: Unicode character APIs
+ * Description: API for the Unicode character APIs
+ *
+ * This file is automatically generated from the
+ * UCS description files of the Unicode Character Database
+ * http://www.unicode.org/Public/4.0-Update1/UCD-4.0.1.html
+ * using the genUnicode.py Python script.
+ *
+ * Generation date: Mon Mar 27 11:09:52 2006
+ * Sources: Blocks-4.0.1.txt UnicodeData-4.0.1.txt
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_UNICODE_H__
+#define __XML_UNICODE_H__
+
+#include <libxml/xmlversion.h>
+
+#ifdef LIBXML_UNICODE_ENABLED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+XMLPUBFUN int xmlUCSIsAegeanNumbers	(int code);
+XMLPUBFUN int xmlUCSIsAlphabeticPresentationForms	(int code);
+XMLPUBFUN int xmlUCSIsArabic	(int code);
+XMLPUBFUN int xmlUCSIsArabicPresentationFormsA	(int code);
+XMLPUBFUN int xmlUCSIsArabicPresentationFormsB	(int code);
+XMLPUBFUN int xmlUCSIsArmenian	(int code);
+XMLPUBFUN int xmlUCSIsArrows	(int code);
+XMLPUBFUN int xmlUCSIsBasicLatin	(int code);
+XMLPUBFUN int xmlUCSIsBengali	(int code);
+XMLPUBFUN int xmlUCSIsBlockElements	(int code);
+XMLPUBFUN int xmlUCSIsBopomofo	(int code);
+XMLPUBFUN int xmlUCSIsBopomofoExtended	(int code);
+XMLPUBFUN int xmlUCSIsBoxDrawing	(int code);
+XMLPUBFUN int xmlUCSIsBraillePatterns	(int code);
+XMLPUBFUN int xmlUCSIsBuhid	(int code);
+XMLPUBFUN int xmlUCSIsByzantineMusicalSymbols	(int code);
+XMLPUBFUN int xmlUCSIsCJKCompatibility	(int code);
+XMLPUBFUN int xmlUCSIsCJKCompatibilityForms	(int code);
+XMLPUBFUN int xmlUCSIsCJKCompatibilityIdeographs	(int code);
+XMLPUBFUN int xmlUCSIsCJKCompatibilityIdeographsSupplement	(int code);
+XMLPUBFUN int xmlUCSIsCJKRadicalsSupplement	(int code);
+XMLPUBFUN int xmlUCSIsCJKSymbolsandPunctuation	(int code);
+XMLPUBFUN int xmlUCSIsCJKUnifiedIdeographs	(int code);
+XMLPUBFUN int xmlUCSIsCJKUnifiedIdeographsExtensionA	(int code);
+XMLPUBFUN int xmlUCSIsCJKUnifiedIdeographsExtensionB	(int code);
+XMLPUBFUN int xmlUCSIsCherokee	(int code);
+XMLPUBFUN int xmlUCSIsCombiningDiacriticalMarks	(int code);
+XMLPUBFUN int xmlUCSIsCombiningDiacriticalMarksforSymbols	(int code);
+XMLPUBFUN int xmlUCSIsCombiningHalfMarks	(int code);
+XMLPUBFUN int xmlUCSIsCombiningMarksforSymbols	(int code);
+XMLPUBFUN int xmlUCSIsControlPictures	(int code);
+XMLPUBFUN int xmlUCSIsCurrencySymbols	(int code);
+XMLPUBFUN int xmlUCSIsCypriotSyllabary	(int code);
+XMLPUBFUN int xmlUCSIsCyrillic	(int code);
+XMLPUBFUN int xmlUCSIsCyrillicSupplement	(int code);
+XMLPUBFUN int xmlUCSIsDeseret	(int code);
+XMLPUBFUN int xmlUCSIsDevanagari	(int code);
+XMLPUBFUN int xmlUCSIsDingbats	(int code);
+XMLPUBFUN int xmlUCSIsEnclosedAlphanumerics	(int code);
+XMLPUBFUN int xmlUCSIsEnclosedCJKLettersandMonths	(int code);
+XMLPUBFUN int xmlUCSIsEthiopic	(int code);
+XMLPUBFUN int xmlUCSIsGeneralPunctuation	(int code);
+XMLPUBFUN int xmlUCSIsGeometricShapes	(int code);
+XMLPUBFUN int xmlUCSIsGeorgian	(int code);
+XMLPUBFUN int xmlUCSIsGothic	(int code);
+XMLPUBFUN int xmlUCSIsGreek	(int code);
+XMLPUBFUN int xmlUCSIsGreekExtended	(int code);
+XMLPUBFUN int xmlUCSIsGreekandCoptic	(int code);
+XMLPUBFUN int xmlUCSIsGujarati	(int code);
+XMLPUBFUN int xmlUCSIsGurmukhi	(int code);
+XMLPUBFUN int xmlUCSIsHalfwidthandFullwidthForms	(int code);
+XMLPUBFUN int xmlUCSIsHangulCompatibilityJamo	(int code);
+XMLPUBFUN int xmlUCSIsHangulJamo	(int code);
+XMLPUBFUN int xmlUCSIsHangulSyllables	(int code);
+XMLPUBFUN int xmlUCSIsHanunoo	(int code);
+XMLPUBFUN int xmlUCSIsHebrew	(int code);
+XMLPUBFUN int xmlUCSIsHighPrivateUseSurrogates	(int code);
+XMLPUBFUN int xmlUCSIsHighSurrogates	(int code);
+XMLPUBFUN int xmlUCSIsHiragana	(int code);
+XMLPUBFUN int xmlUCSIsIPAExtensions	(int code);
+XMLPUBFUN int xmlUCSIsIdeographicDescriptionCharacters	(int code);
+XMLPUBFUN int xmlUCSIsKanbun	(int code);
+XMLPUBFUN int xmlUCSIsKangxiRadicals	(int code);
+XMLPUBFUN int xmlUCSIsKannada	(int code);
+XMLPUBFUN int xmlUCSIsKatakana	(int code);
+XMLPUBFUN int xmlUCSIsKatakanaPhoneticExtensions	(int code);
+XMLPUBFUN int xmlUCSIsKhmer	(int code);
+XMLPUBFUN int xmlUCSIsKhmerSymbols	(int code);
+XMLPUBFUN int xmlUCSIsLao	(int code);
+XMLPUBFUN int xmlUCSIsLatin1Supplement	(int code);
+XMLPUBFUN int xmlUCSIsLatinExtendedA	(int code);
+XMLPUBFUN int xmlUCSIsLatinExtendedB	(int code);
+XMLPUBFUN int xmlUCSIsLatinExtendedAdditional	(int code);
+XMLPUBFUN int xmlUCSIsLetterlikeSymbols	(int code);
+XMLPUBFUN int xmlUCSIsLimbu	(int code);
+XMLPUBFUN int xmlUCSIsLinearBIdeograms	(int code);
+XMLPUBFUN int xmlUCSIsLinearBSyllabary	(int code);
+XMLPUBFUN int xmlUCSIsLowSurrogates	(int code);
+XMLPUBFUN int xmlUCSIsMalayalam	(int code);
+XMLPUBFUN int xmlUCSIsMathematicalAlphanumericSymbols	(int code);
+XMLPUBFUN int xmlUCSIsMathematicalOperators	(int code);
+XMLPUBFUN int xmlUCSIsMiscellaneousMathematicalSymbolsA	(int code);
+XMLPUBFUN int xmlUCSIsMiscellaneousMathematicalSymbolsB	(int code);
+XMLPUBFUN int xmlUCSIsMiscellaneousSymbols	(int code);
+XMLPUBFUN int xmlUCSIsMiscellaneousSymbolsandArrows	(int code);
+XMLPUBFUN int xmlUCSIsMiscellaneousTechnical	(int code);
+XMLPUBFUN int xmlUCSIsMongolian	(int code);
+XMLPUBFUN int xmlUCSIsMusicalSymbols	(int code);
+XMLPUBFUN int xmlUCSIsMyanmar	(int code);
+XMLPUBFUN int xmlUCSIsNumberForms	(int code);
+XMLPUBFUN int xmlUCSIsOgham	(int code);
+XMLPUBFUN int xmlUCSIsOldItalic	(int code);
+XMLPUBFUN int xmlUCSIsOpticalCharacterRecognition	(int code);
+XMLPUBFUN int xmlUCSIsOriya	(int code);
+XMLPUBFUN int xmlUCSIsOsmanya	(int code);
+XMLPUBFUN int xmlUCSIsPhoneticExtensions	(int code);
+XMLPUBFUN int xmlUCSIsPrivateUse	(int code);
+XMLPUBFUN int xmlUCSIsPrivateUseArea	(int code);
+XMLPUBFUN int xmlUCSIsRunic	(int code);
+XMLPUBFUN int xmlUCSIsShavian	(int code);
+XMLPUBFUN int xmlUCSIsSinhala	(int code);
+XMLPUBFUN int xmlUCSIsSmallFormVariants	(int code);
+XMLPUBFUN int xmlUCSIsSpacingModifierLetters	(int code);
+XMLPUBFUN int xmlUCSIsSpecials	(int code);
+XMLPUBFUN int xmlUCSIsSuperscriptsandSubscripts	(int code);
+XMLPUBFUN int xmlUCSIsSupplementalArrowsA	(int code);
+XMLPUBFUN int xmlUCSIsSupplementalArrowsB	(int code);
+XMLPUBFUN int xmlUCSIsSupplementalMathematicalOperators	(int code);
+XMLPUBFUN int xmlUCSIsSupplementaryPrivateUseAreaA	(int code);
+XMLPUBFUN int xmlUCSIsSupplementaryPrivateUseAreaB	(int code);
+XMLPUBFUN int xmlUCSIsSyriac	(int code);
+XMLPUBFUN int xmlUCSIsTagalog	(int code);
+XMLPUBFUN int xmlUCSIsTagbanwa	(int code);
+XMLPUBFUN int xmlUCSIsTags	(int code);
+XMLPUBFUN int xmlUCSIsTaiLe	(int code);
+XMLPUBFUN int xmlUCSIsTaiXuanJingSymbols	(int code);
+XMLPUBFUN int xmlUCSIsTamil	(int code);
+XMLPUBFUN int xmlUCSIsTelugu	(int code);
+XMLPUBFUN int xmlUCSIsThaana	(int code);
+XMLPUBFUN int xmlUCSIsThai	(int code);
+XMLPUBFUN int xmlUCSIsTibetan	(int code);
+XMLPUBFUN int xmlUCSIsUgaritic	(int code);
+XMLPUBFUN int xmlUCSIsUnifiedCanadianAboriginalSyllabics	(int code);
+XMLPUBFUN int xmlUCSIsVariationSelectors	(int code);
+XMLPUBFUN int xmlUCSIsVariationSelectorsSupplement	(int code);
+XMLPUBFUN int xmlUCSIsYiRadicals	(int code);
+XMLPUBFUN int xmlUCSIsYiSyllables	(int code);
+XMLPUBFUN int xmlUCSIsYijingHexagramSymbols	(int code);
+
+XMLPUBFUN int xmlUCSIsBlock	(int code, const char *block);
+
+XMLPUBFUN int xmlUCSIsCatC	(int code);
+XMLPUBFUN int xmlUCSIsCatCc	(int code);
+XMLPUBFUN int xmlUCSIsCatCf	(int code);
+XMLPUBFUN int xmlUCSIsCatCo	(int code);
+XMLPUBFUN int xmlUCSIsCatCs	(int code);
+XMLPUBFUN int xmlUCSIsCatL	(int code);
+XMLPUBFUN int xmlUCSIsCatLl	(int code);
+XMLPUBFUN int xmlUCSIsCatLm	(int code);
+XMLPUBFUN int xmlUCSIsCatLo	(int code);
+XMLPUBFUN int xmlUCSIsCatLt	(int code);
+XMLPUBFUN int xmlUCSIsCatLu	(int code);
+XMLPUBFUN int xmlUCSIsCatM	(int code);
+XMLPUBFUN int xmlUCSIsCatMc	(int code);
+XMLPUBFUN int xmlUCSIsCatMe	(int code);
+XMLPUBFUN int xmlUCSIsCatMn	(int code);
+XMLPUBFUN int xmlUCSIsCatN	(int code);
+XMLPUBFUN int xmlUCSIsCatNd	(int code);
+XMLPUBFUN int xmlUCSIsCatNl	(int code);
+XMLPUBFUN int xmlUCSIsCatNo	(int code);
+XMLPUBFUN int xmlUCSIsCatP	(int code);
+XMLPUBFUN int xmlUCSIsCatPc	(int code);
+XMLPUBFUN int xmlUCSIsCatPd	(int code);
+XMLPUBFUN int xmlUCSIsCatPe	(int code);
+XMLPUBFUN int xmlUCSIsCatPf	(int code);
+XMLPUBFUN int xmlUCSIsCatPi	(int code);
+XMLPUBFUN int xmlUCSIsCatPo	(int code);
+XMLPUBFUN int xmlUCSIsCatPs	(int code);
+XMLPUBFUN int xmlUCSIsCatS	(int code);
+XMLPUBFUN int xmlUCSIsCatSc	(int code);
+XMLPUBFUN int xmlUCSIsCatSk	(int code);
+XMLPUBFUN int xmlUCSIsCatSm	(int code);
+XMLPUBFUN int xmlUCSIsCatSo	(int code);
+XMLPUBFUN int xmlUCSIsCatZ	(int code);
+XMLPUBFUN int xmlUCSIsCatZl	(int code);
+XMLPUBFUN int xmlUCSIsCatZp	(int code);
+XMLPUBFUN int xmlUCSIsCatZs	(int code);
+
+XMLPUBFUN int xmlUCSIsCat	(int code, const char *cat);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBXML_UNICODE_ENABLED */
+
+#endif /* __XML_UNICODE_H__ */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlversion.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlversion.h
new file mode 100644
index 00000000..cec304f6
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlversion.h
@@ -0,0 +1,511 @@
+/*
+ * Summary: compile-time version information
+ * Description: compile-time version information for the XML library
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_VERSION_H__
+#define __XML_VERSION_H__
+
+#include <libxml/xmlexports.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * use those to be sure nothing nasty will happen if
+ * your library and includes mismatch
+ */
+#ifndef LIBXML2_COMPILING_MSCCDEF
+XMLPUBFUN void xmlCheckVersion(int version);
+#endif /* LIBXML2_COMPILING_MSCCDEF */
+
+/**
+ * LIBXML_DOTTED_VERSION:
+ *
+ * the version string like "1.2.3"
+ */
+#define LIBXML_DOTTED_VERSION "2.12.9"
+
+/**
+ * LIBXML_VERSION:
+ *
+ * the version number: 1.2.3 value is 10203
+ */
+#define LIBXML_VERSION 21209
+
+/**
+ * LIBXML_VERSION_STRING:
+ *
+ * the version number string, 1.2.3 value is "10203"
+ */
+#define LIBXML_VERSION_STRING "21209"
+
+/**
+ * LIBXML_VERSION_EXTRA:
+ *
+ * extra version information, used to show a git commit description
+ */
+#define LIBXML_VERSION_EXTRA ""
+
+/**
+ * LIBXML_TEST_VERSION:
+ *
+ * Macro to check that the libxml version in use is compatible with
+ * the version the software has been compiled against
+ */
+#define LIBXML_TEST_VERSION xmlCheckVersion(21209);
+
+#ifndef VMS
+#if 0
+/**
+ * WITH_TRIO:
+ *
+ * defined if the trio support need to be configured in
+ */
+#define WITH_TRIO
+#else
+/**
+ * WITHOUT_TRIO:
+ *
+ * defined if the trio support should not be configured in
+ */
+#define WITHOUT_TRIO
+#endif
+#else /* VMS */
+/**
+ * WITH_TRIO:
+ *
+ * defined if the trio support need to be configured in
+ */
+#define WITH_TRIO 1
+#endif /* VMS */
+
+/**
+ * LIBXML_THREAD_ENABLED:
+ *
+ * Whether the thread support is configured in
+ */
+#if 1
+#define LIBXML_THREAD_ENABLED
+#endif
+
+/**
+ * LIBXML_THREAD_ALLOC_ENABLED:
+ *
+ * Whether the allocation hooks are per-thread
+ */
+#if 0
+#define LIBXML_THREAD_ALLOC_ENABLED
+#endif
+
+/**
+ * LIBXML_TREE_ENABLED:
+ *
+ * Whether the DOM like tree manipulation API support is configured in
+ */
+#if 1
+#define LIBXML_TREE_ENABLED
+#endif
+
+/**
+ * LIBXML_OUTPUT_ENABLED:
+ *
+ * Whether the serialization/saving support is configured in
+ */
+#if 1
+#define LIBXML_OUTPUT_ENABLED
+#endif
+
+/**
+ * LIBXML_PUSH_ENABLED:
+ *
+ * Whether the push parsing interfaces are configured in
+ */
+#if 1
+#define LIBXML_PUSH_ENABLED
+#endif
+
+/**
+ * LIBXML_READER_ENABLED:
+ *
+ * Whether the xmlReader parsing interface is configured in
+ */
+#if 1
+#define LIBXML_READER_ENABLED
+#endif
+
+/**
+ * LIBXML_PATTERN_ENABLED:
+ *
+ * Whether the xmlPattern node selection interface is configured in
+ */
+#if 1
+#define LIBXML_PATTERN_ENABLED
+#endif
+
+/**
+ * LIBXML_WRITER_ENABLED:
+ *
+ * Whether the xmlWriter saving interface is configured in
+ */
+#if 1
+#define LIBXML_WRITER_ENABLED
+#endif
+
+/**
+ * LIBXML_SAX1_ENABLED:
+ *
+ * Whether the older SAX1 interface is configured in
+ */
+#if 1
+#define LIBXML_SAX1_ENABLED
+#endif
+
+/**
+ * LIBXML_FTP_ENABLED:
+ *
+ * Whether the FTP support is configured in
+ */
+#if 0
+#define LIBXML_FTP_ENABLED
+#endif
+
+/**
+ * LIBXML_HTTP_ENABLED:
+ *
+ * Whether the HTTP support is configured in
+ */
+#if 1
+#define LIBXML_HTTP_ENABLED
+#endif
+
+/**
+ * LIBXML_VALID_ENABLED:
+ *
+ * Whether the DTD validation support is configured in
+ */
+#if 1
+#define LIBXML_VALID_ENABLED
+#endif
+
+/**
+ * LIBXML_HTML_ENABLED:
+ *
+ * Whether the HTML support is configured in
+ */
+#if 1
+#define LIBXML_HTML_ENABLED
+#endif
+
+/**
+ * LIBXML_LEGACY_ENABLED:
+ *
+ * Whether the deprecated APIs are compiled in for compatibility
+ */
+#if 0
+#define LIBXML_LEGACY_ENABLED
+#endif
+
+/**
+ * LIBXML_C14N_ENABLED:
+ *
+ * Whether the Canonicalization support is configured in
+ */
+#if 1
+#define LIBXML_C14N_ENABLED
+#endif
+
+/**
+ * LIBXML_CATALOG_ENABLED:
+ *
+ * Whether the Catalog support is configured in
+ */
+#if 1
+#define LIBXML_CATALOG_ENABLED
+#endif
+
+/**
+ * LIBXML_XPATH_ENABLED:
+ *
+ * Whether XPath is configured in
+ */
+#if 1
+#define LIBXML_XPATH_ENABLED
+#endif
+
+/**
+ * LIBXML_XPTR_ENABLED:
+ *
+ * Whether XPointer is configured in
+ */
+#if 1
+#define LIBXML_XPTR_ENABLED
+#endif
+
+/**
+ * LIBXML_XPTR_LOCS_ENABLED:
+ *
+ * Whether support for XPointer locations is configured in
+ */
+#if 0
+#define LIBXML_XPTR_LOCS_ENABLED
+#endif
+
+/**
+ * LIBXML_XINCLUDE_ENABLED:
+ *
+ * Whether XInclude is configured in
+ */
+#if 1
+#define LIBXML_XINCLUDE_ENABLED
+#endif
+
+/**
+ * LIBXML_ICONV_ENABLED:
+ *
+ * Whether iconv support is available
+ */
+#if 1
+#define LIBXML_ICONV_ENABLED
+#endif
+
+/**
+ * LIBXML_ICU_ENABLED:
+ *
+ * Whether icu support is available
+ */
+#if 0
+#define LIBXML_ICU_ENABLED
+#endif
+
+/**
+ * LIBXML_ISO8859X_ENABLED:
+ *
+ * Whether ISO-8859-* support is made available in case iconv is not
+ */
+#if 1
+#define LIBXML_ISO8859X_ENABLED
+#endif
+
+/**
+ * LIBXML_DEBUG_ENABLED:
+ *
+ * Whether Debugging module is configured in
+ */
+#if 1
+#define LIBXML_DEBUG_ENABLED
+#endif
+
+/**
+ * DEBUG_MEMORY_LOCATION:
+ *
+ * Whether the memory debugging is configured in
+ */
+#if 0
+#define DEBUG_MEMORY_LOCATION
+#endif
+
+/**
+ * LIBXML_DEBUG_RUNTIME:
+ *
+ * Removed
+ */
+#if 0
+#define LIBXML_DEBUG_RUNTIME
+#endif
+
+/**
+ * LIBXML_UNICODE_ENABLED:
+ *
+ * Whether the Unicode related interfaces are compiled in
+ */
+#if 1
+#define LIBXML_UNICODE_ENABLED
+#endif
+
+/**
+ * LIBXML_REGEXP_ENABLED:
+ *
+ * Whether the regular expressions interfaces are compiled in
+ */
+#if 1
+#define LIBXML_REGEXP_ENABLED
+#endif
+
+/**
+ * LIBXML_AUTOMATA_ENABLED:
+ *
+ * Whether the automata interfaces are compiled in
+ */
+#if 1
+#define LIBXML_AUTOMATA_ENABLED
+#endif
+
+/**
+ * LIBXML_EXPR_ENABLED:
+ *
+ * Whether the formal expressions interfaces are compiled in
+ *
+ * This code is unused and disabled unconditionally for now.
+ */
+#if 0
+#define LIBXML_EXPR_ENABLED
+#endif
+
+/**
+ * LIBXML_SCHEMAS_ENABLED:
+ *
+ * Whether the Schemas validation interfaces are compiled in
+ */
+#if 1
+#define LIBXML_SCHEMAS_ENABLED
+#endif
+
+/**
+ * LIBXML_SCHEMATRON_ENABLED:
+ *
+ * Whether the Schematron validation interfaces are compiled in
+ */
+#if 1
+#define LIBXML_SCHEMATRON_ENABLED
+#endif
+
+/**
+ * LIBXML_MODULES_ENABLED:
+ *
+ * Whether the module interfaces are compiled in
+ */
+#if 1
+#define LIBXML_MODULES_ENABLED
+/**
+ * LIBXML_MODULE_EXTENSION:
+ *
+ * the string suffix used by dynamic modules (usually shared libraries)
+ */
+#define LIBXML_MODULE_EXTENSION ".so" 
+#endif
+
+/**
+ * LIBXML_ZLIB_ENABLED:
+ *
+ * Whether the Zlib support is compiled in
+ */
+#if 1
+#define LIBXML_ZLIB_ENABLED
+#endif
+
+/**
+ * LIBXML_LZMA_ENABLED:
+ *
+ * Whether the Lzma support is compiled in
+ */
+#if 0
+#define LIBXML_LZMA_ENABLED
+#endif
+
+#ifdef __GNUC__
+/** DOC_DISABLE */
+
+#ifndef ATTRIBUTE_UNUSED
+# if ((__GNUC__ > 2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 7)))
+#  define ATTRIBUTE_UNUSED __attribute__((unused))
+# else
+#  define ATTRIBUTE_UNUSED
+# endif
+#endif
+
+#ifndef LIBXML_ATTR_ALLOC_SIZE
+# if (!defined(__clang__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 3))))
+#  define LIBXML_ATTR_ALLOC_SIZE(x) __attribute__((alloc_size(x)))
+# else
+#  define LIBXML_ATTR_ALLOC_SIZE(x)
+# endif
+#else
+# define LIBXML_ATTR_ALLOC_SIZE(x)
+#endif
+
+#ifndef LIBXML_ATTR_FORMAT
+# if ((__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)))
+#  define LIBXML_ATTR_FORMAT(fmt,args) __attribute__((__format__(__printf__,fmt,args)))
+# else
+#  define LIBXML_ATTR_FORMAT(fmt,args)
+# endif
+#else
+# define LIBXML_ATTR_FORMAT(fmt,args)
+#endif
+
+#ifndef XML_DEPRECATED
+#  if defined (IN_LIBXML) || (__GNUC__ * 100 + __GNUC_MINOR__ < 301)
+#    define XML_DEPRECATED
+/* Available since at least GCC 3.1 */
+#  else
+#    define XML_DEPRECATED __attribute__((deprecated))
+#  endif
+#endif
+
+#if defined(__clang__) || (__GNUC__ * 100 + __GNUC_MINOR__ >= 406)
+  #if defined(__clang__) || (__GNUC__ * 100 + __GNUC_MINOR__ >= 800)
+    #define XML_IGNORE_FPTR_CAST_WARNINGS \
+      _Pragma("GCC diagnostic push") \
+      _Pragma("GCC diagnostic ignored \"-Wpedantic\"") \
+      _Pragma("GCC diagnostic ignored \"-Wcast-function-type\"")
+  #else
+    #define XML_IGNORE_FPTR_CAST_WARNINGS \
+      _Pragma("GCC diagnostic push") \
+      _Pragma("GCC diagnostic ignored \"-Wpedantic\"")
+  #endif
+  #define XML_POP_WARNINGS \
+    _Pragma("GCC diagnostic pop")
+#else
+  #define XML_IGNORE_FPTR_CAST_WARNINGS
+  #define XML_POP_WARNINGS
+#endif
+
+#else /* ! __GNUC__ */
+#define ATTRIBUTE_UNUSED
+#define LIBXML_ATTR_ALLOC_SIZE(x)
+#define LIBXML_ATTR_FORMAT(fmt,args)
+#ifndef XML_DEPRECATED
+#  if defined (IN_LIBXML) || !defined (_MSC_VER)
+#    define XML_DEPRECATED
+/* Available since Visual Studio 2005 */
+#  elif defined (_MSC_VER) && (_MSC_VER >= 1400)
+#    define XML_DEPRECATED __declspec(deprecated)
+#  endif
+#endif
+#if defined (_MSC_VER) && (_MSC_VER >= 1400)
+#  define XML_IGNORE_FPTR_CAST_WARNINGS __pragma(warning(push))
+#else
+#  define XML_IGNORE_FPTR_CAST_WARNINGS
+#endif
+#ifndef XML_POP_WARNINGS
+#  if defined (_MSC_VER) && (_MSC_VER >= 1400)
+#    define XML_POP_WARNINGS __pragma(warning(pop))
+#  else
+#    define XML_POP_WARNINGS
+#  endif
+#endif
+#endif /* __GNUC__ */
+
+#define XML_NO_ATTR
+
+#ifdef LIBXML_THREAD_ENABLED
+  #define XML_DECLARE_GLOBAL(name, type, attrs) \
+    attrs XMLPUBFUN type *__##name(void);
+  #define XML_GLOBAL_MACRO(name) (*__##name())
+#else
+  #define XML_DECLARE_GLOBAL(name, type, attrs) \
+    attrs XMLPUBVAR type name;
+#endif
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif
+
+
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlwriter.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlwriter.h
new file mode 100644
index 00000000..339f2511
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xmlwriter.h
@@ -0,0 +1,488 @@
+/*
+ * Summary: text writing API for XML
+ * Description: text writing API for XML
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Alfred Mickautsch <alfred@mickautsch.de>
+ */
+
+#ifndef __XML_XMLWRITER_H__
+#define __XML_XMLWRITER_H__
+
+#include <libxml/xmlversion.h>
+
+#ifdef LIBXML_WRITER_ENABLED
+
+#include <stdarg.h>
+#include <libxml/xmlIO.h>
+#include <libxml/list.h>
+#include <libxml/xmlstring.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+    typedef struct _xmlTextWriter xmlTextWriter;
+    typedef xmlTextWriter *xmlTextWriterPtr;
+
+/*
+ * Constructors & Destructor
+ */
+    XMLPUBFUN xmlTextWriterPtr
+        xmlNewTextWriter(xmlOutputBufferPtr out);
+    XMLPUBFUN xmlTextWriterPtr
+        xmlNewTextWriterFilename(const char *uri, int compression);
+    XMLPUBFUN xmlTextWriterPtr
+        xmlNewTextWriterMemory(xmlBufferPtr buf, int compression);
+    XMLPUBFUN xmlTextWriterPtr
+        xmlNewTextWriterPushParser(xmlParserCtxtPtr ctxt, int compression);
+    XMLPUBFUN xmlTextWriterPtr
+        xmlNewTextWriterDoc(xmlDocPtr * doc, int compression);
+    XMLPUBFUN xmlTextWriterPtr
+        xmlNewTextWriterTree(xmlDocPtr doc, xmlNodePtr node,
+                             int compression);
+    XMLPUBFUN void xmlFreeTextWriter(xmlTextWriterPtr writer);
+
+/*
+ * Functions
+ */
+
+
+/*
+ * Document
+ */
+    XMLPUBFUN int
+        xmlTextWriterStartDocument(xmlTextWriterPtr writer,
+                                   const char *version,
+                                   const char *encoding,
+                                   const char *standalone);
+    XMLPUBFUN int xmlTextWriterEndDocument(xmlTextWriterPtr
+                                                   writer);
+
+/*
+ * Comments
+ */
+    XMLPUBFUN int xmlTextWriterStartComment(xmlTextWriterPtr
+                                                    writer);
+    XMLPUBFUN int xmlTextWriterEndComment(xmlTextWriterPtr writer);
+    XMLPUBFUN int
+        xmlTextWriterWriteFormatComment(xmlTextWriterPtr writer,
+                                        const char *format, ...)
+					LIBXML_ATTR_FORMAT(2,3);
+    XMLPUBFUN int
+        xmlTextWriterWriteVFormatComment(xmlTextWriterPtr writer,
+                                         const char *format,
+                                         va_list argptr)
+					 LIBXML_ATTR_FORMAT(2,0);
+    XMLPUBFUN int xmlTextWriterWriteComment(xmlTextWriterPtr
+                                                    writer,
+                                                    const xmlChar *
+                                                    content);
+
+/*
+ * Elements
+ */
+    XMLPUBFUN int
+        xmlTextWriterStartElement(xmlTextWriterPtr writer,
+                                  const xmlChar * name);
+    XMLPUBFUN int xmlTextWriterStartElementNS(xmlTextWriterPtr
+                                                      writer,
+                                                      const xmlChar *
+                                                      prefix,
+                                                      const xmlChar * name,
+                                                      const xmlChar *
+                                                      namespaceURI);
+    XMLPUBFUN int xmlTextWriterEndElement(xmlTextWriterPtr writer);
+    XMLPUBFUN int xmlTextWriterFullEndElement(xmlTextWriterPtr
+                                                      writer);
+
+/*
+ * Elements conveniency functions
+ */
+    XMLPUBFUN int
+        xmlTextWriterWriteFormatElement(xmlTextWriterPtr writer,
+                                        const xmlChar * name,
+                                        const char *format, ...)
+					LIBXML_ATTR_FORMAT(3,4);
+    XMLPUBFUN int
+        xmlTextWriterWriteVFormatElement(xmlTextWriterPtr writer,
+                                         const xmlChar * name,
+                                         const char *format,
+                                         va_list argptr)
+					 LIBXML_ATTR_FORMAT(3,0);
+    XMLPUBFUN int xmlTextWriterWriteElement(xmlTextWriterPtr
+                                                    writer,
+                                                    const xmlChar * name,
+                                                    const xmlChar *
+                                                    content);
+    XMLPUBFUN int
+        xmlTextWriterWriteFormatElementNS(xmlTextWriterPtr writer,
+                                          const xmlChar * prefix,
+                                          const xmlChar * name,
+                                          const xmlChar * namespaceURI,
+                                          const char *format, ...)
+					  LIBXML_ATTR_FORMAT(5,6);
+    XMLPUBFUN int
+        xmlTextWriterWriteVFormatElementNS(xmlTextWriterPtr writer,
+                                           const xmlChar * prefix,
+                                           const xmlChar * name,
+                                           const xmlChar * namespaceURI,
+                                           const char *format,
+                                           va_list argptr)
+					   LIBXML_ATTR_FORMAT(5,0);
+    XMLPUBFUN int xmlTextWriterWriteElementNS(xmlTextWriterPtr
+                                                      writer,
+                                                      const xmlChar *
+                                                      prefix,
+                                                      const xmlChar * name,
+                                                      const xmlChar *
+                                                      namespaceURI,
+                                                      const xmlChar *
+                                                      content);
+
+/*
+ * Text
+ */
+    XMLPUBFUN int
+        xmlTextWriterWriteFormatRaw(xmlTextWriterPtr writer,
+                                    const char *format, ...)
+				    LIBXML_ATTR_FORMAT(2,3);
+    XMLPUBFUN int
+        xmlTextWriterWriteVFormatRaw(xmlTextWriterPtr writer,
+                                     const char *format, va_list argptr)
+				     LIBXML_ATTR_FORMAT(2,0);
+    XMLPUBFUN int
+        xmlTextWriterWriteRawLen(xmlTextWriterPtr writer,
+                                 const xmlChar * content, int len);
+    XMLPUBFUN int
+        xmlTextWriterWriteRaw(xmlTextWriterPtr writer,
+                              const xmlChar * content);
+    XMLPUBFUN int xmlTextWriterWriteFormatString(xmlTextWriterPtr
+                                                         writer,
+                                                         const char
+                                                         *format, ...)
+							 LIBXML_ATTR_FORMAT(2,3);
+    XMLPUBFUN int xmlTextWriterWriteVFormatString(xmlTextWriterPtr
+                                                          writer,
+                                                          const char
+                                                          *format,
+                                                          va_list argptr)
+							  LIBXML_ATTR_FORMAT(2,0);
+    XMLPUBFUN int xmlTextWriterWriteString(xmlTextWriterPtr writer,
+                                                   const xmlChar *
+                                                   content);
+    XMLPUBFUN int xmlTextWriterWriteBase64(xmlTextWriterPtr writer,
+                                                   const char *data,
+                                                   int start, int len);
+    XMLPUBFUN int xmlTextWriterWriteBinHex(xmlTextWriterPtr writer,
+                                                   const char *data,
+                                                   int start, int len);
+
+/*
+ * Attributes
+ */
+    XMLPUBFUN int
+        xmlTextWriterStartAttribute(xmlTextWriterPtr writer,
+                                    const xmlChar * name);
+    XMLPUBFUN int xmlTextWriterStartAttributeNS(xmlTextWriterPtr
+                                                        writer,
+                                                        const xmlChar *
+                                                        prefix,
+                                                        const xmlChar *
+                                                        name,
+                                                        const xmlChar *
+                                                        namespaceURI);
+    XMLPUBFUN int xmlTextWriterEndAttribute(xmlTextWriterPtr
+                                                    writer);
+
+/*
+ * Attributes conveniency functions
+ */
+    XMLPUBFUN int
+        xmlTextWriterWriteFormatAttribute(xmlTextWriterPtr writer,
+                                          const xmlChar * name,
+                                          const char *format, ...)
+					  LIBXML_ATTR_FORMAT(3,4);
+    XMLPUBFUN int
+        xmlTextWriterWriteVFormatAttribute(xmlTextWriterPtr writer,
+                                           const xmlChar * name,
+                                           const char *format,
+                                           va_list argptr)
+					   LIBXML_ATTR_FORMAT(3,0);
+    XMLPUBFUN int xmlTextWriterWriteAttribute(xmlTextWriterPtr
+                                                      writer,
+                                                      const xmlChar * name,
+                                                      const xmlChar *
+                                                      content);
+    XMLPUBFUN int
+        xmlTextWriterWriteFormatAttributeNS(xmlTextWriterPtr writer,
+                                            const xmlChar * prefix,
+                                            const xmlChar * name,
+                                            const xmlChar * namespaceURI,
+                                            const char *format, ...)
+					    LIBXML_ATTR_FORMAT(5,6);
+    XMLPUBFUN int
+        xmlTextWriterWriteVFormatAttributeNS(xmlTextWriterPtr writer,
+                                             const xmlChar * prefix,
+                                             const xmlChar * name,
+                                             const xmlChar * namespaceURI,
+                                             const char *format,
+                                             va_list argptr)
+					     LIBXML_ATTR_FORMAT(5,0);
+    XMLPUBFUN int xmlTextWriterWriteAttributeNS(xmlTextWriterPtr
+                                                        writer,
+                                                        const xmlChar *
+                                                        prefix,
+                                                        const xmlChar *
+                                                        name,
+                                                        const xmlChar *
+                                                        namespaceURI,
+                                                        const xmlChar *
+                                                        content);
+
+/*
+ * PI's
+ */
+    XMLPUBFUN int
+        xmlTextWriterStartPI(xmlTextWriterPtr writer,
+                             const xmlChar * target);
+    XMLPUBFUN int xmlTextWriterEndPI(xmlTextWriterPtr writer);
+
+/*
+ * PI conveniency functions
+ */
+    XMLPUBFUN int
+        xmlTextWriterWriteFormatPI(xmlTextWriterPtr writer,
+                                   const xmlChar * target,
+                                   const char *format, ...)
+				   LIBXML_ATTR_FORMAT(3,4);
+    XMLPUBFUN int
+        xmlTextWriterWriteVFormatPI(xmlTextWriterPtr writer,
+                                    const xmlChar * target,
+                                    const char *format, va_list argptr)
+				    LIBXML_ATTR_FORMAT(3,0);
+    XMLPUBFUN int
+        xmlTextWriterWritePI(xmlTextWriterPtr writer,
+                             const xmlChar * target,
+                             const xmlChar * content);
+
+/**
+ * xmlTextWriterWriteProcessingInstruction:
+ *
+ * This macro maps to xmlTextWriterWritePI
+ */
+#define xmlTextWriterWriteProcessingInstruction xmlTextWriterWritePI
+
+/*
+ * CDATA
+ */
+    XMLPUBFUN int xmlTextWriterStartCDATA(xmlTextWriterPtr writer);
+    XMLPUBFUN int xmlTextWriterEndCDATA(xmlTextWriterPtr writer);
+
+/*
+ * CDATA conveniency functions
+ */
+    XMLPUBFUN int
+        xmlTextWriterWriteFormatCDATA(xmlTextWriterPtr writer,
+                                      const char *format, ...)
+				      LIBXML_ATTR_FORMAT(2,3);
+    XMLPUBFUN int
+        xmlTextWriterWriteVFormatCDATA(xmlTextWriterPtr writer,
+                                       const char *format, va_list argptr)
+				       LIBXML_ATTR_FORMAT(2,0);
+    XMLPUBFUN int
+        xmlTextWriterWriteCDATA(xmlTextWriterPtr writer,
+                                const xmlChar * content);
+
+/*
+ * DTD
+ */
+    XMLPUBFUN int
+        xmlTextWriterStartDTD(xmlTextWriterPtr writer,
+                              const xmlChar * name,
+                              const xmlChar * pubid,
+                              const xmlChar * sysid);
+    XMLPUBFUN int xmlTextWriterEndDTD(xmlTextWriterPtr writer);
+
+/*
+ * DTD conveniency functions
+ */
+    XMLPUBFUN int
+        xmlTextWriterWriteFormatDTD(xmlTextWriterPtr writer,
+                                    const xmlChar * name,
+                                    const xmlChar * pubid,
+                                    const xmlChar * sysid,
+                                    const char *format, ...)
+				    LIBXML_ATTR_FORMAT(5,6);
+    XMLPUBFUN int
+        xmlTextWriterWriteVFormatDTD(xmlTextWriterPtr writer,
+                                     const xmlChar * name,
+                                     const xmlChar * pubid,
+                                     const xmlChar * sysid,
+                                     const char *format, va_list argptr)
+				     LIBXML_ATTR_FORMAT(5,0);
+    XMLPUBFUN int
+        xmlTextWriterWriteDTD(xmlTextWriterPtr writer,
+                              const xmlChar * name,
+                              const xmlChar * pubid,
+                              const xmlChar * sysid,
+                              const xmlChar * subset);
+
+/**
+ * xmlTextWriterWriteDocType:
+ *
+ * this macro maps to xmlTextWriterWriteDTD
+ */
+#define xmlTextWriterWriteDocType xmlTextWriterWriteDTD
+
+/*
+ * DTD element definition
+ */
+    XMLPUBFUN int
+        xmlTextWriterStartDTDElement(xmlTextWriterPtr writer,
+                                     const xmlChar * name);
+    XMLPUBFUN int xmlTextWriterEndDTDElement(xmlTextWriterPtr
+                                                     writer);
+
+/*
+ * DTD element definition conveniency functions
+ */
+    XMLPUBFUN int
+        xmlTextWriterWriteFormatDTDElement(xmlTextWriterPtr writer,
+                                           const xmlChar * name,
+                                           const char *format, ...)
+					   LIBXML_ATTR_FORMAT(3,4);
+    XMLPUBFUN int
+        xmlTextWriterWriteVFormatDTDElement(xmlTextWriterPtr writer,
+                                            const xmlChar * name,
+                                            const char *format,
+                                            va_list argptr)
+					    LIBXML_ATTR_FORMAT(3,0);
+    XMLPUBFUN int xmlTextWriterWriteDTDElement(xmlTextWriterPtr
+                                                       writer,
+                                                       const xmlChar *
+                                                       name,
+                                                       const xmlChar *
+                                                       content);
+
+/*
+ * DTD attribute list definition
+ */
+    XMLPUBFUN int
+        xmlTextWriterStartDTDAttlist(xmlTextWriterPtr writer,
+                                     const xmlChar * name);
+    XMLPUBFUN int xmlTextWriterEndDTDAttlist(xmlTextWriterPtr
+                                                     writer);
+
+/*
+ * DTD attribute list definition conveniency functions
+ */
+    XMLPUBFUN int
+        xmlTextWriterWriteFormatDTDAttlist(xmlTextWriterPtr writer,
+                                           const xmlChar * name,
+                                           const char *format, ...)
+					   LIBXML_ATTR_FORMAT(3,4);
+    XMLPUBFUN int
+        xmlTextWriterWriteVFormatDTDAttlist(xmlTextWriterPtr writer,
+                                            const xmlChar * name,
+                                            const char *format,
+                                            va_list argptr)
+					    LIBXML_ATTR_FORMAT(3,0);
+    XMLPUBFUN int xmlTextWriterWriteDTDAttlist(xmlTextWriterPtr
+                                                       writer,
+                                                       const xmlChar *
+                                                       name,
+                                                       const xmlChar *
+                                                       content);
+
+/*
+ * DTD entity definition
+ */
+    XMLPUBFUN int
+        xmlTextWriterStartDTDEntity(xmlTextWriterPtr writer,
+                                    int pe, const xmlChar * name);
+    XMLPUBFUN int xmlTextWriterEndDTDEntity(xmlTextWriterPtr
+                                                    writer);
+
+/*
+ * DTD entity definition conveniency functions
+ */
+    XMLPUBFUN int
+        xmlTextWriterWriteFormatDTDInternalEntity(xmlTextWriterPtr writer,
+                                                  int pe,
+                                                  const xmlChar * name,
+                                                  const char *format, ...)
+						  LIBXML_ATTR_FORMAT(4,5);
+    XMLPUBFUN int
+        xmlTextWriterWriteVFormatDTDInternalEntity(xmlTextWriterPtr writer,
+                                                   int pe,
+                                                   const xmlChar * name,
+                                                   const char *format,
+                                                   va_list argptr)
+						   LIBXML_ATTR_FORMAT(4,0);
+    XMLPUBFUN int
+        xmlTextWriterWriteDTDInternalEntity(xmlTextWriterPtr writer,
+                                            int pe,
+                                            const xmlChar * name,
+                                            const xmlChar * content);
+    XMLPUBFUN int
+        xmlTextWriterWriteDTDExternalEntity(xmlTextWriterPtr writer,
+                                            int pe,
+                                            const xmlChar * name,
+                                            const xmlChar * pubid,
+                                            const xmlChar * sysid,
+                                            const xmlChar * ndataid);
+    XMLPUBFUN int
+        xmlTextWriterWriteDTDExternalEntityContents(xmlTextWriterPtr
+                                                    writer,
+                                                    const xmlChar * pubid,
+                                                    const xmlChar * sysid,
+                                                    const xmlChar *
+                                                    ndataid);
+    XMLPUBFUN int xmlTextWriterWriteDTDEntity(xmlTextWriterPtr
+                                                      writer, int pe,
+                                                      const xmlChar * name,
+                                                      const xmlChar *
+                                                      pubid,
+                                                      const xmlChar *
+                                                      sysid,
+                                                      const xmlChar *
+                                                      ndataid,
+                                                      const xmlChar *
+                                                      content);
+
+/*
+ * DTD notation definition
+ */
+    XMLPUBFUN int
+        xmlTextWriterWriteDTDNotation(xmlTextWriterPtr writer,
+                                      const xmlChar * name,
+                                      const xmlChar * pubid,
+                                      const xmlChar * sysid);
+
+/*
+ * Indentation
+ */
+    XMLPUBFUN int
+        xmlTextWriterSetIndent(xmlTextWriterPtr writer, int indent);
+    XMLPUBFUN int
+        xmlTextWriterSetIndentString(xmlTextWriterPtr writer,
+                                     const xmlChar * str);
+
+    XMLPUBFUN int
+        xmlTextWriterSetQuoteChar(xmlTextWriterPtr writer, xmlChar quotechar);
+
+
+/*
+ * misc
+ */
+    XMLPUBFUN int xmlTextWriterFlush(xmlTextWriterPtr writer);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBXML_WRITER_ENABLED */
+
+#endif                          /* __XML_XMLWRITER_H__ */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xpath.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xpath.h
new file mode 100644
index 00000000..6dae0780
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xpath.h
@@ -0,0 +1,575 @@
+/*
+ * Summary: XML Path Language implementation
+ * Description: API for the XML Path Language implementation
+ *
+ * XML Path Language implementation
+ * XPath is a language for addressing parts of an XML document,
+ * designed to be used by both XSLT and XPointer
+ *     http://www.w3.org/TR/xpath
+ *
+ * Implements
+ * W3C Recommendation 16 November 1999
+ *     http://www.w3.org/TR/1999/REC-xpath-19991116
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XPATH_H__
+#define __XML_XPATH_H__
+
+#include <libxml/xmlversion.h>
+
+#ifdef LIBXML_XPATH_ENABLED
+
+#include <libxml/xmlerror.h>
+#include <libxml/tree.h>
+#include <libxml/hash.h>
+#endif /* LIBXML_XPATH_ENABLED */
+
+#if defined(LIBXML_XPATH_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED)
+#ifdef __cplusplus
+extern "C" {
+#endif
+#endif /* LIBXML_XPATH_ENABLED or LIBXML_SCHEMAS_ENABLED */
+
+#ifdef LIBXML_XPATH_ENABLED
+
+typedef struct _xmlXPathContext xmlXPathContext;
+typedef xmlXPathContext *xmlXPathContextPtr;
+typedef struct _xmlXPathParserContext xmlXPathParserContext;
+typedef xmlXPathParserContext *xmlXPathParserContextPtr;
+
+/**
+ * The set of XPath error codes.
+ */
+
+typedef enum {
+    XPATH_EXPRESSION_OK = 0,
+    XPATH_NUMBER_ERROR,
+    XPATH_UNFINISHED_LITERAL_ERROR,
+    XPATH_START_LITERAL_ERROR,
+    XPATH_VARIABLE_REF_ERROR,
+    XPATH_UNDEF_VARIABLE_ERROR,
+    XPATH_INVALID_PREDICATE_ERROR,
+    XPATH_EXPR_ERROR,
+    XPATH_UNCLOSED_ERROR,
+    XPATH_UNKNOWN_FUNC_ERROR,
+    XPATH_INVALID_OPERAND,
+    XPATH_INVALID_TYPE,
+    XPATH_INVALID_ARITY,
+    XPATH_INVALID_CTXT_SIZE,
+    XPATH_INVALID_CTXT_POSITION,
+    XPATH_MEMORY_ERROR,
+    XPTR_SYNTAX_ERROR,
+    XPTR_RESOURCE_ERROR,
+    XPTR_SUB_RESOURCE_ERROR,
+    XPATH_UNDEF_PREFIX_ERROR,
+    XPATH_ENCODING_ERROR,
+    XPATH_INVALID_CHAR_ERROR,
+    XPATH_INVALID_CTXT,
+    XPATH_STACK_ERROR,
+    XPATH_FORBID_VARIABLE_ERROR,
+    XPATH_OP_LIMIT_EXCEEDED,
+    XPATH_RECURSION_LIMIT_EXCEEDED
+} xmlXPathError;
+
+/*
+ * A node-set (an unordered collection of nodes without duplicates).
+ */
+typedef struct _xmlNodeSet xmlNodeSet;
+typedef xmlNodeSet *xmlNodeSetPtr;
+struct _xmlNodeSet {
+    int nodeNr;			/* number of nodes in the set */
+    int nodeMax;		/* size of the array as allocated */
+    xmlNodePtr *nodeTab;	/* array of nodes in no particular order */
+    /* @@ with_ns to check whether namespace nodes should be looked at @@ */
+};
+
+/*
+ * An expression is evaluated to yield an object, which
+ * has one of the following four basic types:
+ *   - node-set
+ *   - boolean
+ *   - number
+ *   - string
+ *
+ * @@ XPointer will add more types !
+ */
+
+typedef enum {
+    XPATH_UNDEFINED = 0,
+    XPATH_NODESET = 1,
+    XPATH_BOOLEAN = 2,
+    XPATH_NUMBER = 3,
+    XPATH_STRING = 4,
+#ifdef LIBXML_XPTR_LOCS_ENABLED
+    XPATH_POINT = 5,
+    XPATH_RANGE = 6,
+    XPATH_LOCATIONSET = 7,
+#endif
+    XPATH_USERS = 8,
+    XPATH_XSLT_TREE = 9  /* An XSLT value tree, non modifiable */
+} xmlXPathObjectType;
+
+#ifndef LIBXML_XPTR_LOCS_ENABLED
+/** DOC_DISABLE */
+#define XPATH_POINT 5
+#define XPATH_RANGE 6
+#define XPATH_LOCATIONSET 7
+/** DOC_ENABLE */
+#endif
+
+typedef struct _xmlXPathObject xmlXPathObject;
+typedef xmlXPathObject *xmlXPathObjectPtr;
+struct _xmlXPathObject {
+    xmlXPathObjectType type;
+    xmlNodeSetPtr nodesetval;
+    int boolval;
+    double floatval;
+    xmlChar *stringval;
+    void *user;
+    int index;
+    void *user2;
+    int index2;
+};
+
+/**
+ * xmlXPathConvertFunc:
+ * @obj:  an XPath object
+ * @type:  the number of the target type
+ *
+ * A conversion function is associated to a type and used to cast
+ * the new type to primitive values.
+ *
+ * Returns -1 in case of error, 0 otherwise
+ */
+typedef int (*xmlXPathConvertFunc) (xmlXPathObjectPtr obj, int type);
+
+/*
+ * Extra type: a name and a conversion function.
+ */
+
+typedef struct _xmlXPathType xmlXPathType;
+typedef xmlXPathType *xmlXPathTypePtr;
+struct _xmlXPathType {
+    const xmlChar         *name;		/* the type name */
+    xmlXPathConvertFunc func;		/* the conversion function */
+};
+
+/*
+ * Extra variable: a name and a value.
+ */
+
+typedef struct _xmlXPathVariable xmlXPathVariable;
+typedef xmlXPathVariable *xmlXPathVariablePtr;
+struct _xmlXPathVariable {
+    const xmlChar       *name;		/* the variable name */
+    xmlXPathObjectPtr value;		/* the value */
+};
+
+/**
+ * xmlXPathEvalFunc:
+ * @ctxt: an XPath parser context
+ * @nargs: the number of arguments passed to the function
+ *
+ * An XPath evaluation function, the parameters are on the XPath context stack.
+ */
+
+typedef void (*xmlXPathEvalFunc)(xmlXPathParserContextPtr ctxt,
+	                         int nargs);
+
+/*
+ * Extra function: a name and a evaluation function.
+ */
+
+typedef struct _xmlXPathFunct xmlXPathFunct;
+typedef xmlXPathFunct *xmlXPathFuncPtr;
+struct _xmlXPathFunct {
+    const xmlChar      *name;		/* the function name */
+    xmlXPathEvalFunc func;		/* the evaluation function */
+};
+
+/**
+ * xmlXPathAxisFunc:
+ * @ctxt:  the XPath interpreter context
+ * @cur:  the previous node being explored on that axis
+ *
+ * An axis traversal function. To traverse an axis, the engine calls
+ * the first time with cur == NULL and repeat until the function returns
+ * NULL indicating the end of the axis traversal.
+ *
+ * Returns the next node in that axis or NULL if at the end of the axis.
+ */
+
+typedef xmlXPathObjectPtr (*xmlXPathAxisFunc) (xmlXPathParserContextPtr ctxt,
+				 xmlXPathObjectPtr cur);
+
+/*
+ * Extra axis: a name and an axis function.
+ */
+
+typedef struct _xmlXPathAxis xmlXPathAxis;
+typedef xmlXPathAxis *xmlXPathAxisPtr;
+struct _xmlXPathAxis {
+    const xmlChar      *name;		/* the axis name */
+    xmlXPathAxisFunc func;		/* the search function */
+};
+
+/**
+ * xmlXPathFunction:
+ * @ctxt:  the XPath interprestation context
+ * @nargs:  the number of arguments
+ *
+ * An XPath function.
+ * The arguments (if any) are popped out from the context stack
+ * and the result is pushed on the stack.
+ */
+
+typedef void (*xmlXPathFunction) (xmlXPathParserContextPtr ctxt, int nargs);
+
+/*
+ * Function and Variable Lookup.
+ */
+
+/**
+ * xmlXPathVariableLookupFunc:
+ * @ctxt:  an XPath context
+ * @name:  name of the variable
+ * @ns_uri:  the namespace name hosting this variable
+ *
+ * Prototype for callbacks used to plug variable lookup in the XPath
+ * engine.
+ *
+ * Returns the XPath object value or NULL if not found.
+ */
+typedef xmlXPathObjectPtr (*xmlXPathVariableLookupFunc) (void *ctxt,
+                                         const xmlChar *name,
+                                         const xmlChar *ns_uri);
+
+/**
+ * xmlXPathFuncLookupFunc:
+ * @ctxt:  an XPath context
+ * @name:  name of the function
+ * @ns_uri:  the namespace name hosting this function
+ *
+ * Prototype for callbacks used to plug function lookup in the XPath
+ * engine.
+ *
+ * Returns the XPath function or NULL if not found.
+ */
+typedef xmlXPathFunction (*xmlXPathFuncLookupFunc) (void *ctxt,
+					 const xmlChar *name,
+					 const xmlChar *ns_uri);
+
+/**
+ * xmlXPathFlags:
+ * Flags for XPath engine compilation and runtime
+ */
+/**
+ * XML_XPATH_CHECKNS:
+ *
+ * check namespaces at compilation
+ */
+#define XML_XPATH_CHECKNS (1<<0)
+/**
+ * XML_XPATH_NOVAR:
+ *
+ * forbid variables in expression
+ */
+#define XML_XPATH_NOVAR	  (1<<1)
+
+/**
+ * xmlXPathContext:
+ *
+ * Expression evaluation occurs with respect to a context.
+ * he context consists of:
+ *    - a node (the context node)
+ *    - a node list (the context node list)
+ *    - a set of variable bindings
+ *    - a function library
+ *    - the set of namespace declarations in scope for the expression
+ * Following the switch to hash tables, this need to be trimmed up at
+ * the next binary incompatible release.
+ * The node may be modified when the context is passed to libxml2
+ * for an XPath evaluation so you may need to initialize it again
+ * before the next call.
+ */
+
+struct _xmlXPathContext {
+    xmlDocPtr doc;			/* The current document */
+    xmlNodePtr node;			/* The current node */
+
+    int nb_variables_unused;		/* unused (hash table) */
+    int max_variables_unused;		/* unused (hash table) */
+    xmlHashTablePtr varHash;		/* Hash table of defined variables */
+
+    int nb_types;			/* number of defined types */
+    int max_types;			/* max number of types */
+    xmlXPathTypePtr types;		/* Array of defined types */
+
+    int nb_funcs_unused;		/* unused (hash table) */
+    int max_funcs_unused;		/* unused (hash table) */
+    xmlHashTablePtr funcHash;		/* Hash table of defined funcs */
+
+    int nb_axis;			/* number of defined axis */
+    int max_axis;			/* max number of axis */
+    xmlXPathAxisPtr axis;		/* Array of defined axis */
+
+    /* the namespace nodes of the context node */
+    xmlNsPtr *namespaces;		/* Array of namespaces */
+    int nsNr;				/* number of namespace in scope */
+    void *user;				/* function to free */
+
+    /* extra variables */
+    int contextSize;			/* the context size */
+    int proximityPosition;		/* the proximity position */
+
+    /* extra stuff for XPointer */
+    int xptr;				/* is this an XPointer context? */
+    xmlNodePtr here;			/* for here() */
+    xmlNodePtr origin;			/* for origin() */
+
+    /* the set of namespace declarations in scope for the expression */
+    xmlHashTablePtr nsHash;		/* The namespaces hash table */
+    xmlXPathVariableLookupFunc varLookupFunc;/* variable lookup func */
+    void *varLookupData;		/* variable lookup data */
+
+    /* Possibility to link in an extra item */
+    void *extra;                        /* needed for XSLT */
+
+    /* The function name and URI when calling a function */
+    const xmlChar *function;
+    const xmlChar *functionURI;
+
+    /* function lookup function and data */
+    xmlXPathFuncLookupFunc funcLookupFunc;/* function lookup func */
+    void *funcLookupData;		/* function lookup data */
+
+    /* temporary namespace lists kept for walking the namespace axis */
+    xmlNsPtr *tmpNsList;		/* Array of namespaces */
+    int tmpNsNr;			/* number of namespaces in scope */
+
+    /* error reporting mechanism */
+    void *userData;                     /* user specific data block */
+    xmlStructuredErrorFunc error;       /* the callback in case of errors */
+    xmlError lastError;			/* the last error */
+    xmlNodePtr debugNode;		/* the source node XSLT */
+
+    /* dictionary */
+    xmlDictPtr dict;			/* dictionary if any */
+
+    int flags;				/* flags to control compilation */
+
+    /* Cache for reusal of XPath objects */
+    void *cache;
+
+    /* Resource limits */
+    unsigned long opLimit;
+    unsigned long opCount;
+    int depth;
+};
+
+/*
+ * The structure of a compiled expression form is not public.
+ */
+
+typedef struct _xmlXPathCompExpr xmlXPathCompExpr;
+typedef xmlXPathCompExpr *xmlXPathCompExprPtr;
+
+/**
+ * xmlXPathParserContext:
+ *
+ * An XPath parser context. It contains pure parsing information,
+ * an xmlXPathContext, and the stack of objects.
+ */
+struct _xmlXPathParserContext {
+    const xmlChar *cur;			/* the current char being parsed */
+    const xmlChar *base;			/* the full expression */
+
+    int error;				/* error code */
+
+    xmlXPathContextPtr  context;	/* the evaluation context */
+    xmlXPathObjectPtr     value;	/* the current value */
+    int                 valueNr;	/* number of values stacked */
+    int                valueMax;	/* max number of values stacked */
+    xmlXPathObjectPtr *valueTab;	/* stack of values */
+
+    xmlXPathCompExprPtr comp;		/* the precompiled expression */
+    int xptr;				/* it this an XPointer expression */
+    xmlNodePtr         ancestor;	/* used for walking preceding axis */
+
+    int              valueFrame;        /* always zero for compatibility */
+};
+
+/************************************************************************
+ *									*
+ *			Public API					*
+ *									*
+ ************************************************************************/
+
+/**
+ * Objects and Nodesets handling
+ */
+
+XMLPUBVAR double xmlXPathNAN;
+XMLPUBVAR double xmlXPathPINF;
+XMLPUBVAR double xmlXPathNINF;
+
+/* These macros may later turn into functions */
+/**
+ * xmlXPathNodeSetGetLength:
+ * @ns:  a node-set
+ *
+ * Implement a functionality similar to the DOM NodeList.length.
+ *
+ * Returns the number of nodes in the node-set.
+ */
+#define xmlXPathNodeSetGetLength(ns) ((ns) ? (ns)->nodeNr : 0)
+/**
+ * xmlXPathNodeSetItem:
+ * @ns:  a node-set
+ * @index:  index of a node in the set
+ *
+ * Implements a functionality similar to the DOM NodeList.item().
+ *
+ * Returns the xmlNodePtr at the given @index in @ns or NULL if
+ *         @index is out of range (0 to length-1)
+ */
+#define xmlXPathNodeSetItem(ns, index)				\
+		((((ns) != NULL) &&				\
+		  ((index) >= 0) && ((index) < (ns)->nodeNr)) ?	\
+		 (ns)->nodeTab[(index)]				\
+		 : NULL)
+/**
+ * xmlXPathNodeSetIsEmpty:
+ * @ns: a node-set
+ *
+ * Checks whether @ns is empty or not.
+ *
+ * Returns %TRUE if @ns is an empty node-set.
+ */
+#define xmlXPathNodeSetIsEmpty(ns)                                      \
+    (((ns) == NULL) || ((ns)->nodeNr == 0) || ((ns)->nodeTab == NULL))
+
+
+XMLPUBFUN void
+		    xmlXPathFreeObject		(xmlXPathObjectPtr obj);
+XMLPUBFUN xmlNodeSetPtr
+		    xmlXPathNodeSetCreate	(xmlNodePtr val);
+XMLPUBFUN void
+		    xmlXPathFreeNodeSetList	(xmlXPathObjectPtr obj);
+XMLPUBFUN void
+		    xmlXPathFreeNodeSet		(xmlNodeSetPtr obj);
+XMLPUBFUN xmlXPathObjectPtr
+		    xmlXPathObjectCopy		(xmlXPathObjectPtr val);
+XMLPUBFUN int
+		    xmlXPathCmpNodes		(xmlNodePtr node1,
+						 xmlNodePtr node2);
+/**
+ * Conversion functions to basic types.
+ */
+XMLPUBFUN int
+		    xmlXPathCastNumberToBoolean	(double val);
+XMLPUBFUN int
+		    xmlXPathCastStringToBoolean	(const xmlChar * val);
+XMLPUBFUN int
+		    xmlXPathCastNodeSetToBoolean(xmlNodeSetPtr ns);
+XMLPUBFUN int
+		    xmlXPathCastToBoolean	(xmlXPathObjectPtr val);
+
+XMLPUBFUN double
+		    xmlXPathCastBooleanToNumber	(int val);
+XMLPUBFUN double
+		    xmlXPathCastStringToNumber	(const xmlChar * val);
+XMLPUBFUN double
+		    xmlXPathCastNodeToNumber	(xmlNodePtr node);
+XMLPUBFUN double
+		    xmlXPathCastNodeSetToNumber	(xmlNodeSetPtr ns);
+XMLPUBFUN double
+		    xmlXPathCastToNumber	(xmlXPathObjectPtr val);
+
+XMLPUBFUN xmlChar *
+		    xmlXPathCastBooleanToString	(int val);
+XMLPUBFUN xmlChar *
+		    xmlXPathCastNumberToString	(double val);
+XMLPUBFUN xmlChar *
+		    xmlXPathCastNodeToString	(xmlNodePtr node);
+XMLPUBFUN xmlChar *
+		    xmlXPathCastNodeSetToString	(xmlNodeSetPtr ns);
+XMLPUBFUN xmlChar *
+		    xmlXPathCastToString	(xmlXPathObjectPtr val);
+
+XMLPUBFUN xmlXPathObjectPtr
+		    xmlXPathConvertBoolean	(xmlXPathObjectPtr val);
+XMLPUBFUN xmlXPathObjectPtr
+		    xmlXPathConvertNumber	(xmlXPathObjectPtr val);
+XMLPUBFUN xmlXPathObjectPtr
+		    xmlXPathConvertString	(xmlXPathObjectPtr val);
+
+/**
+ * Context handling.
+ */
+XMLPUBFUN xmlXPathContextPtr
+		    xmlXPathNewContext		(xmlDocPtr doc);
+XMLPUBFUN void
+		    xmlXPathFreeContext		(xmlXPathContextPtr ctxt);
+XMLPUBFUN int
+		    xmlXPathContextSetCache(xmlXPathContextPtr ctxt,
+				            int active,
+					    int value,
+					    int options);
+/**
+ * Evaluation functions.
+ */
+XMLPUBFUN long
+		    xmlXPathOrderDocElems	(xmlDocPtr doc);
+XMLPUBFUN int
+		    xmlXPathSetContextNode	(xmlNodePtr node,
+						 xmlXPathContextPtr ctx);
+XMLPUBFUN xmlXPathObjectPtr
+		    xmlXPathNodeEval		(xmlNodePtr node,
+						 const xmlChar *str,
+						 xmlXPathContextPtr ctx);
+XMLPUBFUN xmlXPathObjectPtr
+		    xmlXPathEval		(const xmlChar *str,
+						 xmlXPathContextPtr ctx);
+XMLPUBFUN xmlXPathObjectPtr
+		    xmlXPathEvalExpression	(const xmlChar *str,
+						 xmlXPathContextPtr ctxt);
+XMLPUBFUN int
+		    xmlXPathEvalPredicate	(xmlXPathContextPtr ctxt,
+						 xmlXPathObjectPtr res);
+/**
+ * Separate compilation/evaluation entry points.
+ */
+XMLPUBFUN xmlXPathCompExprPtr
+		    xmlXPathCompile		(const xmlChar *str);
+XMLPUBFUN xmlXPathCompExprPtr
+		    xmlXPathCtxtCompile		(xmlXPathContextPtr ctxt,
+						 const xmlChar *str);
+XMLPUBFUN xmlXPathObjectPtr
+		    xmlXPathCompiledEval	(xmlXPathCompExprPtr comp,
+						 xmlXPathContextPtr ctx);
+XMLPUBFUN int
+		    xmlXPathCompiledEvalToBoolean(xmlXPathCompExprPtr comp,
+						 xmlXPathContextPtr ctxt);
+XMLPUBFUN void
+		    xmlXPathFreeCompExpr	(xmlXPathCompExprPtr comp);
+#endif /* LIBXML_XPATH_ENABLED */
+#if defined(LIBXML_XPATH_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED)
+XML_DEPRECATED
+XMLPUBFUN void
+		    xmlXPathInit		(void);
+XMLPUBFUN int
+		xmlXPathIsNaN	(double val);
+XMLPUBFUN int
+		xmlXPathIsInf	(double val);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBXML_XPATH_ENABLED or LIBXML_SCHEMAS_ENABLED*/
+#endif /* ! __XML_XPATH_H__ */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xpathInternals.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xpathInternals.h
new file mode 100644
index 00000000..d1c90dff
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xpathInternals.h
@@ -0,0 +1,633 @@
+/*
+ * Summary: internal interfaces for XML Path Language implementation
+ * Description: internal interfaces for XML Path Language implementation
+ *              used to build new modules on top of XPath like XPointer and
+ *              XSLT
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XPATH_INTERNALS_H__
+#define __XML_XPATH_INTERNALS_H__
+
+#include <stdio.h>
+#include <libxml/xmlversion.h>
+#include <libxml/xpath.h>
+
+#ifdef LIBXML_XPATH_ENABLED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/************************************************************************
+ *									*
+ *			Helpers						*
+ *									*
+ ************************************************************************/
+
+/*
+ * Many of these macros may later turn into functions. They
+ * shouldn't be used in #ifdef's preprocessor instructions.
+ */
+/**
+ * xmlXPathSetError:
+ * @ctxt:  an XPath parser context
+ * @err:  an xmlXPathError code
+ *
+ * Raises an error.
+ */
+#define xmlXPathSetError(ctxt, err)					\
+    { xmlXPatherror((ctxt), __FILE__, __LINE__, (err));			\
+      if ((ctxt) != NULL) (ctxt)->error = (err); }
+
+/**
+ * xmlXPathSetArityError:
+ * @ctxt:  an XPath parser context
+ *
+ * Raises an XPATH_INVALID_ARITY error.
+ */
+#define xmlXPathSetArityError(ctxt)					\
+    xmlXPathSetError((ctxt), XPATH_INVALID_ARITY)
+
+/**
+ * xmlXPathSetTypeError:
+ * @ctxt:  an XPath parser context
+ *
+ * Raises an XPATH_INVALID_TYPE error.
+ */
+#define xmlXPathSetTypeError(ctxt)					\
+    xmlXPathSetError((ctxt), XPATH_INVALID_TYPE)
+
+/**
+ * xmlXPathGetError:
+ * @ctxt:  an XPath parser context
+ *
+ * Get the error code of an XPath context.
+ *
+ * Returns the context error.
+ */
+#define xmlXPathGetError(ctxt)	  ((ctxt)->error)
+
+/**
+ * xmlXPathCheckError:
+ * @ctxt:  an XPath parser context
+ *
+ * Check if an XPath error was raised.
+ *
+ * Returns true if an error has been raised, false otherwise.
+ */
+#define xmlXPathCheckError(ctxt)  ((ctxt)->error != XPATH_EXPRESSION_OK)
+
+/**
+ * xmlXPathGetDocument:
+ * @ctxt:  an XPath parser context
+ *
+ * Get the document of an XPath context.
+ *
+ * Returns the context document.
+ */
+#define xmlXPathGetDocument(ctxt)	((ctxt)->context->doc)
+
+/**
+ * xmlXPathGetContextNode:
+ * @ctxt: an XPath parser context
+ *
+ * Get the context node of an XPath context.
+ *
+ * Returns the context node.
+ */
+#define xmlXPathGetContextNode(ctxt)	((ctxt)->context->node)
+
+XMLPUBFUN int
+		xmlXPathPopBoolean	(xmlXPathParserContextPtr ctxt);
+XMLPUBFUN double
+		xmlXPathPopNumber	(xmlXPathParserContextPtr ctxt);
+XMLPUBFUN xmlChar *
+		xmlXPathPopString	(xmlXPathParserContextPtr ctxt);
+XMLPUBFUN xmlNodeSetPtr
+		xmlXPathPopNodeSet	(xmlXPathParserContextPtr ctxt);
+XMLPUBFUN void *
+		xmlXPathPopExternal	(xmlXPathParserContextPtr ctxt);
+
+/**
+ * xmlXPathReturnBoolean:
+ * @ctxt:  an XPath parser context
+ * @val:  a boolean
+ *
+ * Pushes the boolean @val on the context stack.
+ */
+#define xmlXPathReturnBoolean(ctxt, val)				\
+    valuePush((ctxt), xmlXPathNewBoolean(val))
+
+/**
+ * xmlXPathReturnTrue:
+ * @ctxt:  an XPath parser context
+ *
+ * Pushes true on the context stack.
+ */
+#define xmlXPathReturnTrue(ctxt)   xmlXPathReturnBoolean((ctxt), 1)
+
+/**
+ * xmlXPathReturnFalse:
+ * @ctxt:  an XPath parser context
+ *
+ * Pushes false on the context stack.
+ */
+#define xmlXPathReturnFalse(ctxt)  xmlXPathReturnBoolean((ctxt), 0)
+
+/**
+ * xmlXPathReturnNumber:
+ * @ctxt:  an XPath parser context
+ * @val:  a double
+ *
+ * Pushes the double @val on the context stack.
+ */
+#define xmlXPathReturnNumber(ctxt, val)					\
+    valuePush((ctxt), xmlXPathNewFloat(val))
+
+/**
+ * xmlXPathReturnString:
+ * @ctxt:  an XPath parser context
+ * @str:  a string
+ *
+ * Pushes the string @str on the context stack.
+ */
+#define xmlXPathReturnString(ctxt, str)					\
+    valuePush((ctxt), xmlXPathWrapString(str))
+
+/**
+ * xmlXPathReturnEmptyString:
+ * @ctxt:  an XPath parser context
+ *
+ * Pushes an empty string on the stack.
+ */
+#define xmlXPathReturnEmptyString(ctxt)					\
+    valuePush((ctxt), xmlXPathNewCString(""))
+
+/**
+ * xmlXPathReturnNodeSet:
+ * @ctxt:  an XPath parser context
+ * @ns:  a node-set
+ *
+ * Pushes the node-set @ns on the context stack.
+ */
+#define xmlXPathReturnNodeSet(ctxt, ns)					\
+    valuePush((ctxt), xmlXPathWrapNodeSet(ns))
+
+/**
+ * xmlXPathReturnEmptyNodeSet:
+ * @ctxt:  an XPath parser context
+ *
+ * Pushes an empty node-set on the context stack.
+ */
+#define xmlXPathReturnEmptyNodeSet(ctxt)				\
+    valuePush((ctxt), xmlXPathNewNodeSet(NULL))
+
+/**
+ * xmlXPathReturnExternal:
+ * @ctxt:  an XPath parser context
+ * @val:  user data
+ *
+ * Pushes user data on the context stack.
+ */
+#define xmlXPathReturnExternal(ctxt, val)				\
+    valuePush((ctxt), xmlXPathWrapExternal(val))
+
+/**
+ * xmlXPathStackIsNodeSet:
+ * @ctxt: an XPath parser context
+ *
+ * Check if the current value on the XPath stack is a node set or
+ * an XSLT value tree.
+ *
+ * Returns true if the current object on the stack is a node-set.
+ */
+#define xmlXPathStackIsNodeSet(ctxt)					\
+    (((ctxt)->value != NULL)						\
+     && (((ctxt)->value->type == XPATH_NODESET)				\
+         || ((ctxt)->value->type == XPATH_XSLT_TREE)))
+
+/**
+ * xmlXPathStackIsExternal:
+ * @ctxt: an XPath parser context
+ *
+ * Checks if the current value on the XPath stack is an external
+ * object.
+ *
+ * Returns true if the current object on the stack is an external
+ * object.
+ */
+#define xmlXPathStackIsExternal(ctxt)					\
+	((ctxt->value != NULL) && (ctxt->value->type == XPATH_USERS))
+
+/**
+ * xmlXPathEmptyNodeSet:
+ * @ns:  a node-set
+ *
+ * Empties a node-set.
+ */
+#define xmlXPathEmptyNodeSet(ns)					\
+    { while ((ns)->nodeNr > 0) (ns)->nodeTab[--(ns)->nodeNr] = NULL; }
+
+/**
+ * CHECK_ERROR:
+ *
+ * Macro to return from the function if an XPath error was detected.
+ */
+#define CHECK_ERROR							\
+    if (ctxt->error != XPATH_EXPRESSION_OK) return
+
+/**
+ * CHECK_ERROR0:
+ *
+ * Macro to return 0 from the function if an XPath error was detected.
+ */
+#define CHECK_ERROR0							\
+    if (ctxt->error != XPATH_EXPRESSION_OK) return(0)
+
+/**
+ * XP_ERROR:
+ * @X:  the error code
+ *
+ * Macro to raise an XPath error and return.
+ */
+#define XP_ERROR(X)							\
+    { xmlXPathErr(ctxt, X); return; }
+
+/**
+ * XP_ERROR0:
+ * @X:  the error code
+ *
+ * Macro to raise an XPath error and return 0.
+ */
+#define XP_ERROR0(X)							\
+    { xmlXPathErr(ctxt, X); return(0); }
+
+/**
+ * CHECK_TYPE:
+ * @typeval:  the XPath type
+ *
+ * Macro to check that the value on top of the XPath stack is of a given
+ * type.
+ */
+#define CHECK_TYPE(typeval)						\
+    if ((ctxt->value == NULL) || (ctxt->value->type != typeval))	\
+        XP_ERROR(XPATH_INVALID_TYPE)
+
+/**
+ * CHECK_TYPE0:
+ * @typeval:  the XPath type
+ *
+ * Macro to check that the value on top of the XPath stack is of a given
+ * type. Return(0) in case of failure
+ */
+#define CHECK_TYPE0(typeval)						\
+    if ((ctxt->value == NULL) || (ctxt->value->type != typeval))	\
+        XP_ERROR0(XPATH_INVALID_TYPE)
+
+/**
+ * CHECK_ARITY:
+ * @x:  the number of expected args
+ *
+ * Macro to check that the number of args passed to an XPath function matches.
+ */
+#define CHECK_ARITY(x)							\
+    if (ctxt == NULL) return;						\
+    if (nargs != (x))							\
+        XP_ERROR(XPATH_INVALID_ARITY);					\
+    if (ctxt->valueNr < (x))						\
+        XP_ERROR(XPATH_STACK_ERROR);
+
+/**
+ * CAST_TO_STRING:
+ *
+ * Macro to try to cast the value on the top of the XPath stack to a string.
+ */
+#define CAST_TO_STRING							\
+    if ((ctxt->value != NULL) && (ctxt->value->type != XPATH_STRING))	\
+        xmlXPathStringFunction(ctxt, 1);
+
+/**
+ * CAST_TO_NUMBER:
+ *
+ * Macro to try to cast the value on the top of the XPath stack to a number.
+ */
+#define CAST_TO_NUMBER							\
+    if ((ctxt->value != NULL) && (ctxt->value->type != XPATH_NUMBER))	\
+        xmlXPathNumberFunction(ctxt, 1);
+
+/**
+ * CAST_TO_BOOLEAN:
+ *
+ * Macro to try to cast the value on the top of the XPath stack to a boolean.
+ */
+#define CAST_TO_BOOLEAN							\
+    if ((ctxt->value != NULL) && (ctxt->value->type != XPATH_BOOLEAN))	\
+        xmlXPathBooleanFunction(ctxt, 1);
+
+/*
+ * Variable Lookup forwarding.
+ */
+
+XMLPUBFUN void
+	xmlXPathRegisterVariableLookup	(xmlXPathContextPtr ctxt,
+					 xmlXPathVariableLookupFunc f,
+					 void *data);
+
+/*
+ * Function Lookup forwarding.
+ */
+
+XMLPUBFUN void
+	    xmlXPathRegisterFuncLookup	(xmlXPathContextPtr ctxt,
+					 xmlXPathFuncLookupFunc f,
+					 void *funcCtxt);
+
+/*
+ * Error reporting.
+ */
+XMLPUBFUN void
+		xmlXPatherror	(xmlXPathParserContextPtr ctxt,
+				 const char *file,
+				 int line,
+				 int no);
+
+XMLPUBFUN void
+		xmlXPathErr	(xmlXPathParserContextPtr ctxt,
+				 int error);
+
+#ifdef LIBXML_DEBUG_ENABLED
+XMLPUBFUN void
+		xmlXPathDebugDumpObject	(FILE *output,
+					 xmlXPathObjectPtr cur,
+					 int depth);
+XMLPUBFUN void
+	    xmlXPathDebugDumpCompExpr(FILE *output,
+					 xmlXPathCompExprPtr comp,
+					 int depth);
+#endif
+/**
+ * NodeSet handling.
+ */
+XMLPUBFUN int
+		xmlXPathNodeSetContains		(xmlNodeSetPtr cur,
+						 xmlNodePtr val);
+XMLPUBFUN xmlNodeSetPtr
+		xmlXPathDifference		(xmlNodeSetPtr nodes1,
+						 xmlNodeSetPtr nodes2);
+XMLPUBFUN xmlNodeSetPtr
+		xmlXPathIntersection		(xmlNodeSetPtr nodes1,
+						 xmlNodeSetPtr nodes2);
+
+XMLPUBFUN xmlNodeSetPtr
+		xmlXPathDistinctSorted		(xmlNodeSetPtr nodes);
+XMLPUBFUN xmlNodeSetPtr
+		xmlXPathDistinct		(xmlNodeSetPtr nodes);
+
+XMLPUBFUN int
+		xmlXPathHasSameNodes		(xmlNodeSetPtr nodes1,
+						 xmlNodeSetPtr nodes2);
+
+XMLPUBFUN xmlNodeSetPtr
+		xmlXPathNodeLeadingSorted	(xmlNodeSetPtr nodes,
+						 xmlNodePtr node);
+XMLPUBFUN xmlNodeSetPtr
+		xmlXPathLeadingSorted		(xmlNodeSetPtr nodes1,
+						 xmlNodeSetPtr nodes2);
+XMLPUBFUN xmlNodeSetPtr
+		xmlXPathNodeLeading		(xmlNodeSetPtr nodes,
+						 xmlNodePtr node);
+XMLPUBFUN xmlNodeSetPtr
+		xmlXPathLeading			(xmlNodeSetPtr nodes1,
+						 xmlNodeSetPtr nodes2);
+
+XMLPUBFUN xmlNodeSetPtr
+		xmlXPathNodeTrailingSorted	(xmlNodeSetPtr nodes,
+						 xmlNodePtr node);
+XMLPUBFUN xmlNodeSetPtr
+		xmlXPathTrailingSorted		(xmlNodeSetPtr nodes1,
+						 xmlNodeSetPtr nodes2);
+XMLPUBFUN xmlNodeSetPtr
+		xmlXPathNodeTrailing		(xmlNodeSetPtr nodes,
+						 xmlNodePtr node);
+XMLPUBFUN xmlNodeSetPtr
+		xmlXPathTrailing		(xmlNodeSetPtr nodes1,
+						 xmlNodeSetPtr nodes2);
+
+
+/**
+ * Extending a context.
+ */
+
+XMLPUBFUN int
+		xmlXPathRegisterNs		(xmlXPathContextPtr ctxt,
+						 const xmlChar *prefix,
+						 const xmlChar *ns_uri);
+XMLPUBFUN const xmlChar *
+		xmlXPathNsLookup		(xmlXPathContextPtr ctxt,
+						 const xmlChar *prefix);
+XMLPUBFUN void
+		xmlXPathRegisteredNsCleanup	(xmlXPathContextPtr ctxt);
+
+XMLPUBFUN int
+		xmlXPathRegisterFunc		(xmlXPathContextPtr ctxt,
+						 const xmlChar *name,
+						 xmlXPathFunction f);
+XMLPUBFUN int
+		xmlXPathRegisterFuncNS		(xmlXPathContextPtr ctxt,
+						 const xmlChar *name,
+						 const xmlChar *ns_uri,
+						 xmlXPathFunction f);
+XMLPUBFUN int
+		xmlXPathRegisterVariable	(xmlXPathContextPtr ctxt,
+						 const xmlChar *name,
+						 xmlXPathObjectPtr value);
+XMLPUBFUN int
+		xmlXPathRegisterVariableNS	(xmlXPathContextPtr ctxt,
+						 const xmlChar *name,
+						 const xmlChar *ns_uri,
+						 xmlXPathObjectPtr value);
+XMLPUBFUN xmlXPathFunction
+		xmlXPathFunctionLookup		(xmlXPathContextPtr ctxt,
+						 const xmlChar *name);
+XMLPUBFUN xmlXPathFunction
+		xmlXPathFunctionLookupNS	(xmlXPathContextPtr ctxt,
+						 const xmlChar *name,
+						 const xmlChar *ns_uri);
+XMLPUBFUN void
+		xmlXPathRegisteredFuncsCleanup	(xmlXPathContextPtr ctxt);
+XMLPUBFUN xmlXPathObjectPtr
+		xmlXPathVariableLookup		(xmlXPathContextPtr ctxt,
+						 const xmlChar *name);
+XMLPUBFUN xmlXPathObjectPtr
+		xmlXPathVariableLookupNS	(xmlXPathContextPtr ctxt,
+						 const xmlChar *name,
+						 const xmlChar *ns_uri);
+XMLPUBFUN void
+		xmlXPathRegisteredVariablesCleanup(xmlXPathContextPtr ctxt);
+
+/**
+ * Utilities to extend XPath.
+ */
+XMLPUBFUN xmlXPathParserContextPtr
+		  xmlXPathNewParserContext	(const xmlChar *str,
+						 xmlXPathContextPtr ctxt);
+XMLPUBFUN void
+		xmlXPathFreeParserContext	(xmlXPathParserContextPtr ctxt);
+
+/* TODO: remap to xmlXPathValuePop and Push. */
+XMLPUBFUN xmlXPathObjectPtr
+		valuePop			(xmlXPathParserContextPtr ctxt);
+XMLPUBFUN int
+		valuePush			(xmlXPathParserContextPtr ctxt,
+						 xmlXPathObjectPtr value);
+
+XMLPUBFUN xmlXPathObjectPtr
+		xmlXPathNewString		(const xmlChar *val);
+XMLPUBFUN xmlXPathObjectPtr
+		xmlXPathNewCString		(const char *val);
+XMLPUBFUN xmlXPathObjectPtr
+		xmlXPathWrapString		(xmlChar *val);
+XMLPUBFUN xmlXPathObjectPtr
+		xmlXPathWrapCString		(char * val);
+XMLPUBFUN xmlXPathObjectPtr
+		xmlXPathNewFloat		(double val);
+XMLPUBFUN xmlXPathObjectPtr
+		xmlXPathNewBoolean		(int val);
+XMLPUBFUN xmlXPathObjectPtr
+		xmlXPathNewNodeSet		(xmlNodePtr val);
+XMLPUBFUN xmlXPathObjectPtr
+		xmlXPathNewValueTree		(xmlNodePtr val);
+XMLPUBFUN int
+		xmlXPathNodeSetAdd		(xmlNodeSetPtr cur,
+						 xmlNodePtr val);
+XMLPUBFUN int
+		xmlXPathNodeSetAddUnique	(xmlNodeSetPtr cur,
+						 xmlNodePtr val);
+XMLPUBFUN int
+		xmlXPathNodeSetAddNs		(xmlNodeSetPtr cur,
+						 xmlNodePtr node,
+						 xmlNsPtr ns);
+XMLPUBFUN void
+		xmlXPathNodeSetSort		(xmlNodeSetPtr set);
+
+XMLPUBFUN void
+		xmlXPathRoot			(xmlXPathParserContextPtr ctxt);
+XMLPUBFUN void
+		xmlXPathEvalExpr		(xmlXPathParserContextPtr ctxt);
+XMLPUBFUN xmlChar *
+		xmlXPathParseName		(xmlXPathParserContextPtr ctxt);
+XMLPUBFUN xmlChar *
+		xmlXPathParseNCName		(xmlXPathParserContextPtr ctxt);
+
+/*
+ * Existing functions.
+ */
+XMLPUBFUN double
+		xmlXPathStringEvalNumber	(const xmlChar *str);
+XMLPUBFUN int
+		xmlXPathEvaluatePredicateResult (xmlXPathParserContextPtr ctxt,
+						 xmlXPathObjectPtr res);
+XMLPUBFUN void
+		xmlXPathRegisterAllFunctions	(xmlXPathContextPtr ctxt);
+XMLPUBFUN xmlNodeSetPtr
+		xmlXPathNodeSetMerge		(xmlNodeSetPtr val1,
+						 xmlNodeSetPtr val2);
+XMLPUBFUN void
+		xmlXPathNodeSetDel		(xmlNodeSetPtr cur,
+						 xmlNodePtr val);
+XMLPUBFUN void
+		xmlXPathNodeSetRemove		(xmlNodeSetPtr cur,
+						 int val);
+XMLPUBFUN xmlXPathObjectPtr
+		xmlXPathNewNodeSetList		(xmlNodeSetPtr val);
+XMLPUBFUN xmlXPathObjectPtr
+		xmlXPathWrapNodeSet		(xmlNodeSetPtr val);
+XMLPUBFUN xmlXPathObjectPtr
+		xmlXPathWrapExternal		(void *val);
+
+XMLPUBFUN int xmlXPathEqualValues(xmlXPathParserContextPtr ctxt);
+XMLPUBFUN int xmlXPathNotEqualValues(xmlXPathParserContextPtr ctxt);
+XMLPUBFUN int xmlXPathCompareValues(xmlXPathParserContextPtr ctxt, int inf, int strict);
+XMLPUBFUN void xmlXPathValueFlipSign(xmlXPathParserContextPtr ctxt);
+XMLPUBFUN void xmlXPathAddValues(xmlXPathParserContextPtr ctxt);
+XMLPUBFUN void xmlXPathSubValues(xmlXPathParserContextPtr ctxt);
+XMLPUBFUN void xmlXPathMultValues(xmlXPathParserContextPtr ctxt);
+XMLPUBFUN void xmlXPathDivValues(xmlXPathParserContextPtr ctxt);
+XMLPUBFUN void xmlXPathModValues(xmlXPathParserContextPtr ctxt);
+
+XMLPUBFUN int xmlXPathIsNodeType(const xmlChar *name);
+
+/*
+ * Some of the axis navigation routines.
+ */
+XMLPUBFUN xmlNodePtr xmlXPathNextSelf(xmlXPathParserContextPtr ctxt,
+			xmlNodePtr cur);
+XMLPUBFUN xmlNodePtr xmlXPathNextChild(xmlXPathParserContextPtr ctxt,
+			xmlNodePtr cur);
+XMLPUBFUN xmlNodePtr xmlXPathNextDescendant(xmlXPathParserContextPtr ctxt,
+			xmlNodePtr cur);
+XMLPUBFUN xmlNodePtr xmlXPathNextDescendantOrSelf(xmlXPathParserContextPtr ctxt,
+			xmlNodePtr cur);
+XMLPUBFUN xmlNodePtr xmlXPathNextParent(xmlXPathParserContextPtr ctxt,
+			xmlNodePtr cur);
+XMLPUBFUN xmlNodePtr xmlXPathNextAncestorOrSelf(xmlXPathParserContextPtr ctxt,
+			xmlNodePtr cur);
+XMLPUBFUN xmlNodePtr xmlXPathNextFollowingSibling(xmlXPathParserContextPtr ctxt,
+			xmlNodePtr cur);
+XMLPUBFUN xmlNodePtr xmlXPathNextFollowing(xmlXPathParserContextPtr ctxt,
+			xmlNodePtr cur);
+XMLPUBFUN xmlNodePtr xmlXPathNextNamespace(xmlXPathParserContextPtr ctxt,
+			xmlNodePtr cur);
+XMLPUBFUN xmlNodePtr xmlXPathNextAttribute(xmlXPathParserContextPtr ctxt,
+			xmlNodePtr cur);
+XMLPUBFUN xmlNodePtr xmlXPathNextPreceding(xmlXPathParserContextPtr ctxt,
+			xmlNodePtr cur);
+XMLPUBFUN xmlNodePtr xmlXPathNextAncestor(xmlXPathParserContextPtr ctxt,
+			xmlNodePtr cur);
+XMLPUBFUN xmlNodePtr xmlXPathNextPrecedingSibling(xmlXPathParserContextPtr ctxt,
+			xmlNodePtr cur);
+/*
+ * The official core of XPath functions.
+ */
+XMLPUBFUN void xmlXPathLastFunction(xmlXPathParserContextPtr ctxt, int nargs);
+XMLPUBFUN void xmlXPathPositionFunction(xmlXPathParserContextPtr ctxt, int nargs);
+XMLPUBFUN void xmlXPathCountFunction(xmlXPathParserContextPtr ctxt, int nargs);
+XMLPUBFUN void xmlXPathIdFunction(xmlXPathParserContextPtr ctxt, int nargs);
+XMLPUBFUN void xmlXPathLocalNameFunction(xmlXPathParserContextPtr ctxt, int nargs);
+XMLPUBFUN void xmlXPathNamespaceURIFunction(xmlXPathParserContextPtr ctxt, int nargs);
+XMLPUBFUN void xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs);
+XMLPUBFUN void xmlXPathStringLengthFunction(xmlXPathParserContextPtr ctxt, int nargs);
+XMLPUBFUN void xmlXPathConcatFunction(xmlXPathParserContextPtr ctxt, int nargs);
+XMLPUBFUN void xmlXPathContainsFunction(xmlXPathParserContextPtr ctxt, int nargs);
+XMLPUBFUN void xmlXPathStartsWithFunction(xmlXPathParserContextPtr ctxt, int nargs);
+XMLPUBFUN void xmlXPathSubstringFunction(xmlXPathParserContextPtr ctxt, int nargs);
+XMLPUBFUN void xmlXPathSubstringBeforeFunction(xmlXPathParserContextPtr ctxt, int nargs);
+XMLPUBFUN void xmlXPathSubstringAfterFunction(xmlXPathParserContextPtr ctxt, int nargs);
+XMLPUBFUN void xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs);
+XMLPUBFUN void xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs);
+XMLPUBFUN void xmlXPathNotFunction(xmlXPathParserContextPtr ctxt, int nargs);
+XMLPUBFUN void xmlXPathTrueFunction(xmlXPathParserContextPtr ctxt, int nargs);
+XMLPUBFUN void xmlXPathFalseFunction(xmlXPathParserContextPtr ctxt, int nargs);
+XMLPUBFUN void xmlXPathLangFunction(xmlXPathParserContextPtr ctxt, int nargs);
+XMLPUBFUN void xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs);
+XMLPUBFUN void xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs);
+XMLPUBFUN void xmlXPathFloorFunction(xmlXPathParserContextPtr ctxt, int nargs);
+XMLPUBFUN void xmlXPathCeilingFunction(xmlXPathParserContextPtr ctxt, int nargs);
+XMLPUBFUN void xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs);
+XMLPUBFUN void xmlXPathBooleanFunction(xmlXPathParserContextPtr ctxt, int nargs);
+
+/**
+ * Really internal functions
+ */
+XMLPUBFUN void xmlXPathNodeSetFreeNs(xmlNsPtr ns);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBXML_XPATH_ENABLED */
+#endif /* ! __XML_XPATH_INTERNALS_H__ */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xpointer.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xpointer.h
new file mode 100644
index 00000000..a5260008
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxml/xpointer.h
@@ -0,0 +1,138 @@
+/*
+ * Summary: API to handle XML Pointers
+ * Description: API to handle XML Pointers
+ * Base implementation was made accordingly to
+ * W3C Candidate Recommendation 7 June 2000
+ * http://www.w3.org/TR/2000/CR-xptr-20000607
+ *
+ * Added support for the element() scheme described in:
+ * W3C Proposed Recommendation 13 November 2002
+ * http://www.w3.org/TR/2002/PR-xptr-element-20021113/
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XPTR_H__
+#define __XML_XPTR_H__
+
+#include <libxml/xmlversion.h>
+
+#ifdef LIBXML_XPTR_ENABLED
+
+#include <libxml/tree.h>
+#include <libxml/xpath.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(LIBXML_XPTR_LOCS_ENABLED)
+/*
+ * A Location Set
+ */
+typedef struct _xmlLocationSet xmlLocationSet;
+typedef xmlLocationSet *xmlLocationSetPtr;
+struct _xmlLocationSet {
+    int locNr;		      /* number of locations in the set */
+    int locMax;		      /* size of the array as allocated */
+    xmlXPathObjectPtr *locTab;/* array of locations */
+};
+
+/*
+ * Handling of location sets.
+ */
+
+XML_DEPRECATED
+XMLPUBFUN xmlLocationSetPtr
+		    xmlXPtrLocationSetCreate	(xmlXPathObjectPtr val);
+XML_DEPRECATED
+XMLPUBFUN void
+		    xmlXPtrFreeLocationSet	(xmlLocationSetPtr obj);
+XML_DEPRECATED
+XMLPUBFUN xmlLocationSetPtr
+		    xmlXPtrLocationSetMerge	(xmlLocationSetPtr val1,
+						 xmlLocationSetPtr val2);
+XML_DEPRECATED
+XMLPUBFUN xmlXPathObjectPtr
+		    xmlXPtrNewRange		(xmlNodePtr start,
+						 int startindex,
+						 xmlNodePtr end,
+						 int endindex);
+XML_DEPRECATED
+XMLPUBFUN xmlXPathObjectPtr
+		    xmlXPtrNewRangePoints	(xmlXPathObjectPtr start,
+						 xmlXPathObjectPtr end);
+XML_DEPRECATED
+XMLPUBFUN xmlXPathObjectPtr
+		    xmlXPtrNewRangeNodePoint	(xmlNodePtr start,
+						 xmlXPathObjectPtr end);
+XML_DEPRECATED
+XMLPUBFUN xmlXPathObjectPtr
+		    xmlXPtrNewRangePointNode	(xmlXPathObjectPtr start,
+						 xmlNodePtr end);
+XML_DEPRECATED
+XMLPUBFUN xmlXPathObjectPtr
+		    xmlXPtrNewRangeNodes	(xmlNodePtr start,
+						 xmlNodePtr end);
+XML_DEPRECATED
+XMLPUBFUN xmlXPathObjectPtr
+		    xmlXPtrNewLocationSetNodes	(xmlNodePtr start,
+						 xmlNodePtr end);
+XML_DEPRECATED
+XMLPUBFUN xmlXPathObjectPtr
+		    xmlXPtrNewLocationSetNodeSet(xmlNodeSetPtr set);
+XML_DEPRECATED
+XMLPUBFUN xmlXPathObjectPtr
+		    xmlXPtrNewRangeNodeObject	(xmlNodePtr start,
+						 xmlXPathObjectPtr end);
+XML_DEPRECATED
+XMLPUBFUN xmlXPathObjectPtr
+		    xmlXPtrNewCollapsedRange	(xmlNodePtr start);
+XML_DEPRECATED
+XMLPUBFUN void
+		    xmlXPtrLocationSetAdd	(xmlLocationSetPtr cur,
+						 xmlXPathObjectPtr val);
+XML_DEPRECATED
+XMLPUBFUN xmlXPathObjectPtr
+		    xmlXPtrWrapLocationSet	(xmlLocationSetPtr val);
+XML_DEPRECATED
+XMLPUBFUN void
+		    xmlXPtrLocationSetDel	(xmlLocationSetPtr cur,
+						 xmlXPathObjectPtr val);
+XML_DEPRECATED
+XMLPUBFUN void
+		    xmlXPtrLocationSetRemove	(xmlLocationSetPtr cur,
+						 int val);
+#endif /* defined(LIBXML_XPTR_LOCS_ENABLED) */
+
+/*
+ * Functions.
+ */
+XMLPUBFUN xmlXPathContextPtr
+		    xmlXPtrNewContext		(xmlDocPtr doc,
+						 xmlNodePtr here,
+						 xmlNodePtr origin);
+XMLPUBFUN xmlXPathObjectPtr
+		    xmlXPtrEval			(const xmlChar *str,
+						 xmlXPathContextPtr ctx);
+
+#if defined(LIBXML_XPTR_LOCS_ENABLED)
+XML_DEPRECATED
+XMLPUBFUN void
+		    xmlXPtrRangeToFunction	(xmlXPathParserContextPtr ctxt,
+						 int nargs);
+XML_DEPRECATED
+XMLPUBFUN xmlNodePtr
+		    xmlXPtrBuildNodeList	(xmlXPathObjectPtr obj);
+XML_DEPRECATED
+XMLPUBFUN void
+		    xmlXPtrEvalRangePredicate	(xmlXPathParserContextPtr ctxt);
+#endif /* defined(LIBXML_XPTR_LOCS_ENABLED) */
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBXML_XPTR_ENABLED */
+#endif /* __XML_XPTR_H__ */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/__init__.py b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/__init__.py
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/attributes.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/attributes.h
new file mode 100644
index 00000000..d9b99a74
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/attributes.h
@@ -0,0 +1,39 @@
+/*
+ * Summary: interface for the XSLT attribute handling
+ * Description: this module handles the specificities of attribute
+ *              and attribute groups processing.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XSLT_ATTRIBUTES_H__
+#define __XML_XSLT_ATTRIBUTES_H__
+
+#include <libxml/tree.h>
+#include "xsltexports.h"
+#include "xsltInternals.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+XSLTPUBFUN void XSLTCALL
+	xsltParseStylesheetAttributeSet	(xsltStylesheetPtr style,
+					 xmlNodePtr cur);
+XSLTPUBFUN void XSLTCALL
+	xsltFreeAttributeSetsHashes	(xsltStylesheetPtr style);
+XSLTPUBFUN void XSLTCALL
+	xsltApplyAttributeSet		(xsltTransformContextPtr ctxt,
+					 xmlNodePtr node,
+					 xmlNodePtr inst,
+					 const xmlChar *attributes);
+XSLTPUBFUN void XSLTCALL
+	xsltResolveStylesheetAttributeSet(xsltStylesheetPtr style);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_XSLT_ATTRIBUTES_H__ */
+
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/documents.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/documents.h
new file mode 100644
index 00000000..ae7c0ca2
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/documents.h
@@ -0,0 +1,93 @@
+/*
+ * Summary: interface for the document handling
+ * Description: implements document loading and cache (multiple
+ *              document() reference for the same resources must
+ *              be equal.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XSLT_DOCUMENTS_H__
+#define __XML_XSLT_DOCUMENTS_H__
+
+#include <libxml/tree.h>
+#include "xsltexports.h"
+#include "xsltInternals.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+XSLTPUBFUN xsltDocumentPtr XSLTCALL
+		xsltNewDocument		(xsltTransformContextPtr ctxt,
+					 xmlDocPtr doc);
+XSLTPUBFUN xsltDocumentPtr XSLTCALL
+		xsltLoadDocument	(xsltTransformContextPtr ctxt,
+					 const xmlChar *URI);
+XSLTPUBFUN xsltDocumentPtr XSLTCALL
+		xsltFindDocument	(xsltTransformContextPtr ctxt,
+					 xmlDocPtr doc);
+XSLTPUBFUN void XSLTCALL
+		xsltFreeDocuments	(xsltTransformContextPtr ctxt);
+
+XSLTPUBFUN xsltDocumentPtr XSLTCALL
+		xsltLoadStyleDocument	(xsltStylesheetPtr style,
+					 const xmlChar *URI);
+XSLTPUBFUN xsltDocumentPtr XSLTCALL
+		xsltNewStyleDocument	(xsltStylesheetPtr style,
+					 xmlDocPtr doc);
+XSLTPUBFUN void XSLTCALL
+		xsltFreeStyleDocuments	(xsltStylesheetPtr style);
+
+/*
+ * Hooks for document loading
+ */
+
+/**
+ * xsltLoadType:
+ *
+ * Enum defining the kind of loader requirement.
+ */
+typedef enum {
+    XSLT_LOAD_START = 0,	/* loading for a top stylesheet */
+    XSLT_LOAD_STYLESHEET = 1,	/* loading for a stylesheet include/import */
+    XSLT_LOAD_DOCUMENT = 2	/* loading document at transformation time */
+} xsltLoadType;
+
+/**
+ * xsltDocLoaderFunc:
+ * @URI: the URI of the document to load
+ * @dict: the dictionary to use when parsing that document
+ * @options: parsing options, a set of xmlParserOption
+ * @ctxt: the context, either a stylesheet or a transformation context
+ * @type: the xsltLoadType indicating the kind of loading required
+ *
+ * An xsltDocLoaderFunc is a signature for a function which can be
+ * registered to load document not provided by the compilation or
+ * transformation API themselve, for example when an xsl:import,
+ * xsl:include is found at compilation time or when a document()
+ * call is made at runtime.
+ *
+ * Returns the pointer to the document (which will be modified and
+ * freed by the engine later), or NULL in case of error.
+ */
+typedef xmlDocPtr (*xsltDocLoaderFunc)		(const xmlChar *URI,
+						 xmlDictPtr dict,
+						 int options,
+						 void *ctxt,
+						 xsltLoadType type);
+
+XSLTPUBFUN void XSLTCALL
+		xsltSetLoaderFunc		(xsltDocLoaderFunc f);
+
+/* the loader may be needed by extension libraries so it is exported */
+XSLTPUBVAR xsltDocLoaderFunc xsltDocDefaultLoader;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_XSLT_DOCUMENTS_H__ */
+
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/extensions.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/extensions.h
new file mode 100644
index 00000000..84d6aa44
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/extensions.h
@@ -0,0 +1,262 @@
+/*
+ * Summary: interface for the extension support
+ * Description: This provide the API needed for simple and module
+ *              extension support.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XSLT_EXTENSION_H__
+#define __XML_XSLT_EXTENSION_H__
+
+#include <libxml/xpath.h>
+#include "xsltexports.h"
+#include "xsltInternals.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Extension Modules API.
+ */
+
+/**
+ * xsltInitGlobals:
+ *
+ * Initialize the global variables for extensions
+ *
+ */
+
+XSLTPUBFUN void XSLTCALL
+		xsltInitGlobals                 (void);
+
+/**
+ * xsltStyleExtInitFunction:
+ * @ctxt:  an XSLT stylesheet
+ * @URI:  the namespace URI for the extension
+ *
+ * A function called at initialization time of an XSLT extension module.
+ *
+ * Returns a pointer to the module specific data for this transformation.
+ */
+typedef void * (*xsltStyleExtInitFunction)	(xsltStylesheetPtr style,
+						 const xmlChar *URI);
+
+/**
+ * xsltStyleExtShutdownFunction:
+ * @ctxt:  an XSLT stylesheet
+ * @URI:  the namespace URI for the extension
+ * @data:  the data associated to this module
+ *
+ * A function called at shutdown time of an XSLT extension module.
+ */
+typedef void (*xsltStyleExtShutdownFunction)	(xsltStylesheetPtr style,
+						 const xmlChar *URI,
+						 void *data);
+
+/**
+ * xsltExtInitFunction:
+ * @ctxt:  an XSLT transformation context
+ * @URI:  the namespace URI for the extension
+ *
+ * A function called at initialization time of an XSLT extension module.
+ *
+ * Returns a pointer to the module specific data for this transformation.
+ */
+typedef void * (*xsltExtInitFunction)	(xsltTransformContextPtr ctxt,
+					 const xmlChar *URI);
+
+/**
+ * xsltExtShutdownFunction:
+ * @ctxt:  an XSLT transformation context
+ * @URI:  the namespace URI for the extension
+ * @data:  the data associated to this module
+ *
+ * A function called at shutdown time of an XSLT extension module.
+ */
+typedef void (*xsltExtShutdownFunction) (xsltTransformContextPtr ctxt,
+					 const xmlChar *URI,
+					 void *data);
+
+XSLTPUBFUN int XSLTCALL
+		xsltRegisterExtModule	(const xmlChar *URI,
+					 xsltExtInitFunction initFunc,
+					 xsltExtShutdownFunction shutdownFunc);
+XSLTPUBFUN int XSLTCALL
+		xsltRegisterExtModuleFull
+					(const xmlChar * URI,
+					 xsltExtInitFunction initFunc,
+					 xsltExtShutdownFunction shutdownFunc,
+					 xsltStyleExtInitFunction styleInitFunc,
+					 xsltStyleExtShutdownFunction styleShutdownFunc);
+
+XSLTPUBFUN int XSLTCALL
+		xsltUnregisterExtModule	(const xmlChar * URI);
+
+XSLTPUBFUN void * XSLTCALL
+		xsltGetExtData		(xsltTransformContextPtr ctxt,
+					 const xmlChar *URI);
+
+XSLTPUBFUN void * XSLTCALL
+		xsltStyleGetExtData	(xsltStylesheetPtr style,
+					 const xmlChar *URI);
+#ifdef XSLT_REFACTORED
+XSLTPUBFUN void * XSLTCALL
+		xsltStyleStylesheetLevelGetExtData(
+					 xsltStylesheetPtr style,
+					 const xmlChar * URI);
+#endif
+XSLTPUBFUN void XSLTCALL
+		xsltShutdownCtxtExts	(xsltTransformContextPtr ctxt);
+
+XSLTPUBFUN void XSLTCALL
+		xsltShutdownExts	(xsltStylesheetPtr style);
+
+XSLTPUBFUN xsltTransformContextPtr XSLTCALL
+		xsltXPathGetTransformContext
+					(xmlXPathParserContextPtr ctxt);
+
+/*
+ * extension functions
+*/
+XSLTPUBFUN int XSLTCALL
+		xsltRegisterExtModuleFunction
+					(const xmlChar *name,
+					 const xmlChar *URI,
+					 xmlXPathFunction function);
+XSLTPUBFUN xmlXPathFunction XSLTCALL
+	xsltExtModuleFunctionLookup	(const xmlChar *name,
+					 const xmlChar *URI);
+XSLTPUBFUN int XSLTCALL
+		xsltUnregisterExtModuleFunction
+					(const xmlChar *name,
+					 const xmlChar *URI);
+
+/*
+ * extension elements
+ */
+typedef xsltElemPreCompPtr (*xsltPreComputeFunction)
+					(xsltStylesheetPtr style,
+					 xmlNodePtr inst,
+					 xsltTransformFunction function);
+
+XSLTPUBFUN xsltElemPreCompPtr XSLTCALL
+		xsltNewElemPreComp	(xsltStylesheetPtr style,
+					 xmlNodePtr inst,
+					 xsltTransformFunction function);
+XSLTPUBFUN void XSLTCALL
+		xsltInitElemPreComp	(xsltElemPreCompPtr comp,
+					 xsltStylesheetPtr style,
+					 xmlNodePtr inst,
+					 xsltTransformFunction function,
+					 xsltElemPreCompDeallocator freeFunc);
+
+XSLTPUBFUN int XSLTCALL
+		xsltRegisterExtModuleElement
+					(const xmlChar *name,
+					 const xmlChar *URI,
+					 xsltPreComputeFunction precomp,
+					 xsltTransformFunction transform);
+XSLTPUBFUN xsltTransformFunction XSLTCALL
+		xsltExtElementLookup	(xsltTransformContextPtr ctxt,
+					 const xmlChar *name,
+					 const xmlChar *URI);
+XSLTPUBFUN xsltTransformFunction XSLTCALL
+		xsltExtModuleElementLookup
+					(const xmlChar *name,
+					 const xmlChar *URI);
+XSLTPUBFUN xsltPreComputeFunction XSLTCALL
+		xsltExtModuleElementPreComputeLookup
+					(const xmlChar *name,
+					 const xmlChar *URI);
+XSLTPUBFUN int XSLTCALL
+		xsltUnregisterExtModuleElement
+					(const xmlChar *name,
+					 const xmlChar *URI);
+
+/*
+ * top-level elements
+ */
+typedef void (*xsltTopLevelFunction)	(xsltStylesheetPtr style,
+					 xmlNodePtr inst);
+
+XSLTPUBFUN int XSLTCALL
+		xsltRegisterExtModuleTopLevel
+					(const xmlChar *name,
+					 const xmlChar *URI,
+					 xsltTopLevelFunction function);
+XSLTPUBFUN xsltTopLevelFunction XSLTCALL
+		xsltExtModuleTopLevelLookup
+					(const xmlChar *name,
+					 const xmlChar *URI);
+XSLTPUBFUN int XSLTCALL
+		xsltUnregisterExtModuleTopLevel
+					(const xmlChar *name,
+					 const xmlChar *URI);
+
+
+/* These 2 functions are deprecated for use within modules. */
+XSLTPUBFUN int XSLTCALL
+		xsltRegisterExtFunction	(xsltTransformContextPtr ctxt,
+					 const xmlChar *name,
+					 const xmlChar *URI,
+					 xmlXPathFunction function);
+XSLTPUBFUN int XSLTCALL
+		xsltRegisterExtElement	(xsltTransformContextPtr ctxt,
+					 const xmlChar *name,
+					 const xmlChar *URI,
+					 xsltTransformFunction function);
+
+/*
+ * Extension Prefix handling API.
+ * Those are used by the XSLT (pre)processor.
+ */
+
+XSLTPUBFUN int XSLTCALL
+		xsltRegisterExtPrefix	(xsltStylesheetPtr style,
+					 const xmlChar *prefix,
+					 const xmlChar *URI);
+XSLTPUBFUN int XSLTCALL
+		xsltCheckExtPrefix	(xsltStylesheetPtr style,
+					 const xmlChar *URI);
+XSLTPUBFUN int XSLTCALL
+		xsltCheckExtURI		(xsltStylesheetPtr style,
+					 const xmlChar *URI);
+XSLTPUBFUN int XSLTCALL
+		xsltInitCtxtExts	(xsltTransformContextPtr ctxt);
+XSLTPUBFUN void XSLTCALL
+		xsltFreeCtxtExts	(xsltTransformContextPtr ctxt);
+XSLTPUBFUN void XSLTCALL
+		xsltFreeExts		(xsltStylesheetPtr style);
+
+XSLTPUBFUN xsltElemPreCompPtr XSLTCALL
+		xsltPreComputeExtModuleElement
+					(xsltStylesheetPtr style,
+					 xmlNodePtr inst);
+/*
+ * Extension Infos access.
+ * Used by exslt initialisation
+ */
+
+XSLTPUBFUN xmlHashTablePtr XSLTCALL
+		xsltGetExtInfo		(xsltStylesheetPtr style,
+					 const xmlChar *URI);
+
+/**
+ * Test of the extension module API
+ */
+XSLTPUBFUN void XSLTCALL
+		xsltRegisterTestModule	(void);
+XSLTPUBFUN void XSLTCALL
+		xsltDebugDumpExtensions	(FILE * output);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_XSLT_EXTENSION_H__ */
+
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/extra.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/extra.h
new file mode 100644
index 00000000..e512fd03
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/extra.h
@@ -0,0 +1,72 @@
+/*
+ * Summary: interface for the non-standard features
+ * Description: implement some extension outside the XSLT namespace
+ *              but not EXSLT with is in a different library.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XSLT_EXTRA_H__
+#define __XML_XSLT_EXTRA_H__
+
+#include <libxml/xpath.h>
+#include "xsltexports.h"
+#include "xsltInternals.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * XSLT_LIBXSLT_NAMESPACE:
+ *
+ * This is the libxslt namespace for specific extensions.
+ */
+#define XSLT_LIBXSLT_NAMESPACE ((xmlChar *) "http://xmlsoft.org/XSLT/namespace")
+
+/**
+ * XSLT_SAXON_NAMESPACE:
+ *
+ * This is Michael Kay's Saxon processor namespace for extensions.
+ */
+#define XSLT_SAXON_NAMESPACE ((xmlChar *) "http://icl.com/saxon")
+
+/**
+ * XSLT_XT_NAMESPACE:
+ *
+ * This is James Clark's XT processor namespace for extensions.
+ */
+#define XSLT_XT_NAMESPACE ((xmlChar *) "http://www.jclark.com/xt")
+
+/**
+ * XSLT_XALAN_NAMESPACE:
+ *
+ * This is the Apache project XALAN processor namespace for extensions.
+ */
+#define XSLT_XALAN_NAMESPACE ((xmlChar *)	\
+	                        "org.apache.xalan.xslt.extensions.Redirect")
+
+
+XSLTPUBFUN void XSLTCALL
+		xsltFunctionNodeSet	(xmlXPathParserContextPtr ctxt,
+					 int nargs);
+XSLTPUBFUN void XSLTCALL
+		xsltDebug		(xsltTransformContextPtr ctxt,
+					 xmlNodePtr node,
+					 xmlNodePtr inst,
+					 xsltElemPreCompPtr comp);
+
+
+XSLTPUBFUN void XSLTCALL
+		xsltRegisterExtras	(xsltTransformContextPtr ctxt);
+XSLTPUBFUN void XSLTCALL
+		xsltRegisterAllExtras	(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_XSLT_EXTRA_H__ */
+
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/functions.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/functions.h
new file mode 100644
index 00000000..5455b7f4
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/functions.h
@@ -0,0 +1,78 @@
+/*
+ * Summary: interface for the XSLT functions not from XPath
+ * Description: a set of extra functions coming from XSLT but not in XPath
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard and Bjorn Reese <breese@users.sourceforge.net>
+ */
+
+#ifndef __XML_XSLT_FUNCTIONS_H__
+#define __XML_XSLT_FUNCTIONS_H__
+
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+#include "xsltexports.h"
+#include "xsltInternals.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * XSLT_REGISTER_FUNCTION_LOOKUP:
+ *
+ * Registering macro, not general purpose at all but used in different modules.
+ */
+#define XSLT_REGISTER_FUNCTION_LOOKUP(ctxt)			\
+    xmlXPathRegisterFuncLookup((ctxt)->xpathCtxt,		\
+	xsltXPathFunctionLookup,				\
+	(void *)(ctxt->xpathCtxt));
+
+XSLTPUBFUN xmlXPathFunction XSLTCALL
+	xsltXPathFunctionLookup		(void *vctxt,
+					 const xmlChar *name,
+					 const xmlChar *ns_uri);
+
+/*
+ * Interfaces for the functions implementations.
+ */
+
+XSLTPUBFUN void XSLTCALL
+	xsltDocumentFunction		(xmlXPathParserContextPtr ctxt,
+					 int nargs);
+XSLTPUBFUN void XSLTCALL
+	xsltKeyFunction			(xmlXPathParserContextPtr ctxt,
+					 int nargs);
+XSLTPUBFUN void XSLTCALL
+	xsltUnparsedEntityURIFunction	(xmlXPathParserContextPtr ctxt,
+					 int nargs);
+XSLTPUBFUN void XSLTCALL
+	xsltFormatNumberFunction	(xmlXPathParserContextPtr ctxt,
+					 int nargs);
+XSLTPUBFUN void XSLTCALL
+	xsltGenerateIdFunction		(xmlXPathParserContextPtr ctxt,
+					 int nargs);
+XSLTPUBFUN void XSLTCALL
+	xsltSystemPropertyFunction	(xmlXPathParserContextPtr ctxt,
+					 int nargs);
+XSLTPUBFUN void XSLTCALL
+	xsltElementAvailableFunction	(xmlXPathParserContextPtr ctxt,
+					 int nargs);
+XSLTPUBFUN void XSLTCALL
+	xsltFunctionAvailableFunction	(xmlXPathParserContextPtr ctxt,
+					 int nargs);
+
+/*
+ * And the registration
+ */
+
+XSLTPUBFUN void XSLTCALL
+	xsltRegisterAllFunctions	(xmlXPathContextPtr ctxt);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_XSLT_FUNCTIONS_H__ */
+
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/imports.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/imports.h
new file mode 100644
index 00000000..95e44e51
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/imports.h
@@ -0,0 +1,75 @@
+/*
+ * Summary: interface for the XSLT import support
+ * Description: macros and fuctions needed to implement and
+ *              access the import tree
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XSLT_IMPORTS_H__
+#define __XML_XSLT_IMPORTS_H__
+
+#include <libxml/tree.h>
+#include "xsltexports.h"
+#include "xsltInternals.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * XSLT_GET_IMPORT_PTR:
+ *
+ * A macro to import pointers from the stylesheet cascading order.
+ */
+#define XSLT_GET_IMPORT_PTR(res, style, name) {			\
+    xsltStylesheetPtr st = style;				\
+    res = NULL;							\
+    while (st != NULL) {					\
+	if (st->name != NULL) { res = st->name; break; }	\
+	st = xsltNextImport(st);				\
+    }}
+
+/**
+ * XSLT_GET_IMPORT_INT:
+ *
+ * A macro to import intergers from the stylesheet cascading order.
+ */
+#define XSLT_GET_IMPORT_INT(res, style, name) {			\
+    xsltStylesheetPtr st = style;				\
+    res = -1;							\
+    while (st != NULL) {					\
+	if (st->name != -1) { res = st->name; break; }	\
+	st = xsltNextImport(st);				\
+    }}
+
+/*
+ * Module interfaces
+ */
+XSLTPUBFUN int XSLTCALL
+			xsltParseStylesheetImport(xsltStylesheetPtr style,
+						  xmlNodePtr cur);
+XSLTPUBFUN int XSLTCALL
+			xsltParseStylesheetInclude
+						 (xsltStylesheetPtr style,
+						  xmlNodePtr cur);
+XSLTPUBFUN xsltStylesheetPtr XSLTCALL
+			xsltNextImport		 (xsltStylesheetPtr style);
+XSLTPUBFUN int XSLTCALL
+			xsltNeedElemSpaceHandling(xsltTransformContextPtr ctxt);
+XSLTPUBFUN int XSLTCALL
+			xsltFindElemSpaceHandling(xsltTransformContextPtr ctxt,
+						  xmlNodePtr node);
+XSLTPUBFUN xsltTemplatePtr XSLTCALL
+			xsltFindTemplate	 (xsltTransformContextPtr ctxt,
+						  const xmlChar *name,
+						  const xmlChar *nameURI);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_XSLT_IMPORTS_H__ */
+
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/keys.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/keys.h
new file mode 100644
index 00000000..757d1224
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/keys.h
@@ -0,0 +1,53 @@
+/*
+ * Summary:  interface for the key matching used in key() and template matches.
+ * Description: implementation of the key mechanims.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XSLT_KEY_H__
+#define __XML_XSLT_KEY_H__
+
+#include <libxml/xpath.h>
+#include "xsltexports.h"
+#include "xsltInternals.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * NODE_IS_KEYED:
+ *
+ * check for bit 15 set
+ */
+#define NODE_IS_KEYED (1 >> 15)
+
+XSLTPUBFUN int XSLTCALL
+		xsltAddKey		(xsltStylesheetPtr style,
+					 const xmlChar *name,
+					 const xmlChar *nameURI,
+					 const xmlChar *match,
+					 const xmlChar *use,
+					 xmlNodePtr inst);
+XSLTPUBFUN xmlNodeSetPtr XSLTCALL
+		xsltGetKey		(xsltTransformContextPtr ctxt,
+					 const xmlChar *name,
+					 const xmlChar *nameURI,
+					 const xmlChar *value);
+XSLTPUBFUN void XSLTCALL
+		xsltInitCtxtKeys	(xsltTransformContextPtr ctxt,
+					 xsltDocumentPtr doc);
+XSLTPUBFUN void XSLTCALL
+		xsltFreeKeys		(xsltStylesheetPtr style);
+XSLTPUBFUN void XSLTCALL
+		xsltFreeDocumentKeys	(xsltDocumentPtr doc);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_XSLT_H__ */
+
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/namespaces.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/namespaces.h
new file mode 100644
index 00000000..fa2d3b4c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/namespaces.h
@@ -0,0 +1,68 @@
+/*
+ * Summary: interface for the XSLT namespace handling
+ * Description: set of function easing the processing and generation
+ *              of namespace nodes in XSLT.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XSLT_NAMESPACES_H__
+#define __XML_XSLT_NAMESPACES_H__
+
+#include <libxml/tree.h>
+#include "xsltexports.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Used within nsAliases hashtable when the default namespace is required
+ * but it's not been explicitly defined
+ */
+/**
+ * UNDEFINED_DEFAULT_NS:
+ *
+ * Special value for undefined namespace, internal
+ */
+#define	UNDEFINED_DEFAULT_NS	(const xmlChar *) -1L
+
+XSLTPUBFUN void XSLTCALL
+		xsltNamespaceAlias	(xsltStylesheetPtr style,
+					 xmlNodePtr node);
+XSLTPUBFUN xmlNsPtr XSLTCALL
+		xsltGetNamespace	(xsltTransformContextPtr ctxt,
+					 xmlNodePtr cur,
+					 xmlNsPtr ns,
+					 xmlNodePtr out);
+XSLTPUBFUN xmlNsPtr XSLTCALL
+		xsltGetPlainNamespace	(xsltTransformContextPtr ctxt,
+					 xmlNodePtr cur,
+					 xmlNsPtr ns,
+					 xmlNodePtr out);
+XSLTPUBFUN xmlNsPtr XSLTCALL
+		xsltGetSpecialNamespace	(xsltTransformContextPtr ctxt,
+					 xmlNodePtr cur,
+					 const xmlChar *URI,
+					 const xmlChar *prefix,
+					 xmlNodePtr out);
+XSLTPUBFUN xmlNsPtr XSLTCALL
+		xsltCopyNamespace	(xsltTransformContextPtr ctxt,
+					 xmlNodePtr elem,
+					 xmlNsPtr ns);
+XSLTPUBFUN xmlNsPtr XSLTCALL
+		xsltCopyNamespaceList	(xsltTransformContextPtr ctxt,
+					 xmlNodePtr node,
+					 xmlNsPtr cur);
+XSLTPUBFUN void XSLTCALL
+		xsltFreeNamespaceAliasHashes
+					(xsltStylesheetPtr style);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_XSLT_NAMESPACES_H__ */
+
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/numbersInternals.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/numbersInternals.h
new file mode 100644
index 00000000..85245928
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/numbersInternals.h
@@ -0,0 +1,73 @@
+/*
+ * Summary: Implementation of the XSLT number functions
+ * Description: Implementation of the XSLT number functions
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Bjorn Reese <breese@users.sourceforge.net> and Daniel Veillard
+ */
+
+#ifndef __XML_XSLT_NUMBERSINTERNALS_H__
+#define __XML_XSLT_NUMBERSINTERNALS_H__
+
+#include <libxml/tree.h>
+#include "xsltexports.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _xsltCompMatch;
+
+/**
+ * xsltNumberData:
+ *
+ * This data structure is just a wrapper to pass xsl:number data in.
+ */
+typedef struct _xsltNumberData xsltNumberData;
+typedef xsltNumberData *xsltNumberDataPtr;
+
+struct _xsltNumberData {
+    const xmlChar *level;
+    const xmlChar *count;
+    const xmlChar *from;
+    const xmlChar *value;
+    const xmlChar *format;
+    int has_format;
+    int digitsPerGroup;
+    int groupingCharacter;
+    int groupingCharacterLen;
+    xmlDocPtr doc;
+    xmlNodePtr node;
+    struct _xsltCompMatch *countPat;
+    struct _xsltCompMatch *fromPat;
+
+    /*
+     * accelerators
+     */
+};
+
+/**
+ * xsltFormatNumberInfo,:
+ *
+ * This data structure lists the various parameters needed to format numbers.
+ */
+typedef struct _xsltFormatNumberInfo xsltFormatNumberInfo;
+typedef xsltFormatNumberInfo *xsltFormatNumberInfoPtr;
+
+struct _xsltFormatNumberInfo {
+    int	    integer_hash;	/* Number of '#' in integer part */
+    int	    integer_digits;	/* Number of '0' in integer part */
+    int	    frac_digits;	/* Number of '0' in fractional part */
+    int	    frac_hash;		/* Number of '#' in fractional part */
+    int	    group;		/* Number of chars per display 'group' */
+    int     multiplier;		/* Scaling for percent or permille */
+    char    add_decimal;	/* Flag for whether decimal point appears in pattern */
+    char    is_multiplier_set;	/* Flag to catch multiple occurences of percent/permille */
+    char    is_negative_pattern;/* Flag for processing -ve prefix/suffix */
+};
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __XML_XSLT_NUMBERSINTERNALS_H__ */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/pattern.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/pattern.h
new file mode 100644
index 00000000..a0991c0c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/pattern.h
@@ -0,0 +1,84 @@
+/*
+ * Summary: interface for the pattern matching used in template matches.
+ * Description: the implementation of the lookup of the right template
+ *              for a given node must be really fast in order to keep
+ *              decent performances.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XSLT_PATTERN_H__
+#define __XML_XSLT_PATTERN_H__
+
+#include "xsltInternals.h"
+#include "xsltexports.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * xsltCompMatch:
+ *
+ * Data structure used for the implementation of patterns.
+ * It is kept private (in pattern.c).
+ */
+typedef struct _xsltCompMatch xsltCompMatch;
+typedef xsltCompMatch *xsltCompMatchPtr;
+
+/*
+ * Pattern related interfaces.
+ */
+
+XSLTPUBFUN xsltCompMatchPtr XSLTCALL
+		xsltCompilePattern	(const xmlChar *pattern,
+					 xmlDocPtr doc,
+					 xmlNodePtr node,
+					 xsltStylesheetPtr style,
+					 xsltTransformContextPtr runtime);
+XSLTPUBFUN void XSLTCALL
+		xsltFreeCompMatchList	(xsltCompMatchPtr comp);
+XSLTPUBFUN int XSLTCALL
+		xsltTestCompMatchList	(xsltTransformContextPtr ctxt,
+					 xmlNodePtr node,
+					 xsltCompMatchPtr comp);
+XSLTPUBFUN void XSLTCALL
+		xsltCompMatchClearCache	(xsltTransformContextPtr ctxt,
+					 xsltCompMatchPtr comp);
+XSLTPUBFUN void XSLTCALL
+		xsltNormalizeCompSteps	(void *payload,
+					 void *data,
+					 const xmlChar *name);
+
+/*
+ * Template related interfaces.
+ */
+XSLTPUBFUN int XSLTCALL
+		xsltAddTemplate		(xsltStylesheetPtr style,
+					 xsltTemplatePtr cur,
+					 const xmlChar *mode,
+					 const xmlChar *modeURI);
+XSLTPUBFUN xsltTemplatePtr XSLTCALL
+		xsltGetTemplate		(xsltTransformContextPtr ctxt,
+					 xmlNodePtr node,
+					 xsltStylesheetPtr style);
+XSLTPUBFUN void XSLTCALL
+		xsltFreeTemplateHashes	(xsltStylesheetPtr style);
+XSLTPUBFUN void XSLTCALL
+		xsltCleanupTemplates	(xsltStylesheetPtr style);
+
+#if 0
+int		xsltMatchPattern	(xsltTransformContextPtr ctxt,
+					 xmlNodePtr node,
+					 const xmlChar *pattern,
+					 xmlDocPtr ctxtdoc,
+					 xmlNodePtr ctxtnode);
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_XSLT_PATTERN_H__ */
+
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/preproc.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/preproc.h
new file mode 100644
index 00000000..2a2fc7e4
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/preproc.h
@@ -0,0 +1,43 @@
+/*
+ * Summary: precomputing stylesheets
+ * Description: this is the compilation phase, where most of the
+ *              stylesheet is "compiled" into faster to use data.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XSLT_PRECOMP_H__
+#define __XML_XSLT_PRECOMP_H__
+
+#include <libxml/tree.h>
+#include "xsltexports.h"
+#include "xsltInternals.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Interfaces
+ */
+XSLTPUBVAR const xmlChar *xsltExtMarker;
+
+XSLTPUBFUN xsltElemPreCompPtr XSLTCALL
+		xsltDocumentComp	(xsltStylesheetPtr style,
+					 xmlNodePtr inst,
+					 xsltTransformFunction function);
+
+XSLTPUBFUN void XSLTCALL
+		xsltStylePreCompute	(xsltStylesheetPtr style,
+					 xmlNodePtr inst);
+XSLTPUBFUN void XSLTCALL
+		xsltFreeStylePreComps	(xsltStylesheetPtr style);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_XSLT_PRECOMP_H__ */
+
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/security.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/security.h
new file mode 100644
index 00000000..bab5c8c6
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/security.h
@@ -0,0 +1,104 @@
+/*
+ * Summary: interface for the libxslt security framework
+ * Description: the libxslt security framework allow to restrict
+ *              the access to new resources (file or URL) from
+ *              the stylesheet at runtime.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XSLT_SECURITY_H__
+#define __XML_XSLT_SECURITY_H__
+
+#include <libxml/tree.h>
+#include "xsltexports.h"
+#include "xsltInternals.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * xsltSecurityPref:
+ *
+ * structure to indicate the preferences for security in the XSLT
+ * transformation.
+ */
+typedef struct _xsltSecurityPrefs xsltSecurityPrefs;
+typedef xsltSecurityPrefs *xsltSecurityPrefsPtr;
+
+/**
+ * xsltSecurityOption:
+ *
+ * the set of option that can be configured
+ */
+typedef enum {
+    XSLT_SECPREF_READ_FILE = 1,
+    XSLT_SECPREF_WRITE_FILE,
+    XSLT_SECPREF_CREATE_DIRECTORY,
+    XSLT_SECPREF_READ_NETWORK,
+    XSLT_SECPREF_WRITE_NETWORK
+} xsltSecurityOption;
+
+/**
+ * xsltSecurityCheck:
+ *
+ * User provided function to check the value of a string like a file
+ * path or an URL ...
+ */
+typedef int (*xsltSecurityCheck)	(xsltSecurityPrefsPtr sec,
+					 xsltTransformContextPtr ctxt,
+					 const char *value);
+
+/*
+ * Module interfaces
+ */
+XSLTPUBFUN xsltSecurityPrefsPtr XSLTCALL
+		    xsltNewSecurityPrefs	(void);
+XSLTPUBFUN void XSLTCALL
+		    xsltFreeSecurityPrefs	(xsltSecurityPrefsPtr sec);
+XSLTPUBFUN int XSLTCALL
+		    xsltSetSecurityPrefs	(xsltSecurityPrefsPtr sec,
+						 xsltSecurityOption option,
+						 xsltSecurityCheck func);
+XSLTPUBFUN xsltSecurityCheck XSLTCALL
+		    xsltGetSecurityPrefs	(xsltSecurityPrefsPtr sec,
+						 xsltSecurityOption option);
+
+XSLTPUBFUN void XSLTCALL
+		    xsltSetDefaultSecurityPrefs	(xsltSecurityPrefsPtr sec);
+XSLTPUBFUN xsltSecurityPrefsPtr XSLTCALL
+		    xsltGetDefaultSecurityPrefs	(void);
+
+XSLTPUBFUN int XSLTCALL
+		    xsltSetCtxtSecurityPrefs	(xsltSecurityPrefsPtr sec,
+						 xsltTransformContextPtr ctxt);
+
+XSLTPUBFUN int XSLTCALL
+		    xsltSecurityAllow		(xsltSecurityPrefsPtr sec,
+						 xsltTransformContextPtr ctxt,
+						 const char *value);
+XSLTPUBFUN int XSLTCALL
+		    xsltSecurityForbid		(xsltSecurityPrefsPtr sec,
+						 xsltTransformContextPtr ctxt,
+						 const char *value);
+/*
+ * internal interfaces
+ */
+XSLTPUBFUN int XSLTCALL
+		    xsltCheckWrite		(xsltSecurityPrefsPtr sec,
+						 xsltTransformContextPtr ctxt,
+						 const xmlChar *URL);
+XSLTPUBFUN int XSLTCALL
+		    xsltCheckRead		(xsltSecurityPrefsPtr sec,
+						 xsltTransformContextPtr ctxt,
+						 const xmlChar *URL);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_XSLT_SECURITY_H__ */
+
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/templates.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/templates.h
new file mode 100644
index 00000000..84a9de4d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/templates.h
@@ -0,0 +1,77 @@
+/*
+ * Summary: interface for the template processing
+ * Description: This set of routine encapsulates XPath calls
+ *              and Attribute Value Templates evaluation.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XSLT_TEMPLATES_H__
+#define __XML_XSLT_TEMPLATES_H__
+
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+#include "xsltexports.h"
+#include "xsltInternals.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+XSLTPUBFUN int XSLTCALL
+		xsltEvalXPathPredicate		(xsltTransformContextPtr ctxt,
+						 xmlXPathCompExprPtr comp,
+		                                 xmlNsPtr *nsList,
+						 int nsNr);
+XSLTPUBFUN xmlChar * XSLTCALL
+		xsltEvalTemplateString		(xsltTransformContextPtr ctxt,
+						 xmlNodePtr contextNode,
+						 xmlNodePtr inst);
+XSLTPUBFUN xmlChar * XSLTCALL
+		xsltEvalAttrValueTemplate	(xsltTransformContextPtr ctxt,
+						 xmlNodePtr node,
+						 const xmlChar *name,
+						 const xmlChar *ns);
+XSLTPUBFUN const xmlChar * XSLTCALL
+		xsltEvalStaticAttrValueTemplate	(xsltStylesheetPtr style,
+						 xmlNodePtr node,
+						 const xmlChar *name,
+						 const xmlChar *ns,
+						 int *found);
+
+/* TODO: this is obviously broken ... the namespaces should be passed too ! */
+XSLTPUBFUN xmlChar * XSLTCALL
+		xsltEvalXPathString		(xsltTransformContextPtr ctxt,
+						 xmlXPathCompExprPtr comp);
+XSLTPUBFUN xmlChar * XSLTCALL
+		xsltEvalXPathStringNs		(xsltTransformContextPtr ctxt,
+						 xmlXPathCompExprPtr comp,
+						 int nsNr,
+						 xmlNsPtr *nsList);
+
+XSLTPUBFUN xmlNodePtr * XSLTCALL
+		xsltTemplateProcess		(xsltTransformContextPtr ctxt,
+						 xmlNodePtr node);
+XSLTPUBFUN xmlAttrPtr XSLTCALL
+		xsltAttrListTemplateProcess	(xsltTransformContextPtr ctxt,
+						 xmlNodePtr target,
+						 xmlAttrPtr cur);
+XSLTPUBFUN xmlAttrPtr XSLTCALL
+		xsltAttrTemplateProcess		(xsltTransformContextPtr ctxt,
+						 xmlNodePtr target,
+						 xmlAttrPtr attr);
+XSLTPUBFUN xmlChar * XSLTCALL
+		xsltAttrTemplateValueProcess	(xsltTransformContextPtr ctxt,
+						 const xmlChar* attr);
+XSLTPUBFUN xmlChar * XSLTCALL
+		xsltAttrTemplateValueProcessNode(xsltTransformContextPtr ctxt,
+						 const xmlChar* str,
+						 xmlNodePtr node);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_XSLT_TEMPLATES_H__ */
+
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/transform.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/transform.h
new file mode 100644
index 00000000..5a6f7959
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/transform.h
@@ -0,0 +1,207 @@
+/*
+ * Summary: the XSLT engine transformation part.
+ * Description: This module implements the bulk of the actual
+ *              transformation processing. Most of the xsl: element
+ *              constructs are implemented in this module.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XSLT_TRANSFORM_H__
+#define __XML_XSLT_TRANSFORM_H__
+
+#include <libxml/parser.h>
+#include <libxml/xmlIO.h>
+#include "xsltexports.h"
+#include <libxslt/xsltInternals.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * XInclude default processing.
+ */
+XSLTPUBFUN void XSLTCALL
+		xsltSetXIncludeDefault	(int xinclude);
+XSLTPUBFUN int XSLTCALL
+		xsltGetXIncludeDefault	(void);
+
+/**
+ * Export context to users.
+ */
+XSLTPUBFUN xsltTransformContextPtr XSLTCALL
+		xsltNewTransformContext	(xsltStylesheetPtr style,
+					 xmlDocPtr doc);
+
+XSLTPUBFUN void XSLTCALL
+		xsltFreeTransformContext(xsltTransformContextPtr ctxt);
+
+XSLTPUBFUN xmlDocPtr XSLTCALL
+		xsltApplyStylesheetUser	(xsltStylesheetPtr style,
+					 xmlDocPtr doc,
+					 const char **params,
+					 const char *output,
+					 FILE * profile,
+					 xsltTransformContextPtr userCtxt);
+XSLTPUBFUN void XSLTCALL
+                xsltProcessOneNode      (xsltTransformContextPtr ctxt,
+                                         xmlNodePtr node,
+                                         xsltStackElemPtr params);
+/**
+ * Private Interfaces.
+ */
+XSLTPUBFUN void XSLTCALL
+		xsltApplyStripSpaces	(xsltTransformContextPtr ctxt,
+					 xmlNodePtr node);
+XSLTPUBFUN xmlDocPtr XSLTCALL
+		xsltApplyStylesheet	(xsltStylesheetPtr style,
+					 xmlDocPtr doc,
+					 const char **params);
+XSLTPUBFUN xmlDocPtr XSLTCALL
+		xsltProfileStylesheet	(xsltStylesheetPtr style,
+					 xmlDocPtr doc,
+					 const char **params,
+					 FILE * output);
+XSLTPUBFUN int XSLTCALL
+		xsltRunStylesheet	(xsltStylesheetPtr style,
+					 xmlDocPtr doc,
+					 const char **params,
+					 const char *output,
+					 xmlSAXHandlerPtr SAX,
+					 xmlOutputBufferPtr IObuf);
+XSLTPUBFUN int XSLTCALL
+		xsltRunStylesheetUser	(xsltStylesheetPtr style,
+					 xmlDocPtr doc,
+					 const char **params,
+					 const char *output,
+					 xmlSAXHandlerPtr SAX,
+					 xmlOutputBufferPtr IObuf,
+					 FILE * profile,
+					 xsltTransformContextPtr userCtxt);
+XSLTPUBFUN void XSLTCALL
+		xsltApplyOneTemplate	(xsltTransformContextPtr ctxt,
+					 xmlNodePtr node,
+					 xmlNodePtr list,
+					 xsltTemplatePtr templ,
+					 xsltStackElemPtr params);
+XSLTPUBFUN void XSLTCALL
+		xsltDocumentElem	(xsltTransformContextPtr ctxt,
+	                                 xmlNodePtr node,
+					 xmlNodePtr inst,
+					 xsltElemPreCompPtr comp);
+XSLTPUBFUN void XSLTCALL
+		xsltSort		(xsltTransformContextPtr ctxt,
+	                                 xmlNodePtr node,
+					 xmlNodePtr inst,
+					 xsltElemPreCompPtr comp);
+XSLTPUBFUN void XSLTCALL
+		xsltCopy		(xsltTransformContextPtr ctxt,
+	                                 xmlNodePtr node,
+					 xmlNodePtr inst,
+					 xsltElemPreCompPtr comp);
+XSLTPUBFUN void XSLTCALL
+		xsltText		(xsltTransformContextPtr ctxt,
+	                                 xmlNodePtr node,
+					 xmlNodePtr inst,
+					 xsltElemPreCompPtr comp);
+XSLTPUBFUN void XSLTCALL
+		xsltElement		(xsltTransformContextPtr ctxt,
+	                                 xmlNodePtr node,
+					 xmlNodePtr inst,
+					 xsltElemPreCompPtr comp);
+XSLTPUBFUN void XSLTCALL
+		xsltComment		(xsltTransformContextPtr ctxt,
+	                                 xmlNodePtr node,
+					 xmlNodePtr inst,
+					 xsltElemPreCompPtr comp);
+XSLTPUBFUN void XSLTCALL
+		xsltAttribute		(xsltTransformContextPtr ctxt,
+	                                 xmlNodePtr node,
+					 xmlNodePtr inst,
+					 xsltElemPreCompPtr comp);
+XSLTPUBFUN void XSLTCALL
+		xsltProcessingInstruction(xsltTransformContextPtr ctxt,
+	                                 xmlNodePtr node,
+					 xmlNodePtr inst,
+					 xsltElemPreCompPtr comp);
+XSLTPUBFUN void XSLTCALL
+		xsltCopyOf		(xsltTransformContextPtr ctxt,
+	                                 xmlNodePtr node,
+					 xmlNodePtr inst,
+					 xsltElemPreCompPtr comp);
+XSLTPUBFUN void XSLTCALL
+		xsltValueOf		(xsltTransformContextPtr ctxt,
+	                                 xmlNodePtr node,
+					 xmlNodePtr inst,
+					 xsltElemPreCompPtr comp);
+XSLTPUBFUN void XSLTCALL
+		xsltNumber		(xsltTransformContextPtr ctxt,
+	                                 xmlNodePtr node,
+					 xmlNodePtr inst,
+					 xsltElemPreCompPtr comp);
+XSLTPUBFUN void XSLTCALL
+		xsltApplyImports	(xsltTransformContextPtr ctxt,
+	                                 xmlNodePtr node,
+					 xmlNodePtr inst,
+					 xsltElemPreCompPtr comp);
+XSLTPUBFUN void XSLTCALL
+		xsltCallTemplate	(xsltTransformContextPtr ctxt,
+	                                 xmlNodePtr node,
+					 xmlNodePtr inst,
+					 xsltElemPreCompPtr comp);
+XSLTPUBFUN void XSLTCALL
+		xsltApplyTemplates	(xsltTransformContextPtr ctxt,
+	                                 xmlNodePtr node,
+					 xmlNodePtr inst,
+					 xsltElemPreCompPtr comp);
+XSLTPUBFUN void XSLTCALL
+		xsltChoose		(xsltTransformContextPtr ctxt,
+	                                 xmlNodePtr node,
+					 xmlNodePtr inst,
+					 xsltElemPreCompPtr comp);
+XSLTPUBFUN void XSLTCALL
+		xsltIf			(xsltTransformContextPtr ctxt,
+	                                 xmlNodePtr node,
+					 xmlNodePtr inst,
+					 xsltElemPreCompPtr comp);
+XSLTPUBFUN void XSLTCALL
+		xsltForEach		(xsltTransformContextPtr ctxt,
+	                                 xmlNodePtr node,
+					 xmlNodePtr inst,
+					 xsltElemPreCompPtr comp);
+XSLTPUBFUN void XSLTCALL
+		xsltRegisterAllElement	(xsltTransformContextPtr ctxt);
+
+XSLTPUBFUN xmlNodePtr XSLTCALL
+		xsltCopyTextString	(xsltTransformContextPtr ctxt,
+					 xmlNodePtr target,
+					 const xmlChar *string,
+					 int noescape);
+
+/* Following 2 functions needed for libexslt/functions.c */
+XSLTPUBFUN void XSLTCALL
+		xsltLocalVariablePop	(xsltTransformContextPtr ctxt,
+					 int limitNr,
+					 int level);
+XSLTPUBFUN int XSLTCALL
+		xsltLocalVariablePush	(xsltTransformContextPtr ctxt,
+					 xsltStackElemPtr variable,
+					 int level);
+/*
+ * Hook for the debugger if activated.
+ */
+XSLTPUBFUN void XSLTCALL
+		xslHandleDebugger	(xmlNodePtr cur,
+					 xmlNodePtr node,
+					 xsltTemplatePtr templ,
+					 xsltTransformContextPtr ctxt);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_XSLT_TRANSFORM_H__ */
+
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/variables.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/variables.h
new file mode 100644
index 00000000..e2adee0f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/variables.h
@@ -0,0 +1,118 @@
+/*
+ * Summary: interface for the variable matching and lookup.
+ * Description: interface for the variable matching and lookup.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XSLT_VARIABLES_H__
+#define __XML_XSLT_VARIABLES_H__
+
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+#include "xsltexports.h"
+#include "xsltInternals.h"
+#include "functions.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * XSLT_REGISTER_VARIABLE_LOOKUP:
+ *
+ * Registering macro, not general purpose at all but used in different modules.
+ */
+
+#define XSLT_REGISTER_VARIABLE_LOOKUP(ctxt)			\
+    xmlXPathRegisterVariableLookup((ctxt)->xpathCtxt,		\
+	       xsltXPathVariableLookup,	(void *)(ctxt));	\
+    xsltRegisterAllFunctions((ctxt)->xpathCtxt);		\
+    xsltRegisterAllElement(ctxt);				\
+    (ctxt)->xpathCtxt->extra = ctxt
+
+/*
+ * Flags for memory management of RVTs
+ */
+
+/**
+ * XSLT_RVT_LOCAL:
+ *
+ * RVT is destroyed after the current instructions ends.
+ */
+#define XSLT_RVT_LOCAL       1
+
+/**
+ * XSLT_RVT_FUNC_RESULT:
+ *
+ * RVT is part of results returned with func:result. The RVT won't be
+ * destroyed after exiting a template and will be reset to XSLT_RVT_LOCAL or
+ * XSLT_RVT_VARIABLE in the template that receives the return value.
+ */
+#define XSLT_RVT_FUNC_RESULT 2
+
+/**
+ * XSLT_RVT_GLOBAL:
+ *
+ * RVT is part of a global variable.
+ */
+#define XSLT_RVT_GLOBAL      3
+
+/*
+ * Interfaces for the variable module.
+ */
+
+XSLTPUBFUN int XSLTCALL
+		xsltEvalGlobalVariables		(xsltTransformContextPtr ctxt);
+XSLTPUBFUN int XSLTCALL
+		xsltEvalUserParams		(xsltTransformContextPtr ctxt,
+						 const char **params);
+XSLTPUBFUN int XSLTCALL
+		xsltQuoteUserParams		(xsltTransformContextPtr ctxt,
+						 const char **params);
+XSLTPUBFUN int XSLTCALL
+		xsltEvalOneUserParam		(xsltTransformContextPtr ctxt,
+						 const xmlChar * name,
+						 const xmlChar * value);
+XSLTPUBFUN int XSLTCALL
+		xsltQuoteOneUserParam		(xsltTransformContextPtr ctxt,
+						 const xmlChar * name,
+						 const xmlChar * value);
+
+XSLTPUBFUN void XSLTCALL
+		xsltParseGlobalVariable		(xsltStylesheetPtr style,
+						 xmlNodePtr cur);
+XSLTPUBFUN void XSLTCALL
+		xsltParseGlobalParam		(xsltStylesheetPtr style,
+						 xmlNodePtr cur);
+XSLTPUBFUN void XSLTCALL
+		xsltParseStylesheetVariable	(xsltTransformContextPtr ctxt,
+						 xmlNodePtr cur);
+XSLTPUBFUN void XSLTCALL
+		xsltParseStylesheetParam	(xsltTransformContextPtr ctxt,
+						 xmlNodePtr cur);
+XSLTPUBFUN xsltStackElemPtr XSLTCALL
+		xsltParseStylesheetCallerParam	(xsltTransformContextPtr ctxt,
+						 xmlNodePtr cur);
+XSLTPUBFUN int XSLTCALL
+		xsltAddStackElemList		(xsltTransformContextPtr ctxt,
+						 xsltStackElemPtr elems);
+XSLTPUBFUN void XSLTCALL
+		xsltFreeGlobalVariables		(xsltTransformContextPtr ctxt);
+XSLTPUBFUN xmlXPathObjectPtr XSLTCALL
+		xsltVariableLookup		(xsltTransformContextPtr ctxt,
+						 const xmlChar *name,
+						 const xmlChar *ns_uri);
+XSLTPUBFUN xmlXPathObjectPtr XSLTCALL
+		xsltXPathVariableLookup		(void *ctxt,
+						 const xmlChar *name,
+						 const xmlChar *ns_uri);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_XSLT_VARIABLES_H__ */
+
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/xslt.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/xslt.h
new file mode 100644
index 00000000..02f491a5
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/xslt.h
@@ -0,0 +1,110 @@
+/*
+ * Summary: Interfaces, constants and types related to the XSLT engine
+ * Description: Interfaces, constants and types related to the XSLT engine
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XSLT_H__
+#define __XML_XSLT_H__
+
+#include <libxml/tree.h>
+#include "xsltexports.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * XSLT_DEFAULT_VERSION:
+ *
+ * The default version of XSLT supported.
+ */
+#define XSLT_DEFAULT_VERSION     "1.0"
+
+/**
+ * XSLT_DEFAULT_VENDOR:
+ *
+ * The XSLT "vendor" string for this processor.
+ */
+#define XSLT_DEFAULT_VENDOR      "libxslt"
+
+/**
+ * XSLT_DEFAULT_URL:
+ *
+ * The XSLT "vendor" URL for this processor.
+ */
+#define XSLT_DEFAULT_URL         "http://xmlsoft.org/XSLT/"
+
+/**
+ * XSLT_NAMESPACE:
+ *
+ * The XSLT specification namespace.
+ */
+#define XSLT_NAMESPACE ((const xmlChar *)"http://www.w3.org/1999/XSL/Transform")
+
+/**
+ * XSLT_PARSE_OPTIONS:
+ *
+ * The set of options to pass to an xmlReadxxx when loading files for
+ * XSLT consumption.
+ */
+#define XSLT_PARSE_OPTIONS \
+ XML_PARSE_NOENT | XML_PARSE_DTDLOAD | XML_PARSE_DTDATTR | XML_PARSE_NOCDATA
+
+/**
+ * xsltMaxDepth:
+ *
+ * This value is used to detect templates loops.
+ */
+XSLTPUBVAR int xsltMaxDepth;
+
+/**
+ *  * xsltMaxVars:
+ *   *
+ *    * This value is used to detect templates loops.
+ *     */
+XSLTPUBVAR int xsltMaxVars;
+
+/**
+ * xsltEngineVersion:
+ *
+ * The version string for libxslt.
+ */
+XSLTPUBVAR const char *xsltEngineVersion;
+
+/**
+ * xsltLibxsltVersion:
+ *
+ * The version of libxslt compiled.
+ */
+XSLTPUBVAR const int xsltLibxsltVersion;
+
+/**
+ * xsltLibxmlVersion:
+ *
+ * The version of libxml libxslt was compiled against.
+ */
+XSLTPUBVAR const int xsltLibxmlVersion;
+
+/*
+ * Global initialization function.
+ */
+
+XSLTPUBFUN void XSLTCALL
+		xsltInit		(void);
+
+/*
+ * Global cleanup function.
+ */
+XSLTPUBFUN void XSLTCALL
+		xsltCleanupGlobals	(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_XSLT_H__ */
+
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/xsltInternals.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/xsltInternals.h
new file mode 100644
index 00000000..6faa07db
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/xsltInternals.h
@@ -0,0 +1,1995 @@
+/*
+ * Summary: internal data structures, constants and functions
+ * Description: Internal data structures, constants and functions used
+ *              by the XSLT engine.
+ *              They are not part of the API or ABI, i.e. they can change
+ *              without prior notice, use carefully.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XSLT_INTERNALS_H__
+#define __XML_XSLT_INTERNALS_H__
+
+#include <libxml/tree.h>
+#include <libxml/hash.h>
+#include <libxml/xpath.h>
+#include <libxml/xmlerror.h>
+#include <libxml/dict.h>
+#include <libxml/xmlstring.h>
+#include <libxslt/xslt.h>
+#include "xsltexports.h"
+#include "numbersInternals.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* #define XSLT_DEBUG_PROFILE_CACHE */
+
+/**
+ * XSLT_IS_TEXT_NODE:
+ *
+ * check if the argument is a text node
+ */
+#define XSLT_IS_TEXT_NODE(n) ((n != NULL) && \
+    (((n)->type == XML_TEXT_NODE) || \
+     ((n)->type == XML_CDATA_SECTION_NODE)))
+
+
+/**
+ * XSLT_MARK_RES_TREE_FRAG:
+ *
+ * internal macro to set up tree fragments
+ */
+#define XSLT_MARK_RES_TREE_FRAG(n) \
+    (n)->name = (char *) xmlStrdup(BAD_CAST " fake node libxslt");
+
+/**
+ * XSLT_IS_RES_TREE_FRAG:
+ *
+ * internal macro to test tree fragments
+ */
+#define XSLT_IS_RES_TREE_FRAG(n) \
+    ((n != NULL) && ((n)->type == XML_DOCUMENT_NODE) && \
+     ((n)->name != NULL) && ((n)->name[0] == ' '))
+
+/**
+ * XSLT_REFACTORED_KEYCOMP:
+ *
+ * Internal define to enable on-demand xsl:key computation.
+ * That's the only mode now but the define is kept for compatibility
+ */
+#define XSLT_REFACTORED_KEYCOMP
+
+/**
+ * XSLT_FAST_IF:
+ *
+ * Internal define to enable usage of xmlXPathCompiledEvalToBoolean()
+ * for XSLT "tests"; e.g. in <xsl:if test="/foo/bar">
+ */
+#define XSLT_FAST_IF
+
+/**
+ * XSLT_REFACTORED:
+ *
+ * Internal define to enable the refactored parts of Libxslt.
+ */
+/* #define XSLT_REFACTORED */
+/* ==================================================================== */
+
+/**
+ * XSLT_REFACTORED_VARS:
+ *
+ * Internal define to enable the refactored variable part of libxslt
+ */
+#define XSLT_REFACTORED_VARS
+
+#ifdef XSLT_REFACTORED
+
+extern const xmlChar *xsltXSLTAttrMarker;
+
+
+/* TODO: REMOVE: #define XSLT_REFACTORED_EXCLRESNS */
+
+/* TODO: REMOVE: #define XSLT_REFACTORED_NSALIAS */
+
+/**
+ * XSLT_REFACTORED_XSLT_NSCOMP
+ *
+ * Internal define to enable the pointer-comparison of
+ * namespaces of XSLT elements.
+ */
+/* #define XSLT_REFACTORED_XSLT_NSCOMP */
+
+#ifdef XSLT_REFACTORED_XSLT_NSCOMP
+
+extern const xmlChar *xsltConstNamespaceNameXSLT;
+
+/**
+ * IS_XSLT_ELEM_FAST:
+ *
+ * quick test to detect XSLT elements
+ */
+#define IS_XSLT_ELEM_FAST(n) \
+    (((n) != NULL) && ((n)->ns != NULL) && \
+    ((n)->ns->href == xsltConstNamespaceNameXSLT))
+
+/**
+ * IS_XSLT_ATTR_FAST:
+ *
+ * quick test to detect XSLT attributes
+ */
+#define IS_XSLT_ATTR_FAST(a) \
+    (((a) != NULL) && ((a)->ns != NULL) && \
+    ((a)->ns->href == xsltConstNamespaceNameXSLT))
+
+/**
+ * XSLT_HAS_INTERNAL_NSMAP:
+ *
+ * check for namespace mapping
+ */
+#define XSLT_HAS_INTERNAL_NSMAP(s) \
+    (((s) != NULL) && ((s)->principal) && \
+     ((s)->principal->principalData) && \
+     ((s)->principal->principalData->nsMap))
+
+/**
+ * XSLT_GET_INTERNAL_NSMAP:
+ *
+ * get pointer to namespace map
+ */
+#define XSLT_GET_INTERNAL_NSMAP(s) ((s)->principal->principalData->nsMap)
+
+#else /* XSLT_REFACTORED_XSLT_NSCOMP */
+
+/**
+ * IS_XSLT_ELEM_FAST:
+ *
+ * quick check whether this is an xslt element
+ */
+#define IS_XSLT_ELEM_FAST(n) \
+    (((n) != NULL) && ((n)->ns != NULL) && \
+     (xmlStrEqual((n)->ns->href, XSLT_NAMESPACE)))
+
+/**
+ * IS_XSLT_ATTR_FAST:
+ *
+ * quick check for xslt namespace attribute
+ */
+#define IS_XSLT_ATTR_FAST(a) \
+    (((a) != NULL) && ((a)->ns != NULL) && \
+     (xmlStrEqual((a)->ns->href, XSLT_NAMESPACE)))
+
+
+#endif /* XSLT_REFACTORED_XSLT_NSCOMP */
+
+
+/**
+ * XSLT_REFACTORED_MANDATORY_VERSION:
+ *
+ * TODO: Currently disabled to surpress regression test failures, since
+ *  the old behaviour was that a missing version attribute
+ *  produced a only a warning and not an error, which was incerrect.
+ *  So the regression tests need to be fixed if this is enabled.
+ */
+/* #define XSLT_REFACTORED_MANDATORY_VERSION */
+
+/**
+ * xsltPointerList:
+ *
+ * Pointer-list for various purposes.
+ */
+typedef struct _xsltPointerList xsltPointerList;
+typedef xsltPointerList *xsltPointerListPtr;
+struct _xsltPointerList {
+    void **items;
+    int number;
+    int size;
+};
+
+#endif
+
+/**
+ * XSLT_REFACTORED_PARSING:
+ *
+ * Internal define to enable the refactored parts of Libxslt
+ * related to parsing.
+ */
+/* #define XSLT_REFACTORED_PARSING */
+
+/**
+ * XSLT_MAX_SORT:
+ *
+ * Max number of specified xsl:sort on an element.
+ */
+#define XSLT_MAX_SORT 15
+
+/**
+ * XSLT_PAT_NO_PRIORITY:
+ *
+ * Specific value for pattern without priority expressed.
+ */
+#define XSLT_PAT_NO_PRIORITY -12345789
+
+/**
+ * xsltRuntimeExtra:
+ *
+ * Extra information added to the transformation context.
+ */
+typedef struct _xsltRuntimeExtra xsltRuntimeExtra;
+typedef xsltRuntimeExtra *xsltRuntimeExtraPtr;
+struct _xsltRuntimeExtra {
+    void       *info;		/* pointer to the extra data */
+    xmlFreeFunc deallocate;	/* pointer to the deallocation routine */
+    union {			/* dual-purpose field */
+        void   *ptr;		/* data not needing deallocation */
+	int    ival;		/* integer value storage */
+    } val;
+};
+
+/**
+ * XSLT_RUNTIME_EXTRA_LST:
+ * @ctxt: the transformation context
+ * @nr: the index
+ *
+ * Macro used to access extra information stored in the context
+ */
+#define XSLT_RUNTIME_EXTRA_LST(ctxt, nr) (ctxt)->extras[(nr)].info
+/**
+ * XSLT_RUNTIME_EXTRA_FREE:
+ * @ctxt: the transformation context
+ * @nr: the index
+ *
+ * Macro used to free extra information stored in the context
+ */
+#define XSLT_RUNTIME_EXTRA_FREE(ctxt, nr) (ctxt)->extras[(nr)].deallocate
+/**
+ * XSLT_RUNTIME_EXTRA:
+ * @ctxt: the transformation context
+ * @nr: the index
+ *
+ * Macro used to define extra information stored in the context
+ */
+#define	XSLT_RUNTIME_EXTRA(ctxt, nr, typ) (ctxt)->extras[(nr)].val.typ
+
+/**
+ * xsltTemplate:
+ *
+ * The in-memory structure corresponding to an XSLT Template.
+ */
+typedef struct _xsltTemplate xsltTemplate;
+typedef xsltTemplate *xsltTemplatePtr;
+struct _xsltTemplate {
+    struct _xsltTemplate *next;/* chained list sorted by priority */
+    struct _xsltStylesheet *style;/* the containing stylesheet */
+    xmlChar *match;	/* the matching string */
+    float priority;	/* as given from the stylesheet, not computed */
+    const xmlChar *name; /* the local part of the name QName */
+    const xmlChar *nameURI; /* the URI part of the name QName */
+    const xmlChar *mode;/* the local part of the mode QName */
+    const xmlChar *modeURI;/* the URI part of the mode QName */
+    xmlNodePtr content;	/* the template replacement value */
+    xmlNodePtr elem;	/* the source element */
+
+    /*
+    * TODO: @inheritedNsNr and @inheritedNs won't be used in the
+    *  refactored code.
+    */
+    int inheritedNsNr;  /* number of inherited namespaces */
+    xmlNsPtr *inheritedNs;/* inherited non-excluded namespaces */
+
+    /* Profiling information */
+    int nbCalls;        /* the number of time the template was called */
+    unsigned long time; /* the time spent in this template */
+    void *params;       /* xsl:param instructions */
+
+    int              templNr;		/* Nb of templates in the stack */
+    int              templMax;		/* Size of the templtes stack */
+    xsltTemplatePtr *templCalledTab;	/* templates called */
+    int             *templCountTab;  /* .. and how often */
+
+    /* Conflict resolution */
+    int position;
+};
+
+/**
+ * xsltDecimalFormat:
+ *
+ * Data structure of decimal-format.
+ */
+typedef struct _xsltDecimalFormat xsltDecimalFormat;
+typedef xsltDecimalFormat *xsltDecimalFormatPtr;
+struct _xsltDecimalFormat {
+    struct _xsltDecimalFormat *next; /* chained list */
+    xmlChar *name;
+    /* Used for interpretation of pattern */
+    xmlChar *digit;
+    xmlChar *patternSeparator;
+    /* May appear in result */
+    xmlChar *minusSign;
+    xmlChar *infinity;
+    xmlChar *noNumber; /* Not-a-number */
+    /* Used for interpretation of pattern and may appear in result */
+    xmlChar *decimalPoint;
+    xmlChar *grouping;
+    xmlChar *percent;
+    xmlChar *permille;
+    xmlChar *zeroDigit;
+    const xmlChar *nsUri;
+};
+
+/**
+ * xsltDocument:
+ *
+ * Data structure associated to a parsed document.
+ */
+typedef struct _xsltDocument xsltDocument;
+typedef xsltDocument *xsltDocumentPtr;
+struct _xsltDocument {
+    struct _xsltDocument *next;	/* documents are kept in a chained list */
+    int main;			/* is this the main document */
+    xmlDocPtr doc;		/* the parsed document */
+    void *keys;			/* key tables storage */
+    struct _xsltDocument *includes; /* subsidiary includes */
+    int preproc;		/* pre-processing already done */
+    int nbKeysComputed;
+};
+
+/**
+ * xsltKeyDef:
+ *
+ * Representation of an xsl:key.
+ */
+typedef struct _xsltKeyDef xsltKeyDef;
+typedef xsltKeyDef *xsltKeyDefPtr;
+struct _xsltKeyDef {
+    struct _xsltKeyDef *next;
+    xmlNodePtr inst;
+    xmlChar *name;
+    xmlChar *nameURI;
+    xmlChar *match;
+    xmlChar *use;
+    xmlXPathCompExprPtr comp;
+    xmlXPathCompExprPtr usecomp;
+    xmlNsPtr *nsList;           /* the namespaces in scope */
+    int nsNr;                   /* the number of namespaces in scope */
+};
+
+/**
+ * xsltKeyTable:
+ *
+ * Holds the computed keys for key definitions of the same QName.
+ * Is owned by an xsltDocument.
+ */
+typedef struct _xsltKeyTable xsltKeyTable;
+typedef xsltKeyTable *xsltKeyTablePtr;
+struct _xsltKeyTable {
+    struct _xsltKeyTable *next;
+    xmlChar *name;
+    xmlChar *nameURI;
+    xmlHashTablePtr keys;
+};
+
+/*
+ * The in-memory structure corresponding to an XSLT Stylesheet.
+ * NOTE: most of the content is simply linked from the doc tree
+ *       structure, no specific allocation is made.
+ */
+typedef struct _xsltStylesheet xsltStylesheet;
+typedef xsltStylesheet *xsltStylesheetPtr;
+
+typedef struct _xsltTransformContext xsltTransformContext;
+typedef xsltTransformContext *xsltTransformContextPtr;
+
+/**
+ * xsltElemPreComp:
+ *
+ * The in-memory structure corresponding to element precomputed data,
+ * designed to be extended by extension implementors.
+ */
+typedef struct _xsltElemPreComp xsltElemPreComp;
+typedef xsltElemPreComp *xsltElemPreCompPtr;
+
+/**
+ * xsltTransformFunction:
+ * @ctxt: the XSLT transformation context
+ * @node: the input node
+ * @inst: the stylesheet node
+ * @comp: the compiled information from the stylesheet
+ *
+ * Signature of the function associated to elements part of the
+ * stylesheet language like xsl:if or xsl:apply-templates.
+ */
+typedef void (*xsltTransformFunction) (xsltTransformContextPtr ctxt,
+	                               xmlNodePtr node,
+				       xmlNodePtr inst,
+			               xsltElemPreCompPtr comp);
+
+/**
+ * xsltSortFunc:
+ * @ctxt:    a transformation context
+ * @sorts:   the node-set to sort
+ * @nbsorts: the number of sorts
+ *
+ * Signature of the function to use during sorting
+ */
+typedef void (*xsltSortFunc) (xsltTransformContextPtr ctxt, xmlNodePtr *sorts,
+			      int nbsorts);
+
+typedef enum {
+    XSLT_FUNC_COPY=1,
+    XSLT_FUNC_SORT,
+    XSLT_FUNC_TEXT,
+    XSLT_FUNC_ELEMENT,
+    XSLT_FUNC_ATTRIBUTE,
+    XSLT_FUNC_COMMENT,
+    XSLT_FUNC_PI,
+    XSLT_FUNC_COPYOF,
+    XSLT_FUNC_VALUEOF,
+    XSLT_FUNC_NUMBER,
+    XSLT_FUNC_APPLYIMPORTS,
+    XSLT_FUNC_CALLTEMPLATE,
+    XSLT_FUNC_APPLYTEMPLATES,
+    XSLT_FUNC_CHOOSE,
+    XSLT_FUNC_IF,
+    XSLT_FUNC_FOREACH,
+    XSLT_FUNC_DOCUMENT,
+    XSLT_FUNC_WITHPARAM,
+    XSLT_FUNC_PARAM,
+    XSLT_FUNC_VARIABLE,
+    XSLT_FUNC_WHEN,
+    XSLT_FUNC_EXTENSION
+#ifdef XSLT_REFACTORED
+    ,
+    XSLT_FUNC_OTHERWISE,
+    XSLT_FUNC_FALLBACK,
+    XSLT_FUNC_MESSAGE,
+    XSLT_FUNC_INCLUDE,
+    XSLT_FUNC_ATTRSET,
+    XSLT_FUNC_LITERAL_RESULT_ELEMENT,
+    XSLT_FUNC_UNKOWN_FORWARDS_COMPAT
+#endif
+} xsltStyleType;
+
+/**
+ * xsltElemPreCompDeallocator:
+ * @comp:  the #xsltElemPreComp to free up
+ *
+ * Deallocates an #xsltElemPreComp structure.
+ */
+typedef void (*xsltElemPreCompDeallocator) (xsltElemPreCompPtr comp);
+
+/**
+ * xsltElemPreComp:
+ *
+ * The basic structure for compiled items of the AST of the XSLT processor.
+ * This structure is also intended to be extended by extension implementors.
+ * TODO: This is somehow not nice, since it has a "free" field, which
+ *   derived stylesheet-structs do not have.
+ */
+struct _xsltElemPreComp {
+    xsltElemPreCompPtr next;		/* next item in the global chained
+					   list held by xsltStylesheet. */
+    xsltStyleType type;		/* type of the element */
+    xsltTransformFunction func;	/* handling function */
+    xmlNodePtr inst;			/* the node in the stylesheet's tree
+					   corresponding to this item */
+
+    /* end of common part */
+    xsltElemPreCompDeallocator free;	/* the deallocator */
+};
+
+/**
+ * xsltStylePreComp:
+ *
+ * The abstract basic structure for items of the XSLT processor.
+ * This includes:
+ * 1) compiled forms of XSLT instructions (xsl:if, xsl:attribute, etc.)
+ * 2) compiled forms of literal result elements
+ * 3) compiled forms of extension elements
+ */
+typedef struct _xsltStylePreComp xsltStylePreComp;
+typedef xsltStylePreComp *xsltStylePreCompPtr;
+
+#ifdef XSLT_REFACTORED
+
+/*
+* Some pointer-list utility functions.
+*/
+XSLTPUBFUN xsltPointerListPtr XSLTCALL
+		xsltPointerListCreate		(int initialSize);
+XSLTPUBFUN void XSLTCALL
+		xsltPointerListFree		(xsltPointerListPtr list);
+XSLTPUBFUN void XSLTCALL
+		xsltPointerListClear		(xsltPointerListPtr list);
+XSLTPUBFUN int XSLTCALL
+		xsltPointerListAddSize		(xsltPointerListPtr list,
+						 void *item,
+						 int initialSize);
+
+/************************************************************************
+ *									*
+ * Refactored structures                                                *
+ *									*
+ ************************************************************************/
+
+typedef struct _xsltNsListContainer xsltNsListContainer;
+typedef xsltNsListContainer *xsltNsListContainerPtr;
+struct _xsltNsListContainer {
+    xmlNsPtr *list;
+    int totalNumber;
+    int xpathNumber;
+};
+
+/**
+ * XSLT_ITEM_COMPATIBILITY_FIELDS:
+ *
+ * Fields for API compatibility to the structure
+ * _xsltElemPreComp which is used for extension functions.
+ * Note that @next is used for storage; it does not reflect a next
+ * sibling in the tree.
+ * TODO: Evaluate if we really need such a compatibility.
+ */
+#define XSLT_ITEM_COMPATIBILITY_FIELDS \
+    xsltElemPreCompPtr next;\
+    xsltStyleType type;\
+    xsltTransformFunction func;\
+    xmlNodePtr inst;
+
+/**
+ * XSLT_ITEM_NAVIGATION_FIELDS:
+ *
+ * Currently empty.
+ * TODO: It is intended to hold navigational fields in the future.
+ */
+#define XSLT_ITEM_NAVIGATION_FIELDS
+/*
+    xsltStylePreCompPtr parent;\
+    xsltStylePreCompPtr children;\
+    xsltStylePreCompPtr nextItem;
+*/
+
+/**
+ * XSLT_ITEM_NSINSCOPE_FIELDS:
+ *
+ * The in-scope namespaces.
+ */
+#define XSLT_ITEM_NSINSCOPE_FIELDS xsltNsListContainerPtr inScopeNs;
+
+/**
+ * XSLT_ITEM_COMMON_FIELDS:
+ *
+ * Common fields used for all items.
+ */
+#define XSLT_ITEM_COMMON_FIELDS \
+    XSLT_ITEM_COMPATIBILITY_FIELDS \
+    XSLT_ITEM_NAVIGATION_FIELDS \
+    XSLT_ITEM_NSINSCOPE_FIELDS
+
+/**
+ * _xsltStylePreComp:
+ *
+ * The abstract basic structure for items of the XSLT processor.
+ * This includes:
+ * 1) compiled forms of XSLT instructions (e.g. xsl:if, xsl:attribute, etc.)
+ * 2) compiled forms of literal result elements
+ * 3) various properties for XSLT instructions (e.g. xsl:when,
+ *    xsl:with-param)
+ *
+ * REVISIT TODO: Keep this structure equal to the fields
+ *   defined by XSLT_ITEM_COMMON_FIELDS
+ */
+struct _xsltStylePreComp {
+    xsltElemPreCompPtr next;    /* next item in the global chained
+				   list held by xsltStylesheet */
+    xsltStyleType type;         /* type of the item */
+    xsltTransformFunction func; /* handling function */
+    xmlNodePtr inst;		/* the node in the stylesheet's tree
+				   corresponding to this item. */
+    /* Currently no navigational fields. */
+    xsltNsListContainerPtr inScopeNs;
+};
+
+/**
+ * xsltStyleBasicEmptyItem:
+ *
+ * Abstract structure only used as a short-cut for
+ * XSLT items with no extra fields.
+ * NOTE that it is intended that this structure looks the same as
+ *  _xsltStylePreComp.
+ */
+typedef struct _xsltStyleBasicEmptyItem xsltStyleBasicEmptyItem;
+typedef xsltStyleBasicEmptyItem *xsltStyleBasicEmptyItemPtr;
+
+struct _xsltStyleBasicEmptyItem {
+    XSLT_ITEM_COMMON_FIELDS
+};
+
+/**
+ * xsltStyleBasicExpressionItem:
+ *
+ * Abstract structure only used as a short-cut for
+ * XSLT items with just an expression.
+ */
+typedef struct _xsltStyleBasicExpressionItem xsltStyleBasicExpressionItem;
+typedef xsltStyleBasicExpressionItem *xsltStyleBasicExpressionItemPtr;
+
+struct _xsltStyleBasicExpressionItem {
+    XSLT_ITEM_COMMON_FIELDS
+
+    const xmlChar *select; /* TODO: Change this to "expression". */
+    xmlXPathCompExprPtr comp; /* TODO: Change this to compExpr. */
+};
+
+/************************************************************************
+ *									*
+ * XSLT-instructions/declarations                                       *
+ *									*
+ ************************************************************************/
+
+/**
+ * xsltStyleItemElement:
+ *
+ * <!-- Category: instruction -->
+ * <xsl:element
+ *  name = { qname }
+ *  namespace = { uri-reference }
+ *  use-attribute-sets = qnames>
+ *  <!-- Content: template -->
+ * </xsl:element>
+ */
+typedef struct _xsltStyleItemElement xsltStyleItemElement;
+typedef xsltStyleItemElement *xsltStyleItemElementPtr;
+
+struct _xsltStyleItemElement {
+    XSLT_ITEM_COMMON_FIELDS
+
+    const xmlChar *use;
+    int      has_use;
+    const xmlChar *name;
+    int      has_name;
+    const xmlChar *ns;
+    const xmlChar *nsPrefix;
+    int      has_ns;
+};
+
+/**
+ * xsltStyleItemAttribute:
+ *
+ * <!-- Category: instruction -->
+ * <xsl:attribute
+ *  name = { qname }
+ *  namespace = { uri-reference }>
+ *  <!-- Content: template -->
+ * </xsl:attribute>
+ */
+typedef struct _xsltStyleItemAttribute xsltStyleItemAttribute;
+typedef xsltStyleItemAttribute *xsltStyleItemAttributePtr;
+
+struct _xsltStyleItemAttribute {
+    XSLT_ITEM_COMMON_FIELDS
+    const xmlChar *name;
+    int      has_name;
+    const xmlChar *ns;
+    const xmlChar *nsPrefix;
+    int      has_ns;
+};
+
+/**
+ * xsltStyleItemText:
+ *
+ * <!-- Category: instruction -->
+ * <xsl:text
+ *  disable-output-escaping = "yes" | "no">
+ *  <!-- Content: #PCDATA -->
+ * </xsl:text>
+ */
+typedef struct _xsltStyleItemText xsltStyleItemText;
+typedef xsltStyleItemText *xsltStyleItemTextPtr;
+
+struct _xsltStyleItemText {
+    XSLT_ITEM_COMMON_FIELDS
+    int      noescape;		/* text */
+};
+
+/**
+ * xsltStyleItemComment:
+ *
+ * <!-- Category: instruction -->
+ *  <xsl:comment>
+ *  <!-- Content: template -->
+ * </xsl:comment>
+ */
+typedef xsltStyleBasicEmptyItem xsltStyleItemComment;
+typedef xsltStyleItemComment *xsltStyleItemCommentPtr;
+
+/**
+ * xsltStyleItemPI:
+ *
+ * <!-- Category: instruction -->
+ *  <xsl:processing-instruction
+ *  name = { ncname }>
+ *  <!-- Content: template -->
+ * </xsl:processing-instruction>
+ */
+typedef struct _xsltStyleItemPI xsltStyleItemPI;
+typedef xsltStyleItemPI *xsltStyleItemPIPtr;
+
+struct _xsltStyleItemPI {
+    XSLT_ITEM_COMMON_FIELDS
+    const xmlChar *name;
+    int      has_name;
+};
+
+/**
+ * xsltStyleItemApplyImports:
+ *
+ * <!-- Category: instruction -->
+ * <xsl:apply-imports />
+ */
+typedef xsltStyleBasicEmptyItem xsltStyleItemApplyImports;
+typedef xsltStyleItemApplyImports *xsltStyleItemApplyImportsPtr;
+
+/**
+ * xsltStyleItemApplyTemplates:
+ *
+ * <!-- Category: instruction -->
+ *  <xsl:apply-templates
+ *  select = node-set-expression
+ *  mode = qname>
+ *  <!-- Content: (xsl:sort | xsl:with-param)* -->
+ * </xsl:apply-templates>
+ */
+typedef struct _xsltStyleItemApplyTemplates xsltStyleItemApplyTemplates;
+typedef xsltStyleItemApplyTemplates *xsltStyleItemApplyTemplatesPtr;
+
+struct _xsltStyleItemApplyTemplates {
+    XSLT_ITEM_COMMON_FIELDS
+
+    const xmlChar *mode;	/* apply-templates */
+    const xmlChar *modeURI;	/* apply-templates */
+    const xmlChar *select;	/* sort, copy-of, value-of, apply-templates */
+    xmlXPathCompExprPtr comp;	/* a precompiled XPath expression */
+    /* TODO: with-params */
+};
+
+/**
+ * xsltStyleItemCallTemplate:
+ *
+ * <!-- Category: instruction -->
+ *  <xsl:call-template
+ *  name = qname>
+ *  <!-- Content: xsl:with-param* -->
+ * </xsl:call-template>
+ */
+typedef struct _xsltStyleItemCallTemplate xsltStyleItemCallTemplate;
+typedef xsltStyleItemCallTemplate *xsltStyleItemCallTemplatePtr;
+
+struct _xsltStyleItemCallTemplate {
+    XSLT_ITEM_COMMON_FIELDS
+
+    xsltTemplatePtr templ;	/* call-template */
+    const xmlChar *name;	/* element, attribute, pi */
+    int      has_name;		/* element, attribute, pi */
+    const xmlChar *ns;		/* element */
+    int      has_ns;		/* element */
+    /* TODO: with-params */
+};
+
+/**
+ * xsltStyleItemCopy:
+ *
+ * <!-- Category: instruction -->
+ * <xsl:copy
+ *  use-attribute-sets = qnames>
+ *  <!-- Content: template -->
+ * </xsl:copy>
+ */
+typedef struct _xsltStyleItemCopy xsltStyleItemCopy;
+typedef xsltStyleItemCopy *xsltStyleItemCopyPtr;
+
+struct _xsltStyleItemCopy {
+   XSLT_ITEM_COMMON_FIELDS
+    const xmlChar *use;		/* copy, element */
+    int      has_use;		/* copy, element */
+};
+
+/**
+ * xsltStyleItemIf:
+ *
+ * <!-- Category: instruction -->
+ *  <xsl:if
+ *  test = boolean-expression>
+ *  <!-- Content: template -->
+ * </xsl:if>
+ */
+typedef struct _xsltStyleItemIf xsltStyleItemIf;
+typedef xsltStyleItemIf *xsltStyleItemIfPtr;
+
+struct _xsltStyleItemIf {
+    XSLT_ITEM_COMMON_FIELDS
+
+    const xmlChar *test;	/* if */
+    xmlXPathCompExprPtr comp;	/* a precompiled XPath expression */
+};
+
+
+/**
+ * xsltStyleItemCopyOf:
+ *
+ * <!-- Category: instruction -->
+ * <xsl:copy-of
+ *  select = expression />
+ */
+typedef xsltStyleBasicExpressionItem xsltStyleItemCopyOf;
+typedef xsltStyleItemCopyOf *xsltStyleItemCopyOfPtr;
+
+/**
+ * xsltStyleItemValueOf:
+ *
+ * <!-- Category: instruction -->
+ * <xsl:value-of
+ *  select = string-expression
+ *  disable-output-escaping = "yes" | "no" />
+ */
+typedef struct _xsltStyleItemValueOf xsltStyleItemValueOf;
+typedef xsltStyleItemValueOf *xsltStyleItemValueOfPtr;
+
+struct _xsltStyleItemValueOf {
+    XSLT_ITEM_COMMON_FIELDS
+
+    const xmlChar *select;
+    xmlXPathCompExprPtr comp;	/* a precompiled XPath expression */
+    int      noescape;
+};
+
+/**
+ * xsltStyleItemNumber:
+ *
+ * <!-- Category: instruction -->
+ *  <xsl:number
+ *  level = "single" | "multiple" | "any"
+ *  count = pattern
+ *  from = pattern
+ *  value = number-expression
+ *  format = { string }
+ *  lang = { nmtoken }
+ *  letter-value = { "alphabetic" | "traditional" }
+ *  grouping-separator = { char }
+ *  grouping-size = { number } />
+ */
+typedef struct _xsltStyleItemNumber xsltStyleItemNumber;
+typedef xsltStyleItemNumber *xsltStyleItemNumberPtr;
+
+struct _xsltStyleItemNumber {
+    XSLT_ITEM_COMMON_FIELDS
+    xsltNumberData numdata;	/* number */
+};
+
+/**
+ * xsltStyleItemChoose:
+ *
+ * <!-- Category: instruction -->
+ *  <xsl:choose>
+ *  <!-- Content: (xsl:when+, xsl:otherwise?) -->
+ * </xsl:choose>
+ */
+typedef xsltStyleBasicEmptyItem xsltStyleItemChoose;
+typedef xsltStyleItemChoose *xsltStyleItemChoosePtr;
+
+/**
+ * xsltStyleItemFallback:
+ *
+ * <!-- Category: instruction -->
+ *  <xsl:fallback>
+ *  <!-- Content: template -->
+ * </xsl:fallback>
+ */
+typedef xsltStyleBasicEmptyItem xsltStyleItemFallback;
+typedef xsltStyleItemFallback *xsltStyleItemFallbackPtr;
+
+/**
+ * xsltStyleItemForEach:
+ *
+ * <!-- Category: instruction -->
+ * <xsl:for-each
+ *   select = node-set-expression>
+ *   <!-- Content: (xsl:sort*, template) -->
+ * </xsl:for-each>
+ */
+typedef xsltStyleBasicExpressionItem xsltStyleItemForEach;
+typedef xsltStyleItemForEach *xsltStyleItemForEachPtr;
+
+/**
+ * xsltStyleItemMessage:
+ *
+ * <!-- Category: instruction -->
+ * <xsl:message
+ *   terminate = "yes" | "no">
+ *   <!-- Content: template -->
+ * </xsl:message>
+ */
+typedef struct _xsltStyleItemMessage xsltStyleItemMessage;
+typedef xsltStyleItemMessage *xsltStyleItemMessagePtr;
+
+struct _xsltStyleItemMessage {
+    XSLT_ITEM_COMMON_FIELDS
+    int terminate;
+};
+
+/**
+ * xsltStyleItemDocument:
+ *
+ * NOTE: This is not an instruction of XSLT 1.0.
+ */
+typedef struct _xsltStyleItemDocument xsltStyleItemDocument;
+typedef xsltStyleItemDocument *xsltStyleItemDocumentPtr;
+
+struct _xsltStyleItemDocument {
+    XSLT_ITEM_COMMON_FIELDS
+    int      ver11;		/* assigned: in xsltDocumentComp;
+                                  read: nowhere;
+                                  TODO: Check if we need. */
+    const xmlChar *filename;	/* document URL */
+    int has_filename;
+};
+
+/************************************************************************
+ *									*
+ * Non-instructions (actually properties of instructions/declarations)  *
+ *									*
+ ************************************************************************/
+
+/**
+ * xsltStyleBasicItemVariable:
+ *
+ * Basic struct for xsl:variable, xsl:param and xsl:with-param.
+ * It's currently important to have equal fields, since
+ * xsltParseStylesheetCallerParam() is used with xsl:with-param from
+ * the xslt side and with xsl:param from the exslt side (in
+ * exsltFuncFunctionFunction()).
+ *
+ * FUTURE NOTE: In XSLT 2.0 xsl:param, xsl:variable and xsl:with-param
+ *   have additional different fields.
+ */
+typedef struct _xsltStyleBasicItemVariable xsltStyleBasicItemVariable;
+typedef xsltStyleBasicItemVariable *xsltStyleBasicItemVariablePtr;
+
+struct _xsltStyleBasicItemVariable {
+    XSLT_ITEM_COMMON_FIELDS
+
+    const xmlChar *select;
+    xmlXPathCompExprPtr comp;
+
+    const xmlChar *name;
+    int      has_name;
+    const xmlChar *ns;
+    int      has_ns;
+};
+
+/**
+ * xsltStyleItemVariable:
+ *
+ * <!-- Category: top-level-element -->
+ * <xsl:param
+ *   name = qname
+ *   select = expression>
+ *   <!-- Content: template -->
+ * </xsl:param>
+ */
+typedef xsltStyleBasicItemVariable xsltStyleItemVariable;
+typedef xsltStyleItemVariable *xsltStyleItemVariablePtr;
+
+/**
+ * xsltStyleItemParam:
+ *
+ * <!-- Category: top-level-element -->
+ * <xsl:param
+ *   name = qname
+ *   select = expression>
+ *   <!-- Content: template -->
+ * </xsl:param>
+ */
+typedef struct _xsltStyleItemParam xsltStyleItemParam;
+typedef xsltStyleItemParam *xsltStyleItemParamPtr;
+
+struct _xsltStyleItemParam {
+    XSLT_ITEM_COMMON_FIELDS
+
+    const xmlChar *select;
+    xmlXPathCompExprPtr comp;
+
+    const xmlChar *name;
+    int      has_name;
+    const xmlChar *ns;
+    int      has_ns;
+};
+
+/**
+ * xsltStyleItemWithParam:
+ *
+ * <xsl:with-param
+ *  name = qname
+ *  select = expression>
+ *  <!-- Content: template -->
+ * </xsl:with-param>
+ */
+typedef xsltStyleBasicItemVariable xsltStyleItemWithParam;
+typedef xsltStyleItemWithParam *xsltStyleItemWithParamPtr;
+
+/**
+ * xsltStyleItemSort:
+ *
+ * Reflects the XSLT xsl:sort item.
+ * Allowed parents: xsl:apply-templates, xsl:for-each
+ * <xsl:sort
+ *   select = string-expression
+ *   lang = { nmtoken }
+ *   data-type = { "text" | "number" | qname-but-not-ncname }
+ *   order = { "ascending" | "descending" }
+ *   case-order = { "upper-first" | "lower-first" } />
+ */
+typedef struct _xsltStyleItemSort xsltStyleItemSort;
+typedef xsltStyleItemSort *xsltStyleItemSortPtr;
+
+struct _xsltStyleItemSort {
+    XSLT_ITEM_COMMON_FIELDS
+
+    const xmlChar *stype;       /* sort */
+    int      has_stype;		/* sort */
+    int      number;		/* sort */
+    const xmlChar *order;	/* sort */
+    int      has_order;		/* sort */
+    int      descending;	/* sort */
+    const xmlChar *lang;	/* sort */
+    int      has_lang;		/* sort */
+    const xmlChar *case_order;	/* sort */
+    int      lower_first;	/* sort */
+
+    const xmlChar *use;
+    int      has_use;
+
+    const xmlChar *select;	/* sort, copy-of, value-of, apply-templates */
+
+    xmlXPathCompExprPtr comp;	/* a precompiled XPath expression */
+};
+
+
+/**
+ * xsltStyleItemWhen:
+ *
+ * <xsl:when
+ *   test = boolean-expression>
+ *   <!-- Content: template -->
+ * </xsl:when>
+ * Allowed parent: xsl:choose
+ */
+typedef struct _xsltStyleItemWhen xsltStyleItemWhen;
+typedef xsltStyleItemWhen *xsltStyleItemWhenPtr;
+
+struct _xsltStyleItemWhen {
+    XSLT_ITEM_COMMON_FIELDS
+
+    const xmlChar *test;
+    xmlXPathCompExprPtr comp;
+};
+
+/**
+ * xsltStyleItemOtherwise:
+ *
+ * Allowed parent: xsl:choose
+ * <xsl:otherwise>
+ *   <!-- Content: template -->
+ * </xsl:otherwise>
+ */
+typedef struct _xsltStyleItemOtherwise xsltStyleItemOtherwise;
+typedef xsltStyleItemOtherwise *xsltStyleItemOtherwisePtr;
+
+struct _xsltStyleItemOtherwise {
+    XSLT_ITEM_COMMON_FIELDS
+};
+
+typedef struct _xsltStyleItemInclude xsltStyleItemInclude;
+typedef xsltStyleItemInclude *xsltStyleItemIncludePtr;
+
+struct _xsltStyleItemInclude {
+    XSLT_ITEM_COMMON_FIELDS
+    xsltDocumentPtr include;
+};
+
+/************************************************************************
+ *									*
+ *  XSLT elements in forwards-compatible mode                           *
+ *									*
+ ************************************************************************/
+
+typedef struct _xsltStyleItemUknown xsltStyleItemUknown;
+typedef xsltStyleItemUknown *xsltStyleItemUknownPtr;
+struct _xsltStyleItemUknown {
+    XSLT_ITEM_COMMON_FIELDS
+};
+
+
+/************************************************************************
+ *									*
+ *  Extension elements                                                  *
+ *									*
+ ************************************************************************/
+
+/*
+ * xsltStyleItemExtElement:
+ *
+ * Reflects extension elements.
+ *
+ * NOTE: Due to the fact that the structure xsltElemPreComp is most
+ * probably already heavily in use out there by users, so we cannot
+ * easily change it, we'll create an intermediate structure which will
+ * hold an xsltElemPreCompPtr.
+ * BIG NOTE: The only problem I see here is that the user processes the
+ *  content of the stylesheet tree, possibly he'll lookup the node->psvi
+ *  fields in order to find subsequent extension functions.
+ *  In this case, the user's code will break, since the node->psvi
+ *  field will hold now the xsltStyleItemExtElementPtr and not
+ *  the xsltElemPreCompPtr.
+ *  However the place where the structure is anchored in the node-tree,
+ *  namely node->psvi, has beed already once been moved from node->_private
+ *  to node->psvi, so we have a precedent here, which, I think, should allow
+ *  us to change such semantics without headaches.
+ */
+typedef struct _xsltStyleItemExtElement xsltStyleItemExtElement;
+typedef xsltStyleItemExtElement *xsltStyleItemExtElementPtr;
+struct _xsltStyleItemExtElement {
+    XSLT_ITEM_COMMON_FIELDS
+    xsltElemPreCompPtr item;
+};
+
+/************************************************************************
+ *									*
+ *  Literal result elements                                             *
+ *									*
+ ************************************************************************/
+
+typedef struct _xsltEffectiveNs xsltEffectiveNs;
+typedef xsltEffectiveNs *xsltEffectiveNsPtr;
+struct _xsltEffectiveNs {
+    xsltEffectiveNsPtr nextInStore; /* storage next */
+    xsltEffectiveNsPtr next; /* next item in the list */
+    const xmlChar *prefix;
+    const xmlChar *nsName;
+    /*
+    * Indicates if eclared on the literal result element; dunno if really
+    * needed.
+    */
+    int holdByElem;
+};
+
+/*
+ * Info for literal result elements.
+ * This will be set on the elem->psvi field and will be
+ * shared by literal result elements, which have the same
+ * excluded result namespaces; i.e., this *won't* be created uniquely
+ * for every literal result element.
+ */
+typedef struct _xsltStyleItemLRElementInfo xsltStyleItemLRElementInfo;
+typedef xsltStyleItemLRElementInfo *xsltStyleItemLRElementInfoPtr;
+struct _xsltStyleItemLRElementInfo {
+    XSLT_ITEM_COMMON_FIELDS
+    /*
+    * @effectiveNs is the set of effective ns-nodes
+    *  on the literal result element, which will be added to the result
+    *  element if not already existing in the result tree.
+    *  This means that excluded namespaces (via exclude-result-prefixes,
+    *  extension-element-prefixes and the XSLT namespace) not added
+    *  to the set.
+    *  Namespace-aliasing was applied on the @effectiveNs.
+    */
+    xsltEffectiveNsPtr effectiveNs;
+
+};
+
+#ifdef XSLT_REFACTORED
+
+typedef struct _xsltNsAlias xsltNsAlias;
+typedef xsltNsAlias *xsltNsAliasPtr;
+struct _xsltNsAlias {
+    xsltNsAliasPtr next; /* next in the list */
+    xmlNsPtr literalNs;
+    xmlNsPtr targetNs;
+    xmlDocPtr docOfTargetNs;
+};
+#endif
+
+#ifdef XSLT_REFACTORED_XSLT_NSCOMP
+
+typedef struct _xsltNsMap xsltNsMap;
+typedef xsltNsMap *xsltNsMapPtr;
+struct _xsltNsMap {
+    xsltNsMapPtr next; /* next in the list */
+    xmlDocPtr doc;
+    xmlNodePtr elem; /* the element holding the ns-decl */
+    xmlNsPtr ns; /* the xmlNs structure holding the XML namespace name */
+    const xmlChar *origNsName; /* the original XML namespace name */
+    const xmlChar *newNsName; /* the mapped XML namespace name */
+};
+#endif
+
+/************************************************************************
+ *									*
+ *  Compile-time structures for *internal* use only                     *
+ *									*
+ ************************************************************************/
+
+typedef struct _xsltPrincipalStylesheetData xsltPrincipalStylesheetData;
+typedef xsltPrincipalStylesheetData *xsltPrincipalStylesheetDataPtr;
+
+typedef struct _xsltNsList xsltNsList;
+typedef xsltNsList *xsltNsListPtr;
+struct _xsltNsList {
+    xsltNsListPtr next; /* next in the list */
+    xmlNsPtr ns;
+};
+
+/*
+* xsltVarInfo:
+*
+* Used at compilation time for parameters and variables.
+*/
+typedef struct _xsltVarInfo xsltVarInfo;
+typedef xsltVarInfo *xsltVarInfoPtr;
+struct _xsltVarInfo {
+    xsltVarInfoPtr next; /* next in the list */
+    xsltVarInfoPtr prev;
+    int depth; /* the depth in the tree */
+    const xmlChar *name;
+    const xmlChar *nsName;
+};
+
+/**
+ * xsltCompilerNodeInfo:
+ *
+ * Per-node information during compile-time.
+ */
+typedef struct _xsltCompilerNodeInfo xsltCompilerNodeInfo;
+typedef xsltCompilerNodeInfo *xsltCompilerNodeInfoPtr;
+struct _xsltCompilerNodeInfo {
+    xsltCompilerNodeInfoPtr next;
+    xsltCompilerNodeInfoPtr prev;
+    xmlNodePtr node;
+    int depth;
+    xsltTemplatePtr templ;   /* The owning template */
+    int category;	     /* XSLT element, LR-element or
+                                extension element */
+    xsltStyleType type;
+    xsltElemPreCompPtr item; /* The compiled information */
+    /* The current in-scope namespaces */
+    xsltNsListContainerPtr inScopeNs;
+    /* The current excluded result namespaces */
+    xsltPointerListPtr exclResultNs;
+    /* The current extension instruction namespaces */
+    xsltPointerListPtr extElemNs;
+
+    /* The current info for literal result elements. */
+    xsltStyleItemLRElementInfoPtr litResElemInfo;
+    /*
+    * Set to 1 if in-scope namespaces changed,
+    *  or excluded result namespaces changed,
+    *  or extension element namespaces changed.
+    * This will trigger creation of new infos
+    *  for literal result elements.
+    */
+    int nsChanged;
+    int preserveWhitespace;
+    int stripWhitespace;
+    int isRoot; /* whether this is the stylesheet's root node */
+    int forwardsCompat; /* whether forwards-compatible mode is enabled */
+    /* whether the content of an extension element was processed */
+    int extContentHandled;
+    /* the type of the current child */
+    xsltStyleType curChildType;
+};
+
+/**
+ * XSLT_CCTXT:
+ *
+ * get pointer to compiler context
+ */
+#define XSLT_CCTXT(style) ((xsltCompilerCtxtPtr) style->compCtxt)
+
+typedef enum {
+    XSLT_ERROR_SEVERITY_ERROR = 0,
+    XSLT_ERROR_SEVERITY_WARNING
+} xsltErrorSeverityType;
+
+typedef struct _xsltCompilerCtxt xsltCompilerCtxt;
+typedef xsltCompilerCtxt *xsltCompilerCtxtPtr;
+struct _xsltCompilerCtxt {
+    void *errorCtxt;            /* user specific error context */
+    /*
+    * used for error/warning reports; e.g. XSLT_ERROR_SEVERITY_WARNING */
+    xsltErrorSeverityType errSeverity;
+    int warnings;		/* TODO: number of warnings found at
+                                   compilation */
+    int errors;			/* TODO: number of errors found at
+                                   compilation */
+    xmlDictPtr dict;
+    xsltStylesheetPtr style;
+    int simplified; /* whether this is a simplified stylesheet */
+    /* TODO: structured/unstructured error contexts. */
+    int depth; /* Current depth of processing */
+
+    xsltCompilerNodeInfoPtr inode;
+    xsltCompilerNodeInfoPtr inodeList;
+    xsltCompilerNodeInfoPtr inodeLast;
+    xsltPointerListPtr tmpList; /* Used for various purposes */
+    /*
+    * The XSLT version as specified by the stylesheet's root element.
+    */
+    int isInclude;
+    int hasForwardsCompat; /* whether forwards-compatible mode was used
+			     in a parsing episode */
+    int maxNodeInfos; /* TEMP TODO: just for the interest */
+    int maxLREs;  /* TEMP TODO: just for the interest */
+    /*
+    * In order to keep the old behaviour, applying strict rules of
+    * the spec can be turned off. This has effect only on special
+    * mechanisms like whitespace-stripping in the stylesheet.
+    */
+    int strict;
+    xsltPrincipalStylesheetDataPtr psData;
+    xsltStyleItemUknownPtr unknownItem;
+    int hasNsAliases; /* Indicator if there was an xsl:namespace-alias. */
+    xsltNsAliasPtr nsAliases;
+    xsltVarInfoPtr ivars; /* Storage of local in-scope variables/params. */
+    xsltVarInfoPtr ivar; /* topmost local variable/param. */
+};
+
+#else /* XSLT_REFACTORED */
+/*
+* The old structures before refactoring.
+*/
+
+/**
+ * _xsltStylePreComp:
+ *
+ * The in-memory structure corresponding to XSLT stylesheet constructs
+ * precomputed data.
+ */
+struct _xsltStylePreComp {
+    xsltElemPreCompPtr next;	/* chained list */
+    xsltStyleType type;		/* type of the element */
+    xsltTransformFunction func; /* handling function */
+    xmlNodePtr inst;		/* the instruction */
+
+    /*
+     * Pre computed values.
+     */
+
+    const xmlChar *stype;       /* sort */
+    int      has_stype;		/* sort */
+    int      number;		/* sort */
+    const xmlChar *order;	/* sort */
+    int      has_order;		/* sort */
+    int      descending;	/* sort */
+    const xmlChar *lang;	/* sort */
+    int      has_lang;		/* sort */
+    const xmlChar *case_order;	/* sort */
+    int      lower_first;	/* sort */
+
+    const xmlChar *use;		/* copy, element */
+    int      has_use;		/* copy, element */
+
+    int      noescape;		/* text */
+
+    const xmlChar *name;	/* element, attribute, pi */
+    int      has_name;		/* element, attribute, pi */
+    const xmlChar *ns;		/* element */
+    int      has_ns;		/* element */
+
+    const xmlChar *mode;	/* apply-templates */
+    const xmlChar *modeURI;	/* apply-templates */
+
+    const xmlChar *test;	/* if */
+
+    xsltTemplatePtr templ;	/* call-template */
+
+    const xmlChar *select;	/* sort, copy-of, value-of, apply-templates */
+
+    int      ver11;		/* document */
+    const xmlChar *filename;	/* document URL */
+    int      has_filename;	/* document */
+
+    xsltNumberData numdata;	/* number */
+
+    xmlXPathCompExprPtr comp;	/* a precompiled XPath expression */
+    xmlNsPtr *nsList;		/* the namespaces in scope */
+    int nsNr;			/* the number of namespaces in scope */
+};
+
+#endif /* XSLT_REFACTORED */
+
+
+/*
+ * The in-memory structure corresponding to an XSLT Variable
+ * or Param.
+ */
+typedef struct _xsltStackElem xsltStackElem;
+typedef xsltStackElem *xsltStackElemPtr;
+struct _xsltStackElem {
+    struct _xsltStackElem *next;/* chained list */
+    xsltStylePreCompPtr comp;   /* the compiled form */
+    int computed;		/* was the evaluation done */
+    const xmlChar *name;	/* the local part of the name QName */
+    const xmlChar *nameURI;	/* the URI part of the name QName */
+    const xmlChar *select;	/* the eval string */
+    xmlNodePtr tree;		/* the sequence constructor if no eval
+				    string or the location */
+    xmlXPathObjectPtr value;	/* The value if computed */
+    xmlDocPtr fragment;		/* The Result Tree Fragments (needed for XSLT 1.0)
+				   which are bound to the variable's lifetime. */
+    int level;                  /* the depth in the tree;
+                                   -1 if persistent (e.g. a given xsl:with-param) */
+    xsltTransformContextPtr context; /* The transformation context; needed to cache
+                                        the variables */
+    int flags;
+};
+
+#ifdef XSLT_REFACTORED
+
+struct _xsltPrincipalStylesheetData {
+    /*
+    * Namespace dictionary for ns-prefixes and ns-names:
+    * TODO: Shared between stylesheets, and XPath mechanisms.
+    *   Not used yet.
+    */
+    xmlDictPtr namespaceDict;
+    /*
+    * Global list of in-scope namespaces.
+    */
+    xsltPointerListPtr inScopeNamespaces;
+    /*
+    * Global list of information for [xsl:]excluded-result-prefixes.
+    */
+    xsltPointerListPtr exclResultNamespaces;
+    /*
+    * Global list of information for [xsl:]extension-element-prefixes.
+    */
+    xsltPointerListPtr extElemNamespaces;
+    xsltEffectiveNsPtr effectiveNs;
+#ifdef XSLT_REFACTORED_XSLT_NSCOMP
+    /*
+    * Namespace name map to get rid of string comparison of namespace names.
+    */
+    xsltNsMapPtr nsMap;
+#endif
+};
+
+
+#endif
+/*
+ * Note that we added a @compCtxt field to anchor an stylesheet compilation
+ * context, since, due to historical reasons, various compile-time function
+ * take only the stylesheet as argument and not a compilation context.
+ */
+struct _xsltStylesheet {
+    /*
+     * The stylesheet import relation is kept as a tree.
+     */
+    struct _xsltStylesheet *parent;
+    struct _xsltStylesheet *next;
+    struct _xsltStylesheet *imports;
+
+    xsltDocumentPtr docList;		/* the include document list */
+
+    /*
+     * General data on the style sheet document.
+     */
+    xmlDocPtr doc;		/* the parsed XML stylesheet */
+    xmlHashTablePtr stripSpaces;/* the hash table of the strip-space and
+				   preserve space elements */
+    int             stripAll;	/* strip-space * (1) preserve-space * (-1) */
+    xmlHashTablePtr cdataSection;/* the hash table of the cdata-section */
+
+    /*
+     * Global variable or parameters.
+     */
+    xsltStackElemPtr variables; /* linked list of param and variables */
+
+    /*
+     * Template descriptions.
+     */
+    xsltTemplatePtr templates;           /* the ordered list of templates */
+    xmlHashTablePtr templatesHash;       /* hash table or wherever compiled
+                                            templates information is stored */
+    struct _xsltCompMatch *rootMatch;    /* template based on / */
+    struct _xsltCompMatch *keyMatch;     /* template based on key() */
+    struct _xsltCompMatch *elemMatch;    /* template based on * */
+    struct _xsltCompMatch *attrMatch;    /* template based on @* */
+    struct _xsltCompMatch *parentMatch;  /* template based on .. */
+    struct _xsltCompMatch *textMatch;    /* template based on text() */
+    struct _xsltCompMatch *piMatch;      /* template based on
+                                            processing-instruction() */
+    struct _xsltCompMatch *commentMatch; /* template based on comment() */
+
+    /*
+     * Namespace aliases.
+     * NOTE: Not used in the refactored code.
+     */
+    xmlHashTablePtr nsAliases;	/* the namespace alias hash tables */
+
+    /*
+     * Attribute sets.
+     */
+    xmlHashTablePtr attributeSets;/* the attribute sets hash tables */
+
+    /*
+     * Namespaces.
+     * TODO: Eliminate this.
+     */
+    xmlHashTablePtr nsHash;     /* the set of namespaces in use:
+                                   ATTENTION: This is used for
+                                   execution of XPath expressions; unfortunately
+                                   it restricts the stylesheet to have distinct
+                                   prefixes.
+				   TODO: We need to get rid of this.
+				 */
+    void           *nsDefs;     /* ATTENTION TODO: This is currently used to store
+				   xsltExtDefPtr (in extensions.c) and
+                                   *not* xmlNsPtr.
+				 */
+
+    /*
+     * Key definitions.
+     */
+    void *keys;			/* key definitions */
+
+    /*
+     * Output related stuff.
+     */
+    xmlChar *method;		/* the output method */
+    xmlChar *methodURI;		/* associated namespace if any */
+    xmlChar *version;		/* version string */
+    xmlChar *encoding;		/* encoding string */
+    int omitXmlDeclaration;     /* omit-xml-declaration = "yes" | "no" */
+
+    /*
+     * Number formatting.
+     */
+    xsltDecimalFormatPtr decimalFormat;
+    int standalone;             /* standalone = "yes" | "no" */
+    xmlChar *doctypePublic;     /* doctype-public string */
+    xmlChar *doctypeSystem;     /* doctype-system string */
+    int indent;			/* should output being indented */
+    xmlChar *mediaType;		/* media-type string */
+
+    /*
+     * Precomputed blocks.
+     */
+    xsltElemPreCompPtr preComps;/* list of precomputed blocks */
+    int warnings;		/* number of warnings found at compilation */
+    int errors;			/* number of errors found at compilation */
+
+    xmlChar  *exclPrefix;	/* last excluded prefixes */
+    xmlChar **exclPrefixTab;	/* array of excluded prefixes */
+    int       exclPrefixNr;	/* number of excluded prefixes in scope */
+    int       exclPrefixMax;	/* size of the array */
+
+    void     *_private;		/* user defined data */
+
+    /*
+     * Extensions.
+     */
+    xmlHashTablePtr extInfos;	/* the extension data */
+    int		    extrasNr;	/* the number of extras required */
+
+    /*
+     * For keeping track of nested includes
+     */
+    xsltDocumentPtr includes;	/* points to last nested include */
+
+    /*
+     * dictionary: shared between stylesheet, context and documents.
+     */
+    xmlDictPtr dict;
+    /*
+     * precompiled attribute value templates.
+     */
+    void *attVTs;
+    /*
+     * if namespace-alias has an alias for the default stylesheet prefix
+     * NOTE: Not used in the refactored code.
+     */
+    const xmlChar *defaultAlias;
+    /*
+     * bypass pre-processing (already done) (used in imports)
+     */
+    int nopreproc;
+    /*
+     * all document text strings were internalized
+     */
+    int internalized;
+    /*
+     * Literal Result Element as Stylesheet c.f. section 2.3
+     */
+    int literal_result;
+    /*
+    * The principal stylesheet
+    */
+    xsltStylesheetPtr principal;
+#ifdef XSLT_REFACTORED
+    /*
+    * Compilation context used during compile-time.
+    */
+    xsltCompilerCtxtPtr compCtxt; /* TODO: Change this to (void *). */
+
+    xsltPrincipalStylesheetDataPtr principalData;
+#endif
+    /*
+     * Forwards-compatible processing
+     */
+    int forwards_compatible;
+
+    xmlHashTablePtr namedTemplates; /* hash table of named templates */
+
+    xmlXPathContextPtr xpathCtxt;
+
+    unsigned long opLimit;
+    unsigned long opCount;
+};
+
+typedef struct _xsltTransformCache xsltTransformCache;
+typedef xsltTransformCache *xsltTransformCachePtr;
+struct _xsltTransformCache {
+    xmlDocPtr RVT;
+    int nbRVT;
+    xsltStackElemPtr stackItems;
+    int nbStackItems;
+#ifdef XSLT_DEBUG_PROFILE_CACHE
+    int dbgCachedRVTs;
+    int dbgReusedRVTs;
+    int dbgCachedVars;
+    int dbgReusedVars;
+#endif
+};
+
+/*
+ * The in-memory structure corresponding to an XSLT Transformation.
+ */
+typedef enum {
+    XSLT_OUTPUT_XML = 0,
+    XSLT_OUTPUT_HTML,
+    XSLT_OUTPUT_TEXT
+} xsltOutputType;
+
+typedef void *
+(*xsltNewLocaleFunc)(const xmlChar *lang, int lowerFirst);
+typedef void
+(*xsltFreeLocaleFunc)(void *locale);
+typedef xmlChar *
+(*xsltGenSortKeyFunc)(void *locale, const xmlChar *lang);
+
+typedef enum {
+    XSLT_STATE_OK = 0,
+    XSLT_STATE_ERROR,
+    XSLT_STATE_STOPPED
+} xsltTransformState;
+
+struct _xsltTransformContext {
+    xsltStylesheetPtr style;		/* the stylesheet used */
+    xsltOutputType type;		/* the type of output */
+
+    xsltTemplatePtr  templ;		/* the current template */
+    int              templNr;		/* Nb of templates in the stack */
+    int              templMax;		/* Size of the templtes stack */
+    xsltTemplatePtr *templTab;		/* the template stack */
+
+    xsltStackElemPtr  vars;		/* the current variable list */
+    int               varsNr;		/* Nb of variable list in the stack */
+    int               varsMax;		/* Size of the variable list stack */
+    xsltStackElemPtr *varsTab;		/* the variable list stack */
+    int               varsBase;		/* the var base for current templ */
+
+    /*
+     * Extensions
+     */
+    xmlHashTablePtr   extFunctions;	/* the extension functions */
+    xmlHashTablePtr   extElements;	/* the extension elements */
+    xmlHashTablePtr   extInfos;		/* the extension data */
+
+    const xmlChar *mode;		/* the current mode */
+    const xmlChar *modeURI;		/* the current mode URI */
+
+    xsltDocumentPtr docList;		/* the document list */
+
+    xsltDocumentPtr document;		/* the current source document; can be NULL if an RTF */
+    xmlNodePtr node;			/* the current node being processed */
+    xmlNodeSetPtr nodeList;		/* the current node list */
+    /* xmlNodePtr current;			the node */
+
+    xmlDocPtr output;			/* the resulting document */
+    xmlNodePtr insert;			/* the insertion node */
+
+    xmlXPathContextPtr xpathCtxt;	/* the XPath context */
+    xsltTransformState state;		/* the current state */
+
+    /*
+     * Global variables
+     */
+    xmlHashTablePtr   globalVars;	/* the global variables and params */
+
+    xmlNodePtr inst;			/* the instruction in the stylesheet */
+
+    int xinclude;			/* should XInclude be processed */
+
+    const char *      outputFile;	/* the output URI if known */
+
+    int profile;                        /* is this run profiled */
+    long             prof;		/* the current profiled value */
+    int              profNr;		/* Nb of templates in the stack */
+    int              profMax;		/* Size of the templtaes stack */
+    long            *profTab;		/* the profile template stack */
+
+    void            *_private;		/* user defined data */
+
+    int              extrasNr;		/* the number of extras used */
+    int              extrasMax;		/* the number of extras allocated */
+    xsltRuntimeExtraPtr extras;		/* extra per runtime information */
+
+    xsltDocumentPtr  styleList;		/* the stylesheet docs list */
+    void                 * sec;		/* the security preferences if any */
+
+    xmlGenericErrorFunc  error;		/* a specific error handler */
+    void              * errctx;		/* context for the error handler */
+
+    xsltSortFunc      sortfunc;		/* a ctxt specific sort routine */
+
+    /*
+     * handling of temporary Result Value Tree
+     * (XSLT 1.0 term: "Result Tree Fragment")
+     */
+    xmlDocPtr       tmpRVT;		/* list of RVT without persistance */
+    xmlDocPtr       persistRVT;		/* list of persistant RVTs */
+    int             ctxtflags;          /* context processing flags */
+
+    /*
+     * Speed optimization when coalescing text nodes
+     */
+    const xmlChar  *lasttext;		/* last text node content */
+    int             lasttsize;		/* last text node size */
+    int             lasttuse;		/* last text node use */
+    /*
+     * Per Context Debugging
+     */
+    int debugStatus;			/* the context level debug status */
+    unsigned long* traceCode;		/* pointer to the variable holding the mask */
+
+    int parserOptions;			/* parser options xmlParserOption */
+
+    /*
+     * dictionary: shared between stylesheet, context and documents.
+     */
+    xmlDictPtr dict;
+    xmlDocPtr		tmpDoc; /* Obsolete; not used in the library. */
+    /*
+     * all document text strings are internalized
+     */
+    int internalized;
+    int nbKeys;
+    int hasTemplKeyPatterns;
+    xsltTemplatePtr currentTemplateRule; /* the Current Template Rule */
+    xmlNodePtr initialContextNode;
+    xmlDocPtr initialContextDoc;
+    xsltTransformCachePtr cache;
+    void *contextVariable; /* the current variable item */
+    xmlDocPtr localRVT; /* list of local tree fragments; will be freed when
+			   the instruction which created the fragment
+                           exits */
+    xmlDocPtr localRVTBase; /* Obsolete */
+    int keyInitLevel;   /* Needed to catch recursive keys issues */
+    int depth;          /* Needed to catch recursions */
+    int maxTemplateDepth;
+    int maxTemplateVars;
+    unsigned long opLimit;
+    unsigned long opCount;
+    int sourceDocDirty;
+    unsigned long currentId; /* For generate-id() */
+
+    xsltNewLocaleFunc newLocale;
+    xsltFreeLocaleFunc freeLocale;
+    xsltGenSortKeyFunc genSortKey;
+};
+
+/**
+ * CHECK_STOPPED:
+ *
+ * Macro to check if the XSLT processing should be stopped.
+ * Will return from the function.
+ */
+#define CHECK_STOPPED if (ctxt->state == XSLT_STATE_STOPPED) return;
+
+/**
+ * CHECK_STOPPEDE:
+ *
+ * Macro to check if the XSLT processing should be stopped.
+ * Will goto the error: label.
+ */
+#define CHECK_STOPPEDE if (ctxt->state == XSLT_STATE_STOPPED) goto error;
+
+/**
+ * CHECK_STOPPED0:
+ *
+ * Macro to check if the XSLT processing should be stopped.
+ * Will return from the function with a 0 value.
+ */
+#define CHECK_STOPPED0 if (ctxt->state == XSLT_STATE_STOPPED) return(0);
+
+/*
+ * The macro XML_CAST_FPTR is a hack to avoid a gcc warning about
+ * possible incompatibilities between function pointers and object
+ * pointers.  It is defined in libxml/hash.h within recent versions
+ * of libxml2, but is put here for compatibility.
+ */
+#ifndef XML_CAST_FPTR
+/**
+ * XML_CAST_FPTR:
+ * @fptr:  pointer to a function
+ *
+ * Macro to do a casting from an object pointer to a
+ * function pointer without encountering a warning from
+ * gcc
+ *
+ * #define XML_CAST_FPTR(fptr) (*(void **)(&fptr))
+ * This macro violated ISO C aliasing rules (gcc4 on s390 broke)
+ * so it is disabled now
+ */
+
+#define XML_CAST_FPTR(fptr) fptr
+#endif
+/*
+ * Functions associated to the internal types
+xsltDecimalFormatPtr	xsltDecimalFormatGetByName(xsltStylesheetPtr sheet,
+						   xmlChar *name);
+ */
+XSLTPUBFUN xsltStylesheetPtr XSLTCALL
+			xsltNewStylesheet	(void);
+XSLTPUBFUN xsltStylesheetPtr XSLTCALL
+			xsltParseStylesheetFile	(const xmlChar* filename);
+XSLTPUBFUN void XSLTCALL
+			xsltFreeStylesheet	(xsltStylesheetPtr style);
+XSLTPUBFUN int XSLTCALL
+			xsltIsBlank		(xmlChar *str);
+XSLTPUBFUN void XSLTCALL
+			xsltFreeStackElemList	(xsltStackElemPtr elem);
+XSLTPUBFUN xsltDecimalFormatPtr XSLTCALL
+			xsltDecimalFormatGetByName(xsltStylesheetPtr style,
+						 xmlChar *name);
+XSLTPUBFUN xsltDecimalFormatPtr XSLTCALL
+			xsltDecimalFormatGetByQName(xsltStylesheetPtr style,
+						 const xmlChar *nsUri,
+                                                 const xmlChar *name);
+
+XSLTPUBFUN xsltStylesheetPtr XSLTCALL
+			xsltParseStylesheetProcess(xsltStylesheetPtr ret,
+						 xmlDocPtr doc);
+XSLTPUBFUN void XSLTCALL
+			xsltParseStylesheetOutput(xsltStylesheetPtr style,
+						 xmlNodePtr cur);
+XSLTPUBFUN xsltStylesheetPtr XSLTCALL
+			xsltParseStylesheetDoc	(xmlDocPtr doc);
+XSLTPUBFUN xsltStylesheetPtr XSLTCALL
+			xsltParseStylesheetImportedDoc(xmlDocPtr doc,
+						xsltStylesheetPtr style);
+XSLTPUBFUN int XSLTCALL
+			xsltParseStylesheetUser(xsltStylesheetPtr style,
+						xmlDocPtr doc);
+XSLTPUBFUN xsltStylesheetPtr XSLTCALL
+			xsltLoadStylesheetPI	(xmlDocPtr doc);
+XSLTPUBFUN void XSLTCALL
+			xsltNumberFormat	(xsltTransformContextPtr ctxt,
+						 xsltNumberDataPtr data,
+						 xmlNodePtr node);
+XSLTPUBFUN xmlXPathError XSLTCALL
+			xsltFormatNumberConversion(xsltDecimalFormatPtr self,
+						 xmlChar *format,
+						 double number,
+						 xmlChar **result);
+
+XSLTPUBFUN void XSLTCALL
+			xsltParseTemplateContent(xsltStylesheetPtr style,
+						 xmlNodePtr templ);
+XSLTPUBFUN int XSLTCALL
+			xsltAllocateExtra	(xsltStylesheetPtr style);
+XSLTPUBFUN int XSLTCALL
+			xsltAllocateExtraCtxt	(xsltTransformContextPtr ctxt);
+/*
+ * Extra functions for Result Value Trees
+ */
+XSLTPUBFUN xmlDocPtr XSLTCALL
+			xsltCreateRVT		(xsltTransformContextPtr ctxt);
+XSLTPUBFUN int XSLTCALL
+			xsltRegisterTmpRVT	(xsltTransformContextPtr ctxt,
+						 xmlDocPtr RVT);
+XSLTPUBFUN int XSLTCALL
+			xsltRegisterLocalRVT	(xsltTransformContextPtr ctxt,
+						 xmlDocPtr RVT);
+XSLTPUBFUN int XSLTCALL
+			xsltRegisterPersistRVT	(xsltTransformContextPtr ctxt,
+						 xmlDocPtr RVT);
+XSLTPUBFUN int XSLTCALL
+			xsltExtensionInstructionResultRegister(
+						 xsltTransformContextPtr ctxt,
+						 xmlXPathObjectPtr obj);
+XSLTPUBFUN int XSLTCALL
+			xsltExtensionInstructionResultFinalize(
+						 xsltTransformContextPtr ctxt);
+XSLTPUBFUN int XSLTCALL
+			xsltFlagRVTs(
+						 xsltTransformContextPtr ctxt,
+						 xmlXPathObjectPtr obj,
+						 int val);
+XSLTPUBFUN void XSLTCALL
+			xsltFreeRVTs		(xsltTransformContextPtr ctxt);
+XSLTPUBFUN void XSLTCALL
+			xsltReleaseRVT		(xsltTransformContextPtr ctxt,
+						 xmlDocPtr RVT);
+/*
+ * Extra functions for Attribute Value Templates
+ */
+XSLTPUBFUN void XSLTCALL
+			xsltCompileAttr		(xsltStylesheetPtr style,
+						 xmlAttrPtr attr);
+XSLTPUBFUN xmlChar * XSLTCALL
+			xsltEvalAVT		(xsltTransformContextPtr ctxt,
+						 void *avt,
+						 xmlNodePtr node);
+XSLTPUBFUN void XSLTCALL
+			xsltFreeAVTList		(void *avt);
+
+/*
+ * Extra function for successful xsltCleanupGlobals / xsltInit sequence.
+ */
+
+XSLTPUBFUN void XSLTCALL
+			xsltUninit		(void);
+
+/************************************************************************
+ *									*
+ *  Compile-time functions for *internal* use only                      *
+ *									*
+ ************************************************************************/
+
+#ifdef XSLT_REFACTORED
+XSLTPUBFUN void XSLTCALL
+			xsltParseSequenceConstructor(
+						 xsltCompilerCtxtPtr cctxt,
+						 xmlNodePtr start);
+XSLTPUBFUN int XSLTCALL
+			xsltParseAnyXSLTElem	(xsltCompilerCtxtPtr cctxt,
+						 xmlNodePtr elem);
+#ifdef XSLT_REFACTORED_XSLT_NSCOMP
+XSLTPUBFUN int XSLTCALL
+			xsltRestoreDocumentNamespaces(
+						 xsltNsMapPtr ns,
+						 xmlDocPtr doc);
+#endif
+#endif /* XSLT_REFACTORED */
+
+/************************************************************************
+ *									*
+ *  Transformation-time functions for *internal* use only               *
+ *									*
+ ************************************************************************/
+XSLTPUBFUN int XSLTCALL
+			xsltInitCtxtKey		(xsltTransformContextPtr ctxt,
+						 xsltDocumentPtr doc,
+						 xsltKeyDefPtr keyd);
+XSLTPUBFUN int XSLTCALL
+			xsltInitAllDocKeys	(xsltTransformContextPtr ctxt);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_XSLT_H__ */
+
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/xsltconfig.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/xsltconfig.h
new file mode 100644
index 00000000..7e2c79ae
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/xsltconfig.h
@@ -0,0 +1,146 @@
+/*
+ * Summary: compile-time version information for the XSLT engine
+ * Description: compile-time version information for the XSLT engine
+ *              this module is autogenerated.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XSLTCONFIG_H__
+#define __XML_XSLTCONFIG_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * LIBXSLT_DOTTED_VERSION:
+ *
+ * the version string like "1.2.3"
+ */
+#define LIBXSLT_DOTTED_VERSION "1.1.42"
+
+/**
+ * LIBXSLT_VERSION:
+ *
+ * the version number: 1.2.3 value is 10203
+ */
+#define LIBXSLT_VERSION 10142
+
+/**
+ * LIBXSLT_VERSION_STRING:
+ *
+ * the version number string, 1.2.3 value is "10203"
+ */
+#define LIBXSLT_VERSION_STRING "10142"
+
+/**
+ * LIBXSLT_VERSION_EXTRA:
+ *
+ * extra version information, used to show a Git commit description
+ */
+#define	LIBXSLT_VERSION_EXTRA ""
+
+/**
+ * WITH_XSLT_DEBUG:
+ *
+ * Activate the compilation of the debug reporting. Speed penalty
+ * is insignifiant and being able to run xsltpoc -v is useful. On
+ * by default unless --without-debug is passed to configure
+ */
+#if 1
+#define WITH_XSLT_DEBUG
+#endif
+
+/**
+ * XSLT_NEED_TRIO:
+ *
+ * should be activated if the existing libc library lacks some of the
+ * string formatting function, in that case reuse the Trio ones already
+ * compiled in the libxml2 library.
+ */
+
+#if 0
+#define XSLT_NEED_TRIO
+#endif
+#ifdef __VMS
+#define HAVE_SYS_STAT_H 1
+#ifndef XSLT_NEED_TRIO
+#define XSLT_NEED_TRIO
+#endif
+#endif
+
+#ifdef	XSLT_NEED_TRIO
+#define	TRIO_REPLACE_STDIO
+#endif
+
+/**
+ * WITH_XSLT_DEBUGGER:
+ *
+ * Activate the compilation of the debugger support. Speed penalty
+ * is insignifiant.
+ * On by default unless --without-debugger is passed to configure
+ */
+#if 1
+#ifndef WITH_DEBUGGER
+#define WITH_DEBUGGER
+#endif
+#endif
+
+/**
+ * WITH_PROFILER:
+ *
+ * Activate the compilation of the profiler. Speed penalty
+ * is insignifiant.
+ * On by default unless --without-profiler is passed to configure
+ */
+#if 1
+#ifndef WITH_PROFILER
+#define WITH_PROFILER
+#endif
+#endif
+
+/**
+ * WITH_MODULES:
+ *
+ * Whether module support is configured into libxslt
+ * Note: no default module path for win32 platforms
+ */
+#if 0
+#ifndef WITH_MODULES
+#define WITH_MODULES
+#endif
+#define LIBXSLT_DEFAULT_PLUGINS_PATH() "/project/build/tmp/libxml2/lib/libxslt-plugins"
+#endif
+
+/**
+ * LIBXSLT_ATTR_FORMAT:
+ *
+ * This macro is used to indicate to GCC the parameters are printf-like
+ */
+#ifdef __GNUC__
+#define LIBXSLT_ATTR_FORMAT(fmt,args) __attribute__((__format__(__printf__,fmt,args)))
+#else
+#define LIBXSLT_ATTR_FORMAT(fmt,args)
+#endif
+
+/**
+ * LIBXSLT_PUBLIC:
+ *
+ * This macro is used to declare PUBLIC variables for Cygwin and for MSC on Windows
+ */
+#if !defined LIBXSLT_PUBLIC
+#if (defined(__CYGWIN__) || defined _MSC_VER) && !defined IN_LIBXSLT && !defined LIBXSLT_STATIC
+#define LIBXSLT_PUBLIC __declspec(dllimport)
+#else
+#define LIBXSLT_PUBLIC
+#endif
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_XSLTCONFIG_H__ */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/xsltexports.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/xsltexports.h
new file mode 100644
index 00000000..95c352fe
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/xsltexports.h
@@ -0,0 +1,64 @@
+/*
+ * Summary: macros for marking symbols as exportable/importable.
+ * Description: macros for marking symbols as exportable/importable.
+ *
+ * Copy: See Copyright for the status of this software.
+ */
+
+#ifndef __XSLT_EXPORTS_H__
+#define __XSLT_EXPORTS_H__
+
+#if defined(_WIN32) || defined(__CYGWIN__)
+/** DOC_DISABLE */
+
+#ifdef LIBXSLT_STATIC
+  #define XSLTPUBLIC
+#elif defined(IN_LIBXSLT)
+  #define XSLTPUBLIC __declspec(dllexport)
+#else
+  #define XSLTPUBLIC __declspec(dllimport)
+#endif
+
+#define XSLTCALL __cdecl
+
+/** DOC_ENABLE */
+#else /* not Windows */
+
+/**
+ * XSLTPUBLIC:
+ *
+ * Macro which declares a public symbol
+ */
+#define XSLTPUBLIC
+
+/**
+ * XSLTCALL:
+ *
+ * Macro which declares the calling convention for exported functions
+ */
+#define XSLTCALL
+
+#endif /* platform switch */
+
+/*
+ * XSLTPUBFUN:
+ *
+ * Macro which declares an exportable function
+ */
+#define XSLTPUBFUN XSLTPUBLIC
+
+/**
+ * XSLTPUBVAR:
+ *
+ * Macro which declares an exportable variable
+ */
+#define XSLTPUBVAR XSLTPUBLIC extern
+
+/* Compatibility */
+#if !defined(LIBXSLT_PUBLIC)
+#define LIBXSLT_PUBLIC XSLTPUBVAR
+#endif
+
+#endif /* __XSLT_EXPORTS_H__ */
+
+
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/xsltlocale.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/xsltlocale.h
new file mode 100644
index 00000000..c8be58d3
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/xsltlocale.h
@@ -0,0 +1,44 @@
+/*
+ * Summary: Locale handling
+ * Description: Interfaces for locale handling. Needed for language dependent
+ *              sorting.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Nick Wellnhofer
+ */
+
+#ifndef __XML_XSLTLOCALE_H__
+#define __XML_XSLTLOCALE_H__
+
+#include <libxml/xmlstring.h>
+#include "xsltexports.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+XSLTPUBFUN void * XSLTCALL
+	xsltNewLocale			(const xmlChar *langName,
+					 int lowerFirst);
+XSLTPUBFUN void XSLTCALL
+	xsltFreeLocale			(void *locale);
+XSLTPUBFUN xmlChar * XSLTCALL
+	xsltStrxfrm			(void *locale,
+					 const xmlChar *string);
+XSLTPUBFUN void XSLTCALL
+	xsltFreeLocales			(void);
+
+/* Backward compatibility */
+typedef void *xsltLocale;
+typedef xmlChar xsltLocaleChar;
+XSLTPUBFUN int XSLTCALL
+	xsltLocaleStrcmp		(void *locale,
+					 const xmlChar *str1,
+					 const xmlChar *str2);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_XSLTLOCALE_H__ */
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/xsltutils.h b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/xsltutils.h
new file mode 100644
index 00000000..2514774b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/libxslt/xsltutils.h
@@ -0,0 +1,343 @@
+/*
+ * Summary: set of utilities for the XSLT engine
+ * Description: interfaces for the utilities module of the XSLT engine.
+ *              things like message handling, profiling, and other
+ *              generally useful routines.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_XSLTUTILS_H__
+#define __XML_XSLTUTILS_H__
+
+#include <libxslt/xsltconfig.h>
+#include <libxml/xpath.h>
+#include <libxml/dict.h>
+#include <libxml/xmlerror.h>
+#include "xsltexports.h"
+#include "xsltInternals.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * XSLT_TODO:
+ *
+ * Macro to flag unimplemented blocks.
+ */
+#define XSLT_TODO							\
+    xsltGenericError(xsltGenericErrorContext,				\
+	    "Unimplemented block at %s:%d\n",				\
+            __FILE__, __LINE__);
+
+/**
+ * XSLT_STRANGE:
+ *
+ * Macro to flag that a problem was detected internally.
+ */
+#define XSLT_STRANGE							\
+    xsltGenericError(xsltGenericErrorContext,				\
+	    "Internal error at %s:%d\n",				\
+            __FILE__, __LINE__);
+
+/**
+ * IS_XSLT_ELEM:
+ *
+ * Checks that the element pertains to XSLT namespace.
+ */
+#define IS_XSLT_ELEM(n)							\
+    (((n) != NULL) && ((n)->type == XML_ELEMENT_NODE) &&                \
+     ((n)->ns != NULL) && (xmlStrEqual((n)->ns->href, XSLT_NAMESPACE)))
+
+/**
+ * IS_XSLT_NAME:
+ *
+ * Checks the value of an element in XSLT namespace.
+ */
+#define IS_XSLT_NAME(n, val)						\
+    (xmlStrEqual((n)->name, (const xmlChar *) (val)))
+
+/**
+ * IS_XSLT_REAL_NODE:
+ *
+ * Check that a node is a 'real' one: document, element, text or attribute.
+ */
+#define IS_XSLT_REAL_NODE(n)						\
+    (((n) != NULL) &&							\
+     (((n)->type == XML_ELEMENT_NODE) ||				\
+      ((n)->type == XML_TEXT_NODE) ||					\
+      ((n)->type == XML_CDATA_SECTION_NODE) ||				\
+      ((n)->type == XML_ATTRIBUTE_NODE) ||				\
+      ((n)->type == XML_DOCUMENT_NODE) ||				\
+      ((n)->type == XML_HTML_DOCUMENT_NODE) ||				\
+      ((n)->type == XML_COMMENT_NODE) ||				\
+      ((n)->type == XML_PI_NODE)))
+
+/*
+ * Our own version of namespaced attributes lookup.
+ */
+XSLTPUBFUN xmlChar * XSLTCALL
+		xsltGetNsProp	(xmlNodePtr node,
+				 const xmlChar *name,
+				 const xmlChar *nameSpace);
+XSLTPUBFUN const xmlChar * XSLTCALL
+		xsltGetCNsProp	(xsltStylesheetPtr style,
+				 xmlNodePtr node,
+				 const xmlChar *name,
+				 const xmlChar *nameSpace);
+XSLTPUBFUN int XSLTCALL
+		xsltGetUTF8Char	(const unsigned char *utf,
+				 int *len);
+#ifdef IN_LIBXSLT
+/** DOC_DISABLE */
+XSLTPUBFUN int XSLTCALL
+		xsltGetUTF8CharZ (const unsigned char *utf,
+				  int *len);
+/** DOC_ENABLE */
+#endif
+
+/*
+ * XSLT Debug Tracing Tracing Types
+ */
+typedef enum {
+	XSLT_TRACE_ALL =		-1,
+	XSLT_TRACE_NONE =		0,
+	XSLT_TRACE_COPY_TEXT =		1<<0,
+	XSLT_TRACE_PROCESS_NODE =	1<<1,
+	XSLT_TRACE_APPLY_TEMPLATE =	1<<2,
+	XSLT_TRACE_COPY =		1<<3,
+	XSLT_TRACE_COMMENT =		1<<4,
+	XSLT_TRACE_PI =			1<<5,
+	XSLT_TRACE_COPY_OF =		1<<6,
+	XSLT_TRACE_VALUE_OF =		1<<7,
+	XSLT_TRACE_CALL_TEMPLATE =	1<<8,
+	XSLT_TRACE_APPLY_TEMPLATES =	1<<9,
+	XSLT_TRACE_CHOOSE =		1<<10,
+	XSLT_TRACE_IF =			1<<11,
+	XSLT_TRACE_FOR_EACH =		1<<12,
+	XSLT_TRACE_STRIP_SPACES =	1<<13,
+	XSLT_TRACE_TEMPLATES =		1<<14,
+	XSLT_TRACE_KEYS =		1<<15,
+	XSLT_TRACE_VARIABLES =		1<<16
+} xsltDebugTraceCodes;
+
+/**
+ * XSLT_TRACE:
+ *
+ * Control the type of xsl debugtrace messages emitted.
+ */
+#define XSLT_TRACE(ctxt,code,call)	\
+	if (ctxt->traceCode && (*(ctxt->traceCode) & code)) \
+	    call
+
+XSLTPUBFUN void XSLTCALL
+		xsltDebugSetDefaultTrace(xsltDebugTraceCodes val);
+XSLTPUBFUN xsltDebugTraceCodes XSLTCALL
+		xsltDebugGetDefaultTrace(void);
+
+/*
+ * XSLT specific error and debug reporting functions.
+ */
+XSLTPUBVAR xmlGenericErrorFunc xsltGenericError;
+XSLTPUBVAR void *xsltGenericErrorContext;
+XSLTPUBVAR xmlGenericErrorFunc xsltGenericDebug;
+XSLTPUBVAR void *xsltGenericDebugContext;
+
+XSLTPUBFUN void XSLTCALL
+		xsltPrintErrorContext		(xsltTransformContextPtr ctxt,
+	                                         xsltStylesheetPtr style,
+						 xmlNodePtr node);
+XSLTPUBFUN void XSLTCALL
+		xsltMessage			(xsltTransformContextPtr ctxt,
+						 xmlNodePtr node,
+						 xmlNodePtr inst);
+XSLTPUBFUN void XSLTCALL
+		xsltSetGenericErrorFunc		(void *ctx,
+						 xmlGenericErrorFunc handler);
+XSLTPUBFUN void XSLTCALL
+		xsltSetGenericDebugFunc		(void *ctx,
+						 xmlGenericErrorFunc handler);
+XSLTPUBFUN void XSLTCALL
+		xsltSetTransformErrorFunc	(xsltTransformContextPtr ctxt,
+						 void *ctx,
+						 xmlGenericErrorFunc handler);
+XSLTPUBFUN void XSLTCALL
+		xsltTransformError		(xsltTransformContextPtr ctxt,
+						 xsltStylesheetPtr style,
+						 xmlNodePtr node,
+						 const char *msg,
+						 ...) LIBXSLT_ATTR_FORMAT(4,5);
+
+XSLTPUBFUN int XSLTCALL
+		xsltSetCtxtParseOptions		(xsltTransformContextPtr ctxt,
+						 int options);
+/*
+ * Sorting.
+ */
+
+XSLTPUBFUN void XSLTCALL
+		xsltDocumentSortFunction	(xmlNodeSetPtr list);
+XSLTPUBFUN void XSLTCALL
+		xsltSetSortFunc			(xsltSortFunc handler);
+XSLTPUBFUN void XSLTCALL
+		xsltSetCtxtSortFunc		(xsltTransformContextPtr ctxt,
+						 xsltSortFunc handler);
+XSLTPUBFUN void XSLTCALL
+		xsltSetCtxtLocaleHandlers	(xsltTransformContextPtr ctxt,
+						 xsltNewLocaleFunc newLocale,
+						 xsltFreeLocaleFunc freeLocale,
+						 xsltGenSortKeyFunc genSortKey);
+XSLTPUBFUN void XSLTCALL
+		xsltDefaultSortFunction		(xsltTransformContextPtr ctxt,
+						 xmlNodePtr *sorts,
+						 int nbsorts);
+XSLTPUBFUN void XSLTCALL
+		xsltDoSortFunction		(xsltTransformContextPtr ctxt,
+						 xmlNodePtr * sorts,
+						 int nbsorts);
+XSLTPUBFUN xmlXPathObjectPtr * XSLTCALL
+		xsltComputeSortResult		(xsltTransformContextPtr ctxt,
+						 xmlNodePtr sort);
+
+/*
+ * QNames handling.
+ */
+
+XSLTPUBFUN const xmlChar * XSLTCALL
+		xsltSplitQName			(xmlDictPtr dict,
+						 const xmlChar *name,
+						 const xmlChar **prefix);
+XSLTPUBFUN const xmlChar * XSLTCALL
+		xsltGetQNameURI			(xmlNodePtr node,
+						 xmlChar **name);
+
+XSLTPUBFUN const xmlChar * XSLTCALL
+		xsltGetQNameURI2		(xsltStylesheetPtr style,
+						 xmlNodePtr node,
+						 const xmlChar **name);
+
+/*
+ * Output, reuse libxml I/O buffers.
+ */
+XSLTPUBFUN int XSLTCALL
+		xsltSaveResultTo		(xmlOutputBufferPtr buf,
+						 xmlDocPtr result,
+						 xsltStylesheetPtr style);
+XSLTPUBFUN int XSLTCALL
+		xsltSaveResultToFilename	(const char *URI,
+						 xmlDocPtr result,
+						 xsltStylesheetPtr style,
+						 int compression);
+XSLTPUBFUN int XSLTCALL
+		xsltSaveResultToFile		(FILE *file,
+						 xmlDocPtr result,
+						 xsltStylesheetPtr style);
+XSLTPUBFUN int XSLTCALL
+		xsltSaveResultToFd		(int fd,
+						 xmlDocPtr result,
+						 xsltStylesheetPtr style);
+XSLTPUBFUN int XSLTCALL
+		xsltSaveResultToString          (xmlChar **doc_txt_ptr,
+                                                 int * doc_txt_len,
+                                                 xmlDocPtr result,
+                                                 xsltStylesheetPtr style);
+
+/*
+ * XPath interface
+ */
+XSLTPUBFUN xmlXPathCompExprPtr XSLTCALL
+		xsltXPathCompile		(xsltStylesheetPtr style,
+						 const xmlChar *str);
+XSLTPUBFUN xmlXPathCompExprPtr XSLTCALL
+		xsltXPathCompileFlags		(xsltStylesheetPtr style,
+						 const xmlChar *str,
+						 int flags);
+
+#ifdef IN_LIBXSLT
+/** DOC_DISABLE */
+#define XSLT_SOURCE_NODE_MASK       15u
+#define XSLT_SOURCE_NODE_HAS_KEY    1u
+#define XSLT_SOURCE_NODE_HAS_ID     2u
+int
+xsltGetSourceNodeFlags(xmlNodePtr node);
+int
+xsltSetSourceNodeFlags(xsltTransformContextPtr ctxt, xmlNodePtr node,
+                       int flags);
+int
+xsltClearSourceNodeFlags(xmlNodePtr node, int flags);
+void **
+xsltGetPSVIPtr(xmlNodePtr cur);
+/** DOC_ENABLE */
+#endif
+
+#ifdef WITH_PROFILER
+/*
+ * Profiling.
+ */
+XSLTPUBFUN void XSLTCALL
+		xsltSaveProfiling		(xsltTransformContextPtr ctxt,
+						 FILE *output);
+XSLTPUBFUN xmlDocPtr XSLTCALL
+		xsltGetProfileInformation	(xsltTransformContextPtr ctxt);
+
+XSLTPUBFUN long XSLTCALL
+		xsltTimestamp			(void);
+XSLTPUBFUN void XSLTCALL
+		xsltCalibrateAdjust		(long delta);
+#endif
+
+/**
+ * XSLT_TIMESTAMP_TICS_PER_SEC:
+ *
+ * Sampling precision for profiling
+ */
+#define XSLT_TIMESTAMP_TICS_PER_SEC 100000l
+
+/*
+ * Hooks for the debugger.
+ */
+
+typedef enum {
+    XSLT_DEBUG_NONE = 0, /* no debugging allowed */
+    XSLT_DEBUG_INIT,
+    XSLT_DEBUG_STEP,
+    XSLT_DEBUG_STEPOUT,
+    XSLT_DEBUG_NEXT,
+    XSLT_DEBUG_STOP,
+    XSLT_DEBUG_CONT,
+    XSLT_DEBUG_RUN,
+    XSLT_DEBUG_RUN_RESTART,
+    XSLT_DEBUG_QUIT
+} xsltDebugStatusCodes;
+
+XSLTPUBVAR int xslDebugStatus;
+
+typedef void (*xsltHandleDebuggerCallback) (xmlNodePtr cur, xmlNodePtr node,
+			xsltTemplatePtr templ, xsltTransformContextPtr ctxt);
+typedef int (*xsltAddCallCallback) (xsltTemplatePtr templ, xmlNodePtr source);
+typedef void (*xsltDropCallCallback) (void);
+
+XSLTPUBFUN int XSLTCALL
+		xsltGetDebuggerStatus		(void);
+#ifdef WITH_DEBUGGER
+XSLTPUBFUN void XSLTCALL
+		xsltSetDebuggerStatus		(int value);
+XSLTPUBFUN int XSLTCALL
+		xsltSetDebuggerCallbacks	(int no, void *block);
+XSLTPUBFUN int XSLTCALL
+		xslAddCall			(xsltTemplatePtr templ,
+						 xmlNodePtr source);
+XSLTPUBFUN void XSLTCALL
+		xslDropCall			(void);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __XML_XSLTUTILS_H__ */
+
+
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/lxml-version.h b/.venv/lib/python3.12/site-packages/lxml/includes/lxml-version.h
new file mode 100644
index 00000000..101c7c91
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/lxml-version.h
@@ -0,0 +1,3 @@
+#ifndef LXML_VERSION_STRING
+#define LXML_VERSION_STRING "5.3.1"
+#endif
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/relaxng.pxd b/.venv/lib/python3.12/site-packages/lxml/includes/relaxng.pxd
new file mode 100644
index 00000000..5ac96711
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/relaxng.pxd
@@ -0,0 +1,64 @@
+from lxml.includes.tree cimport xmlDoc
+from lxml.includes.xmlerror cimport xmlStructuredErrorFunc
+
+cdef extern from "libxml/relaxng.h" nogil:
+    ctypedef struct xmlRelaxNG
+    ctypedef struct xmlRelaxNGParserCtxt
+    
+    ctypedef struct xmlRelaxNGValidCtxt
+    
+    ctypedef enum xmlRelaxNGValidErr:
+        XML_RELAXNG_OK = 0
+        XML_RELAXNG_ERR_MEMORY = 1
+        XML_RELAXNG_ERR_TYPE = 2
+        XML_RELAXNG_ERR_TYPEVAL = 3
+        XML_RELAXNG_ERR_DUPID = 4
+        XML_RELAXNG_ERR_TYPECMP = 5
+        XML_RELAXNG_ERR_NOSTATE = 6
+        XML_RELAXNG_ERR_NODEFINE = 7
+        XML_RELAXNG_ERR_LISTEXTRA = 8
+        XML_RELAXNG_ERR_LISTEMPTY = 9
+        XML_RELAXNG_ERR_INTERNODATA = 10
+        XML_RELAXNG_ERR_INTERSEQ = 11
+        XML_RELAXNG_ERR_INTEREXTRA = 12
+        XML_RELAXNG_ERR_ELEMNAME = 13
+        XML_RELAXNG_ERR_ATTRNAME = 14
+        XML_RELAXNG_ERR_ELEMNONS = 15
+        XML_RELAXNG_ERR_ATTRNONS = 16
+        XML_RELAXNG_ERR_ELEMWRONGNS = 17
+        XML_RELAXNG_ERR_ATTRWRONGNS = 18
+        XML_RELAXNG_ERR_ELEMEXTRANS = 19
+        XML_RELAXNG_ERR_ATTREXTRANS = 20
+        XML_RELAXNG_ERR_ELEMNOTEMPTY = 21
+        XML_RELAXNG_ERR_NOELEM = 22
+        XML_RELAXNG_ERR_NOTELEM = 23
+        XML_RELAXNG_ERR_ATTRVALID = 24
+        XML_RELAXNG_ERR_CONTENTVALID = 25
+        XML_RELAXNG_ERR_EXTRACONTENT = 26
+        XML_RELAXNG_ERR_INVALIDATTR = 27
+        XML_RELAXNG_ERR_DATAELEM = 28
+        XML_RELAXNG_ERR_VALELEM = 29
+        XML_RELAXNG_ERR_LISTELEM = 30
+        XML_RELAXNG_ERR_DATATYPE = 31
+        XML_RELAXNG_ERR_VALUE = 32
+        XML_RELAXNG_ERR_LIST = 33
+        XML_RELAXNG_ERR_NOGRAMMAR = 34
+        XML_RELAXNG_ERR_EXTRADATA = 35
+        XML_RELAXNG_ERR_LACKDATA = 36
+        XML_RELAXNG_ERR_INTERNAL = 37
+        XML_RELAXNG_ERR_ELEMWRONG = 38
+        XML_RELAXNG_ERR_TEXTWRONG = 39
+        
+    cdef xmlRelaxNGValidCtxt* xmlRelaxNGNewValidCtxt(xmlRelaxNG* schema)
+    cdef int xmlRelaxNGValidateDoc(xmlRelaxNGValidCtxt* ctxt, xmlDoc* doc)
+    cdef xmlRelaxNG* xmlRelaxNGParse(xmlRelaxNGParserCtxt* ctxt)
+    cdef xmlRelaxNGParserCtxt* xmlRelaxNGNewParserCtxt(char* URL)
+    cdef xmlRelaxNGParserCtxt* xmlRelaxNGNewDocParserCtxt(xmlDoc* doc)
+    cdef void xmlRelaxNGFree(xmlRelaxNG* schema)
+    cdef void xmlRelaxNGFreeParserCtxt(xmlRelaxNGParserCtxt* ctxt)
+    cdef void xmlRelaxNGFreeValidCtxt(xmlRelaxNGValidCtxt* ctxt)
+
+    cdef void xmlRelaxNGSetValidStructuredErrors(
+        xmlRelaxNGValidCtxt* ctxt, xmlStructuredErrorFunc serror, void *ctx)
+    cdef void xmlRelaxNGSetParserStructuredErrors(
+        xmlRelaxNGParserCtxt* ctxt, xmlStructuredErrorFunc serror, void *ctx)
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/schematron.pxd b/.venv/lib/python3.12/site-packages/lxml/includes/schematron.pxd
new file mode 100644
index 00000000..181248af
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/schematron.pxd
@@ -0,0 +1,34 @@
+from lxml.includes cimport xmlerror
+from lxml.includes.tree cimport xmlDoc
+
+cdef extern from "libxml/schematron.h" nogil:
+    ctypedef struct xmlSchematron
+    ctypedef struct xmlSchematronParserCtxt
+    ctypedef struct xmlSchematronValidCtxt
+
+    ctypedef enum xmlSchematronValidOptions:
+        XML_SCHEMATRON_OUT_QUIET     =    1 # quiet no report
+        XML_SCHEMATRON_OUT_TEXT      =    2 # build a textual report
+        XML_SCHEMATRON_OUT_XML       =    4 # output SVRL
+        XML_SCHEMATRON_OUT_ERROR     =    8 # output via xmlStructuredErrorFunc
+        XML_SCHEMATRON_OUT_FILE      =  256 # output to a file descriptor
+        XML_SCHEMATRON_OUT_BUFFER    =  512 # output to a buffer
+        XML_SCHEMATRON_OUT_IO        = 1024 # output to I/O mechanism
+
+    cdef xmlSchematronParserCtxt* xmlSchematronNewDocParserCtxt(
+        xmlDoc* doc)
+    cdef xmlSchematronParserCtxt* xmlSchematronNewParserCtxt(
+        char* filename) nogil
+    cdef xmlSchematronValidCtxt* xmlSchematronNewValidCtxt(
+        xmlSchematron* schema, int options)
+
+    cdef xmlSchematron* xmlSchematronParse(xmlSchematronParserCtxt* ctxt)
+    cdef int xmlSchematronValidateDoc(xmlSchematronValidCtxt* ctxt,
+                                      xmlDoc* instance)
+
+    cdef void xmlSchematronFreeParserCtxt(xmlSchematronParserCtxt* ctxt)
+    cdef void xmlSchematronFreeValidCtxt(xmlSchematronValidCtxt* ctxt)
+    cdef void xmlSchematronFree(xmlSchematron* schema)
+    cdef void xmlSchematronSetValidStructuredErrors(
+        xmlSchematronValidCtxt* ctxt,
+        xmlerror.xmlStructuredErrorFunc error_func, void *data)
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/tree.pxd b/.venv/lib/python3.12/site-packages/lxml/includes/tree.pxd
new file mode 100644
index 00000000..5e37d9d6
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/tree.pxd
@@ -0,0 +1,494 @@
+from libc cimport stdio
+from libc.string cimport const_char, const_uchar
+
+cdef extern from "lxml-version.h":
+    # deprecated declaration, use etreepublic.pxd instead
+    cdef char* LXML_VERSION_STRING
+
+cdef extern from "libxml/xmlversion.h":
+    cdef const_char* xmlParserVersion
+    cdef int LIBXML_VERSION
+
+cdef extern from "libxml/xmlstring.h" nogil:
+    ctypedef unsigned char xmlChar
+    ctypedef const xmlChar const_xmlChar "const xmlChar"
+    cdef int xmlStrlen(const_xmlChar* str)
+    cdef xmlChar* xmlStrdup(const_xmlChar* cur)
+    cdef int xmlStrncmp(const_xmlChar* str1, const_xmlChar* str2, int length)
+    cdef int xmlStrcmp(const_xmlChar* str1, const_xmlChar* str2)
+    cdef int xmlStrcasecmp(const xmlChar *str1, const xmlChar *str2)
+    cdef const_xmlChar* xmlStrstr(const_xmlChar* str1, const_xmlChar* str2)
+    cdef const_xmlChar* xmlStrchr(const_xmlChar* str1, xmlChar ch)
+    cdef const_xmlChar* _xcstr "(const xmlChar*)PyBytes_AS_STRING" (object s)
+
+cdef extern from "libxml/encoding.h" nogil:
+    ctypedef enum xmlCharEncoding:
+        XML_CHAR_ENCODING_ERROR = -1 # No char encoding detected
+        XML_CHAR_ENCODING_NONE = 0 # No char encoding detected
+        XML_CHAR_ENCODING_UTF8 = 1 # UTF-8
+        XML_CHAR_ENCODING_UTF16LE = 2 # UTF-16 little endian
+        XML_CHAR_ENCODING_UTF16BE = 3 # UTF-16 big endian
+        XML_CHAR_ENCODING_UCS4LE = 4 # UCS-4 little endian
+        XML_CHAR_ENCODING_UCS4BE = 5 # UCS-4 big endian
+        XML_CHAR_ENCODING_EBCDIC = 6 # EBCDIC uh!
+        XML_CHAR_ENCODING_UCS4_2143 = 7 # UCS-4 unusual ordering
+        XML_CHAR_ENCODING_UCS4_3412 = 8 # UCS-4 unusual ordering
+        XML_CHAR_ENCODING_UCS2 = 9 # UCS-2
+        XML_CHAR_ENCODING_8859_1 = 10 # ISO-8859-1 ISO Latin 1
+        XML_CHAR_ENCODING_8859_2 = 11 # ISO-8859-2 ISO Latin 2
+        XML_CHAR_ENCODING_8859_3 = 12 # ISO-8859-3
+        XML_CHAR_ENCODING_8859_4 = 13 # ISO-8859-4
+        XML_CHAR_ENCODING_8859_5 = 14 # ISO-8859-5
+        XML_CHAR_ENCODING_8859_6 = 15 # ISO-8859-6
+        XML_CHAR_ENCODING_8859_7 = 16 # ISO-8859-7
+        XML_CHAR_ENCODING_8859_8 = 17 # ISO-8859-8
+        XML_CHAR_ENCODING_8859_9 = 18 # ISO-8859-9
+        XML_CHAR_ENCODING_2022_JP = 19 # ISO-2022-JP
+        XML_CHAR_ENCODING_SHIFT_JIS = 20 # Shift_JIS
+        XML_CHAR_ENCODING_EUC_JP = 21 # EUC-JP
+        XML_CHAR_ENCODING_ASCII = 22 # pure ASCII
+
+    ctypedef struct xmlCharEncodingHandler:
+        char* name
+
+    cdef xmlCharEncodingHandler* xmlFindCharEncodingHandler(char* name)
+    cdef xmlCharEncodingHandler* xmlGetCharEncodingHandler(
+        xmlCharEncoding enc)
+    cdef int xmlCharEncCloseFunc(xmlCharEncodingHandler* handler)
+    cdef xmlCharEncoding xmlDetectCharEncoding(const_xmlChar* text, int len)
+    cdef const_char* xmlGetCharEncodingName(xmlCharEncoding enc)
+    cdef xmlCharEncoding xmlParseCharEncoding(char* name)
+    ctypedef int (*xmlCharEncodingOutputFunc)(
+            unsigned char *out_buf, int *outlen, const_uchar *in_buf, int *inlen)
+
+cdef extern from "libxml/chvalid.h" nogil:
+    cdef int xmlIsChar_ch(char c)
+    cdef int xmlIsCharQ(int ch)
+
+cdef extern from "libxml/hash.h":
+    ctypedef struct xmlHashTable
+    ctypedef void (*xmlHashScanner)(void* payload, void* data, const_xmlChar* name) noexcept  # may require GIL!
+    void xmlHashScan(xmlHashTable* table, xmlHashScanner f, void* data) nogil
+    void* xmlHashLookup(xmlHashTable* table, const_xmlChar* name) nogil
+    ctypedef void (*xmlHashDeallocator)(void *payload, xmlChar *name) noexcept
+    cdef xmlHashTable* xmlHashCreate(int size) nogil
+    cdef xmlHashTable* xmlHashCreateDict(int size, xmlDict *dict) nogil
+    cdef int xmlHashSize(xmlHashTable* table) nogil
+    cdef void xmlHashFree(xmlHashTable* table, xmlHashDeallocator f) nogil
+
+cdef extern from * nogil: # actually "libxml/dict.h"
+    # libxml/dict.h appears to be broken to include in C
+    ctypedef struct xmlDict
+    cdef const_xmlChar* xmlDictLookup(xmlDict* dict, const_xmlChar* name, int len)
+    cdef const_xmlChar* xmlDictExists(xmlDict* dict, const_xmlChar* name, int len)
+    cdef int xmlDictOwns(xmlDict* dict, const_xmlChar* name)
+    cdef size_t xmlDictSize(xmlDict* dict)
+
+cdef extern from "libxml/tree.h" nogil:
+    ctypedef struct xmlDoc
+    ctypedef struct xmlAttr
+    ctypedef struct xmlNotationTable
+
+    ctypedef enum xmlElementType:
+        XML_ELEMENT_NODE=           1
+        XML_ATTRIBUTE_NODE=         2
+        XML_TEXT_NODE=              3
+        XML_CDATA_SECTION_NODE=     4
+        XML_ENTITY_REF_NODE=        5
+        XML_ENTITY_NODE=            6
+        XML_PI_NODE=                7
+        XML_COMMENT_NODE=           8
+        XML_DOCUMENT_NODE=          9
+        XML_DOCUMENT_TYPE_NODE=     10
+        XML_DOCUMENT_FRAG_NODE=     11
+        XML_NOTATION_NODE=          12
+        XML_HTML_DOCUMENT_NODE=     13
+        XML_DTD_NODE=               14
+        XML_ELEMENT_DECL=           15
+        XML_ATTRIBUTE_DECL=         16
+        XML_ENTITY_DECL=            17
+        XML_NAMESPACE_DECL=         18
+        XML_XINCLUDE_START=         19
+        XML_XINCLUDE_END=           20
+
+    ctypedef enum xmlElementTypeVal:
+        XML_ELEMENT_TYPE_UNDEFINED= 0
+        XML_ELEMENT_TYPE_EMPTY=     1
+        XML_ELEMENT_TYPE_ANY=       2
+        XML_ELEMENT_TYPE_MIXED=     3
+        XML_ELEMENT_TYPE_ELEMENT=   4
+
+    ctypedef enum xmlElementContentType:
+        XML_ELEMENT_CONTENT_PCDATA=  1
+        XML_ELEMENT_CONTENT_ELEMENT= 2
+        XML_ELEMENT_CONTENT_SEQ=     3
+        XML_ELEMENT_CONTENT_OR=      4
+
+    ctypedef enum xmlElementContentOccur:
+        XML_ELEMENT_CONTENT_ONCE= 1
+        XML_ELEMENT_CONTENT_OPT=  2
+        XML_ELEMENT_CONTENT_MULT= 3
+        XML_ELEMENT_CONTENT_PLUS= 4
+
+    ctypedef enum xmlAttributeType:
+        XML_ATTRIBUTE_CDATA =      1
+        XML_ATTRIBUTE_ID=          2
+        XML_ATTRIBUTE_IDREF=       3
+        XML_ATTRIBUTE_IDREFS=      4
+        XML_ATTRIBUTE_ENTITY=      5
+        XML_ATTRIBUTE_ENTITIES=    6
+        XML_ATTRIBUTE_NMTOKEN=     7
+        XML_ATTRIBUTE_NMTOKENS=    8
+        XML_ATTRIBUTE_ENUMERATION= 9
+        XML_ATTRIBUTE_NOTATION=    10
+    
+    ctypedef enum xmlAttributeDefault:
+        XML_ATTRIBUTE_NONE=     1
+        XML_ATTRIBUTE_REQUIRED= 2
+        XML_ATTRIBUTE_IMPLIED=  3
+        XML_ATTRIBUTE_FIXED=    4
+
+    ctypedef enum xmlEntityType:
+        XML_INTERNAL_GENERAL_ENTITY=          1
+        XML_EXTERNAL_GENERAL_PARSED_ENTITY=   2
+        XML_EXTERNAL_GENERAL_UNPARSED_ENTITY= 3
+        XML_INTERNAL_PARAMETER_ENTITY=        4
+        XML_EXTERNAL_PARAMETER_ENTITY=        5
+        XML_INTERNAL_PREDEFINED_ENTITY=       6
+
+    ctypedef enum xmlDocProperties:
+        XML_DOC_WELLFORMED          = 1    # /* document is XML well formed */
+        XML_DOC_NSVALID             = 2    # /* document is Namespace valid */
+        XML_DOC_OLD10               = 4    # /* parsed with old XML-1.0 parser */
+        XML_DOC_DTDVALID            = 8    # /* DTD validation was successful */
+        XML_DOC_XINCLUDE            = 16   # /* XInclude substitution was done */
+        XML_DOC_USERBUILT           = 32   # /* Document was built using the API
+                                           #    and not by parsing an instance */
+        XML_DOC_INTERNAL            = 64   # /* built for internal processing */
+        XML_DOC_HTML                = 128  # /* parsed or built HTML document */
+
+    ctypedef struct xmlNs:
+        const_xmlChar* href
+        const_xmlChar* prefix
+        xmlNs* next
+
+    ctypedef struct xmlNode:
+        void* _private
+        xmlElementType   type
+        const_xmlChar* name
+        xmlNode* children
+        xmlNode* last
+        xmlNode* parent
+        xmlNode* next
+        xmlNode* prev
+        xmlDoc* doc
+        xmlChar* content
+        xmlAttr* properties
+        xmlNs* ns
+        xmlNs* nsDef
+        unsigned short line
+
+    ctypedef struct xmlElementContent:
+        xmlElementContentType type
+        xmlElementContentOccur ocur
+        const_xmlChar *name
+        xmlElementContent *c1
+        xmlElementContent *c2
+        xmlElementContent *parent
+        const_xmlChar *prefix
+
+    ctypedef struct xmlEnumeration:
+        xmlEnumeration *next
+        const_xmlChar *name
+
+    ctypedef struct xmlAttribute:
+        void* _private
+        xmlElementType type
+        const_xmlChar* name
+        xmlNode* children
+        xmlNode* last
+        xmlDtd* parent
+        xmlNode* next
+        xmlNode* prev
+        xmlDoc* doc
+        xmlAttribute* nexth
+        xmlAttributeType atype
+        xmlAttributeDefault def_ "def"
+        const_xmlChar* defaultValue
+        xmlEnumeration* tree
+        const_xmlChar* prefix
+        const_xmlChar* elem
+
+    ctypedef struct xmlElement:
+        void* _private
+        xmlElementType   type
+        const_xmlChar* name
+        xmlNode* children
+        xmlNode* last
+        xmlNode* parent
+        xmlNode* next
+        xmlNode* prev
+        xmlDoc* doc
+        xmlElementTypeVal etype
+        xmlElementContent* content
+        xmlAttribute* attributes
+        const_xmlChar* prefix
+        void *contModel
+
+    ctypedef struct xmlEntity:
+        void* _private
+        xmlElementType type
+        const_xmlChar* name
+        xmlNode* children
+        xmlNode* last
+        xmlDtd* parent
+        xmlNode* next
+        xmlNode* prev
+        xmlDoc* doc
+        xmlChar* orig
+        xmlChar* content
+        int length
+        xmlEntityType etype
+        const_xmlChar* ExternalID
+        const_xmlChar* SystemID
+        xmlEntity* nexte
+        const_xmlChar* URI
+        int owner
+        int checked
+
+    ctypedef struct xmlDtd:
+        const_xmlChar* name
+        const_xmlChar* ExternalID
+        const_xmlChar* SystemID
+        void* notations
+        void* entities
+        void* pentities
+        void* attributes
+        void* elements
+        xmlNode* children
+        xmlNode* last
+        xmlDoc* doc
+
+    ctypedef struct xmlDoc:
+        xmlElementType type
+        char* name
+        xmlNode* children
+        xmlNode* last
+        xmlNode* parent
+        xmlNode* next
+        xmlNode* prev
+        xmlDoc* doc
+        xmlDict* dict
+        xmlHashTable* ids
+        int standalone
+        const_xmlChar* version
+        const_xmlChar* encoding
+        const_xmlChar* URL
+        void* _private
+        xmlDtd* intSubset
+        xmlDtd* extSubset
+        int properties
+        
+    ctypedef struct xmlAttr:
+        void* _private
+        xmlElementType type
+        const_xmlChar* name
+        xmlNode* children
+        xmlNode* last
+        xmlNode* parent
+        xmlAttr* next
+        xmlAttr* prev
+        xmlDoc* doc
+        xmlNs* ns
+        xmlAttributeType atype
+
+    ctypedef struct xmlID:
+        const_xmlChar* value
+        const_xmlChar* name
+        xmlAttr* attr
+        xmlDoc* doc
+        
+    ctypedef struct xmlBuffer
+
+    ctypedef struct xmlBuf   # new in libxml2 2.9
+
+    ctypedef struct xmlOutputBuffer:
+        xmlBuf* buffer
+        xmlBuf* conv
+        int error
+
+    const_xmlChar* XML_XML_NAMESPACE
+        
+    cdef void xmlFreeDoc(xmlDoc* cur)
+    cdef void xmlFreeDtd(xmlDtd* cur)
+    cdef void xmlFreeNode(xmlNode* cur)
+    cdef void xmlFreeNsList(xmlNs* ns)
+    cdef void xmlFreeNs(xmlNs* ns)
+    cdef void xmlFree(void* buf)
+    
+    cdef xmlNode* xmlNewNode(xmlNs* ns, const_xmlChar* name)
+    cdef xmlNode* xmlNewDocText(xmlDoc* doc, const_xmlChar* content)
+    cdef xmlNode* xmlNewDocComment(xmlDoc* doc, const_xmlChar* content)
+    cdef xmlNode* xmlNewDocPI(xmlDoc* doc, const_xmlChar* name, const_xmlChar* content)
+    cdef xmlNode* xmlNewReference(xmlDoc* doc, const_xmlChar* name)
+    cdef xmlNode* xmlNewCDataBlock(xmlDoc* doc, const_xmlChar* text, int len)
+    cdef xmlNs* xmlNewNs(xmlNode* node, const_xmlChar* href, const_xmlChar* prefix)
+    cdef xmlNode* xmlAddChild(xmlNode* parent, xmlNode* cur)
+    cdef xmlNode* xmlReplaceNode(xmlNode* old, xmlNode* cur)
+    cdef xmlNode* xmlAddPrevSibling(xmlNode* cur, xmlNode* elem)
+    cdef xmlNode* xmlAddNextSibling(xmlNode* cur, xmlNode* elem)
+    cdef xmlNode* xmlNewDocNode(xmlDoc* doc, xmlNs* ns,
+                                const_xmlChar* name, const_xmlChar* content)
+    cdef xmlDoc* xmlNewDoc(const_xmlChar* version)
+    cdef xmlAttr* xmlNewProp(xmlNode* node, const_xmlChar* name, const_xmlChar* value)
+    cdef xmlAttr* xmlNewNsProp(xmlNode* node, xmlNs* ns,
+                               const_xmlChar* name, const_xmlChar* value)
+    cdef xmlChar* xmlGetNoNsProp(xmlNode* node, const_xmlChar* name)
+    cdef xmlChar* xmlGetNsProp(xmlNode* node, const_xmlChar* name, const_xmlChar* nameSpace)
+    cdef void xmlSetNs(xmlNode* node, xmlNs* ns)
+    cdef xmlAttr* xmlSetProp(xmlNode* node, const_xmlChar* name, const_xmlChar* value)
+    cdef xmlAttr* xmlSetNsProp(xmlNode* node, xmlNs* ns,
+                               const_xmlChar* name, const_xmlChar* value)
+    cdef int xmlRemoveID(xmlDoc* doc, xmlAttr* cur)
+    cdef int xmlRemoveProp(xmlAttr* cur)
+    cdef void xmlFreePropList(xmlAttr* cur)
+    cdef xmlChar* xmlGetNodePath(xmlNode* node)
+    cdef void xmlDocDumpMemory(xmlDoc* cur, char** mem, int* size)
+    cdef void xmlDocDumpMemoryEnc(xmlDoc* cur, char** mem, int* size,
+                                  char* encoding)
+    cdef int xmlSaveFileTo(xmlOutputBuffer* out, xmlDoc* cur,
+                           char* encoding)
+
+    cdef void xmlUnlinkNode(xmlNode* cur)
+    cdef xmlNode* xmlDocSetRootElement(xmlDoc* doc, xmlNode* root)
+    cdef xmlNode* xmlDocGetRootElement(xmlDoc* doc)
+    cdef void xmlSetTreeDoc(xmlNode* tree, xmlDoc* doc)
+    cdef xmlAttr* xmlHasProp(xmlNode* node, const_xmlChar* name)
+    cdef xmlAttr* xmlHasNsProp(xmlNode* node, const_xmlChar* name, const_xmlChar* nameSpace)
+    cdef xmlChar* xmlNodeGetContent(xmlNode* cur)
+    cdef int xmlNodeBufGetContent(xmlBuffer* buffer, xmlNode* cur)
+    cdef xmlNs* xmlSearchNs(xmlDoc* doc, xmlNode* node, const_xmlChar* prefix)
+    cdef xmlNs* xmlSearchNsByHref(xmlDoc* doc, xmlNode* node, const_xmlChar* href)
+    cdef int xmlIsBlankNode(xmlNode* node)
+    cdef long xmlGetLineNo(xmlNode* node)
+    cdef void xmlElemDump(stdio.FILE* f, xmlDoc* doc, xmlNode* cur)
+    cdef void xmlNodeDumpOutput(xmlOutputBuffer* buf,
+                                xmlDoc* doc, xmlNode* cur, int level,
+                                int format, const_char* encoding)
+    cdef void xmlBufAttrSerializeTxtContent(xmlOutputBuffer *buf, xmlDoc *doc,
+                                xmlAttr *attr, const_xmlChar *string)
+    cdef void xmlNodeSetName(xmlNode* cur, const_xmlChar* name)
+    cdef void xmlNodeSetContent(xmlNode* cur, const_xmlChar* content)
+    cdef xmlDtd* xmlCopyDtd(xmlDtd* dtd)
+    cdef xmlDoc* xmlCopyDoc(xmlDoc* doc, int recursive)
+    cdef xmlNode* xmlCopyNode(xmlNode* node, int extended)
+    cdef xmlNode* xmlDocCopyNode(xmlNode* node, xmlDoc* doc, int extended)
+    cdef int xmlReconciliateNs(xmlDoc* doc, xmlNode* tree)
+    cdef xmlNs* xmlNewReconciliedNs(xmlDoc* doc, xmlNode* tree, xmlNs* ns)
+    cdef xmlBuffer* xmlBufferCreate()
+    cdef void xmlBufferWriteChar(xmlBuffer* buf, char* string)
+    cdef void xmlBufferFree(xmlBuffer* buf)
+    cdef const_xmlChar* xmlBufferContent(xmlBuffer* buf)
+    cdef int xmlBufferLength(xmlBuffer* buf)
+    cdef const_xmlChar* xmlBufContent(xmlBuf* buf) # new in libxml2 2.9
+    cdef size_t xmlBufUse(xmlBuf* buf) # new in libxml2 2.9
+    cdef int xmlKeepBlanksDefault(int val)
+    cdef xmlChar* xmlNodeGetBase(xmlDoc* doc, xmlNode* node)
+    cdef xmlDtd* xmlCreateIntSubset(xmlDoc* doc, const_xmlChar* name,
+                                    const_xmlChar* ExternalID, const_xmlChar* SystemID)
+    cdef void xmlNodeSetBase(xmlNode* node, const_xmlChar* uri)
+    cdef int xmlValidateNCName(const_xmlChar* value, int space)
+
+cdef extern from "libxml/uri.h" nogil:
+    cdef const_xmlChar* xmlBuildURI(const_xmlChar* href, const_xmlChar* base)
+
+cdef extern from "libxml/HTMLtree.h" nogil:
+    cdef void htmlNodeDumpFormatOutput(xmlOutputBuffer* buf,
+                                       xmlDoc* doc, xmlNode* cur,
+                                       char* encoding, int format)
+    cdef xmlDoc* htmlNewDoc(const_xmlChar* uri, const_xmlChar* externalID)
+
+cdef extern from "libxml/valid.h" nogil:
+    cdef xmlAttr* xmlGetID(xmlDoc* doc, const_xmlChar* ID)
+    cdef void xmlDumpNotationTable(xmlBuffer* buffer,
+                                   xmlNotationTable* table)
+    cdef int xmlValidateNameValue(const_xmlChar* value)
+
+cdef extern from "libxml/xmlIO.h":
+    cdef int xmlOutputBufferWrite(xmlOutputBuffer* out,
+                                  int len, const_char* str) nogil
+    cdef int xmlOutputBufferWriteString(xmlOutputBuffer* out, const_char* str) nogil
+    cdef int xmlOutputBufferWriteEscape(xmlOutputBuffer* out,
+                                        const_xmlChar* str,
+                                        xmlCharEncodingOutputFunc escapefunc) nogil
+    cdef int xmlOutputBufferFlush(xmlOutputBuffer* out) nogil
+    cdef int xmlOutputBufferClose(xmlOutputBuffer* out) nogil
+
+    ctypedef int (*xmlInputReadCallback)(void* context,
+                                         char* buffer, int len) noexcept nogil
+    ctypedef int (*xmlInputCloseCallback)(void* context) noexcept nogil
+
+    ctypedef int (*xmlOutputWriteCallback)(void* context,
+                                           char* buffer, int len) noexcept
+    ctypedef int (*xmlOutputCloseCallback)(void* context) noexcept
+
+    cdef xmlOutputBuffer* xmlAllocOutputBuffer(
+        xmlCharEncodingHandler* encoder) nogil
+    cdef xmlOutputBuffer* xmlOutputBufferCreateIO(
+        xmlOutputWriteCallback iowrite,
+        xmlOutputCloseCallback ioclose,
+        void * ioctx, 
+        xmlCharEncodingHandler* encoder) nogil
+    cdef xmlOutputBuffer* xmlOutputBufferCreateFile(
+        stdio.FILE* file, xmlCharEncodingHandler* encoder) nogil
+    cdef xmlOutputBuffer* xmlOutputBufferCreateFilename(
+        char* URI, xmlCharEncodingHandler* encoder, int compression) nogil
+
+cdef extern from "libxml/xmlsave.h" nogil:
+    ctypedef struct xmlSaveCtxt
+
+    ctypedef enum xmlSaveOption:
+        XML_SAVE_FORMAT   = 1   # format save output            (2.6.17)
+        XML_SAVE_NO_DECL  = 2   # drop the xml declaration      (2.6.21)
+        XML_SAVE_NO_EMPTY = 4   # no empty tags                 (2.6.22)
+        XML_SAVE_NO_XHTML = 8   # disable XHTML1 specific rules (2.6.22)
+        XML_SAVE_XHTML = 16     # force XHTML1 specific rules         (2.7.2)
+        XML_SAVE_AS_XML = 32    # force XML serialization on HTML doc (2.7.2)
+        XML_SAVE_AS_HTML = 64   # force HTML serialization on XML doc (2.7.2)
+
+    cdef xmlSaveCtxt* xmlSaveToFilename(char* filename, char* encoding,
+                                        int options)
+    cdef xmlSaveCtxt* xmlSaveToBuffer(xmlBuffer* buffer, char* encoding,
+                                      int options) # libxml2 2.6.23
+    cdef long xmlSaveDoc(xmlSaveCtxt* ctxt, xmlDoc* doc)
+    cdef long xmlSaveTree(xmlSaveCtxt* ctxt, xmlNode* node)
+    cdef int xmlSaveClose(xmlSaveCtxt* ctxt)
+    cdef int xmlSaveFlush(xmlSaveCtxt* ctxt)
+    cdef int xmlSaveSetAttrEscape(xmlSaveCtxt* ctxt, void* escape_func)
+    cdef int xmlSaveSetEscape(xmlSaveCtxt* ctxt, void* escape_func)
+
+cdef extern from "libxml/globals.h" nogil:
+    cdef int xmlThrDefKeepBlanksDefaultValue(int onoff)
+    cdef int xmlThrDefLineNumbersDefaultValue(int onoff)
+    cdef int xmlThrDefIndentTreeOutput(int onoff)
+    
+cdef extern from "libxml/xmlmemory.h" nogil:
+    cdef void* xmlMalloc(size_t size)
+    cdef int xmlMemBlocks()
+    cdef int xmlMemUsed()
+    cdef void xmlMemDisplay(stdio.FILE* file)
+    cdef void xmlMemDisplayLast(stdio.FILE* file, long num_bytes)
+    cdef void xmlMemShow(stdio.FILE* file, int count)
+
+cdef extern from "etree_defs.h" nogil:
+    cdef bint _isElement(xmlNode* node)
+    cdef bint _isElementOrXInclude(xmlNode* node)
+    cdef const_xmlChar* _getNs(xmlNode* node)
+    cdef void BEGIN_FOR_EACH_ELEMENT_FROM(xmlNode* tree_top,
+                                          xmlNode* start_node,
+                                          bint inclusive)
+    cdef void END_FOR_EACH_ELEMENT_FROM(xmlNode* start_node)
+    cdef void BEGIN_FOR_EACH_FROM(xmlNode* tree_top,
+                                  xmlNode* start_node,
+                                  bint inclusive)
+    cdef void END_FOR_EACH_FROM(xmlNode* start_node)
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/uri.pxd b/.venv/lib/python3.12/site-packages/lxml/includes/uri.pxd
new file mode 100644
index 00000000..f886a54b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/uri.pxd
@@ -0,0 +1,5 @@
+cdef extern from "libxml/uri.h" nogil:
+    ctypedef struct xmlURI
+
+    cdef xmlURI* xmlParseURI(char* str)
+    cdef void xmlFreeURI(xmlURI* uri)
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/xinclude.pxd b/.venv/lib/python3.12/site-packages/lxml/includes/xinclude.pxd
new file mode 100644
index 00000000..68267175
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/xinclude.pxd
@@ -0,0 +1,22 @@
+from lxml.includes.tree cimport xmlDoc, xmlNode
+
+cdef extern from "libxml/xinclude.h" nogil:
+
+    ctypedef struct xmlXIncludeCtxt
+
+    cdef int xmlXIncludeProcess(xmlDoc* doc)
+    cdef int xmlXIncludeProcessFlags(xmlDoc* doc, int parser_opts)
+    cdef int xmlXIncludeProcessTree(xmlNode* doc)
+    cdef int xmlXIncludeProcessTreeFlags(xmlNode* doc, int parser_opts)
+
+    # libxml2 >= 2.7.4
+    cdef int xmlXIncludeProcessTreeFlagsData(
+            xmlNode* doc, int parser_opts, void* data)
+
+    cdef xmlXIncludeCtxt* xmlXIncludeNewContext(xmlDoc* doc)
+    cdef int xmlXIncludeProcessNode(xmlXIncludeCtxt* ctxt, xmlNode* node)
+    cdef int xmlXIncludeSetFlags(xmlXIncludeCtxt* ctxt, int flags)
+
+    # libxml2 >= 2.6.27
+    cdef int xmlXIncludeProcessFlagsData(
+        xmlDoc* doc, int flags, void* data)
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/xmlerror.pxd b/.venv/lib/python3.12/site-packages/lxml/includes/xmlerror.pxd
new file mode 100644
index 00000000..589e38ea
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/xmlerror.pxd
@@ -0,0 +1,852 @@
+
+# --- BEGIN: GENERATED CONSTANTS ---
+
+# This section is generated by the script 'update-error-constants.py'.
+
+cdef extern from "libxml/xmlerror.h":
+    ctypedef enum xmlErrorLevel:
+        XML_ERR_NONE                                       =       0
+        XML_ERR_WARNING                                    =       1 # A simple warning
+        XML_ERR_ERROR                                      =       2 # A recoverable error
+        XML_ERR_FATAL                                      =       3 # A fatal error
+
+    ctypedef enum xmlErrorDomain:
+        XML_FROM_NONE                                      =       0
+        XML_FROM_PARSER                                    =       1 # The XML parser
+        XML_FROM_TREE                                      =       2 # The tree module
+        XML_FROM_NAMESPACE                                 =       3 # The XML Namespace module
+        XML_FROM_DTD                                       =       4 # The XML DTD validation with parser contex
+        XML_FROM_HTML                                      =       5 # The HTML parser
+        XML_FROM_MEMORY                                    =       6 # The memory allocator
+        XML_FROM_OUTPUT                                    =       7 # The serialization code
+        XML_FROM_IO                                        =       8 # The Input/Output stack
+        XML_FROM_FTP                                       =       9 # The FTP module
+        XML_FROM_HTTP                                      =      10 # The HTTP module
+        XML_FROM_XINCLUDE                                  =      11 # The XInclude processing
+        XML_FROM_XPATH                                     =      12 # The XPath module
+        XML_FROM_XPOINTER                                  =      13 # The XPointer module
+        XML_FROM_REGEXP                                    =      14 # The regular expressions module
+        XML_FROM_DATATYPE                                  =      15 # The W3C XML Schemas Datatype module
+        XML_FROM_SCHEMASP                                  =      16 # The W3C XML Schemas parser module
+        XML_FROM_SCHEMASV                                  =      17 # The W3C XML Schemas validation module
+        XML_FROM_RELAXNGP                                  =      18 # The Relax-NG parser module
+        XML_FROM_RELAXNGV                                  =      19 # The Relax-NG validator module
+        XML_FROM_CATALOG                                   =      20 # The Catalog module
+        XML_FROM_C14N                                      =      21 # The Canonicalization module
+        XML_FROM_XSLT                                      =      22 # The XSLT engine from libxslt
+        XML_FROM_VALID                                     =      23 # The XML DTD validation with valid context
+        XML_FROM_CHECK                                     =      24 # The error checking module
+        XML_FROM_WRITER                                    =      25 # The xmlwriter module
+        XML_FROM_MODULE                                    =      26 # The dynamically loaded module modul
+        XML_FROM_I18N                                      =      27 # The module handling character conversion
+        XML_FROM_SCHEMATRONV                               =      28 # The Schematron validator module
+        XML_FROM_BUFFER                                    =      29 # The buffers module
+        XML_FROM_URI                                       =      30 # The URI module
+
+    ctypedef enum xmlParserErrors:
+        XML_ERR_OK                                         =       0
+        XML_ERR_INTERNAL_ERROR                             =       1
+        XML_ERR_NO_MEMORY                                  =       2
+        XML_ERR_DOCUMENT_START                             =       3
+        XML_ERR_DOCUMENT_EMPTY                             =       4
+        XML_ERR_DOCUMENT_END                               =       5
+        XML_ERR_INVALID_HEX_CHARREF                        =       6
+        XML_ERR_INVALID_DEC_CHARREF                        =       7
+        XML_ERR_INVALID_CHARREF                            =       8
+        XML_ERR_INVALID_CHAR                               =       9
+        XML_ERR_CHARREF_AT_EOF                             =      10
+        XML_ERR_CHARREF_IN_PROLOG                          =      11
+        XML_ERR_CHARREF_IN_EPILOG                          =      12
+        XML_ERR_CHARREF_IN_DTD                             =      13
+        XML_ERR_ENTITYREF_AT_EOF                           =      14
+        XML_ERR_ENTITYREF_IN_PROLOG                        =      15
+        XML_ERR_ENTITYREF_IN_EPILOG                        =      16
+        XML_ERR_ENTITYREF_IN_DTD                           =      17
+        XML_ERR_PEREF_AT_EOF                               =      18
+        XML_ERR_PEREF_IN_PROLOG                            =      19
+        XML_ERR_PEREF_IN_EPILOG                            =      20
+        XML_ERR_PEREF_IN_INT_SUBSET                        =      21
+        XML_ERR_ENTITYREF_NO_NAME                          =      22
+        XML_ERR_ENTITYREF_SEMICOL_MISSING                  =      23
+        XML_ERR_PEREF_NO_NAME                              =      24
+        XML_ERR_PEREF_SEMICOL_MISSING                      =      25
+        XML_ERR_UNDECLARED_ENTITY                          =      26
+        XML_WAR_UNDECLARED_ENTITY                          =      27
+        XML_ERR_UNPARSED_ENTITY                            =      28
+        XML_ERR_ENTITY_IS_EXTERNAL                         =      29
+        XML_ERR_ENTITY_IS_PARAMETER                        =      30
+        XML_ERR_UNKNOWN_ENCODING                           =      31
+        XML_ERR_UNSUPPORTED_ENCODING                       =      32
+        XML_ERR_STRING_NOT_STARTED                         =      33
+        XML_ERR_STRING_NOT_CLOSED                          =      34
+        XML_ERR_NS_DECL_ERROR                              =      35
+        XML_ERR_ENTITY_NOT_STARTED                         =      36
+        XML_ERR_ENTITY_NOT_FINISHED                        =      37
+        XML_ERR_LT_IN_ATTRIBUTE                            =      38
+        XML_ERR_ATTRIBUTE_NOT_STARTED                      =      39
+        XML_ERR_ATTRIBUTE_NOT_FINISHED                     =      40
+        XML_ERR_ATTRIBUTE_WITHOUT_VALUE                    =      41
+        XML_ERR_ATTRIBUTE_REDEFINED                        =      42
+        XML_ERR_LITERAL_NOT_STARTED                        =      43
+        XML_ERR_LITERAL_NOT_FINISHED                       =      44
+        XML_ERR_COMMENT_NOT_FINISHED                       =      45
+        XML_ERR_PI_NOT_STARTED                             =      46
+        XML_ERR_PI_NOT_FINISHED                            =      47
+        XML_ERR_NOTATION_NOT_STARTED                       =      48
+        XML_ERR_NOTATION_NOT_FINISHED                      =      49
+        XML_ERR_ATTLIST_NOT_STARTED                        =      50
+        XML_ERR_ATTLIST_NOT_FINISHED                       =      51
+        XML_ERR_MIXED_NOT_STARTED                          =      52
+        XML_ERR_MIXED_NOT_FINISHED                         =      53
+        XML_ERR_ELEMCONTENT_NOT_STARTED                    =      54
+        XML_ERR_ELEMCONTENT_NOT_FINISHED                   =      55
+        XML_ERR_XMLDECL_NOT_STARTED                        =      56
+        XML_ERR_XMLDECL_NOT_FINISHED                       =      57
+        XML_ERR_CONDSEC_NOT_STARTED                        =      58
+        XML_ERR_CONDSEC_NOT_FINISHED                       =      59
+        XML_ERR_EXT_SUBSET_NOT_FINISHED                    =      60
+        XML_ERR_DOCTYPE_NOT_FINISHED                       =      61
+        XML_ERR_MISPLACED_CDATA_END                        =      62
+        XML_ERR_CDATA_NOT_FINISHED                         =      63
+        XML_ERR_RESERVED_XML_NAME                          =      64
+        XML_ERR_SPACE_REQUIRED                             =      65
+        XML_ERR_SEPARATOR_REQUIRED                         =      66
+        XML_ERR_NMTOKEN_REQUIRED                           =      67
+        XML_ERR_NAME_REQUIRED                              =      68
+        XML_ERR_PCDATA_REQUIRED                            =      69
+        XML_ERR_URI_REQUIRED                               =      70
+        XML_ERR_PUBID_REQUIRED                             =      71
+        XML_ERR_LT_REQUIRED                                =      72
+        XML_ERR_GT_REQUIRED                                =      73
+        XML_ERR_LTSLASH_REQUIRED                           =      74
+        XML_ERR_EQUAL_REQUIRED                             =      75
+        XML_ERR_TAG_NAME_MISMATCH                          =      76
+        XML_ERR_TAG_NOT_FINISHED                           =      77
+        XML_ERR_STANDALONE_VALUE                           =      78
+        XML_ERR_ENCODING_NAME                              =      79
+        XML_ERR_HYPHEN_IN_COMMENT                          =      80
+        XML_ERR_INVALID_ENCODING                           =      81
+        XML_ERR_EXT_ENTITY_STANDALONE                      =      82
+        XML_ERR_CONDSEC_INVALID                            =      83
+        XML_ERR_VALUE_REQUIRED                             =      84
+        XML_ERR_NOT_WELL_BALANCED                          =      85
+        XML_ERR_EXTRA_CONTENT                              =      86
+        XML_ERR_ENTITY_CHAR_ERROR                          =      87
+        XML_ERR_ENTITY_PE_INTERNAL                         =      88
+        XML_ERR_ENTITY_LOOP                                =      89
+        XML_ERR_ENTITY_BOUNDARY                            =      90
+        XML_ERR_INVALID_URI                                =      91
+        XML_ERR_URI_FRAGMENT                               =      92
+        XML_WAR_CATALOG_PI                                 =      93
+        XML_ERR_NO_DTD                                     =      94
+        XML_ERR_CONDSEC_INVALID_KEYWORD                    =      95
+        XML_ERR_VERSION_MISSING                            =      96
+        XML_WAR_UNKNOWN_VERSION                            =      97
+        XML_WAR_LANG_VALUE                                 =      98
+        XML_WAR_NS_URI                                     =      99
+        XML_WAR_NS_URI_RELATIVE                            =     100
+        XML_ERR_MISSING_ENCODING                           =     101
+        XML_WAR_SPACE_VALUE                                =     102
+        XML_ERR_NOT_STANDALONE                             =     103
+        XML_ERR_ENTITY_PROCESSING                          =     104
+        XML_ERR_NOTATION_PROCESSING                        =     105
+        XML_WAR_NS_COLUMN                                  =     106
+        XML_WAR_ENTITY_REDEFINED                           =     107
+        XML_ERR_UNKNOWN_VERSION                            =     108
+        XML_ERR_VERSION_MISMATCH                           =     109
+        XML_ERR_NAME_TOO_LONG                              =     110
+        XML_ERR_USER_STOP                                  =     111
+        XML_ERR_COMMENT_ABRUPTLY_ENDED                     =     112
+        XML_NS_ERR_XML_NAMESPACE                           =     200
+        XML_NS_ERR_UNDEFINED_NAMESPACE                     =     201
+        XML_NS_ERR_QNAME                                   =     202
+        XML_NS_ERR_ATTRIBUTE_REDEFINED                     =     203
+        XML_NS_ERR_EMPTY                                   =     204
+        XML_NS_ERR_COLON                                   =     205
+        XML_DTD_ATTRIBUTE_DEFAULT                          =     500
+        XML_DTD_ATTRIBUTE_REDEFINED                        =     501
+        XML_DTD_ATTRIBUTE_VALUE                            =     502
+        XML_DTD_CONTENT_ERROR                              =     503
+        XML_DTD_CONTENT_MODEL                              =     504
+        XML_DTD_CONTENT_NOT_DETERMINIST                    =     505
+        XML_DTD_DIFFERENT_PREFIX                           =     506
+        XML_DTD_ELEM_DEFAULT_NAMESPACE                     =     507
+        XML_DTD_ELEM_NAMESPACE                             =     508
+        XML_DTD_ELEM_REDEFINED                             =     509
+        XML_DTD_EMPTY_NOTATION                             =     510
+        XML_DTD_ENTITY_TYPE                                =     511
+        XML_DTD_ID_FIXED                                   =     512
+        XML_DTD_ID_REDEFINED                               =     513
+        XML_DTD_ID_SUBSET                                  =     514
+        XML_DTD_INVALID_CHILD                              =     515
+        XML_DTD_INVALID_DEFAULT                            =     516
+        XML_DTD_LOAD_ERROR                                 =     517
+        XML_DTD_MISSING_ATTRIBUTE                          =     518
+        XML_DTD_MIXED_CORRUPT                              =     519
+        XML_DTD_MULTIPLE_ID                                =     520
+        XML_DTD_NO_DOC                                     =     521
+        XML_DTD_NO_DTD                                     =     522
+        XML_DTD_NO_ELEM_NAME                               =     523
+        XML_DTD_NO_PREFIX                                  =     524
+        XML_DTD_NO_ROOT                                    =     525
+        XML_DTD_NOTATION_REDEFINED                         =     526
+        XML_DTD_NOTATION_VALUE                             =     527
+        XML_DTD_NOT_EMPTY                                  =     528
+        XML_DTD_NOT_PCDATA                                 =     529
+        XML_DTD_NOT_STANDALONE                             =     530
+        XML_DTD_ROOT_NAME                                  =     531
+        XML_DTD_STANDALONE_WHITE_SPACE                     =     532
+        XML_DTD_UNKNOWN_ATTRIBUTE                          =     533
+        XML_DTD_UNKNOWN_ELEM                               =     534
+        XML_DTD_UNKNOWN_ENTITY                             =     535
+        XML_DTD_UNKNOWN_ID                                 =     536
+        XML_DTD_UNKNOWN_NOTATION                           =     537
+        XML_DTD_STANDALONE_DEFAULTED                       =     538
+        XML_DTD_XMLID_VALUE                                =     539
+        XML_DTD_XMLID_TYPE                                 =     540
+        XML_DTD_DUP_TOKEN                                  =     541
+        XML_HTML_STRUCURE_ERROR                            =     800
+        XML_HTML_UNKNOWN_TAG                               =     801
+        XML_RNGP_ANYNAME_ATTR_ANCESTOR                     =    1000
+        XML_RNGP_ATTR_CONFLICT                             =    1001
+        XML_RNGP_ATTRIBUTE_CHILDREN                        =    1002
+        XML_RNGP_ATTRIBUTE_CONTENT                         =    1003
+        XML_RNGP_ATTRIBUTE_EMPTY                           =    1004
+        XML_RNGP_ATTRIBUTE_NOOP                            =    1005
+        XML_RNGP_CHOICE_CONTENT                            =    1006
+        XML_RNGP_CHOICE_EMPTY                              =    1007
+        XML_RNGP_CREATE_FAILURE                            =    1008
+        XML_RNGP_DATA_CONTENT                              =    1009
+        XML_RNGP_DEF_CHOICE_AND_INTERLEAVE                 =    1010
+        XML_RNGP_DEFINE_CREATE_FAILED                      =    1011
+        XML_RNGP_DEFINE_EMPTY                              =    1012
+        XML_RNGP_DEFINE_MISSING                            =    1013
+        XML_RNGP_DEFINE_NAME_MISSING                       =    1014
+        XML_RNGP_ELEM_CONTENT_EMPTY                        =    1015
+        XML_RNGP_ELEM_CONTENT_ERROR                        =    1016
+        XML_RNGP_ELEMENT_EMPTY                             =    1017
+        XML_RNGP_ELEMENT_CONTENT                           =    1018
+        XML_RNGP_ELEMENT_NAME                              =    1019
+        XML_RNGP_ELEMENT_NO_CONTENT                        =    1020
+        XML_RNGP_ELEM_TEXT_CONFLICT                        =    1021
+        XML_RNGP_EMPTY                                     =    1022
+        XML_RNGP_EMPTY_CONSTRUCT                           =    1023
+        XML_RNGP_EMPTY_CONTENT                             =    1024
+        XML_RNGP_EMPTY_NOT_EMPTY                           =    1025
+        XML_RNGP_ERROR_TYPE_LIB                            =    1026
+        XML_RNGP_EXCEPT_EMPTY                              =    1027
+        XML_RNGP_EXCEPT_MISSING                            =    1028
+        XML_RNGP_EXCEPT_MULTIPLE                           =    1029
+        XML_RNGP_EXCEPT_NO_CONTENT                         =    1030
+        XML_RNGP_EXTERNALREF_EMTPY                         =    1031
+        XML_RNGP_EXTERNAL_REF_FAILURE                      =    1032
+        XML_RNGP_EXTERNALREF_RECURSE                       =    1033
+        XML_RNGP_FORBIDDEN_ATTRIBUTE                       =    1034
+        XML_RNGP_FOREIGN_ELEMENT                           =    1035
+        XML_RNGP_GRAMMAR_CONTENT                           =    1036
+        XML_RNGP_GRAMMAR_EMPTY                             =    1037
+        XML_RNGP_GRAMMAR_MISSING                           =    1038
+        XML_RNGP_GRAMMAR_NO_START                          =    1039
+        XML_RNGP_GROUP_ATTR_CONFLICT                       =    1040
+        XML_RNGP_HREF_ERROR                                =    1041
+        XML_RNGP_INCLUDE_EMPTY                             =    1042
+        XML_RNGP_INCLUDE_FAILURE                           =    1043
+        XML_RNGP_INCLUDE_RECURSE                           =    1044
+        XML_RNGP_INTERLEAVE_ADD                            =    1045
+        XML_RNGP_INTERLEAVE_CREATE_FAILED                  =    1046
+        XML_RNGP_INTERLEAVE_EMPTY                          =    1047
+        XML_RNGP_INTERLEAVE_NO_CONTENT                     =    1048
+        XML_RNGP_INVALID_DEFINE_NAME                       =    1049
+        XML_RNGP_INVALID_URI                               =    1050
+        XML_RNGP_INVALID_VALUE                             =    1051
+        XML_RNGP_MISSING_HREF                              =    1052
+        XML_RNGP_NAME_MISSING                              =    1053
+        XML_RNGP_NEED_COMBINE                              =    1054
+        XML_RNGP_NOTALLOWED_NOT_EMPTY                      =    1055
+        XML_RNGP_NSNAME_ATTR_ANCESTOR                      =    1056
+        XML_RNGP_NSNAME_NO_NS                              =    1057
+        XML_RNGP_PARAM_FORBIDDEN                           =    1058
+        XML_RNGP_PARAM_NAME_MISSING                        =    1059
+        XML_RNGP_PARENTREF_CREATE_FAILED                   =    1060
+        XML_RNGP_PARENTREF_NAME_INVALID                    =    1061
+        XML_RNGP_PARENTREF_NO_NAME                         =    1062
+        XML_RNGP_PARENTREF_NO_PARENT                       =    1063
+        XML_RNGP_PARENTREF_NOT_EMPTY                       =    1064
+        XML_RNGP_PARSE_ERROR                               =    1065
+        XML_RNGP_PAT_ANYNAME_EXCEPT_ANYNAME                =    1066
+        XML_RNGP_PAT_ATTR_ATTR                             =    1067
+        XML_RNGP_PAT_ATTR_ELEM                             =    1068
+        XML_RNGP_PAT_DATA_EXCEPT_ATTR                      =    1069
+        XML_RNGP_PAT_DATA_EXCEPT_ELEM                      =    1070
+        XML_RNGP_PAT_DATA_EXCEPT_EMPTY                     =    1071
+        XML_RNGP_PAT_DATA_EXCEPT_GROUP                     =    1072
+        XML_RNGP_PAT_DATA_EXCEPT_INTERLEAVE                =    1073
+        XML_RNGP_PAT_DATA_EXCEPT_LIST                      =    1074
+        XML_RNGP_PAT_DATA_EXCEPT_ONEMORE                   =    1075
+        XML_RNGP_PAT_DATA_EXCEPT_REF                       =    1076
+        XML_RNGP_PAT_DATA_EXCEPT_TEXT                      =    1077
+        XML_RNGP_PAT_LIST_ATTR                             =    1078
+        XML_RNGP_PAT_LIST_ELEM                             =    1079
+        XML_RNGP_PAT_LIST_INTERLEAVE                       =    1080
+        XML_RNGP_PAT_LIST_LIST                             =    1081
+        XML_RNGP_PAT_LIST_REF                              =    1082
+        XML_RNGP_PAT_LIST_TEXT                             =    1083
+        XML_RNGP_PAT_NSNAME_EXCEPT_ANYNAME                 =    1084
+        XML_RNGP_PAT_NSNAME_EXCEPT_NSNAME                  =    1085
+        XML_RNGP_PAT_ONEMORE_GROUP_ATTR                    =    1086
+        XML_RNGP_PAT_ONEMORE_INTERLEAVE_ATTR               =    1087
+        XML_RNGP_PAT_START_ATTR                            =    1088
+        XML_RNGP_PAT_START_DATA                            =    1089
+        XML_RNGP_PAT_START_EMPTY                           =    1090
+        XML_RNGP_PAT_START_GROUP                           =    1091
+        XML_RNGP_PAT_START_INTERLEAVE                      =    1092
+        XML_RNGP_PAT_START_LIST                            =    1093
+        XML_RNGP_PAT_START_ONEMORE                         =    1094
+        XML_RNGP_PAT_START_TEXT                            =    1095
+        XML_RNGP_PAT_START_VALUE                           =    1096
+        XML_RNGP_PREFIX_UNDEFINED                          =    1097
+        XML_RNGP_REF_CREATE_FAILED                         =    1098
+        XML_RNGP_REF_CYCLE                                 =    1099
+        XML_RNGP_REF_NAME_INVALID                          =    1100
+        XML_RNGP_REF_NO_DEF                                =    1101
+        XML_RNGP_REF_NO_NAME                               =    1102
+        XML_RNGP_REF_NOT_EMPTY                             =    1103
+        XML_RNGP_START_CHOICE_AND_INTERLEAVE               =    1104
+        XML_RNGP_START_CONTENT                             =    1105
+        XML_RNGP_START_EMPTY                               =    1106
+        XML_RNGP_START_MISSING                             =    1107
+        XML_RNGP_TEXT_EXPECTED                             =    1108
+        XML_RNGP_TEXT_HAS_CHILD                            =    1109
+        XML_RNGP_TYPE_MISSING                              =    1110
+        XML_RNGP_TYPE_NOT_FOUND                            =    1111
+        XML_RNGP_TYPE_VALUE                                =    1112
+        XML_RNGP_UNKNOWN_ATTRIBUTE                         =    1113
+        XML_RNGP_UNKNOWN_COMBINE                           =    1114
+        XML_RNGP_UNKNOWN_CONSTRUCT                         =    1115
+        XML_RNGP_UNKNOWN_TYPE_LIB                          =    1116
+        XML_RNGP_URI_FRAGMENT                              =    1117
+        XML_RNGP_URI_NOT_ABSOLUTE                          =    1118
+        XML_RNGP_VALUE_EMPTY                               =    1119
+        XML_RNGP_VALUE_NO_CONTENT                          =    1120
+        XML_RNGP_XMLNS_NAME                                =    1121
+        XML_RNGP_XML_NS                                    =    1122
+        XML_XPATH_EXPRESSION_OK                            =    1200
+        XML_XPATH_NUMBER_ERROR                             =    1201
+        XML_XPATH_UNFINISHED_LITERAL_ERROR                 =    1202
+        XML_XPATH_START_LITERAL_ERROR                      =    1203
+        XML_XPATH_VARIABLE_REF_ERROR                       =    1204
+        XML_XPATH_UNDEF_VARIABLE_ERROR                     =    1205
+        XML_XPATH_INVALID_PREDICATE_ERROR                  =    1206
+        XML_XPATH_EXPR_ERROR                               =    1207
+        XML_XPATH_UNCLOSED_ERROR                           =    1208
+        XML_XPATH_UNKNOWN_FUNC_ERROR                       =    1209
+        XML_XPATH_INVALID_OPERAND                          =    1210
+        XML_XPATH_INVALID_TYPE                             =    1211
+        XML_XPATH_INVALID_ARITY                            =    1212
+        XML_XPATH_INVALID_CTXT_SIZE                        =    1213
+        XML_XPATH_INVALID_CTXT_POSITION                    =    1214
+        XML_XPATH_MEMORY_ERROR                             =    1215
+        XML_XPTR_SYNTAX_ERROR                              =    1216
+        XML_XPTR_RESOURCE_ERROR                            =    1217
+        XML_XPTR_SUB_RESOURCE_ERROR                        =    1218
+        XML_XPATH_UNDEF_PREFIX_ERROR                       =    1219
+        XML_XPATH_ENCODING_ERROR                           =    1220
+        XML_XPATH_INVALID_CHAR_ERROR                       =    1221
+        XML_TREE_INVALID_HEX                               =    1300
+        XML_TREE_INVALID_DEC                               =    1301
+        XML_TREE_UNTERMINATED_ENTITY                       =    1302
+        XML_TREE_NOT_UTF8                                  =    1303
+        XML_SAVE_NOT_UTF8                                  =    1400
+        XML_SAVE_CHAR_INVALID                              =    1401
+        XML_SAVE_NO_DOCTYPE                                =    1402
+        XML_SAVE_UNKNOWN_ENCODING                          =    1403
+        XML_REGEXP_COMPILE_ERROR                           =    1450
+        XML_IO_UNKNOWN                                     =    1500
+        XML_IO_EACCES                                      =    1501
+        XML_IO_EAGAIN                                      =    1502
+        XML_IO_EBADF                                       =    1503
+        XML_IO_EBADMSG                                     =    1504
+        XML_IO_EBUSY                                       =    1505
+        XML_IO_ECANCELED                                   =    1506
+        XML_IO_ECHILD                                      =    1507
+        XML_IO_EDEADLK                                     =    1508
+        XML_IO_EDOM                                        =    1509
+        XML_IO_EEXIST                                      =    1510
+        XML_IO_EFAULT                                      =    1511
+        XML_IO_EFBIG                                       =    1512
+        XML_IO_EINPROGRESS                                 =    1513
+        XML_IO_EINTR                                       =    1514
+        XML_IO_EINVAL                                      =    1515
+        XML_IO_EIO                                         =    1516
+        XML_IO_EISDIR                                      =    1517
+        XML_IO_EMFILE                                      =    1518
+        XML_IO_EMLINK                                      =    1519
+        XML_IO_EMSGSIZE                                    =    1520
+        XML_IO_ENAMETOOLONG                                =    1521
+        XML_IO_ENFILE                                      =    1522
+        XML_IO_ENODEV                                      =    1523
+        XML_IO_ENOENT                                      =    1524
+        XML_IO_ENOEXEC                                     =    1525
+        XML_IO_ENOLCK                                      =    1526
+        XML_IO_ENOMEM                                      =    1527
+        XML_IO_ENOSPC                                      =    1528
+        XML_IO_ENOSYS                                      =    1529
+        XML_IO_ENOTDIR                                     =    1530
+        XML_IO_ENOTEMPTY                                   =    1531
+        XML_IO_ENOTSUP                                     =    1532
+        XML_IO_ENOTTY                                      =    1533
+        XML_IO_ENXIO                                       =    1534
+        XML_IO_EPERM                                       =    1535
+        XML_IO_EPIPE                                       =    1536
+        XML_IO_ERANGE                                      =    1537
+        XML_IO_EROFS                                       =    1538
+        XML_IO_ESPIPE                                      =    1539
+        XML_IO_ESRCH                                       =    1540
+        XML_IO_ETIMEDOUT                                   =    1541
+        XML_IO_EXDEV                                       =    1542
+        XML_IO_NETWORK_ATTEMPT                             =    1543
+        XML_IO_ENCODER                                     =    1544
+        XML_IO_FLUSH                                       =    1545
+        XML_IO_WRITE                                       =    1546
+        XML_IO_NO_INPUT                                    =    1547
+        XML_IO_BUFFER_FULL                                 =    1548
+        XML_IO_LOAD_ERROR                                  =    1549
+        XML_IO_ENOTSOCK                                    =    1550
+        XML_IO_EISCONN                                     =    1551
+        XML_IO_ECONNREFUSED                                =    1552
+        XML_IO_ENETUNREACH                                 =    1553
+        XML_IO_EADDRINUSE                                  =    1554
+        XML_IO_EALREADY                                    =    1555
+        XML_IO_EAFNOSUPPORT                                =    1556
+        XML_XINCLUDE_RECURSION                             =    1600
+        XML_XINCLUDE_PARSE_VALUE                           =    1601
+        XML_XINCLUDE_ENTITY_DEF_MISMATCH                   =    1602
+        XML_XINCLUDE_NO_HREF                               =    1603
+        XML_XINCLUDE_NO_FALLBACK                           =    1604
+        XML_XINCLUDE_HREF_URI                              =    1605
+        XML_XINCLUDE_TEXT_FRAGMENT                         =    1606
+        XML_XINCLUDE_TEXT_DOCUMENT                         =    1607
+        XML_XINCLUDE_INVALID_CHAR                          =    1608
+        XML_XINCLUDE_BUILD_FAILED                          =    1609
+        XML_XINCLUDE_UNKNOWN_ENCODING                      =    1610
+        XML_XINCLUDE_MULTIPLE_ROOT                         =    1611
+        XML_XINCLUDE_XPTR_FAILED                           =    1612
+        XML_XINCLUDE_XPTR_RESULT                           =    1613
+        XML_XINCLUDE_INCLUDE_IN_INCLUDE                    =    1614
+        XML_XINCLUDE_FALLBACKS_IN_INCLUDE                  =    1615
+        XML_XINCLUDE_FALLBACK_NOT_IN_INCLUDE               =    1616
+        XML_XINCLUDE_DEPRECATED_NS                         =    1617
+        XML_XINCLUDE_FRAGMENT_ID                           =    1618
+        XML_CATALOG_MISSING_ATTR                           =    1650
+        XML_CATALOG_ENTRY_BROKEN                           =    1651
+        XML_CATALOG_PREFER_VALUE                           =    1652
+        XML_CATALOG_NOT_CATALOG                            =    1653
+        XML_CATALOG_RECURSION                              =    1654
+        XML_SCHEMAP_PREFIX_UNDEFINED                       =    1700
+        XML_SCHEMAP_ATTRFORMDEFAULT_VALUE                  =    1701
+        XML_SCHEMAP_ATTRGRP_NONAME_NOREF                   =    1702
+        XML_SCHEMAP_ATTR_NONAME_NOREF                      =    1703
+        XML_SCHEMAP_COMPLEXTYPE_NONAME_NOREF               =    1704
+        XML_SCHEMAP_ELEMFORMDEFAULT_VALUE                  =    1705
+        XML_SCHEMAP_ELEM_NONAME_NOREF                      =    1706
+        XML_SCHEMAP_EXTENSION_NO_BASE                      =    1707
+        XML_SCHEMAP_FACET_NO_VALUE                         =    1708
+        XML_SCHEMAP_FAILED_BUILD_IMPORT                    =    1709
+        XML_SCHEMAP_GROUP_NONAME_NOREF                     =    1710
+        XML_SCHEMAP_IMPORT_NAMESPACE_NOT_URI               =    1711
+        XML_SCHEMAP_IMPORT_REDEFINE_NSNAME                 =    1712
+        XML_SCHEMAP_IMPORT_SCHEMA_NOT_URI                  =    1713
+        XML_SCHEMAP_INVALID_BOOLEAN                        =    1714
+        XML_SCHEMAP_INVALID_ENUM                           =    1715
+        XML_SCHEMAP_INVALID_FACET                          =    1716
+        XML_SCHEMAP_INVALID_FACET_VALUE                    =    1717
+        XML_SCHEMAP_INVALID_MAXOCCURS                      =    1718
+        XML_SCHEMAP_INVALID_MINOCCURS                      =    1719
+        XML_SCHEMAP_INVALID_REF_AND_SUBTYPE                =    1720
+        XML_SCHEMAP_INVALID_WHITE_SPACE                    =    1721
+        XML_SCHEMAP_NOATTR_NOREF                           =    1722
+        XML_SCHEMAP_NOTATION_NO_NAME                       =    1723
+        XML_SCHEMAP_NOTYPE_NOREF                           =    1724
+        XML_SCHEMAP_REF_AND_SUBTYPE                        =    1725
+        XML_SCHEMAP_RESTRICTION_NONAME_NOREF               =    1726
+        XML_SCHEMAP_SIMPLETYPE_NONAME                      =    1727
+        XML_SCHEMAP_TYPE_AND_SUBTYPE                       =    1728
+        XML_SCHEMAP_UNKNOWN_ALL_CHILD                      =    1729
+        XML_SCHEMAP_UNKNOWN_ANYATTRIBUTE_CHILD             =    1730
+        XML_SCHEMAP_UNKNOWN_ATTR_CHILD                     =    1731
+        XML_SCHEMAP_UNKNOWN_ATTRGRP_CHILD                  =    1732
+        XML_SCHEMAP_UNKNOWN_ATTRIBUTE_GROUP                =    1733
+        XML_SCHEMAP_UNKNOWN_BASE_TYPE                      =    1734
+        XML_SCHEMAP_UNKNOWN_CHOICE_CHILD                   =    1735
+        XML_SCHEMAP_UNKNOWN_COMPLEXCONTENT_CHILD           =    1736
+        XML_SCHEMAP_UNKNOWN_COMPLEXTYPE_CHILD              =    1737
+        XML_SCHEMAP_UNKNOWN_ELEM_CHILD                     =    1738
+        XML_SCHEMAP_UNKNOWN_EXTENSION_CHILD                =    1739
+        XML_SCHEMAP_UNKNOWN_FACET_CHILD                    =    1740
+        XML_SCHEMAP_UNKNOWN_FACET_TYPE                     =    1741
+        XML_SCHEMAP_UNKNOWN_GROUP_CHILD                    =    1742
+        XML_SCHEMAP_UNKNOWN_IMPORT_CHILD                   =    1743
+        XML_SCHEMAP_UNKNOWN_LIST_CHILD                     =    1744
+        XML_SCHEMAP_UNKNOWN_NOTATION_CHILD                 =    1745
+        XML_SCHEMAP_UNKNOWN_PROCESSCONTENT_CHILD           =    1746
+        XML_SCHEMAP_UNKNOWN_REF                            =    1747
+        XML_SCHEMAP_UNKNOWN_RESTRICTION_CHILD              =    1748
+        XML_SCHEMAP_UNKNOWN_SCHEMAS_CHILD                  =    1749
+        XML_SCHEMAP_UNKNOWN_SEQUENCE_CHILD                 =    1750
+        XML_SCHEMAP_UNKNOWN_SIMPLECONTENT_CHILD            =    1751
+        XML_SCHEMAP_UNKNOWN_SIMPLETYPE_CHILD               =    1752
+        XML_SCHEMAP_UNKNOWN_TYPE                           =    1753
+        XML_SCHEMAP_UNKNOWN_UNION_CHILD                    =    1754
+        XML_SCHEMAP_ELEM_DEFAULT_FIXED                     =    1755
+        XML_SCHEMAP_REGEXP_INVALID                         =    1756
+        XML_SCHEMAP_FAILED_LOAD                            =    1757
+        XML_SCHEMAP_NOTHING_TO_PARSE                       =    1758
+        XML_SCHEMAP_NOROOT                                 =    1759
+        XML_SCHEMAP_REDEFINED_GROUP                        =    1760
+        XML_SCHEMAP_REDEFINED_TYPE                         =    1761
+        XML_SCHEMAP_REDEFINED_ELEMENT                      =    1762
+        XML_SCHEMAP_REDEFINED_ATTRGROUP                    =    1763
+        XML_SCHEMAP_REDEFINED_ATTR                         =    1764
+        XML_SCHEMAP_REDEFINED_NOTATION                     =    1765
+        XML_SCHEMAP_FAILED_PARSE                           =    1766
+        XML_SCHEMAP_UNKNOWN_PREFIX                         =    1767
+        XML_SCHEMAP_DEF_AND_PREFIX                         =    1768
+        XML_SCHEMAP_UNKNOWN_INCLUDE_CHILD                  =    1769
+        XML_SCHEMAP_INCLUDE_SCHEMA_NOT_URI                 =    1770
+        XML_SCHEMAP_INCLUDE_SCHEMA_NO_URI                  =    1771
+        XML_SCHEMAP_NOT_SCHEMA                             =    1772
+        XML_SCHEMAP_UNKNOWN_MEMBER_TYPE                    =    1773
+        XML_SCHEMAP_INVALID_ATTR_USE                       =    1774
+        XML_SCHEMAP_RECURSIVE                              =    1775
+        XML_SCHEMAP_SUPERNUMEROUS_LIST_ITEM_TYPE           =    1776
+        XML_SCHEMAP_INVALID_ATTR_COMBINATION               =    1777
+        XML_SCHEMAP_INVALID_ATTR_INLINE_COMBINATION        =    1778
+        XML_SCHEMAP_MISSING_SIMPLETYPE_CHILD               =    1779
+        XML_SCHEMAP_INVALID_ATTR_NAME                      =    1780
+        XML_SCHEMAP_REF_AND_CONTENT                        =    1781
+        XML_SCHEMAP_CT_PROPS_CORRECT_1                     =    1782
+        XML_SCHEMAP_CT_PROPS_CORRECT_2                     =    1783
+        XML_SCHEMAP_CT_PROPS_CORRECT_3                     =    1784
+        XML_SCHEMAP_CT_PROPS_CORRECT_4                     =    1785
+        XML_SCHEMAP_CT_PROPS_CORRECT_5                     =    1786
+        XML_SCHEMAP_DERIVATION_OK_RESTRICTION_1            =    1787
+        XML_SCHEMAP_DERIVATION_OK_RESTRICTION_2_1_1        =    1788
+        XML_SCHEMAP_DERIVATION_OK_RESTRICTION_2_1_2        =    1789
+        XML_SCHEMAP_DERIVATION_OK_RESTRICTION_2_2          =    1790
+        XML_SCHEMAP_DERIVATION_OK_RESTRICTION_3            =    1791
+        XML_SCHEMAP_WILDCARD_INVALID_NS_MEMBER             =    1792
+        XML_SCHEMAP_INTERSECTION_NOT_EXPRESSIBLE           =    1793
+        XML_SCHEMAP_UNION_NOT_EXPRESSIBLE                  =    1794
+        XML_SCHEMAP_SRC_IMPORT_3_1                         =    1795
+        XML_SCHEMAP_SRC_IMPORT_3_2                         =    1796
+        XML_SCHEMAP_DERIVATION_OK_RESTRICTION_4_1          =    1797
+        XML_SCHEMAP_DERIVATION_OK_RESTRICTION_4_2          =    1798
+        XML_SCHEMAP_DERIVATION_OK_RESTRICTION_4_3          =    1799
+        XML_SCHEMAP_COS_CT_EXTENDS_1_3                     =    1800
+        XML_SCHEMAV_NOROOT                                 =    1801
+        XML_SCHEMAV_UNDECLAREDELEM                         =    1802
+        XML_SCHEMAV_NOTTOPLEVEL                            =    1803
+        XML_SCHEMAV_MISSING                                =    1804
+        XML_SCHEMAV_WRONGELEM                              =    1805
+        XML_SCHEMAV_NOTYPE                                 =    1806
+        XML_SCHEMAV_NOROLLBACK                             =    1807
+        XML_SCHEMAV_ISABSTRACT                             =    1808
+        XML_SCHEMAV_NOTEMPTY                               =    1809
+        XML_SCHEMAV_ELEMCONT                               =    1810
+        XML_SCHEMAV_HAVEDEFAULT                            =    1811
+        XML_SCHEMAV_NOTNILLABLE                            =    1812
+        XML_SCHEMAV_EXTRACONTENT                           =    1813
+        XML_SCHEMAV_INVALIDATTR                            =    1814
+        XML_SCHEMAV_INVALIDELEM                            =    1815
+        XML_SCHEMAV_NOTDETERMINIST                         =    1816
+        XML_SCHEMAV_CONSTRUCT                              =    1817
+        XML_SCHEMAV_INTERNAL                               =    1818
+        XML_SCHEMAV_NOTSIMPLE                              =    1819
+        XML_SCHEMAV_ATTRUNKNOWN                            =    1820
+        XML_SCHEMAV_ATTRINVALID                            =    1821
+        XML_SCHEMAV_VALUE                                  =    1822
+        XML_SCHEMAV_FACET                                  =    1823
+        XML_SCHEMAV_CVC_DATATYPE_VALID_1_2_1               =    1824
+        XML_SCHEMAV_CVC_DATATYPE_VALID_1_2_2               =    1825
+        XML_SCHEMAV_CVC_DATATYPE_VALID_1_2_3               =    1826
+        XML_SCHEMAV_CVC_TYPE_3_1_1                         =    1827
+        XML_SCHEMAV_CVC_TYPE_3_1_2                         =    1828
+        XML_SCHEMAV_CVC_FACET_VALID                        =    1829
+        XML_SCHEMAV_CVC_LENGTH_VALID                       =    1830
+        XML_SCHEMAV_CVC_MINLENGTH_VALID                    =    1831
+        XML_SCHEMAV_CVC_MAXLENGTH_VALID                    =    1832
+        XML_SCHEMAV_CVC_MININCLUSIVE_VALID                 =    1833
+        XML_SCHEMAV_CVC_MAXINCLUSIVE_VALID                 =    1834
+        XML_SCHEMAV_CVC_MINEXCLUSIVE_VALID                 =    1835
+        XML_SCHEMAV_CVC_MAXEXCLUSIVE_VALID                 =    1836
+        XML_SCHEMAV_CVC_TOTALDIGITS_VALID                  =    1837
+        XML_SCHEMAV_CVC_FRACTIONDIGITS_VALID               =    1838
+        XML_SCHEMAV_CVC_PATTERN_VALID                      =    1839
+        XML_SCHEMAV_CVC_ENUMERATION_VALID                  =    1840
+        XML_SCHEMAV_CVC_COMPLEX_TYPE_2_1                   =    1841
+        XML_SCHEMAV_CVC_COMPLEX_TYPE_2_2                   =    1842
+        XML_SCHEMAV_CVC_COMPLEX_TYPE_2_3                   =    1843
+        XML_SCHEMAV_CVC_COMPLEX_TYPE_2_4                   =    1844
+        XML_SCHEMAV_CVC_ELT_1                              =    1845
+        XML_SCHEMAV_CVC_ELT_2                              =    1846
+        XML_SCHEMAV_CVC_ELT_3_1                            =    1847
+        XML_SCHEMAV_CVC_ELT_3_2_1                          =    1848
+        XML_SCHEMAV_CVC_ELT_3_2_2                          =    1849
+        XML_SCHEMAV_CVC_ELT_4_1                            =    1850
+        XML_SCHEMAV_CVC_ELT_4_2                            =    1851
+        XML_SCHEMAV_CVC_ELT_4_3                            =    1852
+        XML_SCHEMAV_CVC_ELT_5_1_1                          =    1853
+        XML_SCHEMAV_CVC_ELT_5_1_2                          =    1854
+        XML_SCHEMAV_CVC_ELT_5_2_1                          =    1855
+        XML_SCHEMAV_CVC_ELT_5_2_2_1                        =    1856
+        XML_SCHEMAV_CVC_ELT_5_2_2_2_1                      =    1857
+        XML_SCHEMAV_CVC_ELT_5_2_2_2_2                      =    1858
+        XML_SCHEMAV_CVC_ELT_6                              =    1859
+        XML_SCHEMAV_CVC_ELT_7                              =    1860
+        XML_SCHEMAV_CVC_ATTRIBUTE_1                        =    1861
+        XML_SCHEMAV_CVC_ATTRIBUTE_2                        =    1862
+        XML_SCHEMAV_CVC_ATTRIBUTE_3                        =    1863
+        XML_SCHEMAV_CVC_ATTRIBUTE_4                        =    1864
+        XML_SCHEMAV_CVC_COMPLEX_TYPE_3_1                   =    1865
+        XML_SCHEMAV_CVC_COMPLEX_TYPE_3_2_1                 =    1866
+        XML_SCHEMAV_CVC_COMPLEX_TYPE_3_2_2                 =    1867
+        XML_SCHEMAV_CVC_COMPLEX_TYPE_4                     =    1868
+        XML_SCHEMAV_CVC_COMPLEX_TYPE_5_1                   =    1869
+        XML_SCHEMAV_CVC_COMPLEX_TYPE_5_2                   =    1870
+        XML_SCHEMAV_ELEMENT_CONTENT                        =    1871
+        XML_SCHEMAV_DOCUMENT_ELEMENT_MISSING               =    1872
+        XML_SCHEMAV_CVC_COMPLEX_TYPE_1                     =    1873
+        XML_SCHEMAV_CVC_AU                                 =    1874
+        XML_SCHEMAV_CVC_TYPE_1                             =    1875
+        XML_SCHEMAV_CVC_TYPE_2                             =    1876
+        XML_SCHEMAV_CVC_IDC                                =    1877
+        XML_SCHEMAV_CVC_WILDCARD                           =    1878
+        XML_SCHEMAV_MISC                                   =    1879
+        XML_XPTR_UNKNOWN_SCHEME                            =    1900
+        XML_XPTR_CHILDSEQ_START                            =    1901
+        XML_XPTR_EVAL_FAILED                               =    1902
+        XML_XPTR_EXTRA_OBJECTS                             =    1903
+        XML_C14N_CREATE_CTXT                               =    1950
+        XML_C14N_REQUIRES_UTF8                             =    1951
+        XML_C14N_CREATE_STACK                              =    1952
+        XML_C14N_INVALID_NODE                              =    1953
+        XML_C14N_UNKNOW_NODE                               =    1954
+        XML_C14N_RELATIVE_NAMESPACE                        =    1955
+        XML_FTP_PASV_ANSWER                                =    2000
+        XML_FTP_EPSV_ANSWER                                =    2001
+        XML_FTP_ACCNT                                      =    2002
+        XML_FTP_URL_SYNTAX                                 =    2003
+        XML_HTTP_URL_SYNTAX                                =    2020
+        XML_HTTP_USE_IP                                    =    2021
+        XML_HTTP_UNKNOWN_HOST                              =    2022
+        XML_SCHEMAP_SRC_SIMPLE_TYPE_1                      =    3000
+        XML_SCHEMAP_SRC_SIMPLE_TYPE_2                      =    3001
+        XML_SCHEMAP_SRC_SIMPLE_TYPE_3                      =    3002
+        XML_SCHEMAP_SRC_SIMPLE_TYPE_4                      =    3003
+        XML_SCHEMAP_SRC_RESOLVE                            =    3004
+        XML_SCHEMAP_SRC_RESTRICTION_BASE_OR_SIMPLETYPE     =    3005
+        XML_SCHEMAP_SRC_LIST_ITEMTYPE_OR_SIMPLETYPE        =    3006
+        XML_SCHEMAP_SRC_UNION_MEMBERTYPES_OR_SIMPLETYPES   =    3007
+        XML_SCHEMAP_ST_PROPS_CORRECT_1                     =    3008
+        XML_SCHEMAP_ST_PROPS_CORRECT_2                     =    3009
+        XML_SCHEMAP_ST_PROPS_CORRECT_3                     =    3010
+        XML_SCHEMAP_COS_ST_RESTRICTS_1_1                   =    3011
+        XML_SCHEMAP_COS_ST_RESTRICTS_1_2                   =    3012
+        XML_SCHEMAP_COS_ST_RESTRICTS_1_3_1                 =    3013
+        XML_SCHEMAP_COS_ST_RESTRICTS_1_3_2                 =    3014
+        XML_SCHEMAP_COS_ST_RESTRICTS_2_1                   =    3015
+        XML_SCHEMAP_COS_ST_RESTRICTS_2_3_1_1               =    3016
+        XML_SCHEMAP_COS_ST_RESTRICTS_2_3_1_2               =    3017
+        XML_SCHEMAP_COS_ST_RESTRICTS_2_3_2_1               =    3018
+        XML_SCHEMAP_COS_ST_RESTRICTS_2_3_2_2               =    3019
+        XML_SCHEMAP_COS_ST_RESTRICTS_2_3_2_3               =    3020
+        XML_SCHEMAP_COS_ST_RESTRICTS_2_3_2_4               =    3021
+        XML_SCHEMAP_COS_ST_RESTRICTS_2_3_2_5               =    3022
+        XML_SCHEMAP_COS_ST_RESTRICTS_3_1                   =    3023
+        XML_SCHEMAP_COS_ST_RESTRICTS_3_3_1                 =    3024
+        XML_SCHEMAP_COS_ST_RESTRICTS_3_3_1_2               =    3025
+        XML_SCHEMAP_COS_ST_RESTRICTS_3_3_2_2               =    3026
+        XML_SCHEMAP_COS_ST_RESTRICTS_3_3_2_1               =    3027
+        XML_SCHEMAP_COS_ST_RESTRICTS_3_3_2_3               =    3028
+        XML_SCHEMAP_COS_ST_RESTRICTS_3_3_2_4               =    3029
+        XML_SCHEMAP_COS_ST_RESTRICTS_3_3_2_5               =    3030
+        XML_SCHEMAP_COS_ST_DERIVED_OK_2_1                  =    3031
+        XML_SCHEMAP_COS_ST_DERIVED_OK_2_2                  =    3032
+        XML_SCHEMAP_S4S_ELEM_NOT_ALLOWED                   =    3033
+        XML_SCHEMAP_S4S_ELEM_MISSING                       =    3034
+        XML_SCHEMAP_S4S_ATTR_NOT_ALLOWED                   =    3035
+        XML_SCHEMAP_S4S_ATTR_MISSING                       =    3036
+        XML_SCHEMAP_S4S_ATTR_INVALID_VALUE                 =    3037
+        XML_SCHEMAP_SRC_ELEMENT_1                          =    3038
+        XML_SCHEMAP_SRC_ELEMENT_2_1                        =    3039
+        XML_SCHEMAP_SRC_ELEMENT_2_2                        =    3040
+        XML_SCHEMAP_SRC_ELEMENT_3                          =    3041
+        XML_SCHEMAP_P_PROPS_CORRECT_1                      =    3042
+        XML_SCHEMAP_P_PROPS_CORRECT_2_1                    =    3043
+        XML_SCHEMAP_P_PROPS_CORRECT_2_2                    =    3044
+        XML_SCHEMAP_E_PROPS_CORRECT_2                      =    3045
+        XML_SCHEMAP_E_PROPS_CORRECT_3                      =    3046
+        XML_SCHEMAP_E_PROPS_CORRECT_4                      =    3047
+        XML_SCHEMAP_E_PROPS_CORRECT_5                      =    3048
+        XML_SCHEMAP_E_PROPS_CORRECT_6                      =    3049
+        XML_SCHEMAP_SRC_INCLUDE                            =    3050
+        XML_SCHEMAP_SRC_ATTRIBUTE_1                        =    3051
+        XML_SCHEMAP_SRC_ATTRIBUTE_2                        =    3052
+        XML_SCHEMAP_SRC_ATTRIBUTE_3_1                      =    3053
+        XML_SCHEMAP_SRC_ATTRIBUTE_3_2                      =    3054
+        XML_SCHEMAP_SRC_ATTRIBUTE_4                        =    3055
+        XML_SCHEMAP_NO_XMLNS                               =    3056
+        XML_SCHEMAP_NO_XSI                                 =    3057
+        XML_SCHEMAP_COS_VALID_DEFAULT_1                    =    3058
+        XML_SCHEMAP_COS_VALID_DEFAULT_2_1                  =    3059
+        XML_SCHEMAP_COS_VALID_DEFAULT_2_2_1                =    3060
+        XML_SCHEMAP_COS_VALID_DEFAULT_2_2_2                =    3061
+        XML_SCHEMAP_CVC_SIMPLE_TYPE                        =    3062
+        XML_SCHEMAP_COS_CT_EXTENDS_1_1                     =    3063
+        XML_SCHEMAP_SRC_IMPORT_1_1                         =    3064
+        XML_SCHEMAP_SRC_IMPORT_1_2                         =    3065
+        XML_SCHEMAP_SRC_IMPORT_2                           =    3066
+        XML_SCHEMAP_SRC_IMPORT_2_1                         =    3067
+        XML_SCHEMAP_SRC_IMPORT_2_2                         =    3068
+        XML_SCHEMAP_INTERNAL                               =    3069 # 3069 non-W3C
+        XML_SCHEMAP_NOT_DETERMINISTIC                      =    3070 # 3070 non-W3C
+        XML_SCHEMAP_SRC_ATTRIBUTE_GROUP_1                  =    3071
+        XML_SCHEMAP_SRC_ATTRIBUTE_GROUP_2                  =    3072
+        XML_SCHEMAP_SRC_ATTRIBUTE_GROUP_3                  =    3073
+        XML_SCHEMAP_MG_PROPS_CORRECT_1                     =    3074
+        XML_SCHEMAP_MG_PROPS_CORRECT_2                     =    3075
+        XML_SCHEMAP_SRC_CT_1                               =    3076
+        XML_SCHEMAP_DERIVATION_OK_RESTRICTION_2_1_3        =    3077
+        XML_SCHEMAP_AU_PROPS_CORRECT_2                     =    3078
+        XML_SCHEMAP_A_PROPS_CORRECT_2                      =    3079
+        XML_SCHEMAP_C_PROPS_CORRECT                        =    3080
+        XML_SCHEMAP_SRC_REDEFINE                           =    3081
+        XML_SCHEMAP_SRC_IMPORT                             =    3082
+        XML_SCHEMAP_WARN_SKIP_SCHEMA                       =    3083
+        XML_SCHEMAP_WARN_UNLOCATED_SCHEMA                  =    3084
+        XML_SCHEMAP_WARN_ATTR_REDECL_PROH                  =    3085
+        XML_SCHEMAP_WARN_ATTR_POINTLESS_PROH               =    3086 # 3085
+        XML_SCHEMAP_AG_PROPS_CORRECT                       =    3087 # 3086
+        XML_SCHEMAP_COS_CT_EXTENDS_1_2                     =    3088 # 3087
+        XML_SCHEMAP_AU_PROPS_CORRECT                       =    3089 # 3088
+        XML_SCHEMAP_A_PROPS_CORRECT_3                      =    3090 # 3089
+        XML_SCHEMAP_COS_ALL_LIMITED                        =    3091 # 3090
+        XML_SCHEMATRONV_ASSERT                             =    4000
+        XML_SCHEMATRONV_REPORT                             =    4001
+        XML_MODULE_OPEN                                    =    4900
+        XML_MODULE_CLOSE                                   =    4901
+        XML_CHECK_FOUND_ELEMENT                            =    5000
+        XML_CHECK_FOUND_ATTRIBUTE                          =    5001
+        XML_CHECK_FOUND_TEXT                               =    5002
+        XML_CHECK_FOUND_CDATA                              =    5003
+        XML_CHECK_FOUND_ENTITYREF                          =    5004
+        XML_CHECK_FOUND_ENTITY                             =    5005
+        XML_CHECK_FOUND_PI                                 =    5006
+        XML_CHECK_FOUND_COMMENT                            =    5007
+        XML_CHECK_FOUND_DOCTYPE                            =    5008
+        XML_CHECK_FOUND_FRAGMENT                           =    5009
+        XML_CHECK_FOUND_NOTATION                           =    5010
+        XML_CHECK_UNKNOWN_NODE                             =    5011
+        XML_CHECK_ENTITY_TYPE                              =    5012
+        XML_CHECK_NO_PARENT                                =    5013
+        XML_CHECK_NO_DOC                                   =    5014
+        XML_CHECK_NO_NAME                                  =    5015
+        XML_CHECK_NO_ELEM                                  =    5016
+        XML_CHECK_WRONG_DOC                                =    5017
+        XML_CHECK_NO_PREV                                  =    5018
+        XML_CHECK_WRONG_PREV                               =    5019
+        XML_CHECK_NO_NEXT                                  =    5020
+        XML_CHECK_WRONG_NEXT                               =    5021
+        XML_CHECK_NOT_DTD                                  =    5022
+        XML_CHECK_NOT_ATTR                                 =    5023
+        XML_CHECK_NOT_ATTR_DECL                            =    5024
+        XML_CHECK_NOT_ELEM_DECL                            =    5025
+        XML_CHECK_NOT_ENTITY_DECL                          =    5026
+        XML_CHECK_NOT_NS_DECL                              =    5027
+        XML_CHECK_NO_HREF                                  =    5028
+        XML_CHECK_WRONG_PARENT                             =    5029
+        XML_CHECK_NS_SCOPE                                 =    5030
+        XML_CHECK_NS_ANCESTOR                              =    5031
+        XML_CHECK_NOT_UTF8                                 =    5032
+        XML_CHECK_NO_DICT                                  =    5033
+        XML_CHECK_NOT_NCNAME                               =    5034
+        XML_CHECK_OUTSIDE_DICT                             =    5035
+        XML_CHECK_WRONG_NAME                               =    5036
+        XML_CHECK_NAME_NOT_NULL                            =    5037
+        XML_I18N_NO_NAME                                   =    6000
+        XML_I18N_NO_HANDLER                                =    6001
+        XML_I18N_EXCESS_HANDLER                            =    6002
+        XML_I18N_CONV_FAILED                               =    6003
+        XML_I18N_NO_OUTPUT                                 =    6004
+        XML_BUF_OVERFLOW                                   =    7000
+
+    ctypedef enum xmlRelaxNGValidErr:
+        XML_RELAXNG_OK                                     =       0
+        XML_RELAXNG_ERR_MEMORY                             =       1
+        XML_RELAXNG_ERR_TYPE                               =       2
+        XML_RELAXNG_ERR_TYPEVAL                            =       3
+        XML_RELAXNG_ERR_DUPID                              =       4
+        XML_RELAXNG_ERR_TYPECMP                            =       5
+        XML_RELAXNG_ERR_NOSTATE                            =       6
+        XML_RELAXNG_ERR_NODEFINE                           =       7
+        XML_RELAXNG_ERR_LISTEXTRA                          =       8
+        XML_RELAXNG_ERR_LISTEMPTY                          =       9
+        XML_RELAXNG_ERR_INTERNODATA                        =      10
+        XML_RELAXNG_ERR_INTERSEQ                           =      11
+        XML_RELAXNG_ERR_INTEREXTRA                         =      12
+        XML_RELAXNG_ERR_ELEMNAME                           =      13
+        XML_RELAXNG_ERR_ATTRNAME                           =      14
+        XML_RELAXNG_ERR_ELEMNONS                           =      15
+        XML_RELAXNG_ERR_ATTRNONS                           =      16
+        XML_RELAXNG_ERR_ELEMWRONGNS                        =      17
+        XML_RELAXNG_ERR_ATTRWRONGNS                        =      18
+        XML_RELAXNG_ERR_ELEMEXTRANS                        =      19
+        XML_RELAXNG_ERR_ATTREXTRANS                        =      20
+        XML_RELAXNG_ERR_ELEMNOTEMPTY                       =      21
+        XML_RELAXNG_ERR_NOELEM                             =      22
+        XML_RELAXNG_ERR_NOTELEM                            =      23
+        XML_RELAXNG_ERR_ATTRVALID                          =      24
+        XML_RELAXNG_ERR_CONTENTVALID                       =      25
+        XML_RELAXNG_ERR_EXTRACONTENT                       =      26
+        XML_RELAXNG_ERR_INVALIDATTR                        =      27
+        XML_RELAXNG_ERR_DATAELEM                           =      28
+        XML_RELAXNG_ERR_VALELEM                            =      29
+        XML_RELAXNG_ERR_LISTELEM                           =      30
+        XML_RELAXNG_ERR_DATATYPE                           =      31
+        XML_RELAXNG_ERR_VALUE                              =      32
+        XML_RELAXNG_ERR_LIST                               =      33
+        XML_RELAXNG_ERR_NOGRAMMAR                          =      34
+        XML_RELAXNG_ERR_EXTRADATA                          =      35
+        XML_RELAXNG_ERR_LACKDATA                           =      36
+        XML_RELAXNG_ERR_INTERNAL                           =      37
+        XML_RELAXNG_ERR_ELEMWRONG                          =      38
+        XML_RELAXNG_ERR_TEXTWRONG                          =      39
+# --- END: GENERATED CONSTANTS ---
+
+cdef extern from "libxml/xmlerror.h" nogil:
+    ctypedef struct xmlError:
+        int domain
+        int code
+        char* message
+        xmlErrorLevel level
+        char* file
+        char* str1
+        char* str2
+        char* str3
+        int line
+        int int1
+        int int2
+        void* node
+
+    ctypedef void (*xmlGenericErrorFunc)(void* ctxt, char* msg, ...) noexcept
+    ctypedef void (*xmlStructuredErrorFunc)(void* userData,
+                                            const xmlError* error) noexcept
+
+    cdef void xmlSetGenericErrorFunc(
+        void* ctxt, xmlGenericErrorFunc func)
+    cdef void xmlSetStructuredErrorFunc(
+        void* ctxt, xmlStructuredErrorFunc func)
+
+cdef extern from "libxml/globals.h" nogil:
+    cdef xmlStructuredErrorFunc xmlStructuredError
+    cdef void* xmlStructuredErrorContext
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/xmlparser.pxd b/.venv/lib/python3.12/site-packages/lxml/includes/xmlparser.pxd
new file mode 100644
index 00000000..a43c74cf
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/xmlparser.pxd
@@ -0,0 +1,265 @@
+from libc.string cimport const_char
+
+from lxml.includes.tree cimport (
+    xmlDoc, xmlNode, xmlEntity, xmlDict, xmlDtd, xmlChar, const_xmlChar)
+from lxml.includes.tree cimport xmlInputReadCallback, xmlInputCloseCallback
+from lxml.includes.xmlerror cimport xmlError, xmlStructuredErrorFunc, xmlErrorLevel
+
+
+cdef extern from "libxml/parser.h" nogil:
+    ctypedef void (*startElementNsSAX2Func)(void* ctx,
+                                            const_xmlChar* localname,
+                                            const_xmlChar* prefix,
+                                            const_xmlChar* URI,
+                                            int nb_namespaces,
+                                            const_xmlChar** namespaces,
+                                            int nb_attributes,
+                                            int nb_defaulted,
+                                            const_xmlChar** attributes) noexcept
+
+    ctypedef void (*endElementNsSAX2Func)(void* ctx,
+                                          const_xmlChar* localname,
+                                          const_xmlChar* prefix,
+                                          const_xmlChar* URI) noexcept
+
+    ctypedef void (*startElementSAXFunc)(void* ctx, const_xmlChar* name, const_xmlChar** atts) noexcept
+
+    ctypedef void (*endElementSAXFunc)(void* ctx, const_xmlChar* name) noexcept
+
+    ctypedef void (*charactersSAXFunc)(void* ctx, const_xmlChar* ch, int len) noexcept
+
+    ctypedef void (*cdataBlockSAXFunc)(void* ctx, const_xmlChar* value, int len) noexcept
+
+    ctypedef void (*commentSAXFunc)(void* ctx, const_xmlChar* value) noexcept
+
+    ctypedef void (*processingInstructionSAXFunc)(void* ctx, 
+                                                  const_xmlChar* target,
+                                                  const_xmlChar* data) noexcept
+
+    ctypedef void (*internalSubsetSAXFunc)(void* ctx, 
+                                            const_xmlChar* name,
+                                            const_xmlChar* externalID,
+                                            const_xmlChar* systemID) noexcept
+
+    ctypedef void (*endDocumentSAXFunc)(void* ctx) noexcept
+
+    ctypedef void (*startDocumentSAXFunc)(void* ctx) noexcept
+
+    ctypedef void (*referenceSAXFunc)(void * ctx, const_xmlChar* name) noexcept
+
+    ctypedef xmlEntity* (*getEntitySAXFunc)(void* ctx, const_xmlChar* name) noexcept
+
+    cdef int XML_SAX2_MAGIC
+
+cdef extern from "libxml/tree.h" nogil:
+    ctypedef struct xmlParserInput:
+        int line
+        int col
+        int length
+        const_xmlChar* base
+        const_xmlChar* cur
+        const_xmlChar* end
+        const_char *filename
+
+    ctypedef struct xmlParserInputBuffer:
+        void* context
+        xmlInputReadCallback  readcallback
+        xmlInputCloseCallback closecallback
+
+    ctypedef struct xmlSAXHandlerV1:
+        # same as xmlSAXHandler, but without namespaces
+        pass
+
+    ctypedef struct xmlSAXHandler:
+        internalSubsetSAXFunc           internalSubset
+        startElementNsSAX2Func          startElementNs
+        endElementNsSAX2Func            endElementNs
+        startElementSAXFunc             startElement
+        endElementSAXFunc               endElement
+        charactersSAXFunc               characters
+        cdataBlockSAXFunc               cdataBlock
+        referenceSAXFunc                reference
+        getEntitySAXFunc                getEntity
+        commentSAXFunc                  comment
+        processingInstructionSAXFunc	processingInstruction
+        startDocumentSAXFunc            startDocument
+        endDocumentSAXFunc              endDocument
+        int                             initialized
+        xmlStructuredErrorFunc          serror
+        void*                           _private
+
+
+cdef extern from "libxml/SAX2.h" nogil:
+    cdef void xmlSAX2StartDocument(void* ctxt)
+
+
+cdef extern from "libxml/xmlIO.h" nogil:
+    cdef xmlParserInputBuffer* xmlAllocParserInputBuffer(int enc)
+
+
+cdef extern from "libxml/parser.h" nogil:
+
+    cdef xmlDict* xmlDictCreate()
+    cdef xmlDict* xmlDictCreateSub(xmlDict* subdict)
+    cdef void xmlDictFree(xmlDict* sub)
+    cdef int xmlDictReference(xmlDict* dict)
+    
+    cdef int XML_COMPLETE_ATTRS  # SAX option for adding DTD default attributes
+    cdef int XML_SKIP_IDS        # SAX option for not building an XML ID dict
+
+    ctypedef enum xmlParserInputState:
+        XML_PARSER_EOF = -1  # nothing is to be parsed
+        XML_PARSER_START = 0  # nothing has been parsed
+        XML_PARSER_MISC = 1  # Misc* before int subset
+        XML_PARSER_PI = 2  # Within a processing instruction
+        XML_PARSER_DTD = 3  # within some DTD content
+        XML_PARSER_PROLOG = 4  # Misc* after internal subset
+        XML_PARSER_COMMENT = 5  # within a comment
+        XML_PARSER_START_TAG = 6  # within a start tag
+        XML_PARSER_CONTENT = 7  # within the content
+        XML_PARSER_CDATA_SECTION = 8  # within a CDATA section
+        XML_PARSER_END_TAG = 9  # within a closing tag
+        XML_PARSER_ENTITY_DECL = 10  # within an entity declaration
+        XML_PARSER_ENTITY_VALUE = 11  # within an entity value in a decl
+        XML_PARSER_ATTRIBUTE_VALUE = 12  # within an attribute value
+        XML_PARSER_SYSTEM_LITERAL = 13  # within a SYSTEM value
+        XML_PARSER_EPILOG = 14  # the Misc* after the last end tag
+        XML_PARSER_IGNORE = 15  # within an IGNORED section
+        XML_PARSER_PUBLIC_LITERAL = 16  # within a PUBLIC value
+
+
+    ctypedef struct xmlParserCtxt:
+        xmlDoc* myDoc
+        xmlDict* dict
+        int dictNames
+        void* _private
+        bint wellFormed
+        bint recovery
+        int options
+        bint disableSAX
+        int errNo
+        xmlParserInputState instate
+        bint replaceEntities
+        int loadsubset  # != 0 if enabled, int value == why
+        bint validate
+        xmlError lastError
+        xmlNode* node
+        xmlSAXHandler* sax
+        void* userData
+        int* spaceTab
+        int spaceMax
+        int nsNr
+        bint html
+        bint progressive
+        int inSubset
+        int charset
+        xmlParserInput* input
+        int inputNr
+        xmlParserInput* inputTab[]
+
+    ctypedef enum xmlParserOption:
+        XML_PARSE_RECOVER = 1 # recover on errors
+        XML_PARSE_NOENT = 2 # substitute entities
+        XML_PARSE_DTDLOAD = 4 # load the external subset
+        XML_PARSE_DTDATTR = 8 # default DTD attributes
+        XML_PARSE_DTDVALID = 16 # validate with the DTD
+        XML_PARSE_NOERROR = 32 # suppress error reports
+        XML_PARSE_NOWARNING = 64 # suppress warning reports
+        XML_PARSE_PEDANTIC = 128 # pedantic error reporting
+        XML_PARSE_NOBLANKS = 256 # remove blank nodes
+        XML_PARSE_SAX1 = 512 # use the SAX1 interface internally
+        XML_PARSE_XINCLUDE = 1024 # Implement XInclude substitution
+        XML_PARSE_NONET = 2048 # Forbid network access
+        XML_PARSE_NODICT = 4096 # Do not reuse the context dictionary
+        XML_PARSE_NSCLEAN = 8192 # remove redundant namespaces declarations
+        XML_PARSE_NOCDATA = 16384 # merge CDATA as text nodes
+        XML_PARSE_NOXINCNODE = 32768 # do not generate XINCLUDE START/END nodes
+        # libxml2 2.6.21+ only:
+        XML_PARSE_COMPACT = 65536 # compact small text nodes
+        # libxml2 2.7.0+ only:
+        XML_PARSE_OLD10 = 131072 # parse using XML-1.0 before update 5
+        XML_PARSE_NOBASEFIX = 262144 # do not fixup XINCLUDE xml:base uris
+        XML_PARSE_HUGE = 524288 # relax any hardcoded limit from the parser
+        # libxml2 2.7.3+ only:
+        XML_PARSE_OLDSAX = 1048576 # parse using SAX2 interface before 2.7.0
+        # libxml2 2.8.0+ only:
+        XML_PARSE_IGNORE_ENC = 2097152 # ignore internal document encoding hint
+        # libxml2 2.9.0+ only:
+        XML_PARSE_BIG_LINES = 4194304 # Store big lines numbers in text PSVI field
+
+    cdef void xmlInitParser()
+    cdef void xmlCleanupParser()
+
+    cdef int xmlLineNumbersDefault(int onoff)
+    cdef xmlParserCtxt* xmlNewParserCtxt()
+    cdef xmlParserInput* xmlNewIOInputStream(xmlParserCtxt* ctxt,
+                                             xmlParserInputBuffer* input,
+                                             int enc)
+    cdef int xmlCtxtUseOptions(xmlParserCtxt* ctxt, int options)
+    cdef void xmlFreeParserCtxt(xmlParserCtxt* ctxt)
+    cdef void xmlCtxtReset(xmlParserCtxt* ctxt)
+    cdef void xmlClearParserCtxt(xmlParserCtxt* ctxt)
+    cdef int xmlParseChunk(xmlParserCtxt* ctxt,
+                           char* chunk, int size, int terminate)
+    cdef xmlDoc* xmlCtxtReadDoc(xmlParserCtxt* ctxt,
+                                char* cur, char* URL, char* encoding,
+                                int options)
+    cdef xmlDoc* xmlCtxtReadFile(xmlParserCtxt* ctxt,
+                                 char* filename, char* encoding,
+                                 int options)
+    cdef xmlDoc* xmlCtxtReadIO(xmlParserCtxt* ctxt, 
+                               xmlInputReadCallback ioread, 
+                               xmlInputCloseCallback ioclose, 
+                               void* ioctx,
+                               char* URL, char* encoding,
+                               int options)
+    cdef xmlDoc* xmlCtxtReadMemory(xmlParserCtxt* ctxt,
+                                   char* buffer, int size,
+                                   char* filename, const_char* encoding,
+                                   int options)
+
+    cdef void xmlErrParser(xmlParserCtxt* ctxt, xmlNode* node,
+                           int domain, int code, xmlErrorLevel level,
+                           const xmlChar *str1, const xmlChar *str2, const xmlChar *str3,
+                           int int1, const char *msg, ...)
+
+
+# iterparse:
+
+    cdef xmlParserCtxt* xmlCreatePushParserCtxt(xmlSAXHandler* sax,
+                                                void* user_data,
+                                                char* chunk,
+                                                int size,
+                                                char* filename)
+
+    cdef int xmlCtxtResetPush(xmlParserCtxt* ctxt,
+                              char* chunk,
+                              int size,
+                              char* filename,
+                              char* encoding)
+
+# entity loaders:
+
+    ctypedef xmlParserInput* (*xmlExternalEntityLoader)(
+        const_char * URL, const_char * ID, xmlParserCtxt* context) noexcept
+    cdef xmlExternalEntityLoader xmlGetExternalEntityLoader()
+    cdef void xmlSetExternalEntityLoader(xmlExternalEntityLoader f)
+
+    cdef xmlEntity* xmlSAX2GetEntity(void* ctxt, const_xmlChar* name) noexcept
+
+# DTDs:
+
+    cdef xmlDtd* xmlParseDTD(const_xmlChar* ExternalID, const_xmlChar* SystemID)
+    cdef xmlDtd* xmlIOParseDTD(xmlSAXHandler* sax,
+                               xmlParserInputBuffer* input,
+                               int enc)
+
+
+cdef extern from "libxml/parserInternals.h" nogil:
+    cdef xmlParserInput* xmlNewInputStream(xmlParserCtxt* ctxt)
+    cdef xmlParserInput* xmlNewStringInputStream(xmlParserCtxt* ctxt, 
+                                                 char* buffer)
+    cdef xmlParserInput* xmlNewInputFromFile(xmlParserCtxt* ctxt, 
+                                             char* filename)
+    cdef void xmlFreeInputStream(xmlParserInput* input)
+    cdef int xmlSwitchEncoding(xmlParserCtxt* ctxt, int enc)
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/xmlschema.pxd b/.venv/lib/python3.12/site-packages/lxml/includes/xmlschema.pxd
new file mode 100644
index 00000000..06741111
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/xmlschema.pxd
@@ -0,0 +1,35 @@
+from lxml.includes.tree cimport xmlDoc
+from lxml.includes.xmlparser cimport xmlSAXHandler
+from lxml.includes.xmlerror cimport xmlStructuredErrorFunc
+
+cdef extern from "libxml/xmlschemas.h" nogil:
+    ctypedef struct xmlSchema
+    ctypedef struct xmlSchemaParserCtxt
+
+    ctypedef struct xmlSchemaSAXPlugStruct
+    ctypedef struct xmlSchemaValidCtxt
+
+    ctypedef enum xmlSchemaValidOption:
+        XML_SCHEMA_VAL_VC_I_CREATE = 1
+
+    cdef xmlSchemaValidCtxt* xmlSchemaNewValidCtxt(xmlSchema* schema) nogil
+    cdef void xmlSchemaSetParserStructuredErrors(xmlSchemaParserCtxt* ctxt,
+        xmlStructuredErrorFunc serror, void *ctx)
+    cdef void xmlSchemaSetValidStructuredErrors(xmlSchemaValidCtxt* ctxt,
+        xmlStructuredErrorFunc serror, void *ctx)
+
+    cdef int xmlSchemaValidateDoc(xmlSchemaValidCtxt* ctxt, xmlDoc* doc) nogil
+    cdef xmlSchema* xmlSchemaParse(xmlSchemaParserCtxt* ctxt) nogil
+    cdef xmlSchemaParserCtxt* xmlSchemaNewParserCtxt(char* URL) nogil
+    cdef xmlSchemaParserCtxt* xmlSchemaNewDocParserCtxt(xmlDoc* doc) nogil
+    cdef void xmlSchemaFree(xmlSchema* schema) nogil
+    cdef void xmlSchemaFreeParserCtxt(xmlSchemaParserCtxt* ctxt) nogil
+    cdef void xmlSchemaFreeValidCtxt(xmlSchemaValidCtxt* ctxt) nogil
+    cdef int xmlSchemaSetValidOptions(xmlSchemaValidCtxt* ctxt,
+                                      int options) nogil
+
+    cdef xmlSchemaSAXPlugStruct* xmlSchemaSAXPlug(xmlSchemaValidCtxt* ctxt,
+                                                  xmlSAXHandler** sax,
+                                                  void** data) nogil
+    cdef int xmlSchemaSAXUnplug(xmlSchemaSAXPlugStruct* sax_plug)
+    cdef int xmlSchemaIsValid(xmlSchemaValidCtxt* ctxt)
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/xpath.pxd b/.venv/lib/python3.12/site-packages/lxml/includes/xpath.pxd
new file mode 100644
index 00000000..22069eb7
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/xpath.pxd
@@ -0,0 +1,136 @@
+from lxml.includes cimport tree
+from lxml.includes cimport xmlerror
+
+from libc.string cimport const_char
+from lxml.includes.tree cimport xmlChar, const_xmlChar
+
+
+cdef extern from "libxml/xpath.h" nogil:
+    ctypedef enum xmlXPathObjectType:
+        XPATH_UNDEFINED = 0
+        XPATH_NODESET = 1
+        XPATH_BOOLEAN = 2
+        XPATH_NUMBER = 3
+        XPATH_STRING = 4
+        XPATH_POINT = 5
+        XPATH_RANGE = 6
+        XPATH_LOCATIONSET = 7
+        XPATH_USERS = 8
+        XPATH_XSLT_TREE = 9
+
+    ctypedef enum xmlXPathError:
+        XPATH_EXPRESSION_OK = 0
+        XPATH_NUMBER_ERROR = 1
+        XPATH_UNFINISHED_LITERAL_ERROR = 2
+        XPATH_START_LITERAL_ERROR = 3
+        XPATH_VARIABLE_REF_ERROR = 4
+        XPATH_UNDEF_VARIABLE_ERROR = 5
+        XPATH_INVALID_PREDICATE_ERROR = 6
+        XPATH_EXPR_ERROR = 7
+        XPATH_UNCLOSED_ERROR = 8
+        XPATH_UNKNOWN_FUNC_ERROR = 9
+        XPATH_INVALID_OPERAND = 10
+        XPATH_INVALID_TYPE = 11
+        XPATH_INVALID_ARITY = 12
+        XPATH_INVALID_CTXT_SIZE = 13
+        XPATH_INVALID_CTXT_POSITION = 14
+        XPATH_MEMORY_ERROR = 15
+        XPTR_SYNTAX_ERROR = 16
+        XPTR_RESOURCE_ERROR = 17
+        XPTR_SUB_RESOURCE_ERROR = 18
+        XPATH_UNDEF_PREFIX_ERROR = 19
+        XPATH_ENCODING_ERROR = 20
+        XPATH_INVALID_CHAR_ERROR = 21
+        XPATH_INVALID_CTXT = 22
+
+    ctypedef struct xmlNodeSet:
+        int nodeNr
+        int nodeMax
+        tree.xmlNode** nodeTab
+        
+    ctypedef struct xmlXPathObject:
+        xmlXPathObjectType type
+        xmlNodeSet* nodesetval
+        bint boolval
+        double floatval
+        xmlChar* stringval
+
+    ctypedef struct xmlXPathContext:
+        tree.xmlDoc* doc
+        tree.xmlNode* node
+        tree.xmlDict* dict
+        tree.xmlHashTable* nsHash
+        const_xmlChar* function
+        const_xmlChar* functionURI
+        xmlerror.xmlStructuredErrorFunc error
+        xmlerror.xmlError lastError
+        void* userData
+
+    ctypedef struct xmlXPathParserContext:
+        xmlXPathContext* context
+        xmlXPathObject* value
+        tree.xmlNode* ancestor
+        int error
+
+    ctypedef struct xmlXPathCompExpr
+
+    ctypedef void (*xmlXPathFunction)(xmlXPathParserContext* ctxt, int nargs)
+    ctypedef xmlXPathFunction (*xmlXPathFuncLookupFunc)(void* ctxt,
+                                                        const_xmlChar* name,
+                                                        const_xmlChar* ns_uri)
+    
+    cdef xmlXPathContext* xmlXPathNewContext(tree.xmlDoc* doc)
+    cdef xmlXPathObject* xmlXPathEvalExpression(const_xmlChar* str,
+                                                xmlXPathContext* ctxt)
+    cdef xmlXPathObject* xmlXPathCompiledEval(xmlXPathCompExpr* comp,
+                                              xmlXPathContext* ctxt)
+    cdef xmlXPathCompExpr* xmlXPathCompile(const_xmlChar* str)
+    cdef xmlXPathCompExpr* xmlXPathCtxtCompile(xmlXPathContext* ctxt,
+                                               const_xmlChar* str)
+    cdef void xmlXPathFreeContext(xmlXPathContext* ctxt)
+    cdef void xmlXPathFreeCompExpr(xmlXPathCompExpr* comp)
+    cdef void xmlXPathFreeObject(xmlXPathObject* obj)
+    cdef int xmlXPathRegisterNs(xmlXPathContext* ctxt,
+                                const_xmlChar* prefix, const_xmlChar* ns_uri)
+    
+    cdef xmlNodeSet* xmlXPathNodeSetCreate(tree.xmlNode* val)
+    cdef void xmlXPathFreeNodeSet(xmlNodeSet* val)
+
+
+cdef extern from "libxml/xpathInternals.h" nogil:
+    cdef int xmlXPathRegisterFunc(xmlXPathContext* ctxt,
+                                  const_xmlChar* name,
+                                  xmlXPathFunction f)
+    cdef int xmlXPathRegisterFuncNS(xmlXPathContext* ctxt,
+                                    const_xmlChar* name,
+                                    const_xmlChar* ns_uri,
+                                    xmlXPathFunction f)
+    cdef void xmlXPathRegisterFuncLookup(xmlXPathContext *ctxt,
+                                         xmlXPathFuncLookupFunc f,
+                                         void *funcCtxt)
+    cdef int xmlXPathRegisterVariable(xmlXPathContext *ctxt, 
+                                      const_xmlChar* name,
+                                      xmlXPathObject* value)
+    cdef int xmlXPathRegisterVariableNS(xmlXPathContext *ctxt, 
+                                        const_xmlChar* name,
+                                        const_xmlChar* ns_uri,
+                                        xmlXPathObject* value)
+    cdef void xmlXPathRegisteredVariablesCleanup(xmlXPathContext *ctxt)
+    cdef void xmlXPathRegisteredNsCleanup(xmlXPathContext *ctxt)
+    cdef xmlXPathObject* valuePop (xmlXPathParserContext *ctxt)
+    cdef int valuePush(xmlXPathParserContext* ctxt, xmlXPathObject *value)
+    
+    cdef xmlXPathObject* xmlXPathNewCString(const_char *val)
+    cdef xmlXPathObject* xmlXPathWrapCString(const_char * val)
+    cdef xmlXPathObject* xmlXPathNewString(const_xmlChar *val)
+    cdef xmlXPathObject* xmlXPathWrapString(const_xmlChar * val)
+    cdef xmlXPathObject* xmlXPathNewFloat(double val)
+    cdef xmlXPathObject* xmlXPathNewBoolean(int val)
+    cdef xmlXPathObject* xmlXPathNewNodeSet(tree.xmlNode* val)
+    cdef xmlXPathObject* xmlXPathNewValueTree(tree.xmlNode* val)
+    cdef void xmlXPathNodeSetAdd(xmlNodeSet* cur,
+                                  tree.xmlNode* val)
+    cdef void xmlXPathNodeSetAddUnique(xmlNodeSet* cur,
+                                        tree.xmlNode* val)
+    cdef xmlXPathObject* xmlXPathWrapNodeSet(xmlNodeSet* val)
+    cdef void xmlXPathErr(xmlXPathParserContext* ctxt, int error)
diff --git a/.venv/lib/python3.12/site-packages/lxml/includes/xslt.pxd b/.venv/lib/python3.12/site-packages/lxml/includes/xslt.pxd
new file mode 100644
index 00000000..abafe432
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/includes/xslt.pxd
@@ -0,0 +1,190 @@
+from lxml.includes.tree cimport xmlDoc, xmlNode, xmlDict, xmlChar, const_xmlChar, xmlOutputBuffer
+from lxml.includes.xmlerror cimport xmlGenericErrorFunc
+from lxml.includes.xpath cimport xmlXPathContext, xmlXPathFunction
+
+from libc.string cimport const_char
+
+cdef extern from "libxslt/xslt.h":
+    cdef int xsltLibxsltVersion
+    cdef int xsltMaxDepth
+
+cdef extern from "libxslt/xsltconfig.h":
+    cdef int LIBXSLT_VERSION
+
+cdef extern from "libxslt/xsltInternals.h" nogil:
+    ctypedef enum xsltTransformState:
+        XSLT_STATE_OK       # 0
+        XSLT_STATE_ERROR    # 1
+        XSLT_STATE_STOPPED  # 2
+
+    ctypedef struct xsltDocument:
+        xmlDoc* doc
+
+    ctypedef struct xsltStylesheet:
+        xmlChar* encoding
+        xmlDoc* doc
+        int errors
+
+    ctypedef struct xsltTransformContext:
+        xsltStylesheet* style
+        xmlXPathContext* xpathCtxt
+        xsltDocument* document
+        void* _private
+        xmlDict* dict
+        int profile
+        xmlNode* node
+        xmlDoc* output
+        xmlNode* insert
+        xmlNode* inst
+        xsltTransformState state
+
+    ctypedef struct xsltStackElem
+
+    ctypedef struct xsltTemplate
+
+    cdef xsltStylesheet* xsltParseStylesheetDoc(xmlDoc* doc)
+    cdef void xsltFreeStylesheet(xsltStylesheet* sheet)
+
+cdef extern from "libxslt/imports.h" nogil:
+    # actually defined in "etree_defs.h"
+    cdef void LXML_GET_XSLT_ENCODING(const_xmlChar* result_var, xsltStylesheet* style)
+
+cdef extern from "libxslt/extensions.h" nogil:
+    ctypedef void (*xsltTransformFunction)(xsltTransformContext* ctxt,
+                                           xmlNode* context_node,
+                                           xmlNode* inst,
+                                           void* precomp_unused) noexcept
+
+    cdef int xsltRegisterExtFunction(xsltTransformContext* ctxt,
+                                     const_xmlChar* name,
+                                     const_xmlChar* URI,
+                                     xmlXPathFunction function)
+    cdef int xsltRegisterExtModuleFunction(const_xmlChar* name, const_xmlChar* URI,
+                                           xmlXPathFunction function)
+    cdef int xsltUnregisterExtModuleFunction(const_xmlChar* name, const_xmlChar* URI)
+    cdef xmlXPathFunction xsltExtModuleFunctionLookup(
+        const_xmlChar* name, const_xmlChar* URI)
+    cdef int xsltRegisterExtPrefix(xsltStylesheet* style, 
+                                   const_xmlChar* prefix, const_xmlChar* URI)
+    cdef int xsltRegisterExtElement(xsltTransformContext* ctxt,
+                                    const_xmlChar* name, const_xmlChar* URI,
+                                    xsltTransformFunction function)
+
+cdef extern from "libxslt/documents.h" nogil:
+    ctypedef enum xsltLoadType:
+        XSLT_LOAD_START
+        XSLT_LOAD_STYLESHEET
+        XSLT_LOAD_DOCUMENT
+
+    ctypedef xmlDoc* (*xsltDocLoaderFunc)(const_xmlChar* URI, xmlDict* dict,
+                                          int options,
+                                          void* ctxt,
+                                          xsltLoadType type) noexcept
+    cdef xsltDocLoaderFunc xsltDocDefaultLoader
+    cdef void xsltSetLoaderFunc(xsltDocLoaderFunc f)
+
+cdef extern from "libxslt/transform.h" nogil:
+    cdef xmlDoc* xsltApplyStylesheet(xsltStylesheet* style, xmlDoc* doc,
+                                     const_char** params)
+    cdef xmlDoc* xsltApplyStylesheetUser(xsltStylesheet* style, xmlDoc* doc,
+                                         const_char** params, const_char* output,
+                                         void* profile,
+                                         xsltTransformContext* context)
+    cdef void xsltProcessOneNode(xsltTransformContext* ctxt,
+                                 xmlNode* contextNode,
+                                 xsltStackElem* params)
+    cdef xsltTransformContext* xsltNewTransformContext(xsltStylesheet* style,
+                                                       xmlDoc* doc)
+    cdef void xsltFreeTransformContext(xsltTransformContext* context)
+    cdef void xsltApplyOneTemplate(xsltTransformContext* ctxt,
+                                   xmlNode* contextNode, xmlNode* list,
+                                   xsltTemplate* templ,
+                                   xsltStackElem* params)
+
+
+cdef extern from "libxslt/xsltutils.h" nogil:
+    cdef int xsltSaveResultToString(xmlChar** doc_txt_ptr,
+                                    int* doc_txt_len,
+                                    xmlDoc* result,
+                                    xsltStylesheet* style)
+    cdef int xsltSaveResultToFilename(const_char *URL,
+                                      xmlDoc* result,
+                                      xsltStylesheet* style,
+                                      int compression)
+    cdef int xsltSaveResultTo(xmlOutputBuffer* buf,
+                              xmlDoc* result,
+                              xsltStylesheet* style)
+    cdef xmlGenericErrorFunc xsltGenericError
+    cdef void *xsltGenericErrorContext
+    cdef void xsltSetGenericErrorFunc(
+        void* ctxt, void (*handler)(void* ctxt, char* msg, ...) nogil)
+    cdef void xsltSetTransformErrorFunc(
+        xsltTransformContext*, void* ctxt,
+        void (*handler)(void* ctxt, char* msg, ...) nogil)
+    cdef void xsltTransformError(xsltTransformContext* ctxt, 
+                                 xsltStylesheet* style, 
+                                 xmlNode* node, char* msg, ...)
+    cdef void xsltSetCtxtParseOptions(
+        xsltTransformContext* ctxt, int options)
+
+
+cdef extern from "libxslt/security.h" nogil:
+    ctypedef struct xsltSecurityPrefs
+    ctypedef enum xsltSecurityOption:
+        XSLT_SECPREF_READ_FILE = 1
+        XSLT_SECPREF_WRITE_FILE = 2
+        XSLT_SECPREF_CREATE_DIRECTORY = 3
+        XSLT_SECPREF_READ_NETWORK = 4
+        XSLT_SECPREF_WRITE_NETWORK = 5
+
+    ctypedef int (*xsltSecurityCheck)(xsltSecurityPrefs* sec,
+                                      xsltTransformContext* ctxt,
+                                      char* value) noexcept
+
+    cdef xsltSecurityPrefs* xsltNewSecurityPrefs()
+    cdef void xsltFreeSecurityPrefs(xsltSecurityPrefs* sec)
+    cdef int xsltSecurityForbid(xsltSecurityPrefs* sec,
+                                xsltTransformContext* ctxt,
+                                char* value)
+    cdef int xsltSecurityAllow(xsltSecurityPrefs* sec,
+                                xsltTransformContext* ctxt,
+                                char* value)
+    cdef int xsltSetSecurityPrefs(xsltSecurityPrefs* sec,
+                                  xsltSecurityOption option,
+                                  xsltSecurityCheck func)
+    cdef xsltSecurityCheck xsltGetSecurityPrefs(
+        xsltSecurityPrefs* sec,
+        xsltSecurityOption option)
+    cdef int xsltSetCtxtSecurityPrefs(xsltSecurityPrefs* sec,
+                                      xsltTransformContext* ctxt)
+    cdef xmlDoc* xsltGetProfileInformation(xsltTransformContext* ctxt)
+
+cdef extern from "libxslt/variables.h" nogil:
+    cdef int xsltQuoteUserParams(xsltTransformContext* ctxt,
+                                 const_char** params)
+    cdef int xsltQuoteOneUserParam(xsltTransformContext* ctxt,
+                                   const_xmlChar* name,
+                                   const_xmlChar* value)
+
+cdef extern from "libxslt/extra.h" nogil:
+    const_xmlChar* XSLT_LIBXSLT_NAMESPACE
+    const_xmlChar* XSLT_XALAN_NAMESPACE
+    const_xmlChar* XSLT_SAXON_NAMESPACE
+    const_xmlChar* XSLT_XT_NAMESPACE
+
+    cdef xmlXPathFunction xsltFunctionNodeSet
+    cdef void xsltRegisterAllExtras()
+
+cdef extern from "libexslt/exslt.h" nogil:
+    cdef void exsltRegisterAll()
+
+    # libexslt 1.1.25+
+    const_xmlChar* EXSLT_DATE_NAMESPACE
+    const_xmlChar* EXSLT_SETS_NAMESPACE
+    const_xmlChar* EXSLT_MATH_NAMESPACE
+    const_xmlChar* EXSLT_STRINGS_NAMESPACE
+
+    cdef int exsltDateXpathCtxtRegister(xmlXPathContext* ctxt, const_xmlChar* prefix)
+    cdef int exsltSetsXpathCtxtRegister(xmlXPathContext* ctxt, const_xmlChar* prefix)
+    cdef int exsltMathXpathCtxtRegister(xmlXPathContext* ctxt, const_xmlChar* prefix)
+    cdef int exsltStrXpathCtxtRegister(xmlXPathContext* ctxt, const_xmlChar* prefix)
diff --git a/.venv/lib/python3.12/site-packages/lxml/isoschematron/__init__.py b/.venv/lib/python3.12/site-packages/lxml/isoschematron/__init__.py
new file mode 100644
index 00000000..ac89fb62
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/isoschematron/__init__.py
@@ -0,0 +1,348 @@
+"""The ``lxml.isoschematron`` package implements ISO Schematron support on top
+of the pure-xslt 'skeleton' implementation.
+"""
+
+import sys
+import os.path
+from lxml import etree as _etree # due to validator __init__ signature
+
+
+# some compat stuff, borrowed from lxml.html
+try:
+    unicode
+except NameError:
+    # Python 3
+    unicode = str
+try:
+    basestring
+except NameError:
+    # Python 3
+    basestring = str
+
+
+__all__ = ['extract_xsd', 'extract_rng', 'iso_dsdl_include',
+           'iso_abstract_expand', 'iso_svrl_for_xslt1',
+           'svrl_validation_errors', 'schematron_schema_valid',
+           'stylesheet_params', 'Schematron']
+
+
+# some namespaces
+#FIXME: Maybe lxml should provide a dedicated place for common namespace
+#FIXME: definitions?
+XML_SCHEMA_NS = "http://www.w3.org/2001/XMLSchema"
+RELAXNG_NS = "http://relaxng.org/ns/structure/1.0"
+SCHEMATRON_NS = "http://purl.oclc.org/dsdl/schematron"
+SVRL_NS = "http://purl.oclc.org/dsdl/svrl"
+
+
+# some helpers
+_schematron_root = '{%s}schema' % SCHEMATRON_NS
+_xml_schema_root = '{%s}schema' % XML_SCHEMA_NS
+_resources_dir = os.path.join(os.path.dirname(__file__), 'resources')
+
+
+# the iso-schematron skeleton implementation steps aka xsl transformations
+extract_xsd = _etree.XSLT(_etree.parse(
+    os.path.join(_resources_dir, 'xsl', 'XSD2Schtrn.xsl')))
+extract_rng = _etree.XSLT(_etree.parse(
+    os.path.join(_resources_dir, 'xsl', 'RNG2Schtrn.xsl')))
+iso_dsdl_include = _etree.XSLT(_etree.parse(
+    os.path.join(_resources_dir, 'xsl', 'iso-schematron-xslt1',
+                 'iso_dsdl_include.xsl')))
+iso_abstract_expand = _etree.XSLT(_etree.parse(
+    os.path.join(_resources_dir, 'xsl', 'iso-schematron-xslt1',
+                 'iso_abstract_expand.xsl')))
+iso_svrl_for_xslt1 = _etree.XSLT(_etree.parse(
+    os.path.join(_resources_dir,
+                 'xsl', 'iso-schematron-xslt1', 'iso_svrl_for_xslt1.xsl')))
+
+
+# svrl result accessors
+svrl_validation_errors = _etree.XPath(
+    '//svrl:failed-assert', namespaces={'svrl': SVRL_NS})
+
+# RelaxNG validator for schematron schemas
+schematron_schema_valid_supported = False
+try:
+    schematron_schema_valid = _etree.RelaxNG(
+        file=os.path.join(_resources_dir, 'rng', 'iso-schematron.rng'))
+    schematron_schema_valid_supported = True
+except _etree.RelaxNGParseError:
+    # Some distributions delete the file due to licensing issues.
+    def schematron_schema_valid(arg):
+        raise NotImplementedError("Validating the ISO schematron requires iso-schematron.rng")
+
+
+def stylesheet_params(**kwargs):
+    """Convert keyword args to a dictionary of stylesheet parameters.
+    XSL stylesheet parameters must be XPath expressions, i.e.:
+
+    * string expressions, like "'5'"
+    * simple (number) expressions, like "5"
+    * valid XPath expressions, like "/a/b/text()"
+
+    This function converts native Python keyword arguments to stylesheet
+    parameters following these rules:
+    If an arg is a string wrap it with XSLT.strparam().
+    If an arg is an XPath object use its path string.
+    If arg is None raise TypeError.
+    Else convert arg to string.
+    """
+    result = {}
+    for key, val in kwargs.items():
+        if isinstance(val, basestring):
+            val = _etree.XSLT.strparam(val)
+        elif val is None:
+            raise TypeError('None not allowed as a stylesheet parameter')
+        elif not isinstance(val, _etree.XPath):
+            val = unicode(val)
+        result[key] = val
+    return result
+
+
+# helper function for use in Schematron __init__
+def _stylesheet_param_dict(paramsDict, kwargsDict):
+    """Return a copy of paramsDict, updated with kwargsDict entries, wrapped as
+    stylesheet arguments.
+    kwargsDict entries with a value of None are ignored.
+    """
+    # beware of changing mutable default arg
+    paramsDict = dict(paramsDict)
+    for k, v in kwargsDict.items():
+        if v is not None: # None values do not override
+            paramsDict[k] = v
+    paramsDict = stylesheet_params(**paramsDict)
+    return paramsDict
+
+
+class Schematron(_etree._Validator):
+    """An ISO Schematron validator.
+
+    Pass a root Element or an ElementTree to turn it into a validator.
+    Alternatively, pass a filename as keyword argument 'file' to parse from
+    the file system.
+
+    Schematron is a less well known, but very powerful schema language.
+    The main idea is to use the capabilities of XPath to put restrictions on
+    the structure and the content of XML documents.
+
+    The standard behaviour is to fail on ``failed-assert`` findings only
+    (``ASSERTS_ONLY``).  To change this, you can either pass a report filter
+    function to the ``error_finder`` parameter (e.g. ``ASSERTS_AND_REPORTS``
+    or a custom ``XPath`` object), or subclass isoschematron.Schematron for
+    complete control of the validation process.
+
+    Built on the Schematron language 'reference' skeleton pure-xslt
+    implementation, the validator is created as an XSLT 1.0 stylesheet using
+    these steps:
+
+     0) (Extract from XML Schema or RelaxNG schema)
+     1) Process inclusions
+     2) Process abstract patterns
+     3) Compile the schematron schema to XSLT
+
+    The ``include`` and ``expand`` keyword arguments can be used to switch off
+    steps 1) and 2).
+    To set parameters for steps 1), 2) and 3) hand parameter dictionaries to the
+    keyword arguments ``include_params``, ``expand_params`` or
+    ``compile_params``.
+    For convenience, the compile-step parameter ``phase`` is also exposed as a
+    keyword argument ``phase``. This takes precedence if the parameter is also
+    given in the parameter dictionary.
+
+    If ``store_schematron`` is set to True, the (included-and-expanded)
+    schematron document tree is stored and available through the ``schematron``
+    property.
+    If ``store_xslt`` is set to True, the validation XSLT document tree will be
+    stored and can be retrieved through the ``validator_xslt`` property.
+    With ``store_report`` set to True (default: False), the resulting validation
+    report document gets stored and can be accessed as the ``validation_report``
+    property.
+
+    If ``validate_schema`` is set to False, the validation of the schema file
+    itself is disabled.  Validation happens by default after building the full
+    schema, unless the schema validation file cannot be found at import time,
+    in which case the validation gets disabled.  Some lxml distributions exclude
+    this file due to licensing issues.  ISO-Schematron validation can then still
+    be used normally, but the schemas themselves cannot be validated.
+
+    Here is a usage example::
+
+      >>> from lxml import etree
+      >>> from lxml.isoschematron import Schematron
+
+      >>> schematron = Schematron(etree.XML('''
+      ... <schema xmlns="http://purl.oclc.org/dsdl/schematron" >
+      ...   <pattern id="id_only_attribute">
+      ...     <title>id is the only permitted attribute name</title>
+      ...     <rule context="*">
+      ...       <report test="@*[not(name()='id')]">Attribute
+      ...         <name path="@*[not(name()='id')]"/> is forbidden<name/>
+      ...       </report>
+      ...     </rule>
+      ...   </pattern>
+      ... </schema>'''),
+      ... error_finder=Schematron.ASSERTS_AND_REPORTS)
+
+      >>> xml = etree.XML('''
+      ... <AAA name="aaa">
+      ...   <BBB id="bbb"/>
+      ...   <CCC color="ccc"/>
+      ... </AAA>
+      ... ''')
+
+      >>> schematron.validate(xml)
+      False
+
+      >>> xml = etree.XML('''
+      ... <AAA id="aaa">
+      ...   <BBB id="bbb"/>
+      ...   <CCC/>
+      ... </AAA>
+      ... ''')
+
+      >>> schematron.validate(xml)
+      True
+    """
+
+    # libxml2 error categorization for validation errors
+    _domain = _etree.ErrorDomains.SCHEMATRONV
+    _level = _etree.ErrorLevels.ERROR
+    _error_type = _etree.ErrorTypes.SCHEMATRONV_ASSERT
+
+    # convenience definitions for common behaviours
+    ASSERTS_ONLY = svrl_validation_errors  # Default
+    ASSERTS_AND_REPORTS = _etree.XPath(
+        '//svrl:failed-assert | //svrl:successful-report',
+        namespaces={'svrl': SVRL_NS})
+
+    def _extract(self, element):
+        """Extract embedded schematron schema from non-schematron host schema.
+        This method will only be called by __init__ if the given schema document
+        is not a schematron schema by itself.
+        Must return a schematron schema document tree or None.
+        """
+        schematron = None
+        if element.tag == _xml_schema_root:
+            schematron = self._extract_xsd(element)
+        elif element.nsmap.get(element.prefix) == RELAXNG_NS:
+            # RelaxNG does not have a single unique root element
+            schematron = self._extract_rng(element)
+        return schematron
+
+    # customization points
+    # etree.XSLT objects that provide the extract, include, expand, compile
+    # steps
+    _extract_xsd = extract_xsd
+    _extract_rng = extract_rng
+    _include = iso_dsdl_include
+    _expand = iso_abstract_expand
+    _compile = iso_svrl_for_xslt1
+
+    # etree.xpath object that determines input document validity when applied to
+    # the svrl result report; must return a list of result elements (empty if
+    # valid)
+    _validation_errors = ASSERTS_ONLY
+
+    def __init__(self, etree=None, file=None, include=True, expand=True,
+                 include_params={}, expand_params={}, compile_params={},
+                 store_schematron=False, store_xslt=False, store_report=False,
+                 phase=None, error_finder=ASSERTS_ONLY,
+                 validate_schema=schematron_schema_valid_supported):
+        super().__init__()
+
+        self._store_report = store_report
+        self._schematron = None
+        self._validator_xslt = None
+        self._validation_report = None
+        if error_finder is not self.ASSERTS_ONLY:
+            self._validation_errors = error_finder
+
+        # parse schema document, may be a schematron schema or an XML Schema or
+        # a RelaxNG schema with embedded schematron rules
+        root = None
+        try:
+            if etree is not None:
+                if _etree.iselement(etree):
+                    root = etree
+                else:
+                    root = etree.getroot()
+            elif file is not None:
+                root = _etree.parse(file).getroot()
+        except Exception:
+            raise _etree.SchematronParseError(
+                "No tree or file given: %s" % sys.exc_info()[1])
+        if root is None:
+            raise ValueError("Empty tree")
+        if root.tag == _schematron_root:
+            schematron = root
+        else:
+            schematron = self._extract(root)
+        if schematron is None:
+            raise _etree.SchematronParseError(
+                "Document is not a schematron schema or schematron-extractable")
+        # perform the iso-schematron skeleton implementation steps to get a
+        # validating xslt
+        if include:
+            schematron = self._include(schematron, **include_params)
+        if expand:
+            schematron = self._expand(schematron, **expand_params)
+        if validate_schema and not schematron_schema_valid(schematron):
+            raise _etree.SchematronParseError(
+                "invalid schematron schema: %s" %
+                schematron_schema_valid.error_log)
+        if store_schematron:
+            self._schematron = schematron
+        # add new compile keyword args here if exposing them
+        compile_kwargs = {'phase': phase}
+        compile_params = _stylesheet_param_dict(compile_params, compile_kwargs)
+        validator_xslt = self._compile(schematron, **compile_params)
+        if store_xslt:
+            self._validator_xslt = validator_xslt
+        self._validator = _etree.XSLT(validator_xslt)
+
+    def __call__(self, etree):
+        """Validate doc using Schematron.
+
+        Returns true if document is valid, false if not.
+        """
+        self._clear_error_log()
+        result = self._validator(etree)
+        if self._store_report:
+            self._validation_report = result
+        errors = self._validation_errors(result)
+        if errors:
+            if _etree.iselement(etree):
+                fname = etree.getroottree().docinfo.URL or '<file>'
+            else:
+                fname = etree.docinfo.URL or '<file>'
+            for error in errors:
+                # Does svrl report the line number, anywhere? Don't think so.
+                self._append_log_message(
+                    domain=self._domain, type=self._error_type,
+                    level=self._level, line=0,
+                    message=_etree.tostring(error, encoding='unicode'),
+                    filename=fname)
+            return False
+        return True
+
+    @property
+    def schematron(self):
+        """ISO-schematron schema document (None if object has been initialized
+        with store_schematron=False).
+        """
+        return self._schematron
+
+    @property
+    def validator_xslt(self):
+        """ISO-schematron skeleton implementation XSLT validator document (None
+        if object has been initialized with store_xslt=False).
+        """
+        return self._validator_xslt
+
+    @property
+    def validation_report(self):
+        """ISO-schematron validation result report (None if result-storing has
+        been turned off).
+        """
+        return self._validation_report
diff --git a/.venv/lib/python3.12/site-packages/lxml/isoschematron/resources/rng/iso-schematron.rng b/.venv/lib/python3.12/site-packages/lxml/isoschematron/resources/rng/iso-schematron.rng
new file mode 100644
index 00000000..a4f504af
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/isoschematron/resources/rng/iso-schematron.rng
@@ -0,0 +1,709 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright © ISO/IEC 2015 -->
+<!--
+  The following permission notice and disclaimer shall be included in all
+  copies of this XML schema ("the Schema"), and derivations of the Schema:
+  
+  Permission is hereby granted, free of charge in perpetuity, to any
+  person obtaining a copy of the Schema, to use, copy, modify, merge and
+  distribute free of charge, copies of the Schema for the purposes of
+  developing, implementing, installing and using software based on the
+  Schema, and to permit persons to whom the Schema is furnished to do so,
+  subject to the following conditions:
+  
+  THE SCHEMA IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+  OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+  ARISING FROM, OUT OF OR IN CONNECTION WITH THE SCHEMA OR THE USE OR
+  OTHER DEALINGS IN THE SCHEMA.
+  
+  In addition, any modified copy of the Schema shall include the following
+  notice:
+  
+  "THIS SCHEMA HAS BEEN MODIFIED FROM THE SCHEMA DEFINED IN ISO/IEC 19757-3,
+  AND SHOULD NOT BE INTERPRETED AS COMPLYING WITH THAT STANDARD".
+-->
+<grammar ns="http://purl.oclc.org/dsdl/schematron" xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+  <start>
+    <ref name="schema"/>
+  </start>
+  <!-- Element declarations -->
+  <define name="schema">
+    <element name="schema">
+      <optional>
+        <attribute name="id">
+          <data type="ID"/>
+        </attribute>
+      </optional>
+      <ref name="rich"/>
+      <optional>
+        <attribute name="schemaVersion">
+          <ref name="non-empty-string"/>
+        </attribute>
+      </optional>
+      <optional>
+        <attribute name="defaultPhase">
+          <data type="IDREF"/>
+        </attribute>
+      </optional>
+      <optional>
+        <attribute name="queryBinding">
+          <ref name="non-empty-string"/>
+        </attribute>
+      </optional>
+      <interleave>
+        <ref name="foreign"/>
+        <zeroOrMore>
+          <ref name="inclusion"/>
+        </zeroOrMore>
+        <group>
+          <optional>
+            <ref name="title"/>
+          </optional>
+          <zeroOrMore>
+            <ref name="ns"/>
+          </zeroOrMore>
+          <zeroOrMore>
+            <ref name="p"/>
+          </zeroOrMore>
+          <zeroOrMore>
+            <ref name="let"/>
+          </zeroOrMore>
+          <zeroOrMore>
+            <ref name="phase"/>
+          </zeroOrMore>
+          <oneOrMore>
+            <ref name="pattern"/>
+          </oneOrMore>
+          <zeroOrMore>
+            <ref name="p"/>
+          </zeroOrMore>
+          <optional>
+            <ref name="diagnostics"/>
+          </optional>
+          <optional>
+            <!-- edited (lxml): required in standard, optional here (since it can be empty anyway) -->
+            <ref name="properties"/>
+          </optional>
+        </group>
+      </interleave>
+    </element>
+  </define>
+  <define name="active">
+    <element name="active">
+      <attribute name="pattern">
+        <data type="IDREF"/>
+      </attribute>
+      <interleave>
+        <ref name="foreign"/>
+        <zeroOrMore>
+          <choice>
+            <text/>
+            <ref name="dir"/>
+            <ref name="emph"/>
+            <ref name="span"/>
+          </choice>
+        </zeroOrMore>
+      </interleave>
+    </element>
+  </define>
+  <define name="assert">
+    <element name="assert">
+      <attribute name="test">
+        <ref name="exprValue"/>
+      </attribute>
+      <optional>
+        <attribute name="flag">
+          <ref name="flagValue"/>
+        </attribute>
+      </optional>
+      <optional>
+        <attribute name="id">
+          <data type="ID"/>
+        </attribute>
+      </optional>
+      <optional>
+        <attribute name="diagnostics">
+          <data type="IDREFS"/>
+        </attribute>
+      </optional>
+      <optional>
+        <attribute name="properties">
+          <data type="IDREFS"/>
+        </attribute>
+      </optional>
+      <ref name="rich"/>
+      <ref name="linkable"/>
+      <interleave>
+        <ref name="foreign"/>
+        <zeroOrMore>
+          <choice>
+            <text/>
+            <ref name="name"/>
+            <ref name="value-of"/>
+            <ref name="emph"/>
+            <ref name="dir"/>
+            <ref name="span"/>
+          </choice>
+        </zeroOrMore>
+      </interleave>
+    </element>
+  </define>
+  <define name="diagnostic">
+    <element name="diagnostic">
+      <attribute name="id">
+        <data type="ID"/>
+      </attribute>
+      <ref name="rich"/>
+      <interleave>
+        <ref name="foreign"/>
+        <zeroOrMore>
+          <choice>
+            <text/>
+            <ref name="value-of"/>
+            <ref name="emph"/>
+            <ref name="dir"/>
+            <ref name="span"/>
+          </choice>
+        </zeroOrMore>
+      </interleave>
+    </element>
+  </define>
+  <define name="diagnostics">
+    <element name="diagnostics">
+      <interleave>
+        <ref name="foreign"/>
+        <zeroOrMore>
+          <ref name="inclusion"/>
+        </zeroOrMore>
+        <zeroOrMore>
+          <ref name="diagnostic"/>
+        </zeroOrMore>
+      </interleave>
+    </element>
+  </define>
+  <define name="dir">
+    <element name="dir">
+      <optional>
+        <attribute name="value">
+          <choice>
+            <value>ltr</value>
+            <value>rtl</value>
+          </choice>
+        </attribute>
+      </optional>
+      <interleave>
+        <ref name="foreign"/>
+        <text/>
+      </interleave>
+    </element>
+  </define>
+  <define name="emph">
+    <element name="emph">
+      <text/>
+    </element>
+  </define>
+  <define name="extends">
+    <element name="extends">
+      <choice>
+        <attribute name="rule">
+          <data type="IDREF"/>
+        </attribute>
+        <attribute name="href">
+          <ref name="uriValue"/>
+        </attribute>
+      </choice>
+      <ref name="foreign-empty"/>
+    </element>
+  </define>
+  <define name="let">
+    <element name="let">
+      <attribute name="name">
+        <ref name="nameValue"/>
+      </attribute>
+      <choice>
+        <attribute name="value">
+          <data type="string" datatypeLibrary=""/>
+        </attribute>
+        <oneOrMore>
+          <ref name="foreign-element"/>
+        </oneOrMore>
+      </choice>
+    </element>
+  </define>
+  <define name="name">
+    <element name="name">
+      <optional>
+        <attribute name="path">
+          <ref name="pathValue"/>
+        </attribute>
+      </optional>
+      <ref name="foreign-empty"/>
+    </element>
+  </define>
+  <define name="ns">
+    <element name="ns">
+      <attribute name="uri">
+        <ref name="uriValue"/>
+      </attribute>
+      <attribute name="prefix">
+        <ref name="nameValue"/>
+      </attribute>
+      <ref name="foreign-empty"/>
+    </element>
+  </define>
+  <define name="p">
+    <element name="p">
+      <optional>
+        <attribute name="id">
+          <data type="ID"/>
+        </attribute>
+      </optional>
+      <optional>
+        <attribute name="class">
+          <ref name="classValue"/>
+        </attribute>
+      </optional>
+      <optional>
+        <attribute name="icon">
+          <ref name="uriValue"/>
+        </attribute>
+      </optional>
+      <interleave>
+        <ref name="foreign"/>
+        <zeroOrMore>
+          <choice>
+            <text/>
+            <ref name="dir"/>
+            <ref name="emph"/>
+            <ref name="span"/>
+          </choice>
+        </zeroOrMore>
+      </interleave>
+    </element>
+  </define>
+  <define name="param">
+    <element name="param">
+      <attribute name="name">
+        <ref name="nameValue"/>
+      </attribute>
+      <attribute name="value">
+        <ref name="non-empty-string"/>
+      </attribute>
+    </element>
+  </define>
+  <define name="pattern">
+    <element name="pattern">
+      <optional>
+        <attribute name="documents">
+          <ref name="pathValue"/>
+        </attribute>
+      </optional>
+      <ref name="rich"/>
+      <interleave>
+        <ref name="foreign"/>
+        <zeroOrMore>
+          <ref name="inclusion"/>
+        </zeroOrMore>
+        <choice>
+          <group>
+            <attribute name="abstract">
+              <value>true</value>
+            </attribute>
+            <attribute name="id">
+              <data type="ID"/>
+            </attribute>
+            <optional>
+              <ref name="title"/>
+            </optional>
+            <group>
+              <zeroOrMore>
+                <ref name="p"/>
+              </zeroOrMore>
+              <zeroOrMore>
+                <ref name="let"/>
+              </zeroOrMore>
+              <zeroOrMore>
+                <ref name="rule"/>
+              </zeroOrMore>
+            </group>
+          </group>
+          <group>
+            <optional>
+              <attribute name="abstract">
+                <value>false</value>
+              </attribute>
+            </optional>
+            <optional>
+              <attribute name="id">
+                <data type="ID"/>
+              </attribute>
+            </optional>
+            <optional>
+              <ref name="title"/>
+            </optional>
+            <group>
+              <zeroOrMore>
+                <ref name="p"/>
+              </zeroOrMore>
+              <zeroOrMore>
+                <ref name="let"/>
+              </zeroOrMore>
+              <zeroOrMore>
+                <ref name="rule"/>
+              </zeroOrMore>
+            </group>
+          </group>
+          <group>
+            <optional>
+              <attribute name="abstract">
+                <value>false</value>
+              </attribute>
+            </optional>
+            <attribute name="is-a">
+              <data type="IDREF"/>
+            </attribute>
+            <optional>
+              <attribute name="id">
+                <data type="ID"/>
+              </attribute>
+            </optional>
+            <optional>
+              <ref name="title"/>
+            </optional>
+            <group>
+              <zeroOrMore>
+                <ref name="p"/>
+              </zeroOrMore>
+              <zeroOrMore>
+                <ref name="param"/>
+              </zeroOrMore>
+            </group>
+          </group>
+        </choice>
+      </interleave>
+    </element>
+  </define>
+  <define name="phase">
+    <element name="phase">
+      <attribute name="id">
+        <data type="ID"/>
+      </attribute>
+      <ref name="rich"/>
+      <interleave>
+        <ref name="foreign"/>
+        <zeroOrMore>
+          <ref name="inclusion"/>
+        </zeroOrMore>
+        <group>
+          <zeroOrMore>
+            <ref name="p"/>
+          </zeroOrMore>
+          <zeroOrMore>
+            <ref name="let"/>
+          </zeroOrMore>
+          <zeroOrMore>
+            <ref name="active"/>
+          </zeroOrMore>
+        </group>
+      </interleave>
+    </element>
+  </define>
+  <define name="properties">
+    <element name="properties">
+      <zeroOrMore>
+        <ref name="property"/>
+      </zeroOrMore>
+    </element>
+  </define>
+  <define name="property">
+    <element name="property">
+      <attribute name="id">
+        <data type="ID"/>
+      </attribute>
+      <optional>
+        <attribute name="role">
+          <ref name="roleValue"/>
+        </attribute>
+      </optional>
+      <optional>
+        <attribute name="scheme"/>
+      </optional>
+      <interleave>
+        <ref name="foreign"/>
+        <zeroOrMore>
+          <choice>
+            <text/>
+            <ref name="name"/>
+            <ref name="value-of"/>
+            <ref name="emph"/>
+            <ref name="dir"/>
+            <ref name="span"/>
+          </choice>
+        </zeroOrMore>
+      </interleave>
+    </element>
+  </define>
+  <define name="report">
+    <element name="report">
+      <attribute name="test">
+        <ref name="exprValue"/>
+      </attribute>
+      <optional>
+        <attribute name="flag">
+          <ref name="flagValue"/>
+        </attribute>
+      </optional>
+      <optional>
+        <attribute name="id">
+          <data type="ID"/>
+        </attribute>
+      </optional>
+      <optional>
+        <attribute name="diagnostics">
+          <data type="IDREFS"/>
+        </attribute>
+      </optional>
+      <optional>
+        <attribute name="properties">
+          <data type="IDREFS"/>
+        </attribute>
+      </optional>
+      <ref name="rich"/>
+      <ref name="linkable"/>
+      <interleave>
+        <ref name="foreign"/>
+        <zeroOrMore>
+          <choice>
+            <text/>
+            <ref name="name"/>
+            <ref name="value-of"/>
+            <ref name="emph"/>
+            <ref name="dir"/>
+            <ref name="span"/>
+          </choice>
+        </zeroOrMore>
+      </interleave>
+    </element>
+  </define>
+  <define name="rule">
+    <element name="rule">
+      <optional>
+        <attribute name="flag">
+          <ref name="flagValue"/>
+        </attribute>
+      </optional>
+      <ref name="rich"/>
+      <ref name="linkable"/>
+      <interleave>
+        <ref name="foreign"/>
+        <zeroOrMore>
+          <ref name="inclusion"/>
+        </zeroOrMore>
+        <choice>
+          <group>
+            <attribute name="abstract">
+              <value>true</value>
+            </attribute>
+            <attribute name="id">
+              <data type="ID"/>
+            </attribute>
+            <zeroOrMore>
+              <ref name="let"/>
+            </zeroOrMore>
+            <oneOrMore>
+              <choice>
+                <ref name="assert"/>
+                <ref name="report"/>
+                <ref name="extends"/>
+                <ref name="p"/>
+              </choice>
+            </oneOrMore>
+          </group>
+          <group>
+            <attribute name="context">
+              <ref name="pathValue"/>
+            </attribute>
+            <optional>
+              <attribute name="id">
+                <data type="ID"/>
+              </attribute>
+            </optional>
+            <optional>
+              <attribute name="abstract">
+                <value>false</value>
+              </attribute>
+            </optional>
+            <zeroOrMore>
+              <ref name="let"/>
+            </zeroOrMore>
+            <oneOrMore>
+              <choice>
+                <ref name="assert"/>
+                <ref name="report"/>
+                <ref name="extends"/>
+                <ref name="p"/>
+              </choice>
+            </oneOrMore>
+          </group>
+        </choice>
+      </interleave>
+    </element>
+  </define>
+  <define name="span">
+    <element name="span">
+      <attribute name="class">
+        <ref name="classValue"/>
+      </attribute>
+      <interleave>
+        <ref name="foreign"/>
+        <text/>
+      </interleave>
+    </element>
+  </define>
+  <define name="title">
+    <element name="title">
+      <zeroOrMore>
+        <choice>
+          <text/>
+          <ref name="dir"/>
+        </choice>
+      </zeroOrMore>
+    </element>
+  </define>
+  <define name="value-of">
+    <element name="value-of">
+      <attribute name="select">
+        <ref name="pathValue"/>
+      </attribute>
+      <ref name="foreign-empty"/>
+    </element>
+  </define>
+  <!-- common declarations -->
+  <define name="inclusion">
+    <element name="include">
+      <attribute name="href">
+        <ref name="uriValue"/>
+      </attribute>
+      <ref name="foreign-empty"/>
+    </element>
+  </define>
+  <define name="rich">
+    <optional>
+      <attribute name="icon">
+        <ref name="uriValue"/>
+      </attribute>
+    </optional>
+    <optional>
+      <attribute name="see">
+        <ref name="uriValue"/>
+      </attribute>
+    </optional>
+    <optional>
+      <attribute name="fpi">
+        <ref name="fpiValue"/>
+      </attribute>
+    </optional>
+    <optional>
+      <attribute name="xml:lang">
+        <ref name="langValue"/>
+      </attribute>
+    </optional>
+    <optional>
+      <attribute name="xml:space">
+        <choice>
+          <value>preserve</value>
+          <value>default</value>
+        </choice>
+      </attribute>
+    </optional>
+  </define>
+  <define name="linkable">
+    <optional>
+      <attribute name="role">
+        <ref name="roleValue"/>
+      </attribute>
+    </optional>
+    <optional>
+      <attribute name="subject">
+        <ref name="pathValue"/>
+      </attribute>
+    </optional>
+  </define>
+  <define name="foreign">
+    <ref name="foreign-attributes"/>
+    <zeroOrMore>
+      <ref name="foreign-element"/>
+    </zeroOrMore>
+  </define>
+  <define name="foreign-empty">
+    <ref name="foreign-attributes"/>
+  </define>
+  <define name="foreign-attributes">
+    <zeroOrMore>
+      <attribute>
+        <anyName>
+          <except>
+            <nsName ns=""/>
+            <nsName ns="http://www.w3.org/XML/1998/namespace"/>
+          </except>
+        </anyName>
+      </attribute>
+    </zeroOrMore>
+  </define>
+  <define name="foreign-element">
+    <element>
+      <anyName>
+        <except>
+          <nsName/>
+        </except>
+      </anyName>
+      <zeroOrMore>
+        <choice>
+          <attribute>
+            <anyName/>
+          </attribute>
+          <ref name="foreign-element"/>
+          <ref name="schema"/>
+          <text/>
+        </choice>
+      </zeroOrMore>
+    </element>
+  </define>
+  <!-- Data types -->
+  <define name="uriValue">
+    <data type="anyURI"/>
+  </define>
+  <define name="pathValue">
+    <data type="string" datatypeLibrary=""/>
+  </define>
+  <define name="exprValue">
+    <data type="string" datatypeLibrary=""/>
+  </define>
+  <define name="fpiValue">
+    <data type="string" datatypeLibrary=""/>
+  </define>
+  <define name="langValue">
+    <data type="language"/>
+  </define>
+  <define name="roleValue">
+    <data type="string" datatypeLibrary=""/>
+  </define>
+  <define name="flagValue">
+    <data type="string" datatypeLibrary=""/>
+  </define>
+  <define name="nameValue">
+    <data type="string" datatypeLibrary=""/>
+  </define>
+  <!-- In the default query language binding, xsd:NCNAME -->
+  <define name="classValue">
+    <data type="string" datatypeLibrary=""/>
+  </define>
+  <define name="non-empty-string">
+    <data type="token">
+      <param name="minLength">1</param>
+    </data>
+  </define>
+</grammar>
diff --git a/.venv/lib/python3.12/site-packages/lxml/isoschematron/resources/xsl/RNG2Schtrn.xsl b/.venv/lib/python3.12/site-packages/lxml/isoschematron/resources/xsl/RNG2Schtrn.xsl
new file mode 100644
index 00000000..21a5d2a0
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/isoschematron/resources/xsl/RNG2Schtrn.xsl
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+	Stylesheet for extracting Schematron information from a RELAX-NG schema.
+	Based on the stylesheet for extracting Schematron information from W3C XML Schema.
+	Created by Eddie Robertsson 2002/06/01
+        2009/12/10      hj: changed Schematron namespace to ISO URI (Holger Joukl)
+-->
+<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
+xmlns:sch="http://purl.oclc.org/dsdl/schematron" xmlns:rng="http://relaxng.org/ns/structure/1.0">
+	<!-- Set the output to be XML with an XML declaration and use indentation -->
+	<xsl:output method="xml" omit-xml-declaration="no" indent="yes" standalone="yes"/>
+	<!-- -->
+	<!-- match schema and call recursive template to extract included schemas -->
+	<!-- -->
+	<xsl:template match="/rng:grammar | /rng:element">
+		<!-- call the schema definition template ... -->
+		<xsl:call-template name="gatherSchema">
+			<!-- ... with current node as the $schemas parameter ... -->
+			<xsl:with-param name="schemas" select="."/>
+			<!-- ... and any includes in the $include parameter -->
+			<xsl:with-param name="includes" select="document(/rng:grammar/rng:include/@href
+| //rng:externalRef/@href)"/>
+		</xsl:call-template>
+	</xsl:template>
+	<!-- -->
+	<!-- gather all included schemas into a single parameter variable -->
+	<!-- -->
+	<xsl:template name="gatherSchema">
+		<xsl:param name="schemas"/>
+		<xsl:param name="includes"/>
+		<xsl:choose>
+			<xsl:when test="count($schemas) &lt; count($schemas | $includes)">
+				<!-- when $includes includes something new, recurse ... -->
+				<xsl:call-template name="gatherSchema">
+					<!-- ... with current $includes added to the $schemas parameter ... -->
+					<xsl:with-param name="schemas" select="$schemas | $includes"/>
+					<!-- ... and any *new* includes in the $include parameter -->
+					<xsl:with-param name="includes" select="document($includes/rng:grammar/rng:include/@href
+| $includes//rng:externalRef/@href)"/>
+				</xsl:call-template>
+			</xsl:when>
+			<xsl:otherwise>
+				<!-- we have the complete set of included schemas, so now let's output the embedded schematron -->
+				<xsl:call-template name="output">
+					<xsl:with-param name="schemas" select="$schemas"/>
+				</xsl:call-template>
+			</xsl:otherwise>
+		</xsl:choose>
+	</xsl:template>
+	<!-- -->
+	<!-- output the schematron information -->
+	<!-- -->
+	<xsl:template name="output">
+		<xsl:param name="schemas"/>
+		<!-- -->
+		<sch:schema>
+			<!-- get header-type elements - eg title and especially ns -->
+			<!-- title (just one) -->
+			<xsl:copy-of select="$schemas//sch:title[1]"/>
+			<!-- get remaining schematron schema children -->
+			<!-- get non-blank namespace elements, dropping duplicates -->
+			<xsl:for-each select="$schemas//sch:ns">
+				<xsl:if test="generate-id(.) = generate-id($schemas//sch:ns[@prefix = current()/@prefix][1])">
+					<xsl:copy-of select="."/>
+				</xsl:if>
+			</xsl:for-each>
+			<xsl:copy-of select="$schemas//sch:phase"/>
+			<xsl:copy-of select="$schemas//sch:pattern"/>
+			<sch:diagnostics>
+				<xsl:copy-of select="$schemas//sch:diagnostics/*"/>
+			</sch:diagnostics>
+		</sch:schema>
+	</xsl:template>
+	<!-- -->
+</xsl:transform>
diff --git a/.venv/lib/python3.12/site-packages/lxml/isoschematron/resources/xsl/XSD2Schtrn.xsl b/.venv/lib/python3.12/site-packages/lxml/isoschematron/resources/xsl/XSD2Schtrn.xsl
new file mode 100644
index 00000000..de0c9ea7
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/isoschematron/resources/xsl/XSD2Schtrn.xsl
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<!--

+        based on an original transform by Eddie Robertsson

+        2001/04/21      fn: added support for included schemas

+        2001/06/27      er: changed XMl Schema prefix from xsd: to xs: and changed to the Rec namespace

+        2009/12/10      hj: changed Schematron namespace to ISO URI (Holger Joukl)

+-->

+<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 

+xmlns:sch="http://purl.oclc.org/dsdl/schematron" xmlns:xs="http://www.w3.org/2001/XMLSchema">

+        <!-- Set the output to be XML with an XML declaration and use indentation -->

+        <xsl:output method="xml" omit-xml-declaration="no" indent="yes" standalone="yes"/>

+        <!-- -->

+        <!-- match schema and call recursive template to extract included schemas -->

+        <!-- -->

+        <xsl:template match="xs:schema">

+                <!-- call the schema definition template ... -->

+                <xsl:call-template name="gatherSchema">

+                        <!-- ... with current current root as the $schemas parameter ... -->

+                        <xsl:with-param name="schemas" select="/"/>

+                        <!-- ... and any includes in the $include parameter -->

+                        <xsl:with-param name="includes" 

+						select="document(/xs:schema/xs:*[self::xs:include or self::xs:import or self::xs:redefine]/@schemaLocation)"/>

+                </xsl:call-template>

+        </xsl:template>

+        <!-- -->

+        <!-- gather all included schemas into a single parameter variable -->

+        <!-- -->

+        <xsl:template name="gatherSchema">

+                <xsl:param name="schemas"/>

+                <xsl:param name="includes"/>

+                <xsl:choose>

+                        <xsl:when test="count($schemas) &lt; count($schemas | $includes)">

+                                <!-- when $includes includes something new, recurse ... -->

+                                <xsl:call-template name="gatherSchema">

+                                        <!-- ... with current $includes added to the $schemas parameter ... -->

+                                        <xsl:with-param name="schemas" select="$schemas | $includes"/>

+                                        <!-- ... and any *new* includes in the $include parameter -->

+                                        <xsl:with-param name="includes" 

+										select="document($includes/xs:schema/xs:*[self::xs:include or self::xs:import or self::xs:redefine]/@schemaLocation)"/>

+                                </xsl:call-template>

+                        </xsl:when>

+                        <xsl:otherwise>

+                                <!-- we have the complete set of included schemas, 

+								so now let's output the embedded schematron -->

+                                <xsl:call-template name="output">

+                                        <xsl:with-param name="schemas" select="$schemas"/>

+                                </xsl:call-template>

+                        </xsl:otherwise>

+                </xsl:choose>

+        </xsl:template>

+        <!-- -->

+        <!-- output the schematron information -->

+        <!-- -->

+        <xsl:template name="output">

+                <xsl:param name="schemas"/>

+                <!-- -->

+                <sch:schema>

+                        <!-- get header-type elements - eg title and especially ns -->

+                        <!-- title (just one) -->

+                        <xsl:copy-of select="$schemas//xs:appinfo/sch:title[1]"/>

+                        <!-- get remaining schematron schema children -->

+                        <!-- get non-blank namespace elements, dropping duplicates -->

+                        <xsl:for-each select="$schemas//xs:appinfo/sch:ns">

+                                <xsl:if test="generate-id(.) = 

+								generate-id($schemas//xs:appinfo/sch:ns[@prefix = current()/@prefix][1])">

+                                        <xsl:copy-of select="."/>

+                                </xsl:if>

+                        </xsl:for-each>

+                        <xsl:copy-of select="$schemas//xs:appinfo/sch:phase"/>

+                        <xsl:copy-of select="$schemas//xs:appinfo/sch:pattern"/>

+                        <sch:diagnostics>

+                                <xsl:copy-of select="$schemas//xs:appinfo/sch:diagnostics/*"/>

+                        </sch:diagnostics>

+                </sch:schema>

+        </xsl:template>

+        <!-- -->

+</xsl:transform>

diff --git a/.venv/lib/python3.12/site-packages/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_abstract_expand.xsl b/.venv/lib/python3.12/site-packages/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_abstract_expand.xsl
new file mode 100644
index 00000000..50183952
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_abstract_expand.xsl
@@ -0,0 +1,313 @@
+<?xml version="1.0" encoding="UTF-8"?><?xar XSLT?>
+
+<!-- 
+     OVERVIEW - iso_abstract_expand.xsl
+     
+	    This is a preprocessor for ISO Schematron, which implements abstract patterns. 
+	    It also 
+	       	* extracts a particular schema using an ID, where there are multiple 
+             schemas, such as when they are embedded in the same NVDL script 
+           * allows parameter substitution inside @context, @test, @select, @path
+	    	   * experimentally, allows parameter recognition and substitution inside
+             text (NOTE: to be removed, for compataibility with other implementations,   
+             please do not use this) 
+		
+		This should be used after iso-dsdl-include.xsl and before the skeleton or
+		meta-stylesheet (e.g. iso-svrl.xsl) . It only requires XSLT 1.
+		 
+		Each kind of inclusion can be turned off (or on) on the command line.
+		 
+-->
+
+<!--
+Open Source Initiative OSI - The MIT License:Licensing
+[OSI Approved License]
+
+This source code was previously available under the zlib/libpng license. 
+Attribution is polite.
+
+The MIT License
+
+Copyright (c) 2004-2010  Rick Jellife and Academia Sinica Computing Centre, Taiwan
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+-->
+
+<!--
+VERSION INFORMATION
+  2013-09-19 RJ
+     * Allow macro expansion in  @path attributes, eg. for   sch:name/@path
+
+  2010-07-10 RJ
+  		* Move to MIT license
+  		
+  2008-09-18 RJ
+  		* move out param test from iso:schema template  to work with XSLT 1. (Noah Fontes)
+  		
+  2008-07-29 RJ 
+  		* Create.  Pull out as distinct XSL in its own namespace from old iso_pre_pro.xsl
+  		* Put everything in private namespace
+  		* Rewrite replace_substring named template so that copyright is clear
+  	
+  2008-07-24 RJ
+       * correct abstract patterns so for correct names: param/@name and
+     param/@value
+    
+  2007-01-12  RJ 
+     * Use ISO namespace
+     * Use pattern/@id not  pattern/@name 
+     * Add Oliver Becker's suggests from old Schematron-love-in list for <copy> 
+     * Add XT -ism?
+  2003 RJ
+     * Original written for old namespace
+     * http://www.topologi.com/resources/iso-pre-pro.xsl
+-->	
+<xslt:stylesheet version="1.0" xmlns:xslt="http://www.w3.org/1999/XSL/Transform" 
+	xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
+    xmlns:iso="http://purl.oclc.org/dsdl/schematron"  
+    xmlns:nvdl="http://purl.oclc.org/dsdl/nvdl"  
+    
+    xmlns:iae="http://www.schematron.com/namespace/iae" 
+     
+      >
+	
+	<xslt:param name="schema-id"></xslt:param>
+	
+	
+	<!-- Driver for the mode -->
+	<xsl:template match="/">
+  		<xsl:apply-templates select="." mode="iae:go" />
+	</xsl:template> 
+	
+	
+	<!-- ================================================================================== -->
+	<!-- Normal processing rules                                                            -->
+	<!-- ================================================================================== -->
+	<!-- Output only the selected schema --> 
+	<xslt:template match="iso:schema" >
+	    <xsl:if test="string-length($schema-id) =0 or @id= $schema-id ">
+	    	<xslt:copy>
+				<xslt:copy-of select="@*" />
+				<xslt:apply-templates  mode="iae:go" /> 
+			</xslt:copy>
+		</xsl:if>
+	</xslt:template>
+	
+ 
+	<!-- Strip out any foreign elements above the Schematron schema .
+		-->
+	<xslt:template match="*[not(ancestor-or-self::iso:*)]"     mode="iae:go"  >
+	   <xslt:apply-templates  mode="iae:go" />
+	</xslt:template>
+	   
+	
+	<!-- ================================================================================== -->
+	<!-- Handle Schematron abstract pattern preprocessing                                   -->
+	<!-- abstract-to-real calls
+			do-pattern calls 
+				macro-expand calls 
+					multi-macro-expand
+						replace-substring                                                   -->
+	<!-- ================================================================================== -->
+	
+	<!--
+		Abstract patterns allow you to say, for example
+		
+		<pattern name="htmlTable" is-a="table">
+			<param name="row" value="html:tr"/>
+			<param name="cell" value="html:td" />
+			<param name="table" value="html:table" />
+		</pattern>
+		
+		For a good introduction, see Uche Ogbujii's article for IBM DeveloperWorks
+		"Discover the flexibility of Schematron abstract patterns"
+		  http://www-128.ibm.com/developerworks/xml/library/x-stron.html
+		However, note that ISO Schematron uses @name and @value attributes on
+		the iso:param element, and @id not @name on the pattern element.
+		
+	-->
+	
+	<!-- Suppress declarations of abstract patterns -->
+	<xslt:template match="iso:pattern[@abstract='true']"  mode="iae:go"  >
+		<xslt:comment>Suppressed abstract pattern <xslt:value-of select="@id"/> was here</xslt:comment>	
+	</xslt:template> 
+	
+	
+	<!-- Suppress uses of abstract patterns -->
+	<xslt:template match="iso:pattern[@is-a]"  mode="iae:go" >
+			
+		<xslt:comment>Start pattern based on abstract <xslt:value-of select="@is-a"/></xslt:comment>
+		
+		<xslt:call-template name="iae:abstract-to-real" >
+			<xslt:with-param name="caller" select="@id" />
+			<xslt:with-param name="is-a" select="@is-a" />
+		</xslt:call-template>
+			
+	</xslt:template>
+	 
+	 
+	
+	<!-- output everything else unchanged -->
+	<xslt:template match="*" priority="-1"  mode="iae:go" >
+	    <xslt:copy>
+			<xslt:copy-of select="@*" />
+			<xslt:apply-templates mode="iae:go"/> 
+		</xslt:copy>
+	</xslt:template>
+	
+	<!-- Templates for macro expansion of abstract patterns -->
+	<!-- Sets up the initial conditions for the recursive call -->
+	<xslt:template name="iae:macro-expand">
+		<xslt:param name="caller"/>
+		<xslt:param name="text" />
+		<xslt:call-template name="iae:multi-macro-expand">
+			<xslt:with-param name="caller" select="$caller"/>
+			<xslt:with-param name="text" select="$text"/>
+			<xslt:with-param name="paramNumber" select="1"/>
+		</xslt:call-template>
+		
+	</xslt:template>
+	
+	<!-- Template to replace the current parameter and then
+	   recurse to replace subsequent parameters. -->
+	    
+	<xslt:template name="iae:multi-macro-expand">
+		<xslt:param name="caller"/>
+		<xslt:param name="text" />
+		<xslt:param name="paramNumber" />
+
+		
+		<xslt:choose>
+			<xslt:when test="//iso:pattern[@id=$caller]/iso:param[ $paramNumber]">
+
+				<xslt:call-template name="iae:multi-macro-expand">
+					<xslt:with-param name="caller" select="$caller"/>	
+					<xslt:with-param name="paramNumber" select="$paramNumber + 1"/>		
+					<xslt:with-param name="text" >
+						<xslt:call-template name="iae:replace-substring">
+							<xslt:with-param name="original" select="$text"/>
+							<xslt:with-param name="substring"
+							select="concat('$', //iso:pattern[@id=$caller]/iso:param[ $paramNumber ]/@name)"/>
+							<xslt:with-param name="replacement"
+								select="//iso:pattern[@id=$caller]/iso:param[ $paramNumber ]/@value"/>			
+						</xslt:call-template>
+					</xslt:with-param>						
+				</xslt:call-template>
+			</xslt:when>
+			<xslt:otherwise><xslt:value-of select="$text" /></xslt:otherwise>		
+		
+		</xslt:choose>
+	</xslt:template>
+	
+	
+	<!-- generate the real pattern from an abstract pattern + parameters-->
+	<xslt:template name="iae:abstract-to-real" >
+		<xslt:param name="caller"/>
+		<xslt:param name="is-a" />
+		<xslt:for-each select="//iso:pattern[@id= $is-a]">
+		<xslt:copy>
+		
+		    <xslt:choose>
+		      <xslt:when test=" string-length( $caller ) = 0">
+		      <xslt:attribute name="id"><xslt:value-of select="concat( generate-id(.) , $is-a)" /></xslt:attribute>
+		      </xslt:when>
+		      <xslt:otherwise>
+				<xslt:attribute name="id"><xslt:value-of select="$caller" /></xslt:attribute>
+		      </xslt:otherwise>
+		    </xslt:choose> 
+			
+			<xslt:apply-templates select="*|text()" mode="iae:do-pattern"    >
+				<xslt:with-param name="caller"><xslt:value-of select="$caller"/></xslt:with-param>
+			</xslt:apply-templates>	
+			
+		</xslt:copy>
+		</xslt:for-each>
+	</xslt:template>
+		
+	
+	<!-- Generate a non-abstract pattern -->
+	<xslt:template mode="iae:do-pattern" match="*">
+		<xslt:param name="caller"/>
+		<xslt:copy>
+			<xslt:for-each select="@*[name()='test' or name()='context' or name()='select'   or name()='path'  ]">
+				<xslt:attribute name="{name()}">
+				<xslt:call-template name="iae:macro-expand">
+						<xslt:with-param name="text"><xslt:value-of select="."/></xslt:with-param>
+						<xslt:with-param name="caller"><xslt:value-of select="$caller"/></xslt:with-param>
+					</xslt:call-template>
+				</xslt:attribute>
+			</xslt:for-each>	
+			<xslt:copy-of select="@*[name()!='test'][name()!='context'][name()!='select'][name()!='path']" />
+			<xsl:for-each select="node()">
+				<xsl:choose>
+				    <!-- Experiment: replace macros in text as well, to allow parameterized assertions
+				        and so on, without having to have spurious <iso:value-of> calls and multiple
+				        delimiting.
+                NOTE: THIS FUNCTIONALITY WILL BE REMOVED IN THE FUTURE    -->
+					<xsl:when test="self::text()">	
+						<xslt:call-template name="iae:macro-expand">
+							<xslt:with-param name="text"><xslt:value-of select="."/></xslt:with-param>
+							<xslt:with-param name="caller"><xslt:value-of select="$caller"/></xslt:with-param>
+						</xslt:call-template>
+					</xsl:when>
+					<xsl:otherwise>
+						<xslt:apply-templates select="." mode="iae:do-pattern">
+							<xslt:with-param name="caller"><xslt:value-of select="$caller"/></xslt:with-param>
+						</xslt:apply-templates>		
+					</xsl:otherwise>
+				</xsl:choose>
+			</xsl:for-each>			
+		</xslt:copy>
+	</xslt:template>
+	
+	<!-- UTILITIES --> 
+	<!-- Simple version of replace-substring function -->
+	<xslt:template name="iae:replace-substring">
+		<xslt:param name="original" />    
+		<xslt:param name="substring" />   
+		<xslt:param name="replacement" select="''"/>
+		
+  <xsl:choose>
+    <xsl:when test="not($original)" /> 
+    <xsl:when test="not(string($substring))">
+      <xsl:value-of select="$original" />
+    </xsl:when> 
+        <xsl:when test="contains($original, $substring)">
+          <xsl:variable name="before" select="substring-before($original, $substring)" />
+          <xsl:variable name="after" select="substring-after($original, $substring)" />
+          
+          <xsl:value-of select="$before" />
+          <xsl:value-of select="$replacement" />
+          <!-- recursion -->
+          <xsl:call-template name="iae:replace-substring">
+            <xsl:with-param name="original" select="$after" />
+            <xsl:with-param name="substring" select="$substring" />
+            <xsl:with-param name="replacement" select="$replacement" /> 
+            </xsl:call-template>
+        </xsl:when>
+        <xsl:otherwise>
+        	<!-- no substitution -->
+        	<xsl:value-of select="$original" />
+        </xsl:otherwise>
+      </xsl:choose> 
+</xslt:template>
+
+
+
+</xslt:stylesheet>
\ No newline at end of file
diff --git a/.venv/lib/python3.12/site-packages/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_dsdl_include.xsl b/.venv/lib/python3.12/site-packages/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_dsdl_include.xsl
new file mode 100644
index 00000000..44e5573b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_dsdl_include.xsl
@@ -0,0 +1,1160 @@
+<?xml version="1.0" encoding="UTF-8"?><?xar XSLT?>
+
+<!-- 
+     OVERVIEW : iso_dsdl_include.xsl
+     
+	    This is an inclusion preprocessor for the non-smart text inclusions
+	    of ISO DSDL. It handles 
+	    	<relax:extRef> for ISO RELAX NG
+	    	<sch:include>  for ISO Schematron and Schematron 1.n
+	    	<sch:extends>  for 2009 draft ISO Schematron
+	    	<xi:xinclude>  simple W3C XIncludes for ISO NVRL and DSRL 
+	    	<crdl:ref>     for draft ISO CRDL
+	    	<dtll:include> for draft ISO DTLL
+	    	<* @xlink:href> for simple W3C XLink 1.1 embedded links
+	    	
+		 
+		This should be the first in any chain of processing. It only requires
+		XSLT 1. Each kind of inclusion can be turned off (or on) on the command line.
+		
+		Ids in fragment identifiers or xpointers will be sought in the following
+		order:
+		    * @xml:id
+		    * id() for typed schemas (e.g. from DTD) [NOTE: XInclude does not support this]
+		    * untyped @id 
+		    
+	The proposed behaviour for the update to ISO Schematron has been implemented. If an
+	include points to an element with the same name as the parent, then that element's
+	contents will be included. This supports the merge style of inclusion.    
+	
+	When an inclusion is made, it is preceded by a PI with target DSDL_INCLUDE_START
+	and the href and closed by a PI with target DSDL_INCLUDE_START and the href. This is
+	to allow better location of problems, though only to the file level. 
+	
+	Limitations:
+	* No rebasing: relative paths will be interpreted based on the initial document's
+	path, not the including document. (Severe limitation!)
+	* No checking for circular references
+	* Not full xpointers: only ID matching
+	* <relax:include> not implemented 
+	* XInclude handling of xml:base and xml:lang not implemented   
+-->
+<!-- 
+  VERSION INFORMATION
+	2009-02-25 
+	* Update DSDL namespace to use schematron.com
+	* Tested with SAXON9, Xalan 2.7.1, IE7, 
+	* IE does not like multiple variables in same template with same name: rename.   
+	2008-09-18
+	* Remove new behaviour for include, because it conflicts with existing usage [KH]
+	* Add extends[@href] element with that merge functionality
+	* Generate PIs to notate source of inclusions for potential better diagnostics
+	
+	2008-09-16
+	* Fix for XSLT1
+	
+	2008-08-28
+	* New behaviour for schematron includes: if the pointed to element is the same as the current,
+	include the children.
+	
+	2008-08-20
+	* Fix bug: in XSLT1 cannot do $document/id('x') but need to use for-each
+	
+	2008-08-04
+	* Add support for inclusions in old namespace  
+	
+	2008-08-03
+	* Fix wrong param name include-relaxng & include-crdl (KH, PH)
+	* Allow inclusion of XSLT and XHTML (KH)
+	* Fix inclusion of fragments (KH)
+	
+	2008-07-25
+	* Add selectable input parameter
+	
+	2008-07-24  
+	* RJ New
+-->
+<!--
+	LEGAL INFORMATION
+	
+	Copyright (c) 2008 Rick Jelliffe 
+	
+	This software is provided 'as-is', without any express or implied warranty. 
+	In no event will the authors be held liable for any damages arising from 
+	the use of this software.
+	
+	Permission is granted to anyone to use this software for any purpose, 
+	including commercial applications, and to alter it and redistribute it freely,
+	subject to the following restrictions:
+	
+	1. The origin of this software must not be misrepresented; you must not claim
+	that you wrote the original software. If you use this software in a product, 
+	an acknowledgment in the product documentation would be appreciated but is 
+	not required.
+	
+	2. Altered source versions must be plainly marked as such, and must not be 
+	misrepresented as being the original software.
+	
+	3. This notice may not be removed or altered from any source distribution.
+-->
+<xslt:stylesheet version="1.0"
+	xmlns:xslt="http://www.w3.org/1999/XSL/Transform"
+	xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+	xmlns:iso="http://purl.oclc.org/dsdl/schematron"
+	xmlns:nvdl="http://purl.oclc.org/dsdl/nvdl"
+	xmlns:xhtml="http://www.w3.org/1999/xhtml"
+	xmlns:schold="http://www.ascc.net/xml/schematron"
+	xmlns:crdl="http://purl.oclc.org/dsdl/crepdl/ns/structure/1.0"
+	xmlns:xi="http://www.w3.org/2001/XInclude"
+	xmlns:dtll="http://www.jenitennison.com/datatypes"
+	xmlns:dsdl="http://www.schematron.com/namespace/dsdl"
+	xmlns:relax="http://relaxng.org/ns/structure/1.0"
+	xmlns:xlink="http://www.w3.org/1999/xlink">
+	<!-- Note: The URL for the dsdl namespace is not official -->
+
+
+	<xsl:param name="include-schematron">true</xsl:param>
+	<xsl:param name="include-crdl">true</xsl:param>
+	<xsl:param name="include-xinclude">true</xsl:param>
+	<xsl:param name="include-dtll">true</xsl:param>
+	<xsl:param name="include-relaxng">true</xsl:param>
+	<xsl:param name="include-xlink">true</xsl:param>
+
+	<xsl:template match="/">
+		<xsl:apply-templates select="." mode="dsdl:go" />
+	</xsl:template>
+
+	<!-- output everything else unchanged -->
+	<xslt:template match="node()" priority="-1" mode="dsdl:go">
+		<xslt:copy>
+			<xslt:copy-of select="@*" />
+			<xslt:apply-templates mode="dsdl:go" />
+		</xslt:copy>
+	</xslt:template>
+
+
+
+	<!-- =========================================================== -->
+	<!-- ISO/IEC 19757 - DSDL Document Schema Definition Languages   -->
+	<!-- Part 2 - Regular grammar-based validation - RELAX NG        -->
+	<!-- This only implements relax:extRef not relax:include which   -->
+	<!-- is complex.                                                 -->
+	<!-- =========================================================== -->
+	<xslt:template match="relax:extRef" mode="dsdl:go">
+
+
+		<!-- Insert subschema -->
+
+		<xsl:variable name="document-uri"
+			select="substring-before(concat(@href,'#'), '#')" />
+		<xsl:variable name="fragment-id"
+			select="substring-after(@href, '#')" />
+
+		<xsl:processing-instruction name="DSDL_INCLUDE_START">
+			<xsl:value-of select="@href" />
+		</xsl:processing-instruction>
+		<xsl:choose>
+			<xsl:when test="not( $include-relaxng = 'true' )">
+				<xslt:copy>
+					<xslt:copy-of select="@*" />
+					<xslt:apply-templates mode="dsdl:go" />
+				</xslt:copy>
+			</xsl:when>
+			<xsl:otherwise>
+
+				<xsl:choose>
+
+					<xsl:when
+						test="string-length( $document-uri ) = 0 and string-length( $fragment-id ) = 0">
+						<xsl:message>
+							Error: Impossible URL in RELAX NG extRef
+							include
+						</xsl:message>
+					</xsl:when>
+
+					<!-- this case is when there is in embedded schema in the same document elsewhere -->
+					<xslt:when
+						test="string-length( $document-uri ) = 0">
+						<xslt:apply-templates mode="dsdl:go"
+							select="//*[@xml:id= $fragment-id ] | id( $fragment-id) | //*[@id= $fragment-id ]" />
+					</xslt:when>
+
+					<xsl:when
+						test="string-length( $fragment-id ) &gt; 0">
+						<xsl:variable name="theDocument_1"
+							select="document( $document-uri,/ )" />
+
+						<xsl:if test="not($theDocument_1)">
+							<xsl:message terminate="no">
+								<xsl:text>Unable to open referenced included file: </xsl:text>
+								<xsl:value-of select="@href" />
+							</xsl:message>
+						</xsl:if>
+						<!-- use a for-each so that the id() function works correctly on the external document -->
+						<xsl:for-each select="$theDocument_1">
+							<xsl:variable name="theFragment_1"
+								select="$theDocument_1//*[@xml:id= $fragment-id ]        
+                  |  id( $fragment-id)          
+              | $theDocument_1//*[@id= $fragment-id ]" />
+							<xsl:if test="not($theFragment_1)">
+								<xsl:message terminate="no">
+									<xsl:text>Unable to locate id attribute: </xsl:text>
+									<xsl:value-of select="@href" />
+								</xsl:message>
+							</xsl:if>
+							<xsl:apply-templates
+								select=" $theFragment_1[1]" mode="dsdl:go" />
+						</xsl:for-each>
+					</xsl:when>
+
+					<xsl:otherwise>
+						<xsl:variable name="theDocument_2"
+							select="document( $document-uri,/ )" />
+						<xsl:variable name="theFragment_2"
+							select="$theDocument_2/*" />
+						<xsl:if test="not($theDocument_2)">
+							<xsl:message terminate="no">
+								<xsl:text>Unable to open referenced included file: </xsl:text>
+								<xsl:value-of select="@href" />
+							</xsl:message>
+						</xsl:if>
+
+						<xsl:if test="not($theFragment_2)">
+							<xsl:message terminate="no">
+								<xsl:text>Unable to locate id attribute: </xsl:text>
+								<xsl:value-of select="@href" />
+							</xsl:message>
+						</xsl:if>
+						<xsl:apply-templates select="$theFragment_2 "
+							mode="dsdl:go" />
+					</xsl:otherwise>
+				</xsl:choose>
+
+			</xsl:otherwise>
+		</xsl:choose>
+
+		<xsl:processing-instruction name="DSDL_INCLUDE_END">
+			<xsl:value-of select="@href" />
+		</xsl:processing-instruction>
+	</xslt:template>
+
+
+
+	<!-- =========================================================== -->
+	<!-- ISO/IEC 19757 - DSDL Document Schema Definition Languages   -->
+	<!-- Part 3 - Rule-based validation - Schematron                 -->
+	<!-- =========================================================== -->
+
+
+	<!-- Extend the URI syntax to allow # references -->
+	<!-- Add experimental support for simple containers like  /xxx:xxx/iso:pattern to allow better includes -->
+	<xsl:template match="iso:include" mode="dsdl:go">
+
+		<xsl:variable name="document-uri"
+			select="substring-before(concat(@href,'#'), '#')" />
+		<xsl:variable name="fragment-id"
+			select="substring-after(@href, '#')" />
+
+
+		<xsl:processing-instruction name="DSDL_INCLUDE_START">
+			<xsl:value-of select="@href" />
+		</xsl:processing-instruction>
+
+		<xsl:choose>
+			<xsl:when test="not( $include-schematron = 'true' )">
+				<xslt:copy>
+					<xslt:copy-of select="@*" />
+					<xslt:apply-templates mode="dsdl:go" />
+				</xslt:copy>
+			</xsl:when>
+			<xsl:otherwise>
+
+				<xsl:choose>
+
+					<xsl:when
+						test="string-length( $document-uri ) = 0 and string-length( $fragment-id ) = 0">
+						<xsl:message>
+							Error: Impossible URL in Schematron include
+						</xsl:message>
+					</xsl:when>
+
+					<!-- this case is when there is in embedded schema in the same document elsewhere -->
+					<xslt:when
+						test="string-length( $document-uri ) = 0">
+						<xslt:apply-templates mode="dsdl:go"
+							select="//iso:*[@xml:id= $fragment-id ] 
+              	 |id( $fragment-id)
+              	 | //iso:*[@id= $fragment-id ]" />
+					</xslt:when>
+
+					<!-- case where there is a fragment in another document (should be an iso: element) -->
+					<!-- There are three cases for includes with fragment:
+						0) No href file or no matching id - error!
+						1) REMOVED
+						
+						2) The linked-to element is sch:schema however the parent of the include
+						is not a schema. In this case, it is an error. (Actually, it should
+						be an error for other kinds of containment problems, but we won't
+						check for them in this version.)
+						
+						3) Otherwise, include the pointed-to element
+					-->
+
+					<xsl:when
+						test="string-length( $fragment-id ) &gt; 0">
+						<xsl:variable name="theDocument_1"
+							select="document( $document-uri,/ )" />
+						<xsl:variable name="originalParent" select=".." />
+
+						<!-- case 0 -->
+						<xsl:if test="not($theDocument_1)">
+							<xsl:message terminate="no">
+								<xsl:text>Unable to open referenced included file: </xsl:text>
+								<xsl:value-of select="@href" />
+							</xsl:message>
+						</xsl:if>
+						<!-- use for-each to rebase id() to external document -->
+						<xsl:for-each select="$theDocument_1">
+							<xsl:variable name="theFragment_1"
+								select=" $theDocument_1//iso:*[@xml:id= $fragment-id ] |
+              	 		id($fragment-id) |
+              			$theDocument_1//iso:*[@id= $fragment-id ]" />
+
+
+							<xsl:choose>
+								<!-- case 0 -->
+								<xsl:when test="not($theFragment_1)">
+									<xsl:message terminate="no">
+										<xsl:text>Unable to locate id attribute: </xsl:text>
+										<xsl:value-of select="@href" />
+									</xsl:message>
+								</xsl:when>
+
+
+								<!-- case 1 REMOVED -->
+
+								<!-- case 2 -->
+								<xsl:when
+									test=" $theFragment_1/self::iso:schema ">
+									<xsl:message>
+										Schema error: Use include to
+										include fragments, not a whole
+										schema
+									</xsl:message>
+								</xsl:when>
+
+								<!-- case 3 -->
+								<xsl:otherwise>
+									<xsl:apply-templates
+										select=" $theFragment_1[1]" mode="dsdl:go" />
+								</xsl:otherwise>
+							</xsl:choose>
+						</xsl:for-each>
+					</xsl:when>
+
+					<!-- Case where there is no ID so we include the whole document -->
+					<!-- Experimental addition: include fragments of children -->
+					<xsl:otherwise>
+						<xsl:variable name="theDocument_2"
+							select="document( $document-uri,/ )" />
+						<xsl:variable name="theFragment_2"
+							select="$theDocument_2/iso:*" />
+						<xsl:variable name="theContainedFragments"
+							select="$theDocument_2/*/iso:* | $theDocument_2/*/xsl:* | $theDocument_2/*/xhtml:*" />
+						<xsl:if test="not($theDocument_2)">
+							<xsl:message terminate="no">
+								<xsl:text>Unable to open referenced included file: </xsl:text>
+								<xsl:value-of select="@href" />
+							</xsl:message>
+						</xsl:if>
+
+						<!-- There are three cases for includes:
+							0) No text specified- error!
+							
+							1) REMOVED
+							
+							2) The linked-to element is sch:schema however the parent of the include
+							is not a schema. In this case, it is an error. (Actually, it should
+							be an error for other kinds of containment problems, but we won't
+							check for them in this version.)
+							
+							3) Otherwise, include the pointed-to element
+						-->
+						<xsl:choose>
+							<!-- case 0 -->
+							<xsl:when
+								test="not($theFragment_2) and not ($theContainedFragments)">
+								<xsl:message terminate="no">
+									<xsl:text>Unable to locate id attribute: </xsl:text>
+									<xsl:value-of select="@href" />
+								</xsl:message>
+							</xsl:when>
+
+							<!-- case 1 removed -->
+
+							<!-- case 2 -->
+							<xsl:when
+								test=" $theFragment_2/self::iso:schema or $theContainedFragments/self::iso:schema">
+								<xsl:message>
+									Schema error: Use include to include
+									fragments, not a whole schema
+								</xsl:message>
+							</xsl:when>
+
+							<!-- If this were XLST 2, we could use  
+								if ($theFragment) then $theFragment else $theContainedFragments
+								here (thanks to KN)
+							-->
+							<!-- case 3 -->
+							<xsl:otherwise>
+								<xsl:apply-templates
+									select="$theFragment_2 " mode="dsdl:go" />
+							</xsl:otherwise>
+						</xsl:choose>
+					</xsl:otherwise>
+				</xsl:choose>
+			</xsl:otherwise>
+		</xsl:choose>
+
+		<xsl:processing-instruction name="DSDL_INCLUDE_END">
+			<xsl:value-of select="@href" />
+		</xsl:processing-instruction>
+	</xsl:template>
+
+
+	<!-- WARNING   sch:extends[@href] is experimental and non standard  -->
+	<!-- Basically, it adds the children of the selected element, not the element itself.  -->
+	<xsl:template match="iso:extends[@href]" mode="dsdl:go">
+
+		<xsl:variable name="document-uri"
+			select="substring-before(concat(@href,'#'), '#')" />
+		<xsl:variable name="fragment-id"
+			select="substring-after(@href, '#')" />
+
+
+		<xsl:processing-instruction name="DSDL_INCLUDE_START">
+			<xsl:value-of select="@href" />
+		</xsl:processing-instruction>
+
+		<xsl:choose>
+			<xsl:when test="not( $include-schematron = 'true' )">
+				<xslt:copy>
+					<xslt:copy-of select="@*" />
+					<xslt:apply-templates mode="dsdl:go" />
+				</xslt:copy>
+			</xsl:when>
+			<xsl:otherwise>
+
+				<xsl:choose>
+
+					<xsl:when
+						test="string-length( $document-uri ) = 0 and string-length( $fragment-id ) = 0">
+						<xsl:message>
+							Error: Impossible URL in Schematron include
+						</xsl:message>
+					</xsl:when>
+
+					<!-- this case is when there is in embedded schema in the same document elsewhere -->
+					<xslt:when
+						test="string-length( $document-uri ) = 0">
+						<xslt:apply-templates mode="dsdl:go"
+							select="//iso:*[@xml:id= $fragment-id ]/* 
+              	 |id( $fragment-id)/*
+              	 | //iso:*[@id= $fragment-id ]/*" />
+					</xslt:when>
+
+					<!-- case where there is a fragment in another document (should be an iso: element) -->
+					<!-- There are three cases for includes with fragment:
+						0) No href file or no matching id - error!
+						1) REMOVED
+						
+						2) REMOVED
+						
+						3) Otherwise, include the pointed-to element
+					-->
+
+					<xsl:when
+						test="string-length( $fragment-id ) &gt; 0">
+						<xsl:variable name="theDocument_1"
+							select="document( $document-uri,/ )" />
+						<xsl:variable name="originalParent" select=".." />
+
+						<!-- case 0 -->
+						<xsl:if test="not($theDocument_1)">
+							<xsl:message terminate="no">
+								<xsl:text>Unable to open referenced included file: </xsl:text>
+								<xsl:value-of select="@href" />
+							</xsl:message>
+						</xsl:if>
+						<!-- use for-each to rebase id() to external document -->
+						<xsl:for-each select="$theDocument_1">
+							<xsl:variable name="theFragment_1"
+								select=" $theDocument_1//iso:*[@xml:id= $fragment-id ] |
+              	 		id($fragment-id) |
+              			$theDocument_1//iso:*[@id= $fragment-id ]" />
+
+
+							<xsl:choose>
+								<!-- case 0 -->
+								<xsl:when test="not($theFragment_1)">
+									<xsl:message terminate="no">
+										<xsl:text>Unable to locate id attribute: </xsl:text>
+										<xsl:value-of select="@href" />
+									</xsl:message>
+								</xsl:when>
+
+
+								<!-- case 1 REMOVED -->
+
+								<!-- case 2 REMOVED -->
+
+
+								<!-- case 3 -->
+								<xsl:otherwise>
+
+									<xsl:apply-templates
+										select=" $theFragment_1[1]/*" mode="dsdl:go" />
+								</xsl:otherwise>
+							</xsl:choose>
+						</xsl:for-each>
+					</xsl:when>
+
+					<!-- Case where there is no ID so we include the whole document -->
+					<!-- Experimental addition: include fragments of children -->
+					<xsl:otherwise>
+						<xsl:variable name="theDocument_2"
+							select="document( $document-uri,/ )" />
+						<xsl:variable name="theFragment_2"
+							select="$theDocument_2/iso:*" />
+						<xsl:variable name="theContainedFragments"
+							select="$theDocument_2/*/iso:* | $theDocument_2/*/xsl:* | $theDocument_2/*/xhtml:*" />
+						<xsl:if test="not($theDocument_2)">
+							<xsl:message terminate="no">
+								<xsl:text>Unable to open referenced included file: </xsl:text>
+								<xsl:value-of select="@href" />
+							</xsl:message>
+						</xsl:if>
+
+						<!-- There are three cases for includes:
+							0) No text specified- error!
+							
+							1) REMOVED
+							
+							2) REMOVED
+							
+							3) Otherwise, include the pointed-to element
+						-->
+						<xsl:choose>
+							<!-- case 0 -->
+							<xsl:when
+								test="not($theFragment_2) and not ($theContainedFragments)">
+								<xsl:message terminate="no">
+									<xsl:text>Unable to locate id attribute: </xsl:text>
+									<xsl:value-of select="@href" />
+								</xsl:message>
+							</xsl:when>
+
+							<!-- case 1 removed -->
+
+							<!-- case 2 removed -->
+
+							<!-- If this were XLST 2, we could use  
+								if ($theFragment) then $theFragment else $theContainedFragments
+								here (thanks to KN)
+							-->
+							<!-- case 3 -->
+							<xsl:otherwise>
+								<xsl:apply-templates
+									select="$theFragment_2/* " mode="dsdl:go" />
+							</xsl:otherwise>
+						</xsl:choose>
+					</xsl:otherwise>
+				</xsl:choose>
+			</xsl:otherwise>
+		</xsl:choose>
+
+		<xsl:processing-instruction name="DSDL_INCLUDE_END">
+			<xsl:value-of select="@href" />
+		</xsl:processing-instruction>
+	</xsl:template>
+
+
+
+	<!-- =========================================================== -->
+	<!-- Handle Schematron 1.6 inclusions: clone of ISO code above   -->
+	<!-- =========================================================== -->
+
+
+	<!-- Extend the URI syntax to allow # references -->
+	<!-- Add experimental support for simple containers like  /xxx:xxx/schold:pattern to allow better includes -->
+	<xsl:template match="schold:include" mode="dsdl:go">
+		<xsl:variable name="document-uri"
+			select="substring-before(concat(@href,'#'), '#')" />
+		<xsl:variable name="fragment-id"
+			select="substring-after(@href, '#')" />
+
+		<xsl:processing-instruction name="DSDL_INCLUDE_START">
+			<xsl:value-of select="@href" />
+		</xsl:processing-instruction>
+
+		<xsl:choose>
+			<xsl:when test="not( $include-schematron = 'true' )">
+				<xslt:copy>
+					<xslt:copy-of select="@*" />
+					<xslt:apply-templates mode="dsdl:go" />
+				</xslt:copy>
+			</xsl:when>
+			<xsl:otherwise>
+				<xsl:choose>
+
+					<xsl:when
+						test="string-length( $document-uri ) = 0 and string-length( $fragment-id ) = 0">
+						<xsl:message>
+							Error: Impossible URL in Schematron include
+						</xsl:message>
+					</xsl:when>
+
+					<!-- this case is when there is in embedded schema in the same document elsewhere -->
+					<xslt:when
+						test="string-length( $document-uri ) = 0">
+						<xslt:apply-templates mode="dsdl:go"
+							select="//schold:*[@xml:id= $fragment-id ] 
+              	 |id( $fragment-id)
+              	 | //schold:*[@id= $fragment-id ]" />
+					</xslt:when>
+
+					<!-- case where there is a fragment in another document (should be an iso: element) -->
+					<xsl:when
+						test="string-length( $fragment-id ) &gt; 0">
+						<xsl:variable name="theDocument_1"
+							select="document( $document-uri,/ )" />
+						<xsl:if test="not($theDocument_1)">
+							<xsl:message terminate="no">
+								<xsl:text>Unable to open referenced included file: </xsl:text>
+								<xsl:value-of select="@href" />
+							</xsl:message>
+						</xsl:if>
+						<!-- use for-each to rebase id() to $theDocument -->
+						<xsl:for-each select="$theDocument_1">
+							<xsl:variable name="theFragment_1"
+								select=" $theDocument_1//schold:*[@xml:id= $fragment-id ] |
+              	id($fragment-id) |
+              	$theDocument_1//schold:*[@id= $fragment-id ]" />
+							<xsl:if
+								test=" $theFragment_1/self::schold:schema ">
+								<xsl:message>
+									Schema error: Use include to include
+									fragments, not a whole schema
+								</xsl:message>
+							</xsl:if>
+							<xsl:if test="not($theFragment_1)">
+								<xsl:message terminate="no">
+									<xsl:text>Unable to locate id attribute: </xsl:text>
+									<xsl:value-of select="@href" />
+								</xsl:message>
+							</xsl:if>
+							<xsl:apply-templates
+								select=" $theFragment_1[1]" mode="dsdl:go" />
+						</xsl:for-each>
+					</xsl:when>
+
+					<!-- Case where there is no ID so we include the whole document -->
+					<!-- Experimental addition: include fragments of children -->
+					<xsl:otherwise>
+						<xsl:variable name="theDocument_2"
+							select="document( $document-uri,/ )" />
+						<xsl:variable name="theFragment_2"
+							select="$theDocument_2/iso:*" />
+						<xsl:variable name="theContainedFragments"
+							select="$theDocument_2/*/schold:* | $theDocument_2/*/xsl:* | $theDocument_2/*/xhtml:*" />
+						<xsl:if test="not($theDocument_2)">
+							<xsl:message terminate="no">
+								<xsl:text>Unable to open referenced included file: </xsl:text>
+								<xsl:value-of select="@href" />
+							</xsl:message>
+						</xsl:if>
+
+						<xsl:if
+							test=" $theFragment_2/self::schold:schema or $theContainedFragments/self::schold:schema">
+							<xsl:message>
+								Schema error: Use include to include
+								fragments, not a whole schema
+							</xsl:message>
+						</xsl:if>
+						<xsl:if
+							test="not($theFragment_2) and not ($theContainedFragments)">
+							<xsl:message terminate="no">
+								<xsl:text>Unable to locate id attribute: </xsl:text>
+								<xsl:value-of select="@href" />
+							</xsl:message>
+						</xsl:if>
+						<!-- If this were XLST 2, we could use  
+							if ($theFragment) then $theFragment else $theContainedFragments
+							here (thanks to KN)
+						-->
+						<xsl:choose>
+							<xsl:when test=" $theFragment_2 ">
+								<xsl:apply-templates
+									select="$theFragment_2 " mode="dsdl:go" />
+							</xsl:when>
+							<xsl:otherwise>
+								<!-- WARNING!  EXPERIMENTAL! Use at your own risk. This may be discontinued! -->
+								<xsl:apply-templates
+									select="  $theContainedFragments " mode="dsdl:go" />
+							</xsl:otherwise>
+						</xsl:choose>
+					</xsl:otherwise>
+				</xsl:choose>
+
+			</xsl:otherwise>
+		</xsl:choose>
+
+		<xsl:processing-instruction name="DSDL_INCLUDE_END">
+			<xsl:value-of select="@href" />
+		</xsl:processing-instruction>
+	</xsl:template>
+	<!-- =========================================================== -->
+	<!-- ISO/IEC 19757 - DSDL Document Schema Definition Languages   -->
+	<!-- Part 5 - DataType Library Language - DTLL                   -->
+	<!-- Committee Draft  Experimental support only                  -->
+	<!-- The <include> element may well be replaced by XInclude in   -->
+	<!-- any final version.                                          -->
+	<!-- =========================================================== -->
+	<xslt:template match="dtll:include" mode="dsdl:go">
+		<!-- Insert subschema -->
+
+		<xsl:variable name="document-uri"
+			select="substring-before(concat(@href,'#'), '#')" />
+		<xsl:variable name="fragment-id"
+			select="substring-after(@href, '#')" />
+		<xsl:processing-instruction name="DSDL_INCLUDE_START">
+			<xsl:value-of select="@href" />
+		</xsl:processing-instruction>
+		<xsl:choose>
+			<xsl:when test="not( $include-dtll = 'true' )">
+				<xslt:copy>
+					<xslt:copy-of select="@*" />
+					<xslt:apply-templates mode="dsdl:go" />
+				</xslt:copy>
+			</xsl:when>
+			<xsl:otherwise>
+				<xsl:choose>
+
+					<xsl:when
+						test="string-length( $document-uri ) = 0 and string-length( $fragment-id ) = 0">
+						<xsl:message>
+							Error: Impossible URL in DTLL include
+						</xsl:message>
+					</xsl:when>
+
+					<!-- this case is when there is in embedded schema in the same document elsewhere -->
+					<xslt:when
+						test="string-length( $document-uri ) = 0">
+						<xslt:apply-templates mode="dsdl:go"
+							select="//*[@xml:id= $fragment-id ] | id( $fragment-id) 
+              	| //*[@id= $fragment-id ]" />
+					</xslt:when>
+
+					<xsl:when
+						test="string-length( $fragment-id ) &gt; 0">
+						<xsl:variable name="theDocument_1"
+							select="document( $document-uri,/ )" />
+						<xsl:if test="not($theDocument_1)">
+							<xsl:message terminate="no">
+								<xsl:text>Unable to open referenced included file: </xsl:text>
+								<xsl:value-of select="@href" />
+							</xsl:message>
+						</xsl:if>
+						<!-- use for-each to rebase id() to $theDocument -->
+						<xsl:for-each select="$theDocument_1">
+							<xsl:variable name="theFragment_1"
+								select="$theDocument_1//*[@xml:id= $fragment-id ]
+               | id( $fragment-id ) 
+               | $theDocument_1//*[@id= $fragment-id ]" />
+							<xsl:if test="not($theFragment_1)">
+								<xsl:message terminate="no">
+									<xsl:text>Unable to locate id attribute: </xsl:text>
+									<xsl:value-of select="@href" />
+								</xsl:message>
+							</xsl:if>
+							<xsl:apply-templates
+								select=" $theFragment_1[1]" mode="dsdl:go" />
+						</xsl:for-each>
+					</xsl:when>
+
+					<xsl:otherwise>
+						<xsl:variable name="theDocument_2"
+							select="document( $document-uri,/ )" />
+						<xsl:variable name="theFragment_2"
+							select="$theDocument_2/*" />
+
+						<xsl:if test="not($theDocument_2)">
+							<xsl:message terminate="no">
+								<xsl:text>Unable to open referenced included file: </xsl:text>
+								<xsl:value-of select="@href" />
+							</xsl:message>
+						</xsl:if>
+
+						<xsl:if test="not($theFragment_2)">
+							<xsl:message terminate="no">
+								<xsl:text>Unable to locate id attribute: </xsl:text>
+								<xsl:value-of select="@href" />
+							</xsl:message>
+						</xsl:if>
+						<xsl:apply-templates select="$theFragment_2 "
+							mode="dsdl:go" />
+					</xsl:otherwise>
+				</xsl:choose>
+
+			</xsl:otherwise>
+		</xsl:choose>
+		<xsl:processing-instruction name="DSDL_INCLUDE_END">
+			<xsl:value-of select="@href" />
+		</xsl:processing-instruction>
+	</xslt:template>
+
+	<!-- =========================================================== -->
+	<!-- ISO/IEC 19757 - DSDL Document Schema Definition Languages   -->
+	<!-- Part 7 - Character Repertoire Description Language - CRDL   -->
+	<!-- Final Committee Draft 2008-01-11 Experimental support only  -->
+	<!-- =========================================================== -->
+	<xslt:template match="crdl:ref" mode="dsdl:go">
+		<!-- Insert subschema -->
+
+		<xsl:variable name="document-uri"
+			select="substring-before(concat(@href,'#'), '#')" />
+		<xsl:variable name="fragment-id"
+			select="substring-after(@href, '#')" />
+		<xsl:processing-instruction name="DSDL_INCLUDE_START">
+			<xsl:value-of select="@href" />
+		</xsl:processing-instruction>
+		<xsl:choose>
+			<xsl:when test="not( $include-crdl = 'true' )">
+				<xslt:copy>
+					<xslt:copy-of select="@*" />
+					<xslt:apply-templates mode="dsdl:go" />
+				</xslt:copy>
+			</xsl:when>
+			<xsl:otherwise>
+				<xsl:choose>
+
+					<xsl:when
+						test="string-length( $document-uri ) = 0 and string-length( $fragment-id ) = 0">
+						<xsl:message>
+							Error: Impossible URL in CRDL include
+						</xsl:message>
+					</xsl:when>
+
+					<!-- this case is when there is in embedded schema in the same document elsewhere -->
+					<xslt:when
+						test="string-length( $document-uri ) = 0">
+
+						<xslt:apply-templates mode="dsdl:go"
+							select="//*[@xml:id= $fragment-id ] | id( $fragment-id)
+              	| //*[@id= $fragment-id ]" />
+					</xslt:when>
+
+					<xsl:when
+						test="string-length( $fragment-id ) &gt; 0">
+						<xsl:variable name="theDocument_1"
+							select="document( $document-uri,/ )" />
+						<xsl:if test="not($theDocument_1)">
+							<xsl:message terminate="no">
+								<xsl:text>Unable to open referenced included file: </xsl:text>
+								<xsl:value-of select="@href" />
+							</xsl:message>
+						</xsl:if>
+						<!-- use for-each to rebase id() to $theDocument -->
+						<xsl:for-each select="$theDocument_1">
+							<xsl:variable name="theFragment_1"
+								select="$theDocument_1//*[@xml:id= $fragment-id ]
+               | id( $fragment-id )
+               | $theDocument_1//*[@id= $fragment-id ]" />
+
+							<xsl:if test="not($theFragment_1)">
+								<xsl:message terminate="no">
+									<xsl:text>Unable to locate id attribute: </xsl:text>
+									<xsl:value-of select="@href" />
+								</xsl:message>
+							</xsl:if>
+							<xsl:apply-templates select=" $theFragment_1 "
+								mode="dsdl:go" />
+						</xsl:for-each>
+					</xsl:when>
+
+					<xsl:otherwise>
+						<xsl:variable name="theDocument_2"
+							select="document( $document-uri,/ )" />
+						<xsl:variable name="theFragment_2"
+							select="$theDocument_2/*" />
+
+						<xsl:if test="not($theDocument_2)">
+							<xsl:message terminate="no">
+								<xsl:text>Unable to open referenced included file: </xsl:text>
+								<xsl:value-of select="@href" />
+							</xsl:message>
+						</xsl:if>
+						<xsl:if test="not($theFragment_2)">
+							<xsl:message terminate="no">
+								<xsl:text>Unable to locate id attribute: </xsl:text>
+								<xsl:value-of select="@href" />
+							</xsl:message>
+						</xsl:if>
+
+						<xsl:apply-templates select="$theFragment_2"
+							mode="dsdl:go" />
+					</xsl:otherwise>
+				</xsl:choose>
+
+			</xsl:otherwise>
+		</xsl:choose>
+		<xsl:processing-instruction name="DSDL_INCLUDE_END">
+			<xsl:value-of select="@href" />
+		</xsl:processing-instruction>
+	</xslt:template>
+
+
+	<!-- =========================================================== -->
+	<!-- ISO/IEC 19757 - DSDL Document Schema Definition Languages   -->
+	<!-- Part 4 - Namespace-based Validation Dispatching Language - NVDL -->
+	<!-- Note: This does not include schemas referenced for          -->
+	<!-- validation, it merely handles any simple XIncludes          -->
+	<!-- =========================================================== -->
+	<!-- ISO/IEC 19757 - DSDL Document Schema Definition Languages   -->
+	<!-- Part 8 - Document Schema Renaming Language - DSRL           -->
+	<!-- Note: Final? Committee Draft   Experimental support only    -->
+	<!-- =========================================================== -->
+	<!-- XInclude support for id based references only, with 1 level -->
+	<!-- of fallback.                                                -->
+	<!-- =========================================================== -->
+
+	<xslt:template mode="dsdl:go"
+		match="xi:include[@href][not(@parseType) or @parseType ='xml']">
+		<!-- Simple inclusions only here -->
+		<xsl:processing-instruction name="DSDL_INCLUDE_START">
+			<xsl:value-of select="@href" />
+		</xsl:processing-instruction>
+		<xsl:choose>
+			<xsl:when test="not( $include-xinclude = 'true' )">
+				<xslt:copy>
+					<xslt:copy-of select="@*" />
+					<xslt:apply-templates mode="dsdl:go" />
+				</xslt:copy>
+			</xsl:when>
+			<xsl:otherwise>
+				<xsl:choose>
+
+					<xsl:when test="contains( @href, '#')">
+						<xsl:message terminate="yes">
+							Fatal error: Xinclude href contains fragment
+							identifier #
+						</xsl:message>
+					</xsl:when>
+
+
+					<xsl:when test="contains( @xpointer, '(')">
+						<xsl:message terminate="yes">
+							Fatal error: Sorry, this software only
+							supports simple ids in XInclude xpointers
+						</xsl:message>
+					</xsl:when>
+
+					<xsl:when
+						test="string-length( @href ) = 0 and string-length( @xpointer ) = 0">
+
+						<xsl:message terminate="yes">
+							Fatal Error: Impossible URL in XInclude
+							include
+						</xsl:message>
+					</xsl:when>
+
+					<!-- this case is when there is in embedded schema in the same document elsewhere -->
+					<xslt:when test="string-length( @href ) = 0">
+
+						<xslt:apply-templates mode="dsdl:go"
+							select="//*[@xml:id= current()/@xpointer  ] | id( @xpointer)
+              	| //*[@id= current()/@xpointer  ]" />
+					</xslt:when>
+
+					<xsl:when
+						test="string-length( @xpointer ) &gt; 0">
+						<xsl:variable name="theDocument_1"
+							select="document( @href,/ )" />
+						<xsl:variable name="theFragment_1"
+							select="$theDocument_1//*[@xml:id= current()/@xpointer  ]
+             
+              | $theDocument_1//*[@id= current()/@xpointer  ]" />
+						<!-- removed
+							| $theDocument_1/id( @xpointer)
+							because it requires rebasing in XSLT1 and that would mess up the use of current()
+						-->
+
+
+						<!-- Allow one level of fallback, to another XInclude -->
+						<xsl:if test="not($theDocument_1)">
+							<xsl:choose>
+								<xsl:when test="xi:fallback">
+									<xsl:variable name="theDocument_2"
+										select="document( xi:fallback[1]/xi:include[not(@parseType)
+                    	 or @parseType='xml']/@href,/ )" />
+									<xsl:variable name="theFragment_2"
+										select="$theDocument_2//*[@xml:id= current()/xi:fallback[1]/xi:include/@xpointer  ]
+              				| $theDocument_2//*[@id= current()/xi:fallback[1]/xi:include/@xpointer  ]" />
+									<!-- removed 
+										| $theDocument_2/id( xi:fallback[1]/xi:include/@xpointer)
+										because it id() would need rebasing in XSLT1 and that would mess up use of current()
+									-->
+
+									<xsl:if
+										test="not($theDocument_2)">
+
+										<xsl:message terminate="no">
+											<xsl:text>Unable to open referenced included file and fallback
+									file: </xsl:text>
+											<xsl:value-of
+												select="@href" />
+										</xsl:message>
+									</xsl:if>
+								</xsl:when>
+								<xsl:otherwise>
+									<xsl:message terminate="no">
+										<xsl:text>Unable to open referenced included file: </xsl:text>
+										<xsl:value-of select="@href" />
+									</xsl:message>
+								</xsl:otherwise>
+							</xsl:choose>
+						</xsl:if>
+						<xsl:apply-templates select=" $theFragment_1"
+							mode="dsdl:go" />
+					</xsl:when>
+
+					<!-- Document but no fragment specified -->
+					<xsl:otherwise>
+						<xsl:variable name="theDocument_3"
+							select="document( @href,/ )" />
+						<xsl:variable name="theFragment_3"
+							select="$theDocument_3/*" />
+
+						<xsl:if test="not($theDocument_3)">
+							<xsl:message terminate="no">
+								<xsl:text>Unable to open referenced included file: </xsl:text>
+								<xsl:value-of select="@href" />
+							</xsl:message>
+						</xsl:if>
+
+						<xsl:apply-templates select="$theFragment_3 "
+							mode="dsdl:go" />
+					</xsl:otherwise>
+				</xsl:choose>
+
+			</xsl:otherwise>
+		</xsl:choose>
+		<xsl:processing-instruction name="DSDL_INCLUDE_END">
+			<xsl:value-of select="@href" />
+		</xsl:processing-instruction>
+	</xslt:template>
+
+	<!-- =========================================================== -->
+	<!-- W3C XLink 1.1 embedded simple links                        -->
+	<!-- =========================================================== -->
+	<xslt:template
+		match="*[@xlink:href][not(parent::*[@xlink:type='complex'])]
+	           [not(@xlink:type) or (@xlink:type='simple')]
+	           [@xlink:show='embed']
+	           [not(@xlink:actuate) or (@xlink:actuate='onLoad')]"
+		mode="dsdl:go" priority="1">
+
+		<xsl:variable name="document-uri"
+			select="substring-before(concat(@xlink:href,'#'), '#')" />
+		<xsl:variable name="fragment-id"
+			select="substring-after(@xlink:href, '#')" />
+		<xsl:processing-instruction name="DSDL_INCLUDE_START">
+			<xsl:value-of select="@xlink:href" />
+		</xsl:processing-instruction>
+		<xsl:choose>
+			<xsl:when test="not( $include-xlink = 'true' )">
+				<xslt:copy>
+					<xslt:copy-of select="@*" />
+					<xslt:apply-templates mode="dsdl:go" />
+				</xslt:copy>
+			</xsl:when>
+			<xsl:otherwise>
+				<xsl:choose>
+
+					<xsl:when
+						test="string-length( $document-uri ) = 0 and string-length( $fragment-id ) = 0">
+						<xsl:message>
+							Error: Impossible URL in XLink embedding
+							link
+						</xsl:message>
+					</xsl:when>
+
+					<!-- this case is when there is in embedded schema in the same document elsewhere -->
+					<xslt:when
+						test="string-length( $document-uri ) = 0">
+						<xslt:apply-templates mode="dsdl:go"
+							select="//*[@xml:id= $fragment-id ] | id( $fragment-id) 
+              	| //*[@id= $fragment-id ]" />
+					</xslt:when>
+
+					<xsl:when
+						test="string-length( $fragment-id ) &gt; 0">
+						<xsl:variable name="theDocument_1"
+							select="document( $document-uri,/ )" />
+						<xsl:if test="not($theDocument_1)">
+							<xsl:message terminate="no">
+								<xsl:text>Unable to open referenced included file: </xsl:text>
+								<xsl:value-of select="@xlink:href" />
+							</xsl:message>
+						</xsl:if>
+						<!-- use for-each to rebase id() to $theDocument -->
+						<xsl:for-each select="$theDocument_1">
+							<xsl:variable name="theFragment_1"
+								select="$theDocument_1//*[@xml:id= $fragment-id ]
+               | id( $fragment-id ) 
+               | $theDocument_1//*[@id= $fragment-id ]" />
+							<xsl:if test="not($theFragment_1)">
+								<xsl:message terminate="no">
+									<xsl:text>Unable to locate id attribute: </xsl:text>
+									<xsl:value-of select="@xlink:href" />
+								</xsl:message>
+							</xsl:if>
+							<xsl:apply-templates
+								select=" $theFragment_1[1]" mode="dsdl:go" />
+						</xsl:for-each>
+					</xsl:when>
+
+					<xsl:otherwise>
+						<xsl:variable name="theDocument_2"
+							select="document( $document-uri,/ )" />
+						<xsl:variable name="theFragment_2"
+							select="$theDocument_2/*" />
+
+						<xsl:if test="not($theDocument_2)">
+							<xsl:message terminate="no">
+								<xsl:text>Unable to open referenced included file: </xsl:text>
+								<xsl:value-of select="@xlink:href" />
+							</xsl:message>
+						</xsl:if>
+
+						<xsl:if test="not($theFragment_2)">
+							<xsl:message terminate="no">
+								<xsl:text>Unable to locate id attribute: </xsl:text>
+								<xsl:value-of select="@xlink:href" />
+							</xsl:message>
+						</xsl:if>
+						<xsl:apply-templates select="$theFragment_2 "
+							mode="dsdl:go" />
+					</xsl:otherwise>
+				</xsl:choose>
+
+			</xsl:otherwise>
+		</xsl:choose>
+
+		<xsl:processing-instruction name="DSDL_INCLUDE_END">
+			<xsl:value-of select="@xlink:href" />
+		</xsl:processing-instruction>
+	</xslt:template>
+
+
+</xslt:stylesheet>
\ No newline at end of file
diff --git a/.venv/lib/python3.12/site-packages/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_schematron_message.xsl b/.venv/lib/python3.12/site-packages/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_schematron_message.xsl
new file mode 100644
index 00000000..d59b8f38
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_schematron_message.xsl
@@ -0,0 +1,55 @@
+<?xml version="1.0" ?><?xar XSLT?>
+<!-- Implmentation for the Schematron XML Schema Language.
+	http://www.ascc.net/xml/resource/schematron/schematron.html
+ 
+ Copyright (c) 2000,2001 Rick Jelliffe and Academia Sinica Computing Center, Taiwan
+
+ This software is provided 'as-is', without any express or implied warranty. 
+ In no event will the authors be held liable for any damages arising from 
+ the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose, 
+ including commercial applications, and to alter it and redistribute it freely,
+ subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not claim
+ that you wrote the original software. If you use this software in a product, 
+ an acknowledgment in the product documentation would be appreciated but is 
+ not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be 
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source distribution.
+-->
+
+<!-- Schematron message -->
+
+<xsl:stylesheet
+   version="1.0"
+   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+   xmlns:axsl="http://www.w3.org/1999/XSL/TransformAlias">
+
+<xsl:import href="iso_schematron_skeleton_for_xslt1.xsl"/>
+
+<xsl:template name="process-prolog">
+   <axsl:output method="text" />
+</xsl:template>
+
+<!-- use default rule for process-root:  copy contens / ignore title -->
+<!-- use default rule for process-pattern: ignore name and see -->
+<!-- use default rule for process-name:  output name -->
+<!-- use default rule for process-assert and process-report:
+     call process-message -->
+
+<xsl:template name="process-message">
+   <xsl:param name="pattern" />
+   <xsl:param name="role" />
+   <axsl:message>
+      <xsl:apply-templates mode="text"  
+      /> (<xsl:value-of select="$pattern" />
+      <xsl:if test="$role"> / <xsl:value-of select="$role" />
+      </xsl:if>)</axsl:message>
+</xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/.venv/lib/python3.12/site-packages/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_schematron_skeleton_for_xslt1.xsl b/.venv/lib/python3.12/site-packages/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_schematron_skeleton_for_xslt1.xsl
new file mode 100644
index 00000000..b0e7175c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_schematron_skeleton_for_xslt1.xsl
@@ -0,0 +1,1796 @@
+<?xml version="1.0"?><?xar XSLT?>
+
+<!-- 
+   OVERVIEW
+   
+   ASCC/Schematron.com Skeleton Module for ISO Schematron (for XSLT1 systems)
+   
+   ISO Schematron is a language for making assertion about the presence or absence
+   of patterns in XML documents. It is typically used for as a schema language, or
+   to augment existing schema languages, and to check business rules. It is very
+   powerful, yet quite simple: a developer only need know XPath and about five other
+   elements.
+   
+   This is an open source implementation of ISO Schematron in XSLT. Although ISO does
+   not allow reference implementations which might compete with the text of the
+   standard, this code has been compiled by Rick Jelliffe, inventor of Schematron
+   and editor of the ISO standard; so developers can certainly use it as an 
+   unofficial reference implementation for clarification. 
+   
+   This implementation is based on one by Oliver Becker. API documentation is 
+   available separately; try www.schematron.com for this. Funding for this
+   stylesheet over the years has come from Topologi Pty. Ltd., Geotempo Ltd.,
+   and ASCC, Tapei.
+   
+   There are two versions of this skeleton: one is tailored for XSLT1 processors
+   and the other is tailored for XSLT2 processors. Future versions of the
+   XSLT2 skeleton may support more features than that the XSLT 1 skeleton.
+-->
+<!--
+   TIPS
+      
+   A tip for new users of Schematron: make your assertions contain positive messages
+   about what is expected, rather than error messages. For example, use the form
+   "An X should have a Y, because Z". 
+   
+   Another tip is that Schematron provides an
+   element <sch:ns> for declaring the namespaces and prefixes used in Xpaths in 
+   attribute values; it does not extend the XML Namespaces mechanism: if a name
+   in an XPath has a prefix, there must be an <sch:ns> element for that prefix; if
+   a name in an XPath does not have a prefix, it is always in no namespace.
+   
+   A tip for implementers of Schematron, either using this API or re-implementing it:
+   make the value of the diagnostics, flags and richer features available if possible;
+   Schematron has many of the optional richer features which, if implemented, provide
+   a compelling alternative approach to validation and business-rules checking compared
+   to other schema languages and programs. 
+   
+   If you create your own meta-stylesheet to override this one, it is a
+   good idea to have both in the same directory and to run the stylesheet
+   from that directory, as many XSLT implementations have ideosyncratic
+   handling of URLs: keep it simple.
+-->
+ 
+
+<!--
+  INVOCATION INFORMATION
+  
+  The following parameters are available
+  
+    phase           NMTOKEN | "#ALL" (default) Select the phase for validation
+    allow-foreign   "true" | "false" (default)   Pass non-Schematron elements to the generated stylesheet
+    sch.exslt.imports semi-colon delimited string of filenames for some EXSLT implementations  
+    message-newline "true" (default) | "false"   Generate an extra newline at the end of messages
+    optimize        "visit-no-attributes"     
+    debug	    "true" | "false" (default)  Debug mode lets compilation continue despite problems
+    attributes "true" | "false"  (Autodetecting) Use only when the schema has no attributes as the context nodes
+    only-child-elements "true" | "false" (Autodetecting) Use only when the schema has no comments
+    or PI  as the context nodes
+    
+  The following parameters can be specified as Schematron variables in diagnostics, assertions and so on.
+    fileNameParameter string	  
+    fileDirParameter string				
+    archiveNameParameter string	  In case of ZIP files
+    archiveDirParameter string	  In case of ZIP files	
+    output-encoding				  Use when outputting to XML
+ 
+ Experimental: USE AT YOUR OWN RISK   
+    visit-text "true" "false"   Also visist text nodes for context. WARNING: NON_STARDARD.
+    select-contents '' | 'key' | '//'   Select different implementation strategies
+ 
+ Conventions: Meta-stylesheets that override this may use the following parameters
+    generate-paths=true|false   generate the @location attribute with XPaths
+    diagnose= yes | no    Add the diagnostics to the assertion test in reports
+    terminate= yes | no   Terminate on the first failed assertion or successful report
+-->
+
+<!-- 
+  XSLT VERSION SUPPORT
+
+  XSLT 1:
+     A schema using the standard XSLT 1 query binding will have a /schema/@queryBinding='xslt' or 
+     nothing.
+
+       * Note: XT does not implement key() and will die if given it. 
+       * Add all formal parameters to default templates
+       * Fix missing apply-templates from process-ns and add params back
+
+  EXSLT:  Experimental support
+     A schema using the EXSLT query binding will have a /schema/@queryBinding='exslt'.
+     It is built on XSLT 1. After experience is gained, this binding is expected to be 
+     formalized as part of ISO Schematron, which currently reserves the "exslt" name for this purpose.
+
+     Some EXSLT engines have the extra functions built-in. For these, there is no need to
+     provide library locations. For engines that require the functions, either hard code
+     them in this script or provide them on the command-line argument.
+ 
+-->
+<!--
+   PROCESS INFORMATION
+   
+   This stylesheet compiles a Schematron schema (*.sch) into XSLT code (*.xsl). 
+   The generated XSLT code can then be run against an XML file (*.xml, etc) and
+   will produce validation results.
+   
+   The output of validation results is performed using named templates (process-*). 
+   These can be overridden easily by making a new XSLT stylesheet that imports this 
+   stylesheet but has its own version of the relevant process-* templates. Several
+   of these invoking stylesheets are available: "iso_svrl.xsl", for example generates
+   ISO Schematron Validation Report Language format results.
+   
+   In this version of the stylesheet, the ISO feature called "abstract patterns" is
+   implemented using macro processing: a prior XSLT stage to which converts uses
+   of abstract patterns into normal patterns. If you do not use abstract patterns,
+   it is not necessary to preprocess the schema.
+   
+   To summarize, a basic process flow for some commandline processor is like this:
+     XSLT -input=xxx.sch  -output=xxx.xsl  -stylesheet=iso_schematron_skeleton.xsl
+     XSLT -input=document.xml  -output=xxx-document.results  -stylesheet=xxx.xsl
+   
+   iso_svrl.xslt is an implementation of Schematron that can use this skeleton and
+   generate ISO SVRL reports. A process flow for some commandline processor would
+   be like this:
+     XSLT -input=xxx.sch  -output=xxx.xsl  -stylesheet=iso_svrl.xsl
+     XSLT -input=document.xml  -output=xxx-document.results  -stylesheet=xxx.xsl
+     
+   It is not impossible that ultimately a third stage, to handle macro-preprocessing
+   and inclusion, might be necessary. (The trade-off is in making this XSLT more
+   complex compared to making the outer process more complex.)
+             
+  This version has so far been tested with
+     Saxon 8
+     MSXML 4 (or 6?)   
+
+ Please note that if you are using SAXON and JAXP, then you should use 
+  System.setProperty("javax.xml.transform.TransformerFactory",
+                          "net.sf.saxon.TransformerFactoryImpl");
+ rather than 
+  System.setProperty("javax.xml.xpath.TransformerFactory",
+                           "net.sf.saxon.TransformerFactoryImpl");
+ which is does not work, at least for the versions of SAXON we tried.
+-->
+<!--
+ LEGAL INFORMATION
+ 
+ Copyright (c) 2000-2008 Rick Jelliffe and Academia Sinica Computing Center, Taiwan
+
+ This software is provided 'as-is', without any express or implied warranty. 
+ In no event will the authors be held liable for any damages arising from 
+ the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose, 
+ including commercial applications, and to alter it and redistribute it freely,
+ subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not claim
+ that you wrote the original software. If you use this software in a product, 
+ an acknowledgment in the product documentation would be appreciated but is 
+ not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be 
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source distribution.
+-->
+<!--
+  NOTE: Compared to the iso_schematron_skeleton_for_saxon.xsl code, this version is currently missing
+     1) localization
+     2) properties
+     3) pattern/@documents
+
+  VERSION INFORMATION 
+   2009-02-25 RJ
+        * Fix up variable names so none are used twice in same template
+        * Tested on SAXON 9, Xalan 2.7.1. Partly tested MSXML.  
+   2008-09-19 RJ
+        * Add mode schematron-select-full-path and param full-path-notation 
+   
+   2008-08-11
+   		* TT report/@flag was missing
+   2008-08-06
+   		* TT Top-level lets need to be implemented using xsl:param not xsl:variable
+   		* TT xsl:param/@select must have XPath or not be specified
+   		
+    Version: 2008-07-28
+   		* KH schematron-get-full-path-3 has [index] even on top step
+   		* RJ fix schematron-get-full-path to have namespace predicate, I don't know why this was removed
+   		
+   Version: 2008-07-24
+   		* RJ clean out commented out namespace handling code
+   		* RJ add support for experimental non-standard attribute report/@action
+   		and assert/@action, and add parameter not in the published API (should
+   		not break anything, it is XSLT1)
+   		* RJ Remove remaining XSLT2 code for ease of reading
+   		
+   Version: 2008-07-14 minor update for inclusion experiments
+   	* RJ Clean up zero-length fragment test on include
+   	* RJ Add experimental support for include containers 
+   	* RJ For path generation, test for //iso:schema not just /iso:schema, for potential embedded Schematron support   
+   	* RJ Don't generate double error messages for old namespace elements
+   	* RJ Experimental iso:rule/iso:title just kept as comment (bigger request Uche Ogbuji)
+   	* RJ Remove spurious debug messages
+   	* RJ Fix bug that prevented including patterns in this (report Roger
+   	Costello)
+  
+   Version: 2007-10-17
+     From this version on I am forking XSLT2 support to a different version of the script.
+     This is due to the increasingly horrible state of the namespace handling code as well
+     as other inconsistencies between the major implementations of different versions.
+     The intent is that future versions of this will have XSLT2 isms removed and be simplified
+     to cope with only XSLT1 and EXLST. Note that though this version is called
+     iso_schematron_skeleton_for_xslt1, the various meta-stylesheets will continue to just call
+     iso_schematron_skeleton: it is up to you to rename the stylesheet to the one you want to
+     use.
+
+       * RJ fix FULL-PATH problem with attribute names
+
+
+   Version: 2007-07-19
+     Accept most changes in David Carlisle's fork, but continue as XSLT1 script: 
+    	http://dpcarlisle.blogspot.com/search/label/schematron
+    	* DPC Remove "optimize" parameter
+    	* DPC Add autodetecting optimize parameter attribute to skip checking attribute
+    	context
+    	* DPC Add autodetecting optimize parameter only-child-elements turn off checking for 
+    	comments and PIs
+    	* DPC (Experimental: NON_STANDARD DANGER!) Add param visit-text to viist text
+    	nodes too for context 
+    	* DPC Fix inclusion syntax to allow #
+    	* DPC Priorities count up from 1000 not down from 4000 to allow more rules
+        * RJ Add new template for titles of schemas, with existing behaviour.  
+        Override process-schema-title for custom processing of title
+    		
+    
+   Version: 2007-04-04
+   	* RJ debug mode param
+	* RJ alter mixed test to only test mixed branches, so the same document
+	could have old and new namespaces schemas in it, but each schema must
+	be distinct, just so as not to overconstrain things.
+   	* KH zero-length include/@href is fatal error, but allow debug mode
+	* SB add hint on SAXON and JAXP
+	* DC generate-full-path-1 generates XLST1 code by default
+   Version: 2007-03-05
+      	* AS Typo for EXSLT randome, improve comment
+      	* KH get-schematron-full-path-2 needs to apply to attributes too
+      	* DP document policy on extensions better
+      	* DC use copy-of not copy for foreign elements
+      	* DC add generate-path-2
+      	* DC don't try to apply templates to attribute axis on attribute nodes, to
+      	stop SAXON warning.
+      	* RJ improve reporting of typos 
+   
+   Version: 2007-02-08
+   		* KH Schematron fullpath implementation: @* handled twice and / missing
+   		* KH Change stylesheetbody from named template to mode to allow implementers more flexibility.
+   		  Move process-ns to outside the stylesheet body.
+   		* DP, FG, fix handling of xslt:key
+   		* FG no iso:title/@class
+   		* Experimental optimization 'visit-no-attributes'
+   		* KH Experimental added schematron-get-full-path-2 which gives prefixed version for humans
+ 		* DC Move stylesheet/@version generation to after namespace handling
+ 		* DC, FG EXSLT namespace handling code
+ 		* FG add ref and commented code from FG's page on namespaces
+ 		* Start adding normalize-space() to parameter code
+ 		* Add a space between diagnostics
+   		   		 
+   Version: 2007-01-22
+   	* DP change = ($start) to = $start and =($phase) to =$phase 
+   	to run under Saxon 8.8j
+	* FG better title section using ( @id | sch:title)[last()]
+	* Default query language binding is "xslt" not "xslt1"
+  
+   Version: 2007-01-19
+   		* Simplify message newline code
+   		* Remove termination and xpath appending to message options: 
+   		   factor out as  iso_schematron_terminator.xsl
+   		* Comment out XSLT2 namespace fix temporarily
+  
+   Version: 2007-01-18 (First beta candidate for comment)
+          * DC remove xml:space="preserve"
+          * FG improve comment on import statement
+          * DC improve comments on invocation section
+          * Add exploratory support for sch:schema[@queryBinding='xpath']
+             by allowing it and warning as lets are found
+          * Be strict about queryBinding spelling errors
+          * Extra comments on the different queryBindings
+          * KH Add option "message-paths" to generate XPath from output 
+          * KH Add option "terminate" to halt with an error after the first assertion
+          * KH refactor paths in schematron-full-path
+          * Improve (?) namespace handling: no dummy attributes for prefix "xsl" generated
+   
+   Version: 2007-01-15
+          * FG fix for calling templates
+          * Add formal parameters to default templates: may help XSLT 2
+          * Fix get-schematron-full-path
+          * Include skeleton1-6 is commented out by default
+
+   Version:2007-01-12 (Pre-beta release to Schematron-love-in maillist)
+           * Add many extra parameters to the process-* calls, so that almost
+           all the information in the schema can be provided to client programs.
+           Also, rearrange the parameters to fit in with the ISO schema, which
+           has "rich" and "linkable" attribute groups.
+           * Warn on diagnostics with no ID once only
+           * Improved path reporting, to handle for namespaces
+           * Add process-title dummy template for API
+           * Add command-line parameter allow-foreign (true|false) to suppress
+            warnings one foreign elements and pass them through to the generated
+            stylesheet
+           * remove legacy templates for the old ASCC namespace and no namespace, 
+              and use an import statement instead. Much cleaner now!
+           * patterns use @id not @name
+           * titles can contain sub-elements
+           * start change sch:rule to allow attributes, PIs and comments 
+           * the default process-* for inline elements add a leading and trailing 
+             space, to reduce the chance of concatenation.
+           * add comments to make the generated code clearer
+           
+   Version:2006-11-07 (ISO: first release private to schematron-love-in maillist for review)
+           * Duplicate pattern templates, for handling ISO namespace
+           * Add priority onto default and paragraph templates
+           * Add namespace checks
+           * Handle key in xsl namespace not iso
+           * Add include
+           * Improve namespace handling
+           * Preliminary XSLT2 and EXSLT support
+	       * Refactor iso:schema for clarity
+
+    Version: 2003-05-26 
+    	    * Fix bug with key 
+    Version: 2003-04-16
+    	   * handle 1.6 let expressions
+    	   * make key use XSLT names, and allow anywhere
+    Version: 2001-06-13
+           * same skeleton now supports namespace or no namespace
+           * parameters to handlers updated for all 1.5 attributes 
+           * diagnostic hints supported: command-line option diagnose=yes|no
+           * phases supported: command-line option phase=#ALL|...
+           * abstract rules
+           * compile-time error messages  
+	   * add utility routine generate-id-from-path
+          
+    Contributors: Rick Jelliffe (original), Oliver Becker (architecture, XSLT2), 
+             Miloslav Nic (diagnostic, phase, options), Ludwig Svenonius (abstract)
+             Uche Ogbuji (misc. bug fixes), Jim Ancona (SAXON workaround),
+	 	     Francis Norton (generate-id-from-path), Robert Leftwich, Bryan Rasmussen,
+             Dave Pawson (include, fallback), Florent Georges (namespaces, exslt, attribute
+             context), Benoit Maisonny (attribute context), John Dumps (process-message newline),
+             Cliff Stanford (diagnostics and other newlines)
+
+    
+    KNOWN TYPICAL LIMITATIONS:
+      * Don't use <sch:ns prefix="xsl" .../> with a namespace other than the standard
+      XSLT one. This would be a bizarre thing to do anyway. 
+      * Don't use other prefixes for the XSLT namespace either; some implementations will
+      not handle it correctly.
+     
+     EXTENSIONS:
+      ISO Schematron is designed as a framework with some standard query language
+      bindings. If you need to support other features, please do so safely by making
+      up your own @queryLanguage name: this makes it clear that your schema requires
+      special features. For example, default ISO Schematron does not support user
+      defined functions; so if you want to use the user defined function feature
+      in XSLT, you need to have a schema with some queryBinding attribute name like
+      "XSLT-with-my-functions" or whatever.
+-->
+
+
+
+
+<xsl:stylesheet version="1.0" 
+	xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
+	xmlns:axsl="http://www.w3.org/1999/XSL/TransformAlias" 
+	xmlns:sch="http://www.ascc.net/xml/schematron"
+    xmlns:iso="http://purl.oclc.org/dsdl/schematron" 
+    xmlns:exsl="http://exslt.org/common"
+    xmlns:msxsl="urn:schemas-microsoft-com:xslt"
+    extension-element-prefixes="exsl  msxsl"
+	 >
+<!-- This program implements ISO Schematron, except for abstract patterns which require a preprocess. -->
+  
+
+<xsl:namespace-alias stylesheet-prefix="axsl" result-prefix="xsl"/>
+
+
+<!-- Category: top-level-element -->
+<xsl:output method="xml" omit-xml-declaration="no" standalone="yes"  indent="yes"/>
+
+
+<xsl:param name="phase">
+  <xsl:choose>
+    <xsl:when test="//sch:schema/@defaultPhase">
+      <xsl:value-of select="//sch:schema/@defaultPhase"/>
+    </xsl:when>   
+    <xsl:when test="//iso:schema/@defaultPhase">
+      <xsl:value-of select="//iso:schema/@defaultPhase"/>
+    </xsl:when>
+    <xsl:otherwise>#ALL</xsl:otherwise>
+  </xsl:choose>
+</xsl:param>
+
+<xsl:param name="allow-foreign">false</xsl:param>
+
+<xsl:param name="message-newline">true</xsl:param>
+
+<!-- DPC set to true if contexts should be checked on attribute nodes
+         defaults to true if there is any possibility that a context could match an attribute,
+         err on the side if caution, a context of *[.='@'] would cause this param to defualt to true
+         even though @ is in a string
+-->
+<xsl:param name="attributes">
+  <xsl:choose>
+    <xsl:when test="//iso:rule[contains(@context,'@') or contains(@context,'attribute')]">true</xsl:when>
+    <xsl:otherwise>false</xsl:otherwise>
+  </xsl:choose>
+</xsl:param>
+
+<!-- DPC set to true if contexts should be checked on just elements in the child axis
+         defaults to true if there is any possibility that a context could match an comment or PI
+         err on the side if caution, a context of *[.='('] would cause this param to defualt to true
+         even though ( is in a string, but node() comment() and processing-instruction()  all have a (
+-->
+<xsl:param name="only-child-elements">
+  <xsl:choose>
+    <xsl:when test="//iso:rule[contains(@context,'(')]">true</xsl:when>
+    <xsl:otherwise>false</xsl:otherwise>
+  </xsl:choose>
+</xsl:param>
+
+<!-- DPC set to true if contexts should be checked on text nodes nodes (if only-child-elements is false)
+         THIS IS NON CONFORMANT BEHAVIOUR JUST FOR DISCUSSION OF A POSSIBLE CHANGE TO THE
+         SPECIFICATION. THIS PARAM SHOULD GO IF THE FINAL DECISION IS THAT THE SPEC DOES NOT CHANGE.
+	 Always defaults to false
+-->
+<xsl:param name="visit-text" select="'false'"/>
+
+<!-- DPC
+  When selecting contexts the specified behaviour is
+    @*|node()[not(self::text())]
+    The automatic settings may use
+      node()[not(self::text())]
+      @*|*
+      *
+  instead for schema for which they are equivalent.
+  If the params are set explictly the above may be used, and also either if
+      @*
+      @*|node()
+   in all cases the result may not be equivalent, for example if you specify no attributes and the schema 
+   does have attribute contexts they will be silently ignored.
+
+  after testing it turns out that
+  node()[not(self::text())] is slower in saxon than *|comment()|processing-instruction() 
+  which I find a bit surprising but anyway I'll use the longr faster version.
+-->
+<xsl:variable name="context-xpath">
+  <xsl:if test="$attributes='true'">@*|</xsl:if>
+  <xsl:choose>
+    <xsl:when test="$only-child-elements='true'">*</xsl:when>
+    <xsl:when test="$visit-text='true'">node()</xsl:when>
+    <xsl:otherwise>*|comment()|processing-instruction()</xsl:otherwise>
+  </xsl:choose>
+</xsl:variable>
+
+<!-- DPC if this is set to 
+    '' use recursive templates to iterate over document tree,
+    'key' select  all contexts with a key rather than walking the tree explictly in each mode
+    '//' select all contexts with // a key rather than walking the tree explictly in each mode (XSLT2 only)
+-->
+<xsl:param name="select-contexts" select="''"/>
+
+
+<xsl:param name="output-encoding"/>
+<!-- e.g. saxon file.xml file.xsl "sch.exslt.imports=.../string.xsl;.../math.xsl" -->
+<xsl:param name="sch.exslt.imports"/>
+
+<!-- Set the language code for messages -->
+<xsl:param name="langCode">default</xsl:param>
+
+<xsl:param name="debug">false</xsl:param>
+
+
+<!-- Set the default for schematron-select-full-path, i.e. the notation for svrl's @location-->
+<xsl:param name="full-path-notation">1</xsl:param>
+
+<!-- Simple namespace check -->
+<xsl:template match="/">
+    <xsl:if  test="//sch:*[ancestor::iso:* or descendant::iso:*]">
+	<xsl:message>Schema error: Schematron elements in old and new namespaces found</xsl:message>
+	<xsl:if test=" $debug = 'false' " />
+    </xsl:if>
+
+    <xsl:apply-templates />
+</xsl:template>
+
+
+<!-- ============================================================== -->
+<!-- ISO SCHEMATRON SCHEMA ELEMENT  -->
+<!-- Not handled: Abstract patterns. A pre-processor is assumed. -->
+<!-- ============================================================== -->
+
+<!-- SCHEMA -->
+<!-- Default uses XSLT 1 -->
+<xsl:template match="iso:schema[not(@queryBinding) or @queryBinding='xslt' 
+     or @queryBinding='xslt1' or @queryBinding='XSLT' or @queryBinding='XSLT1'
+     or @queryBinding='xpath']">
+     <xsl:if test="
+	     @queryBinding='xslt1' or @queryBinding='XSLT' or @queryBinding='XSLT1'">
+	     <xsl:message>Schema error: in the queryBinding attribute, use 'xslt'</xsl:message>
+	</xsl:if>
+	<axsl:stylesheet>
+	    <xsl:apply-templates select="iso:ns"/>
+	    <!-- Handle the namespaces before the version attribute: reported to help SAXON -->
+	    <xsl:attribute name="version">1.0</xsl:attribute>
+	    
+		<xsl:apply-templates select="." mode="stylesheetbody"/>
+		<!-- was xsl:call-template name="stylesheetbody"/ -->
+	</axsl:stylesheet>
+</xsl:template>
+
+<!-- Using EXSLT with all modeles (except function module: not applicable) -->
+<xsl:template match="iso:schema[@queryBinding='exslt']" priority="10">
+    <xsl:comment>This XSLT was automatically generated from a Schematron schema.</xsl:comment>
+	<axsl:stylesheet
+ 	  	xmlns:date="http://exslt.org/dates-and-times"
+ 	  	xmlns:dyn="http://exslt.org/dynamic"
+		xmlns:exsl="http://exslt.org/common"
+		xmlns:math="http://exslt.org/math"
+   		xmlns:random="http://exslt.org/random"
+  		xmlns:regexp="http://exslt.org/regular-expressions"
+   		xmlns:set="http://exslt.org/sets"
+   		xmlns:str="http://exslt.org/strings"
+   		extension-element-prefixes="date dyn exsl math random regexp set str" >
+	
+        <xsl:apply-templates select="iso:ns"/>
+	    <!-- Handle the namespaces before the version attribute: reported to help SAXON -->
+	    <xsl:attribute name="version">1.0</xsl:attribute>
+	    
+	    <xsl:apply-templates select="." mode="stylesheetbody"/>
+		<!-- was xsl:call-template name="stylesheetbody"/ -->
+	</axsl:stylesheet>
+</xsl:template>
+
+
+<!-- Default uses XSLT 1 -->
+<xsl:template match="iso:schema" priority="-1">
+	<xsl:message terminate="yes" >Fail: This implementation of ISO Schematron does not work with 
+	schemas using the "<xsl:value-of select="@queryBinding"/>" query language.</xsl:message>        
+</xsl:template>
+
+<xsl:template match="*" mode="stylesheetbody">
+	<!--xsl:template name="stylesheetbody"-->
+    <xsl:comment>Implementers: please note that overriding process-prolog or process-root is 
+    the preferred method for meta-stylesheets to use where possible. </xsl:comment><xsl:text>&#10;</xsl:text>
+
+   <!-- These parameters may contain strings with the name and directory of the file being
+   validated. For convenience, if the caller only has the information in a single string,
+   that string could be put in fileDirParameter. The archives parameters are available
+   for ZIP archives.
+	-->
+
+	<axsl:param name="archiveDirParameter" />
+	<axsl:param name="archiveNameParameter" />
+	<axsl:param name="fileNameParameter" />
+	<axsl:param name="fileDirParameter" />
+
+    <xsl:call-template name="iso:exslt.add.imports" />
+    <xsl:text>&#10;&#10;</xsl:text><xsl:comment>PHASES</xsl:comment><xsl:text>&#10;</xsl:text>
+	<xsl:call-template name="handle-phase"/>
+    <xsl:text>&#10;&#10;</xsl:text><xsl:comment>PROLOG</xsl:comment><xsl:text>&#10;</xsl:text>
+	<xsl:call-template name="process-prolog"/>
+    <xsl:text>&#10;&#10;</xsl:text><xsl:comment>KEYS</xsl:comment><xsl:text>&#10;</xsl:text>
+	<xsl:apply-templates mode="do-keys"   select="xsl:key  "/>
+    <xsl:text>&#10;&#10;</xsl:text><xsl:comment>DEFAULT RULES</xsl:comment><xsl:text>&#10;</xsl:text>
+    <xsl:call-template name="generate-default-rules" />
+    <xsl:text>&#10;&#10;</xsl:text><xsl:comment>SCHEMA METADATA</xsl:comment><xsl:text>&#10;</xsl:text>
+    <xsl:call-template name="handle-root"/>
+    <xsl:text>&#10;&#10;</xsl:text><xsl:comment>SCHEMATRON PATTERNS</xsl:comment><xsl:text>&#10;</xsl:text>
+ 
+	<xsl:apply-templates select="*[not(self::iso:ns)] " />
+</xsl:template>
+ 
+    <xsl:template name="iso:exslt.add.imports">
+      <xsl:param name="imports" select="$sch.exslt.imports"/>
+      <xsl:choose>
+        <xsl:when test="contains($imports, ';')">
+          <axsl:import href="{ substring-before($imports, ';') }"/>
+          <xsl:call-template name="iso:exslt.add.imports">
+            <xsl:with-param name="imports"  select="substring-after($imports, ';')"/>
+          </xsl:call-template>
+        </xsl:when>
+        <xsl:when test="$imports">
+          <axsl:import href="{ $imports }"/>
+        </xsl:when>
+      </xsl:choose>
+    </xsl:template>
+
+<xsl:template name="handle-phase" >
+	<xsl:if test="not(normalize-space( $phase ) = '#ALL')">
+	  <xsl:if test="not(iso:phase[@id = normalize-space( $phase )])">
+		  <xsl:message>Phase Error: no phase with name <xsl:value-of select="normalize-space( $phase )"
+		  /> has been defined.</xsl:message>
+	  </xsl:if>
+     </xsl:if>
+</xsl:template>
+
+<xsl:template name="generate-default-rules">
+		<xsl:text>&#10;&#10;</xsl:text>
+		<xsl:comment>MODE: SCHEMATRON-SELECT-FULL-PATH</xsl:comment><xsl:text>&#10;</xsl:text>
+		<xsl:comment>This mode can be used to generate an ugly though full XPath for locators</xsl:comment><xsl:text>&#10;</xsl:text>
+   		<axsl:template match="*" mode="schematron-select-full-path">
+   			<xsl:choose>
+   				<xsl:when test=" $full-path-notation = '1' ">
+   					<!-- Use for computers, but rather unreadable for humans -->
+					<axsl:apply-templates select="." mode="schematron-get-full-path"/>
+				</xsl:when>
+   				<xsl:when test=" $full-path-notation = '2' ">
+   					<!-- Use for humans, but no good for paths unless namespaces are known out-of-band -->
+					<axsl:apply-templates select="." mode="schematron-get-full-path-2"/>
+				</xsl:when>
+   				<xsl:when test=" $full-path-notation = '3' "> 
+   					<!-- Obsolescent. Use for humans, but no good for paths unless namespaces are known out-of-band -->
+					<axsl:apply-templates select="." mode="schematron-get-full-path-3"/>
+				</xsl:when>
+
+                   <xsl:otherwise >
+                       <!-- Use for computers, but rather unreadable for humans -->
+                    <axsl:apply-templates select="." mode="schematron-get-full-path"/>
+                </xsl:otherwise>
+			</xsl:choose>
+		</axsl:template>
+	
+
+		<xsl:text>&#10;&#10;</xsl:text>
+		<xsl:comment>MODE: SCHEMATRON-FULL-PATH</xsl:comment><xsl:text>&#10;</xsl:text>
+		<xsl:comment>This mode can be used to generate an ugly though full XPath for locators</xsl:comment><xsl:text>&#10;</xsl:text>
+   		<axsl:template match="*" mode="schematron-get-full-path">
+			<axsl:apply-templates select="parent::*" mode="schematron-get-full-path"/>
+			
+			<!-- XSLT1 syntax -->
+
+			<axsl:text>/</axsl:text>
+			<axsl:choose>
+			<axsl:when test="namespace-uri()=''">
+			<axsl:value-of select="name()"/>
+			<axsl:variable name="p_1" select="1+
+			count(preceding-sibling::*[name()=name(current())])" />
+		<axsl:if test="$p_1&gt;1 or following-sibling::*[name()=name(current())]">
+		  <xsl:text/>[<axsl:value-of select="$p_1"/>]<xsl:text/>
+		</axsl:if>
+		</axsl:when>
+		<axsl:otherwise>
+		<axsl:text>*[local-name()='</axsl:text>
+		<axsl:value-of select="local-name()"/><axsl:text>' and namespace-uri()='</axsl:text>
+		<axsl:value-of select="namespace-uri()"/>
+		<axsl:text>']</axsl:text>
+		<axsl:variable name="p_2" select="1+
+		count(preceding-sibling::*[local-name()=local-name(current())])" />
+		<axsl:if test="$p_2&gt;1 or following-sibling::*[local-name()=local-name(current())]">
+		  <xsl:text/>[<axsl:value-of select="$p_2"/>]<xsl:text/>
+		</axsl:if>
+		</axsl:otherwise>
+		</axsl:choose> 
+       	 	</axsl:template>
+       	 	
+       	 	
+		<axsl:template match="@*" mode="schematron-get-full-path">
+		
+			<!-- XSLT1 syntax -->
+		<axsl:text>/</axsl:text>
+		<axsl:choose>
+		<axsl:when test="namespace-uri()=''">@<axsl:value-of
+		select="name()"/></axsl:when>
+		<axsl:otherwise>
+		<axsl:text>@*[local-name()='</axsl:text>
+		<axsl:value-of select="local-name()"/>
+		<axsl:text>' and namespace-uri()='</axsl:text>
+		<axsl:value-of select="namespace-uri()"/>
+		<axsl:text>']</axsl:text>
+		</axsl:otherwise>
+		</axsl:choose>   
+
+		</axsl:template>
+	
+	
+	<xsl:text>&#10;&#10;</xsl:text>
+	
+	<xsl:comment>MODE: SCHEMATRON-FULL-PATH-2</xsl:comment>
+	<xsl:text>&#10;</xsl:text>
+	<xsl:comment>This mode can be used to generate prefixed XPath for humans</xsl:comment>
+	<xsl:text>&#10;</xsl:text>
+	<!--simplify the error messages by using the namespace prefixes of the
+     instance rather than the generic namespace-uri-styled qualification-->
+	<axsl:template match="node() | @*" mode="schematron-get-full-path-2">
+	<!--report the element hierarchy-->
+		<axsl:for-each select="ancestor-or-self::*">
+			<axsl:text>/</axsl:text>
+			<axsl:value-of select="name(.)"/>
+			<axsl:if test="preceding-sibling::*[name(.)=name(current())]">
+				<axsl:text>[</axsl:text>
+				<axsl:value-of
+					select="count(preceding-sibling::*[name(.)=name(current())])+1"/>
+				<axsl:text>]</axsl:text>
+			</axsl:if>
+		</axsl:for-each>
+		<!--report the attribute-->
+		<axsl:if test="not(self::*)">
+			<axsl:text/>/@<axsl:value-of select="name(.)"/>
+		</axsl:if>
+	</axsl:template>
+
+		<xsl:text>&#10;&#10;</xsl:text>
+		<xsl:comment>MODE: GENERATE-ID-FROM-PATH </xsl:comment><xsl:text>&#10;</xsl:text>
+		<!-- repeatable-id maker derived from Francis Norton's. -->
+		<!-- use this if you need generate ids in separate passes,
+		     because generate-id() is not guaranteed to produce the same
+		     results each time. These ids are not XML names but closer to paths. -->
+		<axsl:template match="/" mode="generate-id-from-path"/>
+		<axsl:template match="text()" mode="generate-id-from-path">
+			<axsl:apply-templates select="parent::*" mode="generate-id-from-path"/>
+			<axsl:value-of select="concat('.text-', 1+count(preceding-sibling::text()), '-')"/>
+		</axsl:template>
+		<axsl:template match="comment()" mode="generate-id-from-path">
+			<axsl:apply-templates select="parent::*" mode="generate-id-from-path"/>
+			<axsl:value-of select="concat('.comment-', 1+count(preceding-sibling::comment()), '-')"/>
+		</axsl:template>
+		<axsl:template match="processing-instruction()" mode="generate-id-from-path">
+			<axsl:apply-templates select="parent::*" mode="generate-id-from-path"/>
+			<axsl:value-of 
+			select="concat('.processing-instruction-', 1+count(preceding-sibling::processing-instruction()), '-')"/>
+		</axsl:template>
+		<axsl:template match="@*" mode="generate-id-from-path">
+			<axsl:apply-templates select="parent::*" mode="generate-id-from-path"/>
+			<axsl:value-of select="concat('.@', name())"/>
+		</axsl:template>
+		<axsl:template match="*" mode="generate-id-from-path" priority="-0.5">
+			<axsl:apply-templates select="parent::*" mode="generate-id-from-path"/>
+			<axsl:text>.</axsl:text>
+<!--
+			<axsl:choose>
+				<axsl:when test="count(. | ../namespace::*) = count(../namespace::*)">
+					<axsl:value-of select="concat('.namespace::-',1+count(namespace::*),'-')"/>
+				</axsl:when>
+				<axsl:otherwise>
+-->
+				<axsl:value-of 
+				select="concat('.',name(),'-',1+count(preceding-sibling::*[name()=name(current())]),'-')"/>
+<!--
+				</axsl:otherwise>
+			</axsl:choose>
+-->
+		</axsl:template>
+		
+		
+	<xsl:comment>MODE: SCHEMATRON-FULL-PATH-3</xsl:comment>
+	
+	<xsl:text>&#10;</xsl:text>
+	<xsl:comment>This mode can be used to generate prefixed XPath for humans 
+	(Top-level element has index)</xsl:comment>
+	<xsl:text>&#10;</xsl:text>
+	<!--simplify the error messages by using the namespace prefixes of the
+     instance rather than the generic namespace-uri-styled qualification-->
+	<axsl:template match="node() | @*" mode="schematron-get-full-path-3">
+	<!--report the element hierarchy-->
+		<axsl:for-each select="ancestor-or-self::*">
+			<axsl:text>/</axsl:text>
+			<axsl:value-of select="name(.)"/>
+			<axsl:if test="parent::*">
+				<axsl:text>[</axsl:text>
+				<axsl:value-of
+					select="count(preceding-sibling::*[name(.)=name(current())])+1"/>
+				<axsl:text>]</axsl:text>
+			</axsl:if>
+		</axsl:for-each>
+		<!--report the attribute-->
+		<axsl:if test="not(self::*)">
+			<axsl:text/>/@<axsl:value-of select="name(.)"/>
+		</axsl:if>
+	</axsl:template>
+
+		<xsl:text>&#10;&#10;</xsl:text>
+		<xsl:comment>MODE: GENERATE-ID-2 </xsl:comment><xsl:text>&#10;</xsl:text>
+		<!-- repeatable-id maker from David Carlisle. -->
+		<!-- use this if you need generate IDs in separate passes,
+		     because generate-id() is not guaranteed to produce the same
+		     results each time. These IDs are well-formed XML NMTOKENS -->
+	<axsl:template match="/" mode="generate-id-2">U</axsl:template>
+
+	<axsl:template match="*" mode="generate-id-2" priority="2">
+		<axsl:text>U</axsl:text>
+		<axsl:number level="multiple" count="*"/>
+	</axsl:template>
+
+	<axsl:template match="node()" mode="generate-id-2">
+		<axsl:text>U.</axsl:text>
+		<axsl:number level="multiple" count="*"/>
+		<axsl:text>n</axsl:text>
+		<axsl:number count="node()"/>
+	</axsl:template>
+
+	<axsl:template match="@*" mode="generate-id-2">
+		<axsl:text>U.</axsl:text>
+		<axsl:number level="multiple" count="*"/>
+		<axsl:text>_</axsl:text>
+		<axsl:value-of select="string-length(local-name(.))"/>
+		<axsl:text>_</axsl:text>
+		<axsl:value-of select="translate(name(),':','.')"/>
+	</axsl:template> 
+
+
+		<xsl:comment>Strip characters</xsl:comment>
+		<axsl:template match="text()" priority="-1" />
+			
+  </xsl:template>
+
+ <xsl:template name="handle-root">
+		<!-- Process the top-level element -->
+		<axsl:template match="/">
+			<xsl:call-template name="process-root">
+				<xsl:with-param 	
+				name="title" select="(@id | iso:title)[last()]"/>
+				<xsl:with-param name="version" select="'iso'" />
+				<xsl:with-param name="schemaVersion" select="@schemaVersion" />
+				<xsl:with-param name="queryBinding" select="@queryBinding" />
+				<xsl:with-param name="contents">
+					<xsl:apply-templates mode="do-all-patterns"/>
+				</xsl:with-param>
+				
+				<!-- "Rich" properties -->
+				<xsl:with-param name="fpi" select="@fpi"/>
+				<xsl:with-param name="icon" select="@icon"/>
+				<xsl:with-param name="id" select="@id"/>
+				<xsl:with-param name="lang" select="@xml:lang"/>
+				<xsl:with-param name="see" select="@see" />
+				<xsl:with-param name="space" select="@xml:space" />
+				
+				
+				<!-- Non-standard extensions not part of the API yet -->
+				<xsl:with-param name="action" select="@action" />
+			</xsl:call-template>
+		</axsl:template>
+ 
+      
+</xsl:template>
+
+<!-- ============================================================== -->
+<!-- ISO SCHEMATRON ELEMENTS -->
+<!-- ============================================================== -->
+
+	<!-- ISO ACTIVE -->
+	<xsl:template match="iso:active">
+                <xsl:if test="not(@pattern)">
+                    <xsl:message>Markup Error: no pattern attribute in &lt;active></xsl:message>
+                </xsl:if>
+
+                <xsl:if test="not(../../iso:pattern[@id = current()/@pattern])
+                and not(../../iso:include)">
+                           <xsl:message>Reference Error: the pattern  "<xsl:value-of select="@pattern"
+						   />" has been activated but is not declared</xsl:message>
+                </xsl:if>
+        </xsl:template>
+
+	<!-- ISO ASSERT and REPORT -->
+	<xsl:template match="iso:assert">
+  
+                <xsl:if test="not(@test)">
+                    <xsl:message>Markup Error: no test attribute in &lt;assert</xsl:message>
+                </xsl:if>
+        <xsl:text>&#10;&#10;		</xsl:text>
+		<xsl:comment>ASSERT <xsl:value-of select="@role" /> </xsl:comment><xsl:text>&#10;</xsl:text>      
+	
+		<axsl:choose>
+			<axsl:when test="{@test}"/>
+			<axsl:otherwise>
+				<xsl:call-template name="process-assert">
+					<xsl:with-param name="test" select="normalize-space(@test)" />
+					<xsl:with-param name="diagnostics" select="@diagnostics"/>
+					<xsl:with-param name="flag" select="@flag"/>
+					
+					<!-- "Rich" properties -->
+					<xsl:with-param name="fpi" select="@fpi"/>
+					<xsl:with-param name="icon" select="@icon"/>
+					<xsl:with-param name="id" select="@id"/>
+					<xsl:with-param name="lang" select="@xml:lang"/>
+					<xsl:with-param name="see" select="@see" />
+					<xsl:with-param name="space" select="@xml:space" />
+					
+					<!-- "Linking" properties -->
+					<xsl:with-param name="role" select="@role" />
+					<xsl:with-param name="subject" select="@subject" />
+				</xsl:call-template>
+ 			
+			</axsl:otherwise>
+		</axsl:choose>
+	</xsl:template>
+	<xsl:template match="iso:report">
+		 
+                <xsl:if test="not(@test)">
+                    <xsl:message>Markup Error: no test attribute in &lt;report></xsl:message>
+                </xsl:if>
+                
+        <xsl:text>&#10;&#10;		</xsl:text>
+		<xsl:comment>REPORT <xsl:value-of select="@role" /> </xsl:comment><xsl:text>&#10;</xsl:text>      
+	
+		<axsl:if test="{@test}">
+		
+			<xsl:call-template name="process-report">
+				<xsl:with-param name="test" select="normalize-space(@test)" />
+				<xsl:with-param name="diagnostics" select="@diagnostics"/>
+					<xsl:with-param name="flag" select="@flag"/>
+					
+					<!-- "Rich" properties -->
+					<xsl:with-param name="fpi" select="@fpi"/>
+					<xsl:with-param name="icon" select="@icon"/>
+					<xsl:with-param name="id" select="@id"/>
+					<xsl:with-param name="lang" select="@xml:lang"/>
+					<xsl:with-param name="see" select="@see" />
+					<xsl:with-param name="space" select="@xml:space" />
+					
+					<!-- "Linking" properties -->
+					<xsl:with-param name="role" select="@role" />
+					<xsl:with-param name="subject" select="@subject" />
+			</xsl:call-template>
+				
+		</axsl:if>
+	</xsl:template>
+
+
+	<!-- ISO DIAGNOSTIC -->
+	<!-- We use a mode here to maintain backwards compatability, instead of adding it
+	     to the other mode.
+	-->
+	<xsl:template match="iso:diagnostic" mode="check-diagnostics">
+              <xsl:if test="not(@id)">
+                    <xsl:message>Markup Error: no id attribute in &lt;diagnostic></xsl:message>
+               </xsl:if>
+    </xsl:template>
+    
+    <xsl:template match="iso:diagnostic"  >
+                <xsl:call-template name="process-diagnostic">
+                
+					<!-- "Rich" properties -->
+					<xsl:with-param name="fpi" select="@fpi"/>
+					<xsl:with-param name="icon" select="@icon"/>
+					<xsl:with-param name="id" select="@id"/>
+					<xsl:with-param name="lang" select="@xml:lang"/>
+					<xsl:with-param name="see" select="@see" />
+					<xsl:with-param name="space" select="@xml:space" />
+               </xsl:call-template>
+        </xsl:template>
+
+	<!-- ISO DIAGNOSTICS -->
+	<xsl:template match="iso:diagnostics" >
+		<xsl:apply-templates mode="check-diagnostics" select="*" />
+	</xsl:template>
+
+	<!-- ISO DIR -->
+	<xsl:template match="iso:dir"  mode="text" >
+		<xsl:call-template name="process-dir">
+			<xsl:with-param name="value" select="@value"/>
+		</xsl:call-template>
+	</xsl:template>
+
+	<!-- ISO EMPH -->
+	<xsl:template match="iso:emph"  mode="text">
+	 
+		<xsl:call-template name="process-emph"/> 
+
+	</xsl:template>
+
+	<!-- ISO EXTENDS -->
+	<xsl:template match="iso:extends">
+		<xsl:if test="not(@rule)">
+                   <xsl:message>Markup Error: no rule attribute in &lt;extends></xsl:message>
+                </xsl:if>
+     		<xsl:if test="not(//iso:rule[@abstract='true'][@id= current()/@rule] )">
+                    <xsl:message>Reference Error: the abstract rule  "<xsl:value-of select="@rule"
+					/>" has been referenced but is not declared</xsl:message>
+                </xsl:if>
+	        <xsl:call-template name="IamEmpty" />
+
+  		<xsl:if test="//iso:rule[@id=current()/@rule]">
+    			<xsl:apply-templates select="//iso:rule[@id=current()/@rule]"
+				mode="extends"/>
+  		</xsl:if>
+
+	</xsl:template>
+
+	<!-- KEY: ISO has no KEY -->
+	<!-- NOTE: 
+	     Key has had a checkered history. Schematron 1.0 allowed it in certain places, but
+	     users came up with a different location, which has now been adopted. 
+	     
+	     XT, the early XSLT processor, did not implement key and died when it was present. 
+	     So there are some versions of the Schematron skeleton for XT that strip out all
+	     key elements.
+	     
+	     Xalan (e.g. Xalan4C 1.0 and a Xalan4J) also had a funny. A fix involved making 
+	     a top-level parameter called $hiddenKey and then using that instead of matching
+	     "key". This has been removed.
+	-->
+	<xsl:template  match="xsl:key" mode="do-keys" >
+	     <xsl:if test="not(@name)">
+              <xsl:message>Markup Error: no name attribute in &lt;key></xsl:message>
+         </xsl:if>
+                <xsl:if test="not(@path) and not(@use)">
+                    <xsl:message>Markup Error: no path or use attribute in &lt;key></xsl:message>
+                </xsl:if>         
+	     <xsl:choose>
+	     	<xsl:when test="parent::iso:rule ">
+	        <xsl:call-template name="IamEmpty" />
+	       <xsl:choose>
+	       	<xsl:when test="@path">
+				<axsl:key match="{../@context}" name="{@name}" use="{@path}"/>
+			</xsl:when>
+			<xsl:otherwise>
+							<axsl:key match="{../@context}" name="{@name}" use="{@use}"/>
+			</xsl:otherwise>
+			</xsl:choose>	
+		</xsl:when>
+		<xsl:otherwise>
+                <xsl:if test="not(@match) ">
+                    <xsl:message>Markup Error: no path or use attribute in &lt;key></xsl:message>
+                </xsl:if>   		
+			<axsl:key>
+      			<xsl:copy-of select="@*"/>
+    		</axsl:key>	
+		</xsl:otherwise>
+		</xsl:choose>
+	</xsl:template>
+
+	<xsl:template match="xsl:key "  /><!-- swallow -->
+
+	<xsl:template match="iso:key "  >
+		<xsl:message>Schema error: The key element is not in the ISO Schematron namespace. Use the XSLT namespace.</xsl:message>
+    </xsl:template>
+
+   <!-- ISO INCLUDE -->
+   <!-- This is only a fallback. Include really needs to have been done before this as a separate pass.-->
+
+   <xsl:template match="iso:include[not(normalize-space(@href))]"
+	   priority="1">
+	<xsl:if test=" $debug = 'false' ">
+		<xsl:message terminate="yes">Schema error: Empty href= attribute for include directive.</xsl:message>
+	</xsl:if>
+
+   </xsl:template>
+
+   <!-- Extend the URI syntax to allow # refererences -->
+   <!-- Add experimental support for simple containers like  /xxx:xxx/iso:pattern to allow better includes -->
+   <xsl:template match="iso:include">
+       <xsl:variable name="document-uri" select="substring-before(concat(@href,'#'), '#')"/>
+       <xsl:variable name="fragment-id" select="substring-after(@href, '#')"/>
+       
+       <xsl:choose> 
+          
+          <xsl:when test="string-length( $document-uri ) = 0 and string-length( $fragment-id ) = 0" >
+          	<xsl:message>Error: Impossible URL in Schematron include</xsl:message>
+          </xsl:when> 
+          
+          <xsl:when test="string-length( $fragment-id ) &gt; 0">
+              <xsl:variable name="theDocument_1" select="document( $document-uri,/ )" />
+              <xsl:variable name="theFragment_1" select="$theDocument_1//iso:*[@id= $fragment-id ]" />
+              <xsl:if test=" $theFragment_1/self::iso:schema ">
+                 <xsl:message>Schema error: Use include to include fragments, not a whole schema</xsl:message>
+              </xsl:if>
+              <xsl:apply-templates select=" $theFragment_1"/>
+		   </xsl:when>
+		  
+		   <xsl:otherwise>
+              <xsl:variable name="theDocument_2" select="document( $document-uri,/ )" />
+              <xsl:variable name="theFragment_2" select="$theDocument_2/iso:*" />
+              <xsl:variable name="theContainedFragments" select="$theDocument_2/*/iso:*" />
+              <xsl:if test=" $theFragment_2/self::iso:schema or $theContainedFragments/self::iso:schema">
+                 <xsl:message>Schema error: Use include to include fragments, not a whole schema</xsl:message>
+              </xsl:if>
+       		<xsl:apply-templates select="$theFragment_2 | $theContainedFragments "/>
+       	   </xsl:otherwise>
+       </xsl:choose>
+   </xsl:template>
+
+   <!-- This is to handle the particular case of including patterns -->  
+   <xsl:template match="iso:include" mode="do-all-patterns">
+       <xsl:variable name="document-uri" select="substring-before(concat(@href,'#'), '#')"/>
+       <xsl:variable name="fragment-id" select="substring-after(@href, '#')"/>
+ 
+       <xsl:choose> 
+          
+          <xsl:when test="string-length( $document-uri ) = 0 and string-length( $fragment-id ) = 0" >
+          	<xsl:message>Error: Impossible URL in Schematron include</xsl:message>
+          </xsl:when> 
+          
+          <xsl:when test="string-length( $fragment-id ) &gt; 0">
+              <xsl:variable name="theDocument_1" select="document( $document-uri,/ )" />
+              <xsl:variable name="theFragment_1" select="$theDocument_1//iso:*[@id= $fragment-id ]" />
+              <xsl:if test=" $theFragment_1/self::iso:schema ">
+                 <xsl:message>Schema error: Use include to include fragments, not a whole schema</xsl:message>
+              </xsl:if>
+              <xsl:apply-templates select=" $theFragment_1" mode="do-all-patterns"/>
+		   </xsl:when>
+		  
+		   <xsl:otherwise>
+		   	  <!-- Import the top-level element if it is in schematron namespace,
+		   	  or its children otherwise, to allow a simple containment mechanism. -->
+              <xsl:variable name="theDocument_2" select="document( $document-uri,/ )" />
+              <xsl:variable name="theFragment_2" select="$theDocument_2/iso:*" />
+              <xsl:variable name="theContainedFragments" select="$theDocument_2/*/iso:*" />
+              <xsl:if test=" $theFragment_2/self::iso:schema or $theContainedFragments/self::iso:schema">
+                 <xsl:message>Schema error: Use include to include fragments, not a whole schema</xsl:message>
+              </xsl:if>
+       		<xsl:apply-templates select="$theFragment_2 | $theContainedFragments "
+       		mode="do-all-patterns" />
+       	   </xsl:otherwise>
+       </xsl:choose>
+   </xsl:template>
+   
+	<!-- ISO LET -->
+	<xsl:template match="iso:let" >
+	  <xsl:if test="ancestor::iso:schema[@queryBinding='xpath']">
+                    <xsl:message>Warning: Variables should not be used with the "xpath" query language binding.</xsl:message>
+       </xsl:if>
+		
+       <!-- lets at the top-level are implemented as parameters -->
+ 
+       	<xsl:choose>
+       		<xsl:when test="parent::iso:schema">
+       			<!-- it is an error to have an empty param/@select because an XPath is expected -->
+	      		 <axsl:param name="{@name}" select="{@value}">
+	      		 		<xsl:if test="string-length(@value) &gt; 0">
+	      		 			<xsl:attribute name="select"><xsl:value-of select="@value"/></xsl:attribute>
+	      		 		</xsl:if>
+	      		 </axsl:param> 
+       		</xsl:when>
+       		<xsl:otherwise>
+				<axsl:variable name="{@name}" select="{@value}"/>
+			</xsl:otherwise>
+		</xsl:choose>
+		  
+	</xsl:template>	
+
+	<!-- ISO NAME -->
+	<xsl:template match="iso:name" mode="text">
+	
+		<xsl:if test="@path">
+			<xsl:call-template name="process-name">
+				<xsl:with-param name="name" select="concat('name(',@path,')')"/>
+			</xsl:call-template>
+		</xsl:if>
+		<xsl:if test="not(@path)">
+			<xsl:call-template name="process-name">
+				<xsl:with-param name="name" select="'name(.)'"/>
+			</xsl:call-template>
+		</xsl:if>
+	    <xsl:call-template name="IamEmpty" />
+	</xsl:template>
+
+	<!-- ISO NS -->
+	<!-- Namespace handling is XSLT is quite tricky and implementation dependent -->
+	<xsl:template match="iso:ns">
+ 		<xsl:call-template name="handle-namespace" />
+	</xsl:template>
+
+    <!-- This template is just to provide the API hook -->
+	<xsl:template match="iso:ns"  mode="do-all-patterns" >
+               <xsl:if test="not(@uri)">
+                    <xsl:message>Markup Error: no uri attribute in &lt;ns></xsl:message>
+                </xsl:if>
+               <xsl:if test="not(@prefix)">
+                    <xsl:message>Markup Error: no prefix attribute in &lt;ns></xsl:message>
+                </xsl:if>
+	        <xsl:call-template name="IamEmpty" />
+		<xsl:call-template name="process-ns" >
+			<xsl:with-param name="prefix" select="@prefix"/>
+			<xsl:with-param name="uri" select="@uri"/>
+		</xsl:call-template>
+	</xsl:template>
+
+	<!-- ISO P -->
+	<xsl:template match="iso:schema/iso:p " mode="do-schema-p" >
+		<xsl:call-template name="process-p">
+			<xsl:with-param name="class" select="@class"/>
+			<xsl:with-param name="icon" select="@icon"/>
+			<xsl:with-param name="id" select="@id"/>
+			<xsl:with-param name="lang" select="@xml:lang"/>
+		</xsl:call-template>
+	</xsl:template>
+	<xsl:template match="iso:pattern/iso:p " mode="do-pattern-p" >
+		<xsl:call-template name="process-p">
+			<xsl:with-param name="class" select="@class"/>
+			<xsl:with-param name="icon" select="@icon"/>
+			<xsl:with-param name="id" select="@id"/>
+			<xsl:with-param name="lang" select="@xml:lang"/>
+		</xsl:call-template>
+	</xsl:template>
+	
+    <!-- Currently, iso:p in other position are not passed through to the API -->
+	<xsl:template match="iso:phase/iso:p" />
+	<xsl:template match="iso:p " priority="-1" />
+
+	<!-- ISO PATTERN -->
+	<xsl:template match="iso:pattern" mode="do-all-patterns">
+	<xsl:if test="($phase = '#ALL') 
+	or (../iso:phase[@id= $phase]/iso:active[@pattern= current()/@id])">
+		<xsl:call-template name="process-pattern">
+			<!-- the following select statement assumes that
+			@id | sch:title returns node-set in document order:
+			we want the title if it is there, otherwise the @id attribute -->
+			<xsl:with-param name="name" select="(@id | iso:title )[last()]"/>
+			<xsl:with-param name="is-a" select="''"/>
+			
+					<!-- "Rich" properties -->
+					<xsl:with-param name="fpi" select="@fpi"/>
+					<xsl:with-param name="icon" select="@icon"/>
+					<xsl:with-param name="id" select="@id"/>
+					<xsl:with-param name="lang" select="@xml:lang"/>
+					<xsl:with-param name="see" select="@see" />
+					<xsl:with-param name="space" select="@xml:space" />
+		</xsl:call-template>
+		<xsl:choose>
+		  <xsl:when test="$select-contexts='key'">
+		    <axsl:apply-templates select="key('M','M{count(preceding-sibling::*)}')" mode="M{count(preceding-sibling::*)}"/>
+		  </xsl:when>
+		  <xsl:when test="$select-contexts='//'">
+		    <axsl:apply-templates mode="M{count(preceding-sibling::*)}">
+		      <xsl:attribute name="select">
+			<xsl:text>//(</xsl:text>
+			<xsl:for-each select="iso:rule/@context">
+			  <xsl:text>(</xsl:text>
+			  <xsl:value-of select="."/>
+			  <xsl:text>)</xsl:text>
+			  <xsl:if test="position()!=last()">|</xsl:if>
+			</xsl:for-each>
+			<xsl:text>)</xsl:text>
+			<xsl:if test="$visit-text='false'">[not(self::text())]</xsl:if>
+		      </xsl:attribute>
+		    </axsl:apply-templates>
+		  </xsl:when>
+		  <xsl:otherwise>
+		    <axsl:apply-templates select="/" mode="M{count(preceding-sibling::*)}"/>
+		  </xsl:otherwise>
+		</xsl:choose>
+        </xsl:if>
+	</xsl:template>
+	
+	<xsl:template match="iso:pattern[@abstract='true']">
+    
+             <xsl:message>Schema implementation error: This schema has abstract patterns, yet they are supposed to be preprocessed out already
+             </xsl:message>
+    </xsl:template>
+
+    <!-- Here is the template for the normal case of patterns -->
+	<xsl:template match="iso:pattern[not(@abstract='true')]">
+     
+      <xsl:if test="($phase = '#ALL') 
+	          or (../iso:phase[@id= $phase]/iso:active[@pattern= current()/@id])">
+ 
+		<xsl:text>&#10;&#10;</xsl:text>
+		<xsl:comment>PATTERN <xsl:value-of select="@id" /> <xsl:value-of select="iso:title" /> </xsl:comment><xsl:text>&#10;</xsl:text>      
+		<xsl:apply-templates />
+		
+		<!-- DPC select-contexts test -->
+		<xsl:if test="not($select-contexts)">
+		  <axsl:template match="text()" priority="-1" mode="M{count(preceding-sibling::*)}">
+		    <!-- strip characters -->
+		  </axsl:template>
+		  
+		  <!-- DPC introduce context-xpath variable -->
+		  <axsl:template match="@*|node()"
+				 priority="-2"
+				 mode="M{ count(preceding-sibling::*) }">
+		    <axsl:apply-templates select="{$context-xpath}" mode="M{count(preceding-sibling::*)}"/>
+		  </axsl:template>
+		</xsl:if>
+      </xsl:if>
+	</xsl:template>
+
+	<!-- ISO PHASE -->
+	<xsl:template match="iso:phase" >
+                <xsl:if test="not(@id)">
+                    <xsl:message>Markup Error: no id attribute in &lt;phase></xsl:message>
+                </xsl:if>
+		  <xsl:apply-templates/>
+	</xsl:template>
+
+	<!-- ISO RULE -->
+	<xsl:template match="iso:rule[not(@abstract='true')] ">
+                <xsl:if test="not(@context)">
+                    <xsl:message>Markup Error: no context attribute in &lt;rule></xsl:message>
+                </xsl:if>
+        <xsl:text>&#10;&#10;	</xsl:text>
+		<xsl:comment>RULE <xsl:value-of select="@id" /> </xsl:comment><xsl:text>&#10;</xsl:text>   
+        <xsl:if test="iso:title">
+		    <xsl:comment><xsl:value-of select="iso:title" /></xsl:comment>
+		  </xsl:if>
+		<!-- DPC select-contexts -->
+		<xsl:if test="$select-contexts='key'">
+		    <axsl:key name="M"
+			      match="{@context}" 
+			      use="'M{count(../preceding-sibling::*)}'"/>
+		</xsl:if>
+   
+	
+<!-- DPC priorities count up from 1000 not down from 4000 (templates in same priority order as before) -->
+		<axsl:template match="{@context}"
+		priority="{1000 + count(following-sibling::*)}" mode="M{count(../preceding-sibling::*)}">
+			<xsl:call-template name="process-rule">
+				<xsl:with-param name="context" select="@context"/>
+				
+					<!-- "Rich" properties -->
+					<xsl:with-param name="fpi" select="@fpi"/>
+					<xsl:with-param name="icon" select="@icon"/>
+					<xsl:with-param name="id" select="@id"/>
+					<xsl:with-param name="lang" select="@xml:lang"/>
+					<xsl:with-param name="see" select="@see" />
+					<xsl:with-param name="space" select="@xml:space" />
+					
+					<!-- "Linking" properties -->
+					<xsl:with-param name="role" select="@role" />
+					<xsl:with-param name="subject" select="@subject" />
+			</xsl:call-template>
+			<xsl:apply-templates/>
+			<!-- DPC introduce context-xpath and select-contexts variables -->
+			<xsl:if test="not($select-contexts)">
+			  <axsl:apply-templates select="{$context-xpath}" mode="M{count(../preceding-sibling::*)}"/>
+			</xsl:if>
+		</axsl:template>
+	</xsl:template>
+
+
+	<!-- ISO ABSTRACT RULE -->
+	<xsl:template match="iso:rule[@abstract='true'] " >
+		<xsl:if test=" not(@id)">
+                    <xsl:message>Markup Error: no id attribute on abstract &lt;rule></xsl:message>
+                </xsl:if>
+ 		<xsl:if test="@context">
+                    <xsl:message>Markup Error: (2) context attribute on abstract &lt;rule></xsl:message>
+                </xsl:if>
+	</xsl:template>
+
+	<xsl:template match="iso:rule[@abstract='true']"
+		mode="extends" >
+                <xsl:if test="@context">
+                    <xsl:message>Markup Error: context attribute on abstract &lt;rule></xsl:message>
+                </xsl:if>
+			<xsl:apply-templates/>
+	</xsl:template>
+
+	<!-- ISO SPAN -->
+	<xsl:template match="iso:span" mode="text">
+		<xsl:call-template name="process-span">
+			<xsl:with-param name="class" select="@class"/>
+		</xsl:call-template>
+	</xsl:template>
+
+	<!-- ISO TITLE -->
+	
+	<xsl:template match="iso:schema/iso:title"  priority="1">
+	     <xsl:call-template name="process-schema-title" />
+	</xsl:template>
+ 
+	
+	<xsl:template match="iso:title" >
+	     <xsl:call-template name="process-title" />
+	</xsl:template>
+ 
+
+	<!-- ISO VALUE-OF -->
+	<xsl:template match="iso:value-of" mode="text" >
+        <xsl:if test="not(@select)">
+            <xsl:message>Markup Error: no select attribute in &lt;value-of></xsl:message>
+        </xsl:if>
+	    <xsl:call-template name="IamEmpty" />
+	         
+		<xsl:choose>
+			<xsl:when test="@select">
+				<xsl:call-template name="process-value-of">
+					<xsl:with-param name="select" select="@select"/>  
+				</xsl:call-template>
+			</xsl:when>
+			<xsl:otherwise >
+				<xsl:call-template name="process-value-of">
+					<xsl:with-param name="select" select="'.'"/>
+				</xsl:call-template>
+			</xsl:otherwise>
+        </xsl:choose> 
+        
+	</xsl:template>
+
+
+<!-- ============================================================== -->
+<!-- DEFAULT TEXT HANDLING  -->
+<!-- ============================================================== -->
+	<xsl:template match="text()" priority="-1" mode="do-keys">
+		<!-- strip characters -->
+	</xsl:template>
+	<xsl:template match="text()" priority="-1" mode="do-all-patterns">
+		<!-- strip characters -->
+	</xsl:template>
+        <xsl:template match="text()" priority="-1" mode="do-schema-p">
+		<!-- strip characters -->
+	</xsl:template>
+        <xsl:template match="text()" priority="-1" mode="do-pattern-p">
+		<!-- strip characters -->
+	</xsl:template>
+	
+	<xsl:template match="text()" priority="-1">
+		<!-- Strip characters -->
+	</xsl:template>
+	
+	<xsl:template match="text()" mode="text">
+		<xsl:value-of select="."/>
+	</xsl:template>
+
+	<xsl:template match="text()" mode="inline-text">
+		<xsl:value-of select="."/>
+	</xsl:template>
+
+<!-- ============================================================== -->
+<!-- UTILITY TEMPLATES -->
+<!-- ============================================================== -->
+<xsl:template name="IamEmpty">
+	<xsl:if test="count( * )">
+		<xsl:message>
+			<xsl:text>Warning: </xsl:text>
+			<xsl:value-of select="name(.)"/>
+			<xsl:text> must not contain any child elements</xsl:text>
+		</xsl:message>
+	</xsl:if>
+</xsl:template>
+
+<xsl:template name="diagnosticsSplit">
+  <!-- Process at the current point the first of the <diagnostic> elements
+       referred to parameter str, and then recurse -->
+  <xsl:param name="str"/>
+  <xsl:variable name="start">
+    <xsl:choose>
+      <xsl:when test="contains($str,' ')">
+	<xsl:value-of  select="substring-before($str,' ')"/>
+      </xsl:when>
+      <xsl:otherwise><xsl:value-of select="$str"/></xsl:otherwise>
+    </xsl:choose>
+  </xsl:variable>
+
+  <xsl:variable name="end">
+    <xsl:if test="contains($str,' ')">
+      <xsl:value-of select="substring-after($str,' ')"/>
+    </xsl:if>
+  </xsl:variable>
+
+  <!-- This works with all namespaces -->
+  <xsl:if test="not(string-length(normalize-space($start)) = 0)
+  		and not(//iso:diagnostic[@id = $start])
+		and not(//sch:diagnostic[@id = $start]) 
+		and not(//diagnostic[@id = $start])">
+	<xsl:message>Reference error: A diagnostic "<xsl:value-of select="string($start)"
+	/>" has been referenced but is not declared</xsl:message>
+  </xsl:if>
+
+  <xsl:if test="string-length(normalize-space($start)) > 0">
+     <xsl:text> </xsl:text>
+     <xsl:apply-templates 
+        select="//iso:diagnostic[@id = $start ]
+        	| //sch:diagnostic[@id = $start ] 
+            | //diagnostic[@id= $start ]"/>
+  </xsl:if>
+
+  <xsl:if test="not($end='')">
+    <xsl:call-template name="diagnosticsSplit">
+      <xsl:with-param name="str" select="$end"/>
+    </xsl:call-template>
+  </xsl:if>
+</xsl:template>
+
+<!-- It would be nice to use this but xsl:namespace does not
+  allow a fallback -->
+<!--xsl:template name="handle-namespace" version="2.0">
+   <xsl:namespace name="{@prefix}" select="@uri">
+</xsl:template-->
+
+<xsl:template name="handle-namespace">
+       <!-- experimental code from http://eccnet.eccnet.com/pipermail/schematron-love-in/2006-June/000104.html -->
+       <!-- Handle namespaces differently for exslt systems, msxml, and default, only using XSLT1 syntax -->
+       <!-- For more info see  http://fgeorges.blogspot.com/2007/01/creating-namespace-nodes-in-xslt-10.html -->
+       <xsl:choose>
+          <!-- The following code works for XSLT1 -->
+        <xsl:when test="function-available('exsl:node-set')">
+           <xsl:variable name="ns-dummy-elements">
+             <xsl:element name="{@prefix}:dummy" namespace="{@uri}"/>
+           </xsl:variable>
+       	   <xsl:variable name="p" select="@prefix"/>
+           <xsl:copy-of select="exsl:node-set($ns-dummy-elements)
+                                  /*/namespace::*[local-name()=$p]"/>
+         </xsl:when>        
+
+   			<!-- End XSLT1  code -->
+  
+        <!-- Not tested yet       
+    	<xsl:when test="function-available('msxsl:node-set')">
+      		<xsl:variable name="ns-dummy-elements">
+        		<xsl:element name="{ $prefix }:e" namespace="{ $uri }"/>
+      		</xsl:variable>
+      		<xsl:copy-of select="msxsl:node-set($ns-dummy-elements)/*/namespace::*"/>
+    	</xsl:when>
+        -->
+        
+        <xsl:when test="@prefix = 'xsl' ">
+           <!-- Do not generate dummy attributes with the xsl: prefix, as these
+                are errors against XSLT, because we presume that the output
+                stylesheet uses the xsl prefix. In any case, there would already
+                be a namespace declaration for the XSLT namespace generated
+                automatically, presumably using "xsl:".
+           -->
+        </xsl:when>
+        
+        <xsl:when test="@uri = 'http://www.w3.org/1999/XSL/Transform'">
+          <xsl:message terminate="yes">
+            <xsl:text>Using the XSLT namespace with a prefix other than "xsl" in </xsl:text>
+            <xsl:text>Schematron rules is not supported </xsl:text>
+            <xsl:text>in this processor: </xsl:text>
+            <xsl:value-of select="system-property('xsl:vendor')"/>
+          </xsl:message>
+        </xsl:when>
+
+        <xsl:otherwise>
+          <xsl:attribute name="{concat(@prefix,':dummy-for-xmlns')}" namespace="{@uri}" />
+           
+        </xsl:otherwise>
+      </xsl:choose>
+
+
+</xsl:template>
+
+<!-- ============================================================== -->
+<!-- UNEXPECTED ELEMENTS -->
+<!-- ============================================================== -->
+
+	<xsl:template match="iso:*"  priority="-2">
+	   <xsl:message>
+			<xsl:text>Error: unrecognized element in ISO Schematron namespace: check spelling
+			and capitalization</xsl:text>
+			<xsl:value-of select="name(.)"/>
+		</xsl:message>
+	</xsl:template>
+	
+	
+	<!-- Swallow old namespace elements: there is an upfront test for them elsewhere -->
+	<xsl:template match="sch:*"  priority="-2" />
+	
+	<xsl:template match="*"  priority="-3">
+	    <xsl:choose>
+	       <xsl:when test=" $allow-foreign = 'false' ">
+				<xsl:message>
+					<xsl:text>Warning: unrecognized element </xsl:text>
+					<xsl:value-of select="name(.)"/>
+				</xsl:message>
+			</xsl:when>
+			<xsl:otherwise>
+				<xsl:copy-of select="." />
+			</xsl:otherwise>
+		</xsl:choose>
+	</xsl:template>
+	
+	<xsl:template match="iso:*" mode="text" priority="-2" />
+	<xsl:template match="*" mode="text" priority="-3">
+	    <xsl:choose>
+	       <xsl:when test=" $allow-foreign = 'false' ">
+				<xsl:message>
+					<xsl:text>Warning: unrecognized element </xsl:text>
+					<xsl:value-of select="name(.)"/>
+				</xsl:message>
+			</xsl:when>
+			<xsl:otherwise>
+				<xsl:copy-of select="." />
+			</xsl:otherwise>
+		</xsl:choose>
+	</xsl:template>
+
+<!-- ============================================================== -->
+<!-- DEFAULT NAMED TEMPLATES -->
+<!-- These are the actions that are performed unless overridden -->
+<!-- ============================================================== -->
+ 
+	<xsl:template name="process-prolog"/>
+	<!-- no params -->
+
+	<xsl:template name="process-root">
+		<xsl:param name="contents"/>
+		<xsl:param name="id" />
+		<xsl:param name="version" />
+		<xsl:param name="schemaVersion" />
+		<xsl:param name="queryBinding" />
+		<xsl:param name="title" />
+
+
+		<!-- "Rich" parameters -->
+		<xsl:param name="fpi" />
+		<xsl:param name="icon" />
+		<xsl:param name="lang" />
+		<xsl:param name="see" />
+		<xsl:param name="space" />
+
+		<xsl:copy-of select="$contents"/>
+	</xsl:template>
+
+	<xsl:template name="process-assert">
+
+		<xsl:param name="test"/>
+		<xsl:param name="diagnostics" />
+		<xsl:param name="id" />
+		<xsl:param name="flag" />
+
+           	<!-- "Linkable" parameters -->
+		<xsl:param name="role"/>
+		<xsl:param name="subject"/>
+
+		<!-- "Rich" parameters -->
+		<xsl:param name="fpi" />
+		<xsl:param name="icon" />
+		<xsl:param name="lang" />
+		<xsl:param name="see" />
+		<xsl:param name="space" />
+
+
+		<xsl:call-template name="process-message">
+			<xsl:with-param name="pattern" select="$test"/>
+			<xsl:with-param name="role" select="$role"/>
+		</xsl:call-template>
+		
+		
+	</xsl:template>
+
+	<xsl:template name="process-report">
+		<xsl:param name="test"/>
+		<xsl:param name="diagnostics" />
+		<xsl:param name="id" />
+		<xsl:param name="flag" />
+
+           	<!-- "Linkable" parameters -->
+		<xsl:param name="role"/>
+		<xsl:param name="subject"/>
+
+		<!-- "Rich" parameters -->
+		<xsl:param name="fpi" />
+		<xsl:param name="icon" /> 
+		<xsl:param name="lang" />
+		<xsl:param name="see" />
+		<xsl:param name="space" />
+
+		<xsl:call-template name="process-message">
+			<xsl:with-param name="pattern" select="$test"/>
+			<xsl:with-param name="role" select="$role"/>
+		</xsl:call-template>
+	</xsl:template>
+
+	<xsl:template name="process-diagnostic">
+		<xsl:param name="id" />
+
+		<!-- "Rich" parameters -->
+		<xsl:param name="fpi" />
+		<xsl:param name="icon" />
+		<xsl:param name="lang" />
+		<xsl:param name="see" />
+		<xsl:param name="space" />
+		
+	    <!-- We generate too much whitespace rather than risking concatenation -->
+		<axsl:text> </axsl:text>
+		<xsl:apply-templates mode="text"/>
+		<axsl:text> </axsl:text>
+	</xsl:template>
+
+	<xsl:template name="process-dir">
+      	<xsl:param name="value" />
+
+	    <!-- We generate too much whitespace rather than risking concatenation -->
+		<axsl:text> </axsl:text>
+		<xsl:apply-templates mode="inline-text"/>
+		<axsl:text> </axsl:text>
+	</xsl:template>
+
+	<xsl:template name="process-emph"> 
+	    <!-- We generate too much whitespace rather than risking concatenation -->
+		<axsl:text> </axsl:text>
+		<xsl:apply-templates mode="inline-text"/>
+		<axsl:text> </axsl:text>
+	</xsl:template>
+	
+	<xsl:template name="process-name">
+		<xsl:param name="name"/>
+		
+		<!-- We generate too much whitespace rather than risking concatenation -->
+		<axsl:text> </axsl:text>
+		<axsl:value-of select="{$name}"/>
+		<axsl:text> </axsl:text>
+		
+    </xsl:template>
+
+	<xsl:template name="process-ns" >
+	<!-- Note that process-ns is for reporting. The sch:ns elements are 
+	     independently used in the sch:schema template to provide namespace bindings -->
+		<xsl:param name="prefix"/>
+		<xsl:param name="uri" />
+      </xsl:template>
+
+	<xsl:template name="process-p">
+		<xsl:param name="id" />
+		<xsl:param name="class" />
+		<xsl:param name="icon" />
+		<xsl:param name="lang" />
+      </xsl:template>
+
+	<xsl:template name="process-pattern">
+		<xsl:param name="id" />
+		<xsl:param name="name" />
+		<xsl:param name="is-a" />
+
+		<!-- "Rich" parameters -->
+		<xsl:param name="fpi" />
+		<xsl:param name="icon" />
+		<xsl:param name="lang" />
+		<xsl:param name="see" />
+		<xsl:param name="space" />
+      </xsl:template>
+      
+
+	<xsl:template name="process-rule">
+		<xsl:param name="context" />
+
+		<xsl:param name="id" />
+		<xsl:param name="flag" />
+
+           	<!-- "Linkable" parameters -->
+		<xsl:param name="role"/>
+		<xsl:param name="subject"/>
+  
+		<!-- "Rich" parameters -->
+		<xsl:param name="fpi" />
+		<xsl:param name="icon" />
+		<xsl:param name="lang" />
+		<xsl:param name="see" />
+		<xsl:param name="space" />
+      </xsl:template>
+
+	<xsl:template name="process-span" >
+		<xsl:param name="class" />
+
+	    <!-- We generate too much whitespace rather than risking concatenation -->
+		<axsl:text> </axsl:text>
+		<xsl:apply-templates mode="inline-text"/>
+		<axsl:text> </axsl:text>		
+	</xsl:template>
+
+	<xsl:template name="process-title" >
+		<xsl:param name="class" />
+	   <xsl:call-template name="process-p">
+	      <xsl:with-param  name="class">title</xsl:with-param>
+	   </xsl:call-template>
+	</xsl:template>
+		
+	<xsl:template name="process-schema-title" >
+		<xsl:param name="class" />
+	   <xsl:call-template name="process-title">
+	      <xsl:with-param  name="class">schema-title</xsl:with-param>
+	   </xsl:call-template>
+	</xsl:template>
+
+	<xsl:template name="process-value-of">
+		<xsl:param name="select"/>
+		
+	    <!-- We generate too much whitespace rather than risking concatenation -->
+		<axsl:text> </axsl:text>
+		<axsl:value-of select="{$select}"/>
+		<axsl:text> </axsl:text>
+	</xsl:template>
+
+	<!-- default output action: the simplest customization is to just override this -->
+	<xsl:template name="process-message">
+		<xsl:param name="pattern" />
+            <xsl:param name="role" />
+
+		<xsl:apply-templates mode="text"/>	
+		 <xsl:if test=" $message-newline = 'true'" >
+			<axsl:value-of  select="string('&#10;')"/>
+		</xsl:if>
+		
+	</xsl:template>
+</xsl:stylesheet>
+
+
+
diff --git a/.venv/lib/python3.12/site-packages/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_svrl_for_xslt1.xsl b/.venv/lib/python3.12/site-packages/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_svrl_for_xslt1.xsl
new file mode 100644
index 00000000..dae74ff6
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/iso_svrl_for_xslt1.xsl
@@ -0,0 +1,588 @@
+<?xml version="1.0" ?>
+<!-- 
+   ISO_SVRL.xsl   
+
+   Implementation of Schematron Validation Report Language from ISO Schematron
+   ISO/IEC 19757 Document Schema Definition Languages (DSDL) 
+     Part 3: Rule-based validation  Schematron 
+     Annex D: Schematron Validation Report Language 
+
+  This ISO Standard is available free as a Publicly Available Specification in PDF from ISO.
+  Also see www.schematron.com for drafts and other information.
+
+  This implementation of SVRL is designed to run with the "Skeleton" implementation 
+  of Schematron which Oliver Becker devised. The skeleton code provides a 
+  Schematron implementation but with named templates for handling all output; 
+  the skeleton provides basic templates for output using this API, but client
+  validators can be written to import the skeleton and override the default output
+  templates as required. (In order to understand this, you must understand that
+  a named template such as "process-assert" in this XSLT stylesheet overrides and
+  replaces any template with the same name in the imported skeleton XSLT file.)
+
+  The other important thing to understand in this code is that there are different
+  versions of the Schematron skeleton. These track the development of Schematron through
+  Schematron 1.5, Schematron 1.6 and now ISO Schematron. One only skeleton must be
+  imported. The code has templates for the different skeletons commented out for 
+  convenience. ISO Schematron has a different namespace than Schematron 1.5 and 1.6;
+  so the ISO Schematron skeleton has been written itself with an optional import
+  statement to in turn import the Schematron 1.6 skeleton. This will allow you to 
+  validate with schemas from either namespace.
+  
+
+  History:  
+    2009-03-18
+    	* Fix atrribute with space "see " which generates wrong name in some processors
+    2008-08-11
+   		* RJ Fix attribute/@select which saxon allows  in XSLT 1
+   2008-08-07
+    	* RJ Add output-encoding attribute to specify final encoding to use
+    	* Alter allow-foreign functionality so that Schematron span, emph and dir elements make 
+    	  it to the output, for better formatting and because span can be used to mark up
+    	  semantically interesting information embedded in diagnostics, which reduces the
+    	  need to extend SVRL itself
+    	* Diagnostic-reference had an invalid attribute @id that duplicated @diagnostic: removed
+  	2008-08-06
+    	* RJ Fix invalid output:  svrl:diagnostic-reference is not contained in an svrl:text
+    	* Output comment to SVRL file giving filename if available (from command-line parameter)
+  	2008-08-04
+  		* RJ move sch: prefix to schold: prefix to prevent confusion (we want people to
+  		be able to switch from old namespace to new namespace without changing the
+  		sch: prefix, so it is better to keep that prefix completely out of the XSLT)
+  		* Extra signature fixes (PH)
+    2008-08-03
+    	* Repair missing class parameter on process-p
+    2008-07-31
+    	* Update skeleton names
+    2007-04-03 
+    	* Add option generate-fired-rule (RG)
+    2007-02-07
+    	* Prefer true|false for parameters. But allow yes|no on some old for compatability
+    	* DP Diagnostics output to svrl:text. Diagnosis put out after assertion text.
+      	* Removed non-SVRL elements and attributes: better handled as an extra layer that invokes this one
+      	* Add more formal parameters
+      	* Correct confusion between $schemaVersion and $queryBinding
+     	* Indent
+     	* Validate against RNC schemas for XSLT 1 and 2 (with regex tests removed)
+     	* Validate output with UniversalTest.sch against RNC schema for ISO SVRL
+    	
+    2007-02-01
+       	* DP. Update formal parameters of overriding named templates to handle more attributes.
+       	* DP. Refactor handling of rich and linkable parameters to a named template.
+
+    2007-01-22
+    	* DP change svrl:ns to svrl:ns-in-attribute-value
+		* Change default when no queryBinding from "unknown" to "xslt"
+	
+    2007-01-18:
+     	* Improve documentation
+     	* KH Add command-line options to generate paths or not 
+       	* Use axsl:attribute rather than xsl:attribute to shut XSLT2 up
+       	* Add extra command-line options to pass to the iso_schematron_skeleton
+  
+    2006-12-01: iso_svrl.xsl Rick Jelliffe, 
+          * update namespace, 
+          * update phase handling,
+          * add flag param to process-assert and process-report & @ flag on output
+  
+    2001: Conformance1-5.xsl Rick Jelliffe, 
+          * Created, using the skeleton code contributed by Oliver Becker
+-->
+<!--
+ Derived from Conformance1-5.xsl.
+
+ Copyright (c) 2001, 2006 Rick Jelliffe and Academia Sinica Computing Center, Taiwan
+
+ This software is provided 'as-is', without any express or implied warranty. 
+ In no event will the authors be held liable for any damages arising from 
+ the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose, 
+ including commercial applications, and to alter it and redistribute it freely,
+ subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not claim
+ that you wrote the original software. If you use this software in a product, 
+ an acknowledgment in the product documentation would be appreciated but is 
+ not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be 
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source distribution.
+-->
+
+<!-- Ideas nabbed from schematrons by Francis N., Miloslav N. and David C. -->
+
+<!-- The command-line parameters are:
+  			phase           NMTOKEN | "#ALL" (default) Select the phase for validation
+    		allow-foreign   "true" | "false" (default)   Pass non-Schematron elements  and rich markup  to the generated stylesheet
+            diagnose= true | false|yes|no    Add the diagnostics to the assertion test in reports (yes|no are obsolete)
+            generate-paths=true|false|yes|no   generate the @location attribute with XPaths (yes|no are obsolete)
+            sch.exslt.imports semi-colon delimited string of filenames for some EXSLT implementations          
+   		 optimize        "visit-no-attributes"     Use only when the schema has no attributes as the context nodes
+		 generate-fired-rule "true"(default) | "false"  Generate fired-rule elements
+            
+-->
+
+<xsl:stylesheet
+   version="1.0"
+   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+	xmlns:xs="http://www.w3.org/2001/XMLSchema"
+   xmlns:axsl="http://www.w3.org/1999/XSL/TransformAlias"
+   xmlns:schold="http://www.ascc.net/xml/schematron" 
+   xmlns:iso="http://purl.oclc.org/dsdl/schematron"
+   xmlns:svrl="http://purl.oclc.org/dsdl/svrl" 
+>
+
+<!-- Select the import statement and adjust the path as 
+   necessary for your system.
+   If not XSLT2 then also remove svrl:active-pattern/@document="{document-uri()}" from process-pattern()
+-->
+<!--
+<xsl:import href="iso_schematron_skeleton_for_saxon.xsl"/>
+--> 
+  
+<xsl:import href="iso_schematron_skeleton_for_xslt1.xsl"/>
+ <!--
+<xsl:import href="iso_schematron_skeleton.xsl"/>
+<xsl:import href="skeleton1-5.xsl"/>
+<xsl:import href="skeleton1-6.xsl"/>
+-->
+
+<xsl:param name="diagnose" >true</xsl:param>
+<xsl:param name="phase" >
+	<xsl:choose>
+		<!-- Handle Schematron 1.5 and 1.6 phases -->
+		<xsl:when test="//schold:schema/@defaultPhase">
+			<xsl:value-of select="//schold:schema/@defaultPhase"/>
+		</xsl:when>
+		<!-- Handle ISO Schematron phases -->
+		<xsl:when test="//iso:schema/@defaultPhase">
+			<xsl:value-of select="//iso:schema/@defaultPhase"/>
+		</xsl:when>
+		<xsl:otherwise>#ALL</xsl:otherwise>
+	</xsl:choose>
+</xsl:param>
+<xsl:param name="allow-foreign" >false</xsl:param>
+<xsl:param name="generate-paths" >true</xsl:param>
+<xsl:param name="generate-fired-rule" >true</xsl:param>
+<xsl:param name="optimize"/>
+
+<xsl:param name="output-encoding" ></xsl:param>
+
+<!-- e.g. saxon file.xml file.xsl "sch.exslt.imports=.../string.xsl;.../math.xsl" -->
+<xsl:param name="sch.exslt.imports" />
+
+
+
+<!-- Experimental: If this file called, then must be generating svrl -->
+<xsl:variable name="svrlTest" select="true()" />
+
+  
+ 
+<!-- ================================================================ -->
+
+<xsl:template name="process-prolog">
+	<axsl:output method="xml" omit-xml-declaration="no" standalone="yes"
+		indent="yes">
+		<xsl:if test=" string-length($output-encoding) &gt; 0">
+			<xsl:attribute name="encoding"><xsl:value-of select=" $output-encoding" /></xsl:attribute>
+		</xsl:if>
+    </axsl:output>
+     
+</xsl:template>
+
+<!-- Overrides skeleton.xsl -->
+<xsl:template name="process-root">
+	<xsl:param name="title"/>
+	<xsl:param name="contents" />
+	<xsl:param name="queryBinding" >xslt1</xsl:param>
+	<xsl:param name="schemaVersion" />
+	<xsl:param name="id" />
+	<xsl:param name="version"/>
+	<!-- "Rich" parameters -->
+	<xsl:param name="fpi" />
+	<xsl:param name="icon" />
+	<xsl:param name="lang" />
+	<xsl:param name="see" />
+	<xsl:param name="space" />
+	
+	<svrl:schematron-output title="{$title}" schemaVersion="{$schemaVersion}" >
+		<xsl:if test=" string-length( normalize-space( $phase )) &gt; 0 and 
+		not( normalize-space( $phase ) = '#ALL') ">
+			<axsl:attribute name="phase">
+				<xsl:value-of select=" $phase " />
+			</axsl:attribute>
+		</xsl:if>
+		<xsl:if test=" $allow-foreign = 'true'">
+		</xsl:if>
+		  <xsl:if  test=" $allow-foreign = 'true'">
+	
+		<xsl:call-template name='richParms'>
+			<xsl:with-param name="fpi" select="$fpi" />
+			<xsl:with-param name="icon" select="$icon"/>
+			<xsl:with-param name="lang" select="$lang"/>
+			<xsl:with-param name="see"  select="$see" />
+			<xsl:with-param name="space"  select="$space" />
+		</xsl:call-template>
+	</xsl:if>
+		 
+		 <axsl:comment><axsl:value-of select="$archiveDirParameter"/>  &#xA0;
+		 <axsl:value-of select="$archiveNameParameter"/> &#xA0;
+		 <axsl:value-of select="$fileNameParameter"/> &#xA0;
+		 <axsl:value-of select="$fileDirParameter"/></axsl:comment> 
+		 
+		
+		<xsl:apply-templates mode="do-schema-p" />
+		<xsl:copy-of select="$contents" />
+	</svrl:schematron-output>
+</xsl:template>
+
+
+<xsl:template name="process-assert">
+	<xsl:param name="test"/>
+	<xsl:param name="diagnostics" />
+	<xsl:param name="id" />
+	<xsl:param name="flag" />
+	<!-- "Linkable" parameters -->
+	<xsl:param name="role"/>
+	<xsl:param name="subject"/>
+	<!-- "Rich" parameters -->
+	<xsl:param name="fpi" />
+	<xsl:param name="icon" />
+	<xsl:param name="lang" />
+	<xsl:param name="see" />
+	<xsl:param name="space" />
+	<svrl:failed-assert test="{$test}" >
+		<xsl:if test="string-length( $id ) &gt; 0">
+			<axsl:attribute name="id">
+				<xsl:value-of select=" $id " />
+			</axsl:attribute>
+		</xsl:if>
+		<xsl:if test=" string-length( $flag ) &gt; 0">
+			<axsl:attribute name="flag">
+				<xsl:value-of select=" $flag " />
+			</axsl:attribute>
+		</xsl:if>
+		<!-- Process rich attributes.  -->
+		<xsl:call-template name="richParms">
+			<xsl:with-param name="fpi" select="$fpi"/>
+			<xsl:with-param name="icon" select="$icon"/>
+			<xsl:with-param name="lang" select="$lang"/>
+			<xsl:with-param name="see" select="$see" />
+			<xsl:with-param name="space" select="$space" />
+		</xsl:call-template>
+		<xsl:call-template name='linkableParms'>
+			<xsl:with-param name="role" select="$role" />
+			<xsl:with-param name="subject" select="$subject"/>
+		</xsl:call-template>
+		<xsl:if test=" $generate-paths = 'true' or $generate-paths= 'yes' ">
+			<!-- true/false is the new way -->
+			<axsl:attribute name="location">
+				<axsl:apply-templates select="." mode="schematron-get-full-path"/>
+			</axsl:attribute>
+		</xsl:if>
+		  
+		<svrl:text>
+			<xsl:apply-templates mode="text" />
+	
+		</svrl:text>
+		    <xsl:if test="$diagnose = 'yes' or $diagnose= 'true' ">
+			<!-- true/false is the new way -->
+				<xsl:call-template name="diagnosticsSplit">
+					<xsl:with-param name="str" select="$diagnostics"/>
+				</xsl:call-template>
+			</xsl:if>
+	</svrl:failed-assert>
+</xsl:template>
+
+<xsl:template name="process-report">
+	<xsl:param name="id"/>
+	<xsl:param name="test"/>
+	<xsl:param name="diagnostics"/>
+	<xsl:param name="flag" />
+	<!-- "Linkable" parameters -->
+	<xsl:param name="role"/>
+	<xsl:param name="subject"/>
+	<!-- "Rich" parameters -->
+	<xsl:param name="fpi" />
+	<xsl:param name="icon" />
+	<xsl:param name="lang" />
+	<xsl:param name="see" />
+	<xsl:param name="space" />
+	<svrl:successful-report test="{$test}" >
+		<xsl:if test=" string-length( $id ) &gt; 0">
+			<axsl:attribute name="id">
+				<xsl:value-of select=" $id " />
+			</axsl:attribute>
+		</xsl:if>
+		<xsl:if test=" string-length( $flag ) &gt; 0">
+			<axsl:attribute name="flag">
+				<xsl:value-of select=" $flag " />
+			</axsl:attribute>
+		</xsl:if>
+		
+		<!-- Process rich attributes.  -->
+		<xsl:call-template name="richParms">
+			<xsl:with-param name="fpi" select="$fpi"/>
+			<xsl:with-param name="icon" select="$icon"/>
+			<xsl:with-param name="lang" select="$lang"/>
+			<xsl:with-param name="see" select="$see" />
+			<xsl:with-param name="space" select="$space" />
+		</xsl:call-template>
+		<xsl:call-template name='linkableParms'>
+			<xsl:with-param name="role" select="$role" />
+			<xsl:with-param name="subject" select="$subject"/>
+		</xsl:call-template>
+		<xsl:if test=" $generate-paths = 'yes' or $generate-paths = 'true' ">
+			<!-- true/false is the new way -->
+			<axsl:attribute name="location">
+				<axsl:apply-templates select="." mode="schematron-get-full-path"/>
+			</axsl:attribute>
+		</xsl:if>
+	 
+		<svrl:text>
+			<xsl:apply-templates mode="text" />
+
+		</svrl:text>
+			<xsl:if test="$diagnose = 'yes' or $diagnose='true' ">
+			<!-- true/false is the new way -->
+				<xsl:call-template name="diagnosticsSplit">
+					<xsl:with-param name="str" select="$diagnostics"/>
+				</xsl:call-template>
+			</xsl:if>
+	</svrl:successful-report>
+</xsl:template>
+
+
+    <!-- Overrides skeleton -->
+	<xsl:template name="process-dir" >
+		<xsl:param name="value" />
+        <xsl:choose>
+        	<xsl:when test=" $allow-foreign = 'true'">
+        		<xsl:copy-of select="."/>
+        	</xsl:when>
+       
+        <xsl:otherwise>
+	    <!-- We generate too much whitespace rather than risking concatenation -->
+		<axsl:text> </axsl:text>
+		<xsl:apply-templates mode="inline-text"/>
+		<axsl:text> </axsl:text>
+		</xsl:otherwise>
+		 </xsl:choose>		
+	</xsl:template>
+
+<xsl:template name="process-diagnostic">
+	<xsl:param name="id"/>
+	<!-- Rich parameters -->
+	<xsl:param name="fpi" />
+	<xsl:param name="icon" />
+	<xsl:param name="lang" />
+	<xsl:param name="see" />
+	<xsl:param name="space" />
+	<svrl:diagnostic-reference diagnostic="{$id}" >
+	  
+		<xsl:call-template name="richParms">
+			<xsl:with-param name="fpi" select="$fpi"/>
+			<xsl:with-param name="icon" select="$icon"/>
+			<xsl:with-param name="lang" select="$lang"/>
+			<xsl:with-param name="see" select="$see" />
+			<xsl:with-param name="space" select="$space" />
+		</xsl:call-template> 
+<xsl:text>
+</xsl:text>
+ 
+		<xsl:apply-templates mode="text"/>
+		 
+	</svrl:diagnostic-reference>
+</xsl:template>
+
+
+    <!-- Overrides skeleton -->
+	<xsl:template name="process-emph" >
+		<xsl:param name="class" />
+        <xsl:choose>
+        	<xsl:when test=" $allow-foreign = 'true'">
+        		<xsl:copy-of select="."/>
+        	</xsl:when> 
+        <xsl:otherwise>
+	    <!-- We generate too much whitespace rather than risking concatenation -->
+		<axsl:text> </axsl:text>
+		<xsl:apply-templates mode="inline-text"/>
+		<axsl:text> </axsl:text>
+		</xsl:otherwise>
+	 	</xsl:choose>	
+	</xsl:template>
+
+<xsl:template name="process-rule">
+	<xsl:param name="id"/>
+	<xsl:param name="context"/>
+	<xsl:param name="flag"/>
+	<!-- "Linkable" parameters -->
+	<xsl:param name="role"/>
+	<xsl:param name="subject"/>
+	<!-- "Rich" parameters -->
+	<xsl:param name="fpi" />
+	<xsl:param name="icon" />
+	<xsl:param name="lang" />
+	<xsl:param name="see" />
+	<xsl:param name="space" />
+	<xsl:if test=" $generate-fired-rule = 'true'">
+	<svrl:fired-rule context="{$context}" >
+		<!-- Process rich attributes.  -->
+		<xsl:call-template name="richParms">
+			<xsl:with-param name="fpi" select="$fpi"/>
+			<xsl:with-param name="icon" select="$icon"/>
+			<xsl:with-param name="lang" select="$lang"/>
+			<xsl:with-param name="see" select="$see" />
+			<xsl:with-param name="space" select="$space" />
+		</xsl:call-template>
+		<xsl:if test=" string( $id )">
+			<xsl:attribute name="id">
+				<xsl:value-of select=" $id " />
+			</xsl:attribute>
+		</xsl:if>
+		<xsl:if test=" string-length( $role ) &gt; 0">
+			<xsl:attribute name="role">
+				<xsl:value-of select=" $role " />
+			</xsl:attribute>
+		</xsl:if> 
+	</svrl:fired-rule>
+</xsl:if>
+</xsl:template>
+
+<xsl:template name="process-ns">
+	<xsl:param name="prefix"/>
+	<xsl:param name="uri"/>
+	<svrl:ns-prefix-in-attribute-values uri="{$uri}" prefix="{$prefix}" />
+</xsl:template>
+
+<xsl:template name="process-p"> 
+	<xsl:param name="icon"/>
+	<xsl:param name="class"/>
+	<xsl:param name="id"/>
+	<xsl:param name="lang"/>
+	 
+	<svrl:text> 
+		<xsl:apply-templates mode="text"/>
+	</svrl:text>
+</xsl:template>
+
+<xsl:template name="process-pattern">
+	<xsl:param name="name"/>
+	<xsl:param name="id"/>
+	<xsl:param name="is-a"/>
+	
+	<!-- "Rich" parameters -->
+	<xsl:param name="fpi" />
+	<xsl:param name="icon" />
+	<xsl:param name="lang" />
+	<xsl:param name="see" />
+	<xsl:param name="space" />
+	<svrl:active-pattern > 
+		<xsl:if test=" string( $id )">
+			<axsl:attribute name="id">
+				<xsl:value-of select=" $id " />
+			</axsl:attribute>
+		</xsl:if>
+		<xsl:if test=" string( $name )">
+			<axsl:attribute name="name">
+				<xsl:value-of select=" $name " />
+			</axsl:attribute>
+		</xsl:if> 
+		 
+		<xsl:call-template name='richParms'>
+			<xsl:with-param name="fpi" select="$fpi"/>
+			<xsl:with-param name="icon" select="$icon"/>
+			<xsl:with-param name="lang" select="$lang"/>
+			<xsl:with-param name="see" select="$see" />
+			<xsl:with-param name="space" select="$space" />
+		</xsl:call-template>
+		
+		<!-- ?? report that this screws up iso:title processing  -->
+		<xsl:apply-templates mode="do-pattern-p"/>
+		<!-- ?? Seems that this apply-templates is never triggered DP -->
+		<axsl:apply-templates />
+	</svrl:active-pattern>
+</xsl:template>
+
+<!-- Overrides skeleton -->
+<xsl:template name="process-message" > 
+	<xsl:param name="pattern"/>
+	<xsl:param name="role"/>
+</xsl:template>
+
+
+    <!-- Overrides skeleton -->
+	<xsl:template name="process-span" >
+		<xsl:param name="class" />
+        <xsl:choose>
+        	<xsl:when test=" $allow-foreign = 'true'">
+        		<xsl:copy-of select="."/>
+        	</xsl:when> 
+        <xsl:otherwise>
+	    <!-- We generate too much whitespace rather than risking concatenation -->
+		<axsl:text> </axsl:text>
+		<xsl:apply-templates mode="inline-text"/>
+		<axsl:text> </axsl:text>
+		</xsl:otherwise>
+	 	</xsl:choose>	
+	</xsl:template>
+
+<!-- =========================================================================== -->
+<!-- processing rich parameters. -->
+<xsl:template name='richParms'>
+	<!-- "Rich" parameters -->
+	<xsl:param name="fpi" />
+	<xsl:param name="icon" />
+	<xsl:param name="lang" />
+	<xsl:param name="see" />
+	<xsl:param name="space" />
+	<!-- Process rich attributes.  -->
+	<xsl:if  test=" $allow-foreign = 'true'">
+	<xsl:if test="string($fpi)"> 
+		<axsl:attribute name="fpi">
+			<xsl:value-of select="$fpi"/>
+		</axsl:attribute>
+	</xsl:if>
+	<xsl:if test="string($icon)"> 
+		<axsl:attribute name="icon">
+			<xsl:value-of select="$icon"/>
+		</axsl:attribute>
+	</xsl:if>
+	<xsl:if test="string($see)"> 
+		<axsl:attribute name="see">
+			<xsl:value-of select="$see"/>
+		</axsl:attribute>
+	</xsl:if>
+	</xsl:if>
+	<xsl:if test="string($space)">
+		<axsl:attribute name="xml:space">
+			<xsl:value-of select="$space"/>
+		</axsl:attribute>
+	</xsl:if>
+	<xsl:if test="string($lang)">
+		<axsl:attribute name="xml:lang">
+			<xsl:value-of select="$lang"/>
+		</axsl:attribute>
+	</xsl:if>
+</xsl:template>
+
+<!-- processing linkable parameters. -->
+<xsl:template name='linkableParms'>
+	<xsl:param name="role"/>
+	<xsl:param name="subject"/>
+	
+	<!-- ISO SVRL has a role attribute to match the Schematron role attribute -->
+	<xsl:if test=" string($role )">
+		<axsl:attribute name="role">
+			<xsl:value-of select=" $role " />
+		</axsl:attribute>
+	</xsl:if>
+	<!-- ISO SVRL does not have a subject attribute to match the Schematron subject attribute.
+       Instead, the Schematron subject attribute is folded into the location attribute -->
+</xsl:template>
+   
+
+</xsl:stylesheet>
+
diff --git a/.venv/lib/python3.12/site-packages/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/readme.txt b/.venv/lib/python3.12/site-packages/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/readme.txt
new file mode 100644
index 00000000..e5d6dfcd
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/isoschematron/resources/xsl/iso-schematron-xslt1/readme.txt
@@ -0,0 +1,84 @@
+ISO SCHEMATRON 2010

+

+XSLT implementation by Rick Jelliffe with assistance from members of Schematron-love-in maillist.

+

+2010-04-21

+

+Two distributions are available. One is for XSLT1 engines. 

+The other is for XSLT2 engines, such as SAXON 9.

+

+

+This version of Schematron splits the process into a pipeline of several different XSLT stages.

+

+1) First, preprocess your Schematron schema with iso_dsdl_include.xsl.  

+This is a macro processor to assemble the schema from various parts. 

+If your schema is not in separate parts, you can skip this stage.

+This stage also generates error messages for some common XPath syntax problems.

+

+2) Second, preprocess the output from stage 1 with iso_abstract_expand.xsl.  

+This is a macro processor to convert abstract patterns to real patterns. 

+If your schema does not use abstract patterns, you can skip this

+stage.

+

+3) Third, compile the Schematron schema into an XSLT script. 

+This will typically use iso_svrl_for_xslt1.xsl or iso_svrl_for_xslt2.xsl 

+(which in turn invoke iso_schematron_skeleton_for_xslt1.xsl or iso_schematron_skeleton_for_saxon.xsl)

+However, other "meta-stylesheets" are also in common use; the principle of operation is the same.

+If your schema uses Schematron phases, supply these as command line/invocation parameters

+to this process.

+

+4) Fourth, run the script generated by stage 3 against the document being validated.

+If you are using the SVRL script, then the output of validation will be an XML document.

+If your schema uses Schematron parameters, supply these as command line/invocation parameters

+to this process. 

+

+

+The XSLT2 distribution also features several next generation features, 

+such as validating multiple documents. See the source code for details.

+

+Schematron assertions can be written in any language, of course; the file

+sch-messages-en.xhtml contains the diagnostics messages from the XSLT2 skeleton

+in English, and this can be used as template to localize the skeleton's

+error messages. Note that typically programming errors in Schematron are XPath

+errors, which requires localized messages from the XSLT engine.

+

+ANT

+---

+To give an example of how to process a document, here is a sample ANT task.

+

+<target  name="schematron-compile-test" >

+

+	   <!-- expand inclusions -->

+	   <xslt basedir="test/schematron"

+	   		style="iso_dsdl_include.xsl" in="test.sch"  out="test1.sch"> 

+	   				<classpath>

+	   					<pathelement location="${lib.dir}/saxon9.jar"/>

+	   				</classpath>

+	   </xslt>

+

+	   <!-- expand abstract patterns -->

+	   <xslt basedir="test/schematron"

+	   		style="iso_abstract_expand.xsl" in="test1.sch"  out="test2.sch"> 

+	   				<classpath>

+	   					<pathelement location="${lib.dir}/saxon9.jar"/>

+	   				</classpath>

+	   </xslt>

+

+

+

+	   <!-- compile it -->

+	   <xslt basedir="test/schematron"

+	   		style="iso_svrl_for_xslt2.xsl" in="test2.sch"  out="test.xsl"> 

+	   				<classpath>

+	   					<pathelement location="${lib.dir}/saxon9.jar"/>

+	   				</classpath>

+	   </xslt>

+	   

+	   <!-- validate -->

+	   <xslt basedir="test/schematron"

+		   		style="test.xsl" in="instance.xml"  out="instance.svrlt"> 

+		   				<classpath>

+		   					<pathelement location="${lib.dir}/saxon9.jar"/>

+		   				</classpath>

+	</xslt>

+		</target>

diff --git a/.venv/lib/python3.12/site-packages/lxml/iterparse.pxi b/.venv/lib/python3.12/site-packages/lxml/iterparse.pxi
new file mode 100644
index 00000000..42b75249
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/iterparse.pxi
@@ -0,0 +1,438 @@
+# iterparse -- event-driven parsing
+
+DEF __ITERPARSE_CHUNK_SIZE = 32768
+
+cdef class iterparse:
+    """iterparse(self, source, events=("end",), tag=None, \
+                  attribute_defaults=False, dtd_validation=False, \
+                  load_dtd=False, no_network=True, remove_blank_text=False, \
+                  remove_comments=False, remove_pis=False, encoding=None, \
+                  html=False, recover=None, huge_tree=False, schema=None)
+
+    Incremental parser.
+
+    Parses XML into a tree and generates tuples (event, element) in a
+    SAX-like fashion. ``event`` is any of 'start', 'end', 'start-ns',
+    'end-ns'.
+
+    For 'start' and 'end', ``element`` is the Element that the parser just
+    found opening or closing.  For 'start-ns', it is a tuple (prefix, URI) of
+    a new namespace declaration.  For 'end-ns', it is simply None.  Note that
+    all start and end events are guaranteed to be properly nested.
+
+    The keyword argument ``events`` specifies a sequence of event type names
+    that should be generated.  By default, only 'end' events will be
+    generated.
+
+    The additional ``tag`` argument restricts the 'start' and 'end' events to
+    those elements that match the given tag.  The ``tag`` argument can also be
+    a sequence of tags to allow matching more than one tag.  By default,
+    events are generated for all elements.  Note that the 'start-ns' and
+    'end-ns' events are not impacted by this restriction.
+
+    The other keyword arguments in the constructor are mainly based on the
+    libxml2 parser configuration.  A DTD will also be loaded if validation or
+    attribute default values are requested.
+
+    Available boolean keyword arguments:
+     - attribute_defaults: read default attributes from DTD
+     - dtd_validation: validate (if DTD is available)
+     - load_dtd: use DTD for parsing
+     - no_network: prevent network access for related files
+     - remove_blank_text: discard blank text nodes
+     - remove_comments: discard comments
+     - remove_pis: discard processing instructions
+     - strip_cdata: replace CDATA sections by normal text content (default: 
+       True for XML, ignored otherwise)
+     - compact: safe memory for short text content (default: True)
+     - resolve_entities: replace entities by their text value (default: True)
+     - huge_tree: disable security restrictions and support very deep trees
+                  and very long text content (only affects libxml2 2.7+)
+     - html: parse input as HTML (default: XML)
+     - recover: try hard to parse through broken input (default: True for HTML,
+                False otherwise)
+
+    Other keyword arguments:
+     - encoding: override the document encoding
+     - schema: an XMLSchema to validate against
+    """
+    cdef _FeedParser _parser
+    cdef object _tag
+    cdef object _events
+    cdef readonly object root
+    cdef object _source
+    cdef object _filename
+    cdef object _error
+    cdef bint _close_source_after_read
+
+    def __init__(self, source, events=("end",), *, tag=None,
+                 attribute_defaults=False, dtd_validation=False,
+                 load_dtd=False, no_network=True, remove_blank_text=False,
+                 compact=True, resolve_entities=True, remove_comments=False,
+                 remove_pis=False, strip_cdata=True, encoding=None,
+                 html=False, recover=None, huge_tree=False, collect_ids=True,
+                 XMLSchema schema=None):
+        if not hasattr(source, 'read'):
+            source = _getFSPathOrObject(source)
+            self._filename = source
+            self._source = open(source, 'rb')
+            self._close_source_after_read = True
+        else:
+            self._filename = _getFilenameForFile(source)
+            self._source = source
+            self._close_source_after_read = False
+
+        if recover is None:
+            recover = html
+
+        if html:
+            # make sure we're not looking for namespaces
+            events = [event for event in events
+                      if event not in ('start-ns', 'end-ns')]
+            parser = HTMLPullParser(
+                events,
+                tag=tag,
+                recover=recover,
+                base_url=self._filename,
+                encoding=encoding,
+                remove_blank_text=remove_blank_text,
+                remove_comments=remove_comments,
+                remove_pis=remove_pis,
+                no_network=no_network,
+                target=None,  # TODO
+                schema=schema,
+                compact=compact)
+        else:
+            parser = XMLPullParser(
+                events,
+                tag=tag,
+                recover=recover,
+                base_url=self._filename,
+                encoding=encoding,
+                attribute_defaults=attribute_defaults,
+                dtd_validation=dtd_validation,
+                load_dtd=load_dtd,
+                no_network=no_network,
+                schema=schema,
+                huge_tree=huge_tree,
+                remove_blank_text=remove_blank_text,
+                resolve_entities=resolve_entities,
+                remove_comments=remove_comments,
+                remove_pis=remove_pis,
+                strip_cdata=strip_cdata,
+                collect_ids=True,
+                target=None,  # TODO
+                compact=compact)
+
+        self._events = parser.read_events()
+        self._parser = parser
+
+    @property
+    def error_log(self):
+        """The error log of the last (or current) parser run.
+        """
+        return self._parser.feed_error_log
+
+    @property
+    def resolvers(self):
+        """The custom resolver registry of the last (or current) parser run.
+        """
+        return self._parser.resolvers
+
+    @property
+    def version(self):
+        """The version of the underlying XML parser."""
+        return self._parser.version
+
+    def set_element_class_lookup(self, ElementClassLookup lookup = None):
+        """set_element_class_lookup(self, lookup = None)
+
+        Set a lookup scheme for element classes generated from this parser.
+
+        Reset it by passing None or nothing.
+        """
+        self._parser.set_element_class_lookup(lookup)
+
+    def makeelement(self, _tag, attrib=None, nsmap=None, **_extra):
+        """makeelement(self, _tag, attrib=None, nsmap=None, **_extra)
+
+        Creates a new element associated with this parser.
+        """
+        self._parser.makeelement(
+            _tag, attrib=None, nsmap=None, **_extra)
+
+    @cython.final
+    cdef _close_source(self):
+        if self._source is None:
+            return
+        if not self._close_source_after_read:
+            self._source = None
+            return
+        try:
+            close = self._source.close
+        except AttributeError:
+            close = None
+        finally:
+            self._source = None
+        if close is not None:
+            close()
+
+    def __iter__(self):
+        return self
+
+    def __next__(self):
+        try:
+            return next(self._events)
+        except StopIteration:
+            pass
+        context = <_SaxParserContext>self._parser._getPushParserContext()
+        if self._source is not None:
+            done = False
+            while not done:
+                try:
+                    done = self._read_more_events(context)
+                    return next(self._events)
+                except StopIteration:
+                    pass  # no events yet
+                except Exception as e:
+                    self._error = e
+                    self._close_source()
+                    try:
+                        return next(self._events)
+                    except StopIteration:
+                        break
+        # nothing left to read or return
+        if self._error is not None:
+            error = self._error
+            self._error = None
+            raise error
+        if (context._validator is not None
+                and not context._validator.isvalid()):
+            _raiseParseError(context._c_ctxt, self._filename,
+                             context._error_log)
+        # no errors => all done
+        raise StopIteration
+
+    @cython.final
+    cdef bint _read_more_events(self, _SaxParserContext context) except -123:
+        data = self._source.read(__ITERPARSE_CHUNK_SIZE)
+        if not isinstance(data, bytes):
+            self._close_source()
+            raise TypeError("reading file objects must return bytes objects")
+        if not data:
+            try:
+                self.root = self._parser.close()
+            finally:
+                self._close_source()
+            return True
+        self._parser.feed(data)
+        return False
+
+
+cdef enum _IterwalkSkipStates:
+    IWSKIP_NEXT_IS_START
+    IWSKIP_SKIP_NEXT
+    IWSKIP_CAN_SKIP
+    IWSKIP_CANNOT_SKIP
+
+
+cdef class iterwalk:
+    """iterwalk(self, element_or_tree, events=("end",), tag=None)
+
+    A tree walker that generates events from an existing tree as if it
+    was parsing XML data with ``iterparse()``.
+
+    Just as for ``iterparse()``, the ``tag`` argument can be a single tag or a
+    sequence of tags.
+
+    After receiving a 'start' or 'start-ns' event, the children and
+    descendants of the current element can be excluded from iteration
+    by calling the ``skip_subtree()`` method.
+    """
+    cdef _MultiTagMatcher _matcher
+    cdef list   _node_stack
+    cdef list   _events
+    cdef object _pop_event
+    cdef object _include_siblings
+    cdef int    _index
+    cdef int    _event_filter
+    cdef _IterwalkSkipStates _skip_state
+
+    def __init__(self, element_or_tree, events=("end",), tag=None):
+        cdef _Element root
+        cdef int ns_count
+        root = _rootNodeOrRaise(element_or_tree)
+        self._event_filter = _buildParseEventFilter(events)
+        if tag is None or tag == '*':
+            self._matcher = None
+        else:
+            self._matcher = _MultiTagMatcher.__new__(_MultiTagMatcher, tag)
+        self._node_stack  = []
+        self._events = []
+        self._pop_event = self._events.pop
+        self._skip_state = IWSKIP_CANNOT_SKIP  # ignore all skip requests by default
+
+        if self._event_filter:
+            self._index = 0
+            if self._matcher is not None and self._event_filter & PARSE_EVENT_FILTER_START:
+                self._matcher.cacheTags(root._doc)
+
+            # When processing an ElementTree, add events for the preceding comments/PIs.
+            if self._event_filter & (PARSE_EVENT_FILTER_COMMENT | PARSE_EVENT_FILTER_PI):
+                if isinstance(element_or_tree, _ElementTree):
+                    self._include_siblings = root
+                    for elem in list(root.itersiblings(preceding=True))[::-1]:
+                        if self._event_filter & PARSE_EVENT_FILTER_COMMENT and elem.tag is Comment:
+                            self._events.append(('comment', elem))
+                        elif self._event_filter & PARSE_EVENT_FILTER_PI and elem.tag is PI:
+                            self._events.append(('pi', elem))
+
+            ns_count = self._start_node(root)
+            self._node_stack.append( (root, ns_count) )
+        else:
+            self._index = -1
+
+    def __iter__(self):
+        return self
+
+    def __next__(self):
+        cdef xmlNode* c_child
+        cdef _Element node
+        cdef _Element next_node
+        cdef int ns_count = 0
+        if self._events:
+            return self._next_event()
+        if self._matcher is not None and self._index >= 0:
+            node = self._node_stack[self._index][0]
+            self._matcher.cacheTags(node._doc)
+
+        # find next node
+        while self._index >= 0:
+            node = self._node_stack[self._index][0]
+
+            if self._skip_state == IWSKIP_SKIP_NEXT:
+                c_child = NULL
+            else:
+                c_child = self._process_non_elements(
+                    node._doc, _findChildForwards(node._c_node, 0))
+            self._skip_state = IWSKIP_CANNOT_SKIP
+
+            while c_child is NULL:
+                # back off through parents
+                self._index -= 1
+                node = self._end_node()
+                if self._index < 0:
+                    break
+                c_child = self._process_non_elements(
+                    node._doc, _nextElement(node._c_node))
+
+            if c_child is not NULL:
+                next_node = _elementFactory(node._doc, c_child)
+                if self._event_filter & (PARSE_EVENT_FILTER_START |
+                                         PARSE_EVENT_FILTER_START_NS):
+                    ns_count = self._start_node(next_node)
+                elif self._event_filter & PARSE_EVENT_FILTER_END_NS:
+                    ns_count = _countNsDefs(next_node._c_node)
+                self._node_stack.append( (next_node, ns_count) )
+                self._index += 1
+            if self._events:
+                return self._next_event()
+
+        if self._include_siblings is not None:
+            node, self._include_siblings = self._include_siblings, None
+            self._process_non_elements(node._doc, _nextElement(node._c_node))
+            if self._events:
+                return self._next_event()
+
+        raise StopIteration
+
+    @cython.final
+    cdef xmlNode* _process_non_elements(self, _Document doc, xmlNode* c_node):
+        while c_node is not NULL and c_node.type != tree.XML_ELEMENT_NODE:
+            if c_node.type == tree.XML_COMMENT_NODE:
+                if self._event_filter & PARSE_EVENT_FILTER_COMMENT:
+                    self._events.append(
+                        ("comment", _elementFactory(doc, c_node)))
+                c_node = _nextElement(c_node)
+            elif c_node.type == tree.XML_PI_NODE:
+                if self._event_filter & PARSE_EVENT_FILTER_PI:
+                    self._events.append(
+                        ("pi", _elementFactory(doc, c_node)))
+                c_node = _nextElement(c_node)
+            else:
+                break
+        return c_node
+
+    @cython.final
+    cdef _next_event(self):
+        if self._skip_state == IWSKIP_NEXT_IS_START:
+            if self._events[0][0] in ('start', 'start-ns'):
+                self._skip_state = IWSKIP_CAN_SKIP
+        return self._pop_event(0)
+
+    def skip_subtree(self):
+        """Prevent descending into the current subtree.
+        Instead, the next returned event will be the 'end' event of the current element
+        (if included), ignoring any children or descendants.
+
+        This has no effect right after an 'end' or 'end-ns' event.
+        """
+        if self._skip_state == IWSKIP_CAN_SKIP:
+            self._skip_state = IWSKIP_SKIP_NEXT
+
+    @cython.final
+    cdef int _start_node(self, _Element node) except -1:
+        cdef int ns_count
+        if self._event_filter & PARSE_EVENT_FILTER_START_NS:
+            ns_count = _appendStartNsEvents(node._c_node, self._events)
+            if self._events:
+                self._skip_state = IWSKIP_NEXT_IS_START
+        elif self._event_filter & PARSE_EVENT_FILTER_END_NS:
+            ns_count = _countNsDefs(node._c_node)
+        else:
+            ns_count = 0
+        if self._event_filter & PARSE_EVENT_FILTER_START:
+            if self._matcher is None or self._matcher.matches(node._c_node):
+                self._events.append( ("start", node) )
+                self._skip_state = IWSKIP_NEXT_IS_START
+        return ns_count
+
+    @cython.final
+    cdef _Element _end_node(self):
+        cdef _Element node
+        cdef int i, ns_count
+        node, ns_count = self._node_stack.pop()
+        if self._event_filter & PARSE_EVENT_FILTER_END:
+            if self._matcher is None or self._matcher.matches(node._c_node):
+                self._events.append( ("end", node) )
+        if self._event_filter & PARSE_EVENT_FILTER_END_NS and ns_count:
+            event = ("end-ns", None)
+            for i in range(ns_count):
+                self._events.append(event)
+        return node
+
+
+cdef int _countNsDefs(xmlNode* c_node) noexcept:
+    cdef xmlNs* c_ns
+    cdef int count
+    count = 0
+    c_ns = c_node.nsDef
+    while c_ns is not NULL:
+        count += (c_ns.href is not NULL)
+        c_ns = c_ns.next
+    return count
+
+
+cdef int _appendStartNsEvents(xmlNode* c_node, list event_list) except -1:
+    cdef xmlNs* c_ns
+    cdef int count
+    count = 0
+    c_ns = c_node.nsDef
+    while c_ns is not NULL:
+        if c_ns.href:
+            ns_tuple = (funicodeOrEmpty(c_ns.prefix),
+                        funicode(c_ns.href))
+            event_list.append( ("start-ns", ns_tuple) )
+            count += 1
+        c_ns = c_ns.next
+    return count
diff --git a/.venv/lib/python3.12/site-packages/lxml/lxml.etree.h b/.venv/lib/python3.12/site-packages/lxml/lxml.etree.h
new file mode 100644
index 00000000..c2a0f5ab
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/lxml.etree.h
@@ -0,0 +1,248 @@
+/* Generated by Cython 3.0.11 */
+
+#ifndef __PYX_HAVE__lxml__etree
+#define __PYX_HAVE__lxml__etree
+
+#include "Python.h"
+struct LxmlDocument;
+struct LxmlElement;
+struct LxmlElementTree;
+struct LxmlElementTagMatcher;
+struct LxmlElementIterator;
+struct LxmlElementBase;
+struct LxmlElementClassLookup;
+struct LxmlFallbackElementClassLookup;
+
+/* "lxml/etree.pyx":355
+ * 
+ * # type of a function that steps from node to node
+ * ctypedef public xmlNode* (*_node_to_node_function)(xmlNode*)             # <<<<<<<<<<<<<<
+ * 
+ * 
+ */
+typedef xmlNode *(*_node_to_node_function)(xmlNode *);
+
+/* "lxml/etree.pyx":371
+ * @cython.final
+ * @cython.freelist(8)
+ * cdef public class _Document [ type LxmlDocumentType, object LxmlDocument ]:             # <<<<<<<<<<<<<<
+ *     """Internal base class to reference a libxml document.
+ * 
+ */
+struct LxmlDocument {
+  PyObject_HEAD
+  struct __pyx_vtabstruct_4lxml_5etree__Document *__pyx_vtab;
+  int _ns_counter;
+  PyObject *_prefix_tail;
+  xmlDoc *_c_doc;
+  struct __pyx_obj_4lxml_5etree__BaseParser *_parser;
+};
+
+/* "lxml/etree.pyx":720
+ * 
+ * @cython.no_gc_clear
+ * cdef public class _Element [ type LxmlElementType, object LxmlElement ]:             # <<<<<<<<<<<<<<
+ *     """Element class.
+ * 
+ */
+struct LxmlElement {
+  PyObject_HEAD
+  struct LxmlDocument *_doc;
+  xmlNode *_c_node;
+  PyObject *_tag;
+};
+
+/* "lxml/etree.pyx":1895
+ * 
+ * 
+ * cdef public class _ElementTree [ type LxmlElementTreeType,             # <<<<<<<<<<<<<<
+ *                                  object LxmlElementTree ]:
+ *     cdef _Document _doc
+ */
+struct LxmlElementTree {
+  PyObject_HEAD
+  struct __pyx_vtabstruct_4lxml_5etree__ElementTree *__pyx_vtab;
+  struct LxmlDocument *_doc;
+  struct LxmlElement *_context_node;
+};
+
+/* "lxml/etree.pyx":2669
+ * 
+ * 
+ * cdef public class _ElementTagMatcher [ object LxmlElementTagMatcher,             # <<<<<<<<<<<<<<
+ *                                        type LxmlElementTagMatcherType ]:
+ *     """
+ */
+struct LxmlElementTagMatcher {
+  PyObject_HEAD
+  struct __pyx_vtabstruct_4lxml_5etree__ElementTagMatcher *__pyx_vtab;
+  PyObject *_pystrings;
+  int _node_type;
+  char *_href;
+  char *_name;
+};
+
+/* "lxml/etree.pyx":2700
+ *                 self._name = NULL
+ * 
+ * cdef public class _ElementIterator(_ElementTagMatcher) [             # <<<<<<<<<<<<<<
+ *     object LxmlElementIterator, type LxmlElementIteratorType ]:
+ *     """
+ */
+struct LxmlElementIterator {
+  struct LxmlElementTagMatcher __pyx_base;
+  struct LxmlElement *_node;
+  _node_to_node_function _next_element;
+};
+
+/* "src/lxml/classlookup.pxi":6
+ * # Custom Element classes
+ * 
+ * cdef public class ElementBase(_Element) [ type LxmlElementBaseType,             # <<<<<<<<<<<<<<
+ *                                           object LxmlElementBase ]:
+ *     """ElementBase(*children, attrib=None, nsmap=None, **_extra)
+ */
+struct LxmlElementBase {
+  struct LxmlElement __pyx_base;
+};
+
+/* "src/lxml/classlookup.pxi":210
+ * # Element class lookup
+ * 
+ * ctypedef public object (*_element_class_lookup_function)(object, _Document, xmlNode*)             # <<<<<<<<<<<<<<
+ * 
+ * # class to store element class lookup functions
+ */
+typedef PyObject *(*_element_class_lookup_function)(PyObject *, struct LxmlDocument *, xmlNode *);
+
+/* "src/lxml/classlookup.pxi":213
+ * 
+ * # class to store element class lookup functions
+ * cdef public class ElementClassLookup [ type LxmlElementClassLookupType,             # <<<<<<<<<<<<<<
+ *                                        object LxmlElementClassLookup ]:
+ *     """ElementClassLookup(self)
+ */
+struct LxmlElementClassLookup {
+  PyObject_HEAD
+  _element_class_lookup_function _lookup_function;
+};
+
+/* "src/lxml/classlookup.pxi":221
+ * 
+ * 
+ * cdef public class FallbackElementClassLookup(ElementClassLookup) \             # <<<<<<<<<<<<<<
+ *          [ type LxmlFallbackElementClassLookupType,
+ *            object LxmlFallbackElementClassLookup ]:
+ */
+struct LxmlFallbackElementClassLookup {
+  struct LxmlElementClassLookup __pyx_base;
+  struct __pyx_vtabstruct_4lxml_5etree_FallbackElementClassLookup *__pyx_vtab;
+  struct LxmlElementClassLookup *fallback;
+  _element_class_lookup_function _fallback_function;
+};
+
+#ifndef __PYX_HAVE_API__lxml__etree
+
+#ifdef CYTHON_EXTERN_C
+    #undef __PYX_EXTERN_C
+    #define __PYX_EXTERN_C CYTHON_EXTERN_C
+#elif defined(__PYX_EXTERN_C)
+    #ifdef _MSC_VER
+    #pragma message ("Please do not define the '__PYX_EXTERN_C' macro externally. Use 'CYTHON_EXTERN_C' instead.")
+    #else
+    #warning Please do not define the '__PYX_EXTERN_C' macro externally. Use 'CYTHON_EXTERN_C' instead.
+    #endif
+#else
+  #ifdef __cplusplus
+    #define __PYX_EXTERN_C extern "C"
+  #else
+    #define __PYX_EXTERN_C extern
+  #endif
+#endif
+
+#ifndef DL_IMPORT
+  #define DL_IMPORT(_T) _T
+#endif
+
+__PYX_EXTERN_C DL_IMPORT(PyTypeObject) LxmlDocumentType;
+__PYX_EXTERN_C DL_IMPORT(PyTypeObject) LxmlElementType;
+__PYX_EXTERN_C DL_IMPORT(PyTypeObject) LxmlElementTreeType;
+__PYX_EXTERN_C DL_IMPORT(PyTypeObject) LxmlElementTagMatcherType;
+__PYX_EXTERN_C DL_IMPORT(PyTypeObject) LxmlElementIteratorType;
+__PYX_EXTERN_C DL_IMPORT(PyTypeObject) LxmlElementBaseType;
+__PYX_EXTERN_C DL_IMPORT(PyTypeObject) LxmlElementClassLookupType;
+__PYX_EXTERN_C DL_IMPORT(PyTypeObject) LxmlFallbackElementClassLookupType;
+
+__PYX_EXTERN_C struct LxmlElement *deepcopyNodeToDocument(struct LxmlDocument *, xmlNode *);
+__PYX_EXTERN_C struct LxmlElementTree *elementTreeFactory(struct LxmlElement *);
+__PYX_EXTERN_C struct LxmlElementTree *newElementTree(struct LxmlElement *, PyObject *);
+__PYX_EXTERN_C struct LxmlElementTree *adoptExternalDocument(xmlDoc *, PyObject *, int);
+__PYX_EXTERN_C struct LxmlElement *elementFactory(struct LxmlDocument *, xmlNode *);
+__PYX_EXTERN_C struct LxmlElement *makeElement(PyObject *, struct LxmlDocument *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *);
+__PYX_EXTERN_C struct LxmlElement *makeSubElement(struct LxmlElement *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *);
+__PYX_EXTERN_C void setElementClassLookupFunction(_element_class_lookup_function, PyObject *);
+__PYX_EXTERN_C PyObject *lookupDefaultElementClass(PyObject *, PyObject *, xmlNode *);
+__PYX_EXTERN_C PyObject *lookupNamespaceElementClass(PyObject *, PyObject *, xmlNode *);
+__PYX_EXTERN_C PyObject *callLookupFallback(struct LxmlFallbackElementClassLookup *, struct LxmlDocument *, xmlNode *);
+__PYX_EXTERN_C int tagMatches(xmlNode *, const xmlChar *, const xmlChar *);
+__PYX_EXTERN_C struct LxmlDocument *documentOrRaise(PyObject *);
+__PYX_EXTERN_C struct LxmlElement *rootNodeOrRaise(PyObject *);
+__PYX_EXTERN_C int hasText(xmlNode *);
+__PYX_EXTERN_C int hasTail(xmlNode *);
+__PYX_EXTERN_C PyObject *textOf(xmlNode *);
+__PYX_EXTERN_C PyObject *tailOf(xmlNode *);
+__PYX_EXTERN_C int setNodeText(xmlNode *, PyObject *);
+__PYX_EXTERN_C int setTailText(xmlNode *, PyObject *);
+__PYX_EXTERN_C PyObject *attributeValue(xmlNode *, xmlAttr *);
+__PYX_EXTERN_C PyObject *attributeValueFromNsName(xmlNode *, const xmlChar *, const xmlChar *);
+__PYX_EXTERN_C PyObject *getAttributeValue(struct LxmlElement *, PyObject *, PyObject *);
+__PYX_EXTERN_C PyObject *iterattributes(struct LxmlElement *, int);
+__PYX_EXTERN_C PyObject *collectAttributes(xmlNode *, int);
+__PYX_EXTERN_C int setAttributeValue(struct LxmlElement *, PyObject *, PyObject *);
+__PYX_EXTERN_C int delAttribute(struct LxmlElement *, PyObject *);
+__PYX_EXTERN_C int delAttributeFromNsName(xmlNode *, const xmlChar *, const xmlChar *);
+__PYX_EXTERN_C int hasChild(xmlNode *);
+__PYX_EXTERN_C xmlNode *findChild(xmlNode *, Py_ssize_t);
+__PYX_EXTERN_C xmlNode *findChildForwards(xmlNode *, Py_ssize_t);
+__PYX_EXTERN_C xmlNode *findChildBackwards(xmlNode *, Py_ssize_t);
+__PYX_EXTERN_C xmlNode *nextElement(xmlNode *);
+__PYX_EXTERN_C xmlNode *previousElement(xmlNode *);
+__PYX_EXTERN_C void appendChild(struct LxmlElement *, struct LxmlElement *);
+__PYX_EXTERN_C int appendChildToElement(struct LxmlElement *, struct LxmlElement *);
+__PYX_EXTERN_C PyObject *pyunicode(const xmlChar *);
+__PYX_EXTERN_C PyObject *utf8(PyObject *);
+__PYX_EXTERN_C PyObject *getNsTag(PyObject *);
+__PYX_EXTERN_C PyObject *getNsTagWithEmptyNs(PyObject *);
+__PYX_EXTERN_C PyObject *namespacedName(xmlNode *);
+__PYX_EXTERN_C PyObject *namespacedNameFromNsName(const xmlChar *, const xmlChar *);
+__PYX_EXTERN_C void iteratorStoreNext(struct LxmlElementIterator *, struct LxmlElement *);
+__PYX_EXTERN_C void initTagMatch(struct LxmlElementTagMatcher *, PyObject *);
+__PYX_EXTERN_C xmlNs *findOrBuildNodeNsPrefix(struct LxmlDocument *, xmlNode *, const xmlChar *, const xmlChar *);
+
+#endif /* !__PYX_HAVE_API__lxml__etree */
+
+/* WARNING: the interface of the module init function changed in CPython 3.5. */
+/* It now returns a PyModuleDef instance instead of a PyModule instance. */
+
+#if PY_MAJOR_VERSION < 3
+PyMODINIT_FUNC initetree(void);
+#else
+/* WARNING: Use PyImport_AppendInittab("etree", PyInit_etree) instead of calling PyInit_etree directly from Python 3.5 */
+PyMODINIT_FUNC PyInit_etree(void);
+
+#if PY_VERSION_HEX >= 0x03050000 && (defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER) || (defined(__cplusplus) && __cplusplus >= 201402L))
+#if defined(__cplusplus) && __cplusplus >= 201402L
+[[deprecated("Use PyImport_AppendInittab(\"etree\", PyInit_etree) instead of calling PyInit_etree directly.")]] inline
+#elif defined(__GNUC__) || defined(__clang__)
+__attribute__ ((__deprecated__("Use PyImport_AppendInittab(\"etree\", PyInit_etree) instead of calling PyInit_etree directly."), __unused__)) __inline__
+#elif defined(_MSC_VER)
+__declspec(deprecated("Use PyImport_AppendInittab(\"etree\", PyInit_etree) instead of calling PyInit_etree directly.")) __inline
+#endif
+static PyObject* __PYX_WARN_IF_PyInit_etree_INIT_CALLED(PyObject* res) {
+  return res;
+}
+#define PyInit_etree() __PYX_WARN_IF_PyInit_etree_INIT_CALLED(PyInit_etree())
+#endif
+#endif
+
+#endif /* !__PYX_HAVE__lxml__etree */
diff --git a/.venv/lib/python3.12/site-packages/lxml/lxml.etree_api.h b/.venv/lib/python3.12/site-packages/lxml/lxml.etree_api.h
new file mode 100644
index 00000000..ff0ca50c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/lxml.etree_api.h
@@ -0,0 +1,195 @@
+/* Generated by Cython 3.0.11 */
+
+#ifndef __PYX_HAVE_API__lxml__etree
+#define __PYX_HAVE_API__lxml__etree
+#ifdef __MINGW64__
+#define MS_WIN64
+#endif
+#include "Python.h"
+#include "lxml.etree.h"
+
+static struct LxmlElement *(*__pyx_api_f_4lxml_5etree_deepcopyNodeToDocument)(struct LxmlDocument *, xmlNode *) = 0;
+#define deepcopyNodeToDocument __pyx_api_f_4lxml_5etree_deepcopyNodeToDocument
+static struct LxmlElementTree *(*__pyx_api_f_4lxml_5etree_elementTreeFactory)(struct LxmlElement *) = 0;
+#define elementTreeFactory __pyx_api_f_4lxml_5etree_elementTreeFactory
+static struct LxmlElementTree *(*__pyx_api_f_4lxml_5etree_newElementTree)(struct LxmlElement *, PyObject *) = 0;
+#define newElementTree __pyx_api_f_4lxml_5etree_newElementTree
+static struct LxmlElementTree *(*__pyx_api_f_4lxml_5etree_adoptExternalDocument)(xmlDoc *, PyObject *, int) = 0;
+#define adoptExternalDocument __pyx_api_f_4lxml_5etree_adoptExternalDocument
+static struct LxmlElement *(*__pyx_api_f_4lxml_5etree_elementFactory)(struct LxmlDocument *, xmlNode *) = 0;
+#define elementFactory __pyx_api_f_4lxml_5etree_elementFactory
+static struct LxmlElement *(*__pyx_api_f_4lxml_5etree_makeElement)(PyObject *, struct LxmlDocument *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *) = 0;
+#define makeElement __pyx_api_f_4lxml_5etree_makeElement
+static struct LxmlElement *(*__pyx_api_f_4lxml_5etree_makeSubElement)(struct LxmlElement *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *) = 0;
+#define makeSubElement __pyx_api_f_4lxml_5etree_makeSubElement
+static void (*__pyx_api_f_4lxml_5etree_setElementClassLookupFunction)(_element_class_lookup_function, PyObject *) = 0;
+#define setElementClassLookupFunction __pyx_api_f_4lxml_5etree_setElementClassLookupFunction
+static PyObject *(*__pyx_api_f_4lxml_5etree_lookupDefaultElementClass)(PyObject *, PyObject *, xmlNode *) = 0;
+#define lookupDefaultElementClass __pyx_api_f_4lxml_5etree_lookupDefaultElementClass
+static PyObject *(*__pyx_api_f_4lxml_5etree_lookupNamespaceElementClass)(PyObject *, PyObject *, xmlNode *) = 0;
+#define lookupNamespaceElementClass __pyx_api_f_4lxml_5etree_lookupNamespaceElementClass
+static PyObject *(*__pyx_api_f_4lxml_5etree_callLookupFallback)(struct LxmlFallbackElementClassLookup *, struct LxmlDocument *, xmlNode *) = 0;
+#define callLookupFallback __pyx_api_f_4lxml_5etree_callLookupFallback
+static int (*__pyx_api_f_4lxml_5etree_tagMatches)(xmlNode *, const xmlChar *, const xmlChar *) = 0;
+#define tagMatches __pyx_api_f_4lxml_5etree_tagMatches
+static struct LxmlDocument *(*__pyx_api_f_4lxml_5etree_documentOrRaise)(PyObject *) = 0;
+#define documentOrRaise __pyx_api_f_4lxml_5etree_documentOrRaise
+static struct LxmlElement *(*__pyx_api_f_4lxml_5etree_rootNodeOrRaise)(PyObject *) = 0;
+#define rootNodeOrRaise __pyx_api_f_4lxml_5etree_rootNodeOrRaise
+static int (*__pyx_api_f_4lxml_5etree_hasText)(xmlNode *) = 0;
+#define hasText __pyx_api_f_4lxml_5etree_hasText
+static int (*__pyx_api_f_4lxml_5etree_hasTail)(xmlNode *) = 0;
+#define hasTail __pyx_api_f_4lxml_5etree_hasTail
+static PyObject *(*__pyx_api_f_4lxml_5etree_textOf)(xmlNode *) = 0;
+#define textOf __pyx_api_f_4lxml_5etree_textOf
+static PyObject *(*__pyx_api_f_4lxml_5etree_tailOf)(xmlNode *) = 0;
+#define tailOf __pyx_api_f_4lxml_5etree_tailOf
+static int (*__pyx_api_f_4lxml_5etree_setNodeText)(xmlNode *, PyObject *) = 0;
+#define setNodeText __pyx_api_f_4lxml_5etree_setNodeText
+static int (*__pyx_api_f_4lxml_5etree_setTailText)(xmlNode *, PyObject *) = 0;
+#define setTailText __pyx_api_f_4lxml_5etree_setTailText
+static PyObject *(*__pyx_api_f_4lxml_5etree_attributeValue)(xmlNode *, xmlAttr *) = 0;
+#define attributeValue __pyx_api_f_4lxml_5etree_attributeValue
+static PyObject *(*__pyx_api_f_4lxml_5etree_attributeValueFromNsName)(xmlNode *, const xmlChar *, const xmlChar *) = 0;
+#define attributeValueFromNsName __pyx_api_f_4lxml_5etree_attributeValueFromNsName
+static PyObject *(*__pyx_api_f_4lxml_5etree_getAttributeValue)(struct LxmlElement *, PyObject *, PyObject *) = 0;
+#define getAttributeValue __pyx_api_f_4lxml_5etree_getAttributeValue
+static PyObject *(*__pyx_api_f_4lxml_5etree_iterattributes)(struct LxmlElement *, int) = 0;
+#define iterattributes __pyx_api_f_4lxml_5etree_iterattributes
+static PyObject *(*__pyx_api_f_4lxml_5etree_collectAttributes)(xmlNode *, int) = 0;
+#define collectAttributes __pyx_api_f_4lxml_5etree_collectAttributes
+static int (*__pyx_api_f_4lxml_5etree_setAttributeValue)(struct LxmlElement *, PyObject *, PyObject *) = 0;
+#define setAttributeValue __pyx_api_f_4lxml_5etree_setAttributeValue
+static int (*__pyx_api_f_4lxml_5etree_delAttribute)(struct LxmlElement *, PyObject *) = 0;
+#define delAttribute __pyx_api_f_4lxml_5etree_delAttribute
+static int (*__pyx_api_f_4lxml_5etree_delAttributeFromNsName)(xmlNode *, const xmlChar *, const xmlChar *) = 0;
+#define delAttributeFromNsName __pyx_api_f_4lxml_5etree_delAttributeFromNsName
+static int (*__pyx_api_f_4lxml_5etree_hasChild)(xmlNode *) = 0;
+#define hasChild __pyx_api_f_4lxml_5etree_hasChild
+static xmlNode *(*__pyx_api_f_4lxml_5etree_findChild)(xmlNode *, Py_ssize_t) = 0;
+#define findChild __pyx_api_f_4lxml_5etree_findChild
+static xmlNode *(*__pyx_api_f_4lxml_5etree_findChildForwards)(xmlNode *, Py_ssize_t) = 0;
+#define findChildForwards __pyx_api_f_4lxml_5etree_findChildForwards
+static xmlNode *(*__pyx_api_f_4lxml_5etree_findChildBackwards)(xmlNode *, Py_ssize_t) = 0;
+#define findChildBackwards __pyx_api_f_4lxml_5etree_findChildBackwards
+static xmlNode *(*__pyx_api_f_4lxml_5etree_nextElement)(xmlNode *) = 0;
+#define nextElement __pyx_api_f_4lxml_5etree_nextElement
+static xmlNode *(*__pyx_api_f_4lxml_5etree_previousElement)(xmlNode *) = 0;
+#define previousElement __pyx_api_f_4lxml_5etree_previousElement
+static void (*__pyx_api_f_4lxml_5etree_appendChild)(struct LxmlElement *, struct LxmlElement *) = 0;
+#define appendChild __pyx_api_f_4lxml_5etree_appendChild
+static int (*__pyx_api_f_4lxml_5etree_appendChildToElement)(struct LxmlElement *, struct LxmlElement *) = 0;
+#define appendChildToElement __pyx_api_f_4lxml_5etree_appendChildToElement
+static PyObject *(*__pyx_api_f_4lxml_5etree_pyunicode)(const xmlChar *) = 0;
+#define pyunicode __pyx_api_f_4lxml_5etree_pyunicode
+static PyObject *(*__pyx_api_f_4lxml_5etree_utf8)(PyObject *) = 0;
+#define utf8 __pyx_api_f_4lxml_5etree_utf8
+static PyObject *(*__pyx_api_f_4lxml_5etree_getNsTag)(PyObject *) = 0;
+#define getNsTag __pyx_api_f_4lxml_5etree_getNsTag
+static PyObject *(*__pyx_api_f_4lxml_5etree_getNsTagWithEmptyNs)(PyObject *) = 0;
+#define getNsTagWithEmptyNs __pyx_api_f_4lxml_5etree_getNsTagWithEmptyNs
+static PyObject *(*__pyx_api_f_4lxml_5etree_namespacedName)(xmlNode *) = 0;
+#define namespacedName __pyx_api_f_4lxml_5etree_namespacedName
+static PyObject *(*__pyx_api_f_4lxml_5etree_namespacedNameFromNsName)(const xmlChar *, const xmlChar *) = 0;
+#define namespacedNameFromNsName __pyx_api_f_4lxml_5etree_namespacedNameFromNsName
+static void (*__pyx_api_f_4lxml_5etree_iteratorStoreNext)(struct LxmlElementIterator *, struct LxmlElement *) = 0;
+#define iteratorStoreNext __pyx_api_f_4lxml_5etree_iteratorStoreNext
+static void (*__pyx_api_f_4lxml_5etree_initTagMatch)(struct LxmlElementTagMatcher *, PyObject *) = 0;
+#define initTagMatch __pyx_api_f_4lxml_5etree_initTagMatch
+static xmlNs *(*__pyx_api_f_4lxml_5etree_findOrBuildNodeNsPrefix)(struct LxmlDocument *, xmlNode *, const xmlChar *, const xmlChar *) = 0;
+#define findOrBuildNodeNsPrefix __pyx_api_f_4lxml_5etree_findOrBuildNodeNsPrefix
+#ifndef __PYX_HAVE_RT_ImportFunction_3_0_11
+#define __PYX_HAVE_RT_ImportFunction_3_0_11
+static int __Pyx_ImportFunction_3_0_11(PyObject *module, const char *funcname, void (**f)(void), const char *sig) {
+    PyObject *d = 0;
+    PyObject *cobj = 0;
+    union {
+        void (*fp)(void);
+        void *p;
+    } tmp;
+    d = PyObject_GetAttrString(module, (char *)"__pyx_capi__");
+    if (!d)
+        goto bad;
+    cobj = PyDict_GetItemString(d, funcname);
+    if (!cobj) {
+        PyErr_Format(PyExc_ImportError,
+            "%.200s does not export expected C function %.200s",
+                PyModule_GetName(module), funcname);
+        goto bad;
+    }
+    if (!PyCapsule_IsValid(cobj, sig)) {
+        PyErr_Format(PyExc_TypeError,
+            "C function %.200s.%.200s has wrong signature (expected %.500s, got %.500s)",
+             PyModule_GetName(module), funcname, sig, PyCapsule_GetName(cobj));
+        goto bad;
+    }
+    tmp.p = PyCapsule_GetPointer(cobj, sig);
+    *f = tmp.fp;
+    if (!(*f))
+        goto bad;
+    Py_DECREF(d);
+    return 0;
+bad:
+    Py_XDECREF(d);
+    return -1;
+}
+#endif
+
+
+static int import_lxml__etree(void) {
+  PyObject *module = 0;
+  module = PyImport_ImportModule("lxml.etree");
+  if (!module) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "deepcopyNodeToDocument", (void (**)(void))&__pyx_api_f_4lxml_5etree_deepcopyNodeToDocument, "struct LxmlElement *(struct LxmlDocument *, xmlNode *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "elementTreeFactory", (void (**)(void))&__pyx_api_f_4lxml_5etree_elementTreeFactory, "struct LxmlElementTree *(struct LxmlElement *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "newElementTree", (void (**)(void))&__pyx_api_f_4lxml_5etree_newElementTree, "struct LxmlElementTree *(struct LxmlElement *, PyObject *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "adoptExternalDocument", (void (**)(void))&__pyx_api_f_4lxml_5etree_adoptExternalDocument, "struct LxmlElementTree *(xmlDoc *, PyObject *, int)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "elementFactory", (void (**)(void))&__pyx_api_f_4lxml_5etree_elementFactory, "struct LxmlElement *(struct LxmlDocument *, xmlNode *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "makeElement", (void (**)(void))&__pyx_api_f_4lxml_5etree_makeElement, "struct LxmlElement *(PyObject *, struct LxmlDocument *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "makeSubElement", (void (**)(void))&__pyx_api_f_4lxml_5etree_makeSubElement, "struct LxmlElement *(struct LxmlElement *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "setElementClassLookupFunction", (void (**)(void))&__pyx_api_f_4lxml_5etree_setElementClassLookupFunction, "void (_element_class_lookup_function, PyObject *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "lookupDefaultElementClass", (void (**)(void))&__pyx_api_f_4lxml_5etree_lookupDefaultElementClass, "PyObject *(PyObject *, PyObject *, xmlNode *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "lookupNamespaceElementClass", (void (**)(void))&__pyx_api_f_4lxml_5etree_lookupNamespaceElementClass, "PyObject *(PyObject *, PyObject *, xmlNode *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "callLookupFallback", (void (**)(void))&__pyx_api_f_4lxml_5etree_callLookupFallback, "PyObject *(struct LxmlFallbackElementClassLookup *, struct LxmlDocument *, xmlNode *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "tagMatches", (void (**)(void))&__pyx_api_f_4lxml_5etree_tagMatches, "int (xmlNode *, const xmlChar *, const xmlChar *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "documentOrRaise", (void (**)(void))&__pyx_api_f_4lxml_5etree_documentOrRaise, "struct LxmlDocument *(PyObject *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "rootNodeOrRaise", (void (**)(void))&__pyx_api_f_4lxml_5etree_rootNodeOrRaise, "struct LxmlElement *(PyObject *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "hasText", (void (**)(void))&__pyx_api_f_4lxml_5etree_hasText, "int (xmlNode *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "hasTail", (void (**)(void))&__pyx_api_f_4lxml_5etree_hasTail, "int (xmlNode *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "textOf", (void (**)(void))&__pyx_api_f_4lxml_5etree_textOf, "PyObject *(xmlNode *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "tailOf", (void (**)(void))&__pyx_api_f_4lxml_5etree_tailOf, "PyObject *(xmlNode *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "setNodeText", (void (**)(void))&__pyx_api_f_4lxml_5etree_setNodeText, "int (xmlNode *, PyObject *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "setTailText", (void (**)(void))&__pyx_api_f_4lxml_5etree_setTailText, "int (xmlNode *, PyObject *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "attributeValue", (void (**)(void))&__pyx_api_f_4lxml_5etree_attributeValue, "PyObject *(xmlNode *, xmlAttr *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "attributeValueFromNsName", (void (**)(void))&__pyx_api_f_4lxml_5etree_attributeValueFromNsName, "PyObject *(xmlNode *, const xmlChar *, const xmlChar *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "getAttributeValue", (void (**)(void))&__pyx_api_f_4lxml_5etree_getAttributeValue, "PyObject *(struct LxmlElement *, PyObject *, PyObject *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "iterattributes", (void (**)(void))&__pyx_api_f_4lxml_5etree_iterattributes, "PyObject *(struct LxmlElement *, int)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "collectAttributes", (void (**)(void))&__pyx_api_f_4lxml_5etree_collectAttributes, "PyObject *(xmlNode *, int)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "setAttributeValue", (void (**)(void))&__pyx_api_f_4lxml_5etree_setAttributeValue, "int (struct LxmlElement *, PyObject *, PyObject *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "delAttribute", (void (**)(void))&__pyx_api_f_4lxml_5etree_delAttribute, "int (struct LxmlElement *, PyObject *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "delAttributeFromNsName", (void (**)(void))&__pyx_api_f_4lxml_5etree_delAttributeFromNsName, "int (xmlNode *, const xmlChar *, const xmlChar *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "hasChild", (void (**)(void))&__pyx_api_f_4lxml_5etree_hasChild, "int (xmlNode *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "findChild", (void (**)(void))&__pyx_api_f_4lxml_5etree_findChild, "xmlNode *(xmlNode *, Py_ssize_t)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "findChildForwards", (void (**)(void))&__pyx_api_f_4lxml_5etree_findChildForwards, "xmlNode *(xmlNode *, Py_ssize_t)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "findChildBackwards", (void (**)(void))&__pyx_api_f_4lxml_5etree_findChildBackwards, "xmlNode *(xmlNode *, Py_ssize_t)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "nextElement", (void (**)(void))&__pyx_api_f_4lxml_5etree_nextElement, "xmlNode *(xmlNode *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "previousElement", (void (**)(void))&__pyx_api_f_4lxml_5etree_previousElement, "xmlNode *(xmlNode *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "appendChild", (void (**)(void))&__pyx_api_f_4lxml_5etree_appendChild, "void (struct LxmlElement *, struct LxmlElement *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "appendChildToElement", (void (**)(void))&__pyx_api_f_4lxml_5etree_appendChildToElement, "int (struct LxmlElement *, struct LxmlElement *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "pyunicode", (void (**)(void))&__pyx_api_f_4lxml_5etree_pyunicode, "PyObject *(const xmlChar *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "utf8", (void (**)(void))&__pyx_api_f_4lxml_5etree_utf8, "PyObject *(PyObject *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "getNsTag", (void (**)(void))&__pyx_api_f_4lxml_5etree_getNsTag, "PyObject *(PyObject *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "getNsTagWithEmptyNs", (void (**)(void))&__pyx_api_f_4lxml_5etree_getNsTagWithEmptyNs, "PyObject *(PyObject *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "namespacedName", (void (**)(void))&__pyx_api_f_4lxml_5etree_namespacedName, "PyObject *(xmlNode *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "namespacedNameFromNsName", (void (**)(void))&__pyx_api_f_4lxml_5etree_namespacedNameFromNsName, "PyObject *(const xmlChar *, const xmlChar *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "iteratorStoreNext", (void (**)(void))&__pyx_api_f_4lxml_5etree_iteratorStoreNext, "void (struct LxmlElementIterator *, struct LxmlElement *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "initTagMatch", (void (**)(void))&__pyx_api_f_4lxml_5etree_initTagMatch, "void (struct LxmlElementTagMatcher *, PyObject *)") < 0) goto bad;
+  if (__Pyx_ImportFunction_3_0_11(module, "findOrBuildNodeNsPrefix", (void (**)(void))&__pyx_api_f_4lxml_5etree_findOrBuildNodeNsPrefix, "xmlNs *(struct LxmlDocument *, xmlNode *, const xmlChar *, const xmlChar *)") < 0) goto bad;
+  Py_DECREF(module); module = 0;
+  return 0;
+  bad:
+  Py_XDECREF(module);
+  return -1;
+}
+
+#endif /* !__PYX_HAVE_API__lxml__etree */
diff --git a/.venv/lib/python3.12/site-packages/lxml/nsclasses.pxi b/.venv/lib/python3.12/site-packages/lxml/nsclasses.pxi
new file mode 100644
index 00000000..a3c86f0e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/nsclasses.pxi
@@ -0,0 +1,281 @@
+# module-level API for namespace implementations
+
+cdef class LxmlRegistryError(LxmlError):
+    """Base class of lxml registry errors.
+    """
+
+cdef class NamespaceRegistryError(LxmlRegistryError):
+    """Error registering a namespace extension.
+    """
+
+
+@cython.internal
+cdef class _NamespaceRegistry:
+    "Dictionary-like namespace registry"
+    cdef object _ns_uri
+    cdef bytes _ns_uri_utf
+    cdef dict _entries
+    cdef char* _c_ns_uri_utf
+    def __cinit__(self, ns_uri):
+        self._ns_uri = ns_uri
+        if ns_uri is None:
+            self._ns_uri_utf = None
+            self._c_ns_uri_utf = NULL
+        else:
+            self._ns_uri_utf = _utf8(ns_uri)
+            self._c_ns_uri_utf = _cstr(self._ns_uri_utf)
+        self._entries = {}
+
+    def update(self, class_dict_iterable):
+        """update(self, class_dict_iterable)
+
+        Forgivingly update the registry.
+
+        ``class_dict_iterable`` may be a dict or some other iterable
+        that yields (name, value) pairs.
+
+        If a value does not match the required type for this registry,
+        or if the name starts with '_', it will be silently discarded.
+        This allows registrations at the module or class level using
+        vars(), globals() etc."""
+        if hasattr(class_dict_iterable, 'items'):
+            class_dict_iterable = class_dict_iterable.items()
+        for name, item in class_dict_iterable:
+            if (name is None or name[:1] != '_') and callable(item):
+                self[name] = item
+
+    def __getitem__(self, name):
+        if name is not None:
+            name = _utf8(name)
+        return self._get(name)
+
+    def __delitem__(self, name):
+        if name is not None:
+            name = _utf8(name)
+        del self._entries[name]
+
+    cdef object _get(self, object name):
+        cdef python.PyObject* dict_result
+        dict_result = python.PyDict_GetItem(self._entries, name)
+        if dict_result is NULL:
+            raise KeyError, "Name not registered."
+        return <object>dict_result
+
+    cdef object _getForString(self, char* name):
+        cdef python.PyObject* dict_result
+        dict_result = python.PyDict_GetItem(self._entries, name)
+        if dict_result is NULL:
+            raise KeyError, "Name not registered."
+        return <object>dict_result
+
+    def __iter__(self):
+        return iter(self._entries)
+
+    def items(self):
+        return list(self._entries.items())
+
+    def iteritems(self):
+        return iter(self._entries.items())
+
+    def clear(self):
+        self._entries.clear()
+
+    def __call__(self, obj):
+        # Usage as decorator:
+        #   ns = lookup.get_namespace("...")
+        #   @ns('abc')
+        #   class element(ElementBase): pass
+        #
+        #   @ns
+        #   class elementname(ElementBase): pass
+
+        if obj is None or python._isString(obj):
+            # @ns(None) or @ns('tag')
+            return partial(self.__deco, obj)
+        # plain @ns decorator
+        self[obj.__name__] = obj
+        return obj
+
+    def __deco(self, name, obj):
+        self[name] = obj
+        return obj
+
+
+@cython.final
+@cython.internal
+cdef class _ClassNamespaceRegistry(_NamespaceRegistry):
+    "Dictionary-like registry for namespace implementation classes"
+    def __setitem__(self, name, item):
+        if not isinstance(item, type) or not issubclass(item, ElementBase):
+            raise NamespaceRegistryError, \
+                "Registered element classes must be subtypes of ElementBase"
+        if name is not None:
+            name = _utf8(name)
+        self._entries[name] = item
+
+    def __repr__(self):
+        return "Namespace(%r)" % self._ns_uri
+
+
+cdef class ElementNamespaceClassLookup(FallbackElementClassLookup):
+    """ElementNamespaceClassLookup(self, fallback=None)
+
+    Element class lookup scheme that searches the Element class in the
+    Namespace registry.
+
+    Usage:
+
+    >>> lookup = ElementNamespaceClassLookup()
+    >>> ns_elements = lookup.get_namespace("http://schema.org/Movie")
+
+    >>> @ns_elements
+    ... class movie(ElementBase):
+    ...     "Element implementation for 'movie' tag (using class name) in schema namespace."
+
+    >>> @ns_elements("movie")
+    ... class MovieElement(ElementBase):
+    ...     "Element implementation for 'movie' tag (explicit tag name) in schema namespace."
+    """
+    cdef dict _namespace_registries
+    def __cinit__(self):
+        self._namespace_registries = {}
+
+    def __init__(self, ElementClassLookup fallback=None):
+        FallbackElementClassLookup.__init__(self, fallback)
+        self._lookup_function = _find_nselement_class
+
+    def get_namespace(self, ns_uri):
+        """get_namespace(self, ns_uri)
+
+        Retrieve the namespace object associated with the given URI.
+        Pass None for the empty namespace.
+
+        Creates a new namespace object if it does not yet exist."""
+        if ns_uri:
+            ns_utf = _utf8(ns_uri)
+        else:
+            ns_utf = None
+        try:
+            return self._namespace_registries[ns_utf]
+        except KeyError:
+            registry = self._namespace_registries[ns_utf] = \
+                       _ClassNamespaceRegistry(ns_uri)
+            return registry
+
+cdef object _find_nselement_class(state, _Document doc, xmlNode* c_node):
+    cdef python.PyObject* dict_result
+    cdef ElementNamespaceClassLookup lookup
+    cdef _NamespaceRegistry registry
+    if state is None:
+        return _lookupDefaultElementClass(None, doc, c_node)
+
+    lookup = <ElementNamespaceClassLookup>state
+    if c_node.type != tree.XML_ELEMENT_NODE:
+        return _callLookupFallback(lookup, doc, c_node)
+
+    c_namespace_utf = _getNs(c_node)
+    if c_namespace_utf is not NULL:
+        dict_result = python.PyDict_GetItem(
+            lookup._namespace_registries, <unsigned char*>c_namespace_utf)
+    else:
+        dict_result = python.PyDict_GetItem(
+            lookup._namespace_registries, None)
+    if dict_result is not NULL:
+        registry = <_NamespaceRegistry>dict_result
+        classes = registry._entries
+
+        if c_node.name is not NULL:
+            dict_result = python.PyDict_GetItem(
+                classes, <unsigned char*>c_node.name)
+        else:
+            dict_result = NULL
+
+        if dict_result is NULL:
+            dict_result = python.PyDict_GetItem(classes, None)
+
+        if dict_result is not NULL:
+            return <object>dict_result
+    return _callLookupFallback(lookup, doc, c_node)
+
+
+################################################################################
+# XPath extension functions
+
+cdef dict __FUNCTION_NAMESPACE_REGISTRIES
+__FUNCTION_NAMESPACE_REGISTRIES = {}
+
+def FunctionNamespace(ns_uri):
+    """FunctionNamespace(ns_uri)
+
+    Retrieve the function namespace object associated with the given
+    URI.
+
+    Creates a new one if it does not yet exist. A function namespace
+    can only be used to register extension functions.
+
+    Usage:
+
+    >>> ns_functions = FunctionNamespace("http://schema.org/Movie")
+
+    >>> @ns_functions  # uses function name
+    ... def add2(x):
+    ...     return x + 2
+
+    >>> @ns_functions("add3")  # uses explicit name
+    ... def add_three(x):
+    ...     return x + 3
+    """
+    ns_utf = _utf8(ns_uri) if ns_uri else None
+    try:
+        return __FUNCTION_NAMESPACE_REGISTRIES[ns_utf]
+    except KeyError:
+        registry = __FUNCTION_NAMESPACE_REGISTRIES[ns_utf] = \
+                   _XPathFunctionNamespaceRegistry(ns_uri)
+        return registry
+
+@cython.internal
+cdef class _FunctionNamespaceRegistry(_NamespaceRegistry):
+    def __setitem__(self, name, item):
+        if not callable(item):
+            raise NamespaceRegistryError, \
+                "Registered functions must be callable."
+        if not name:
+            raise ValueError, \
+                "extensions must have non empty names"
+        self._entries[_utf8(name)] = item
+
+    def __repr__(self):
+        return "FunctionNamespace(%r)" % self._ns_uri
+
+@cython.final
+@cython.internal
+cdef class _XPathFunctionNamespaceRegistry(_FunctionNamespaceRegistry):
+    cdef object _prefix
+    cdef bytes _prefix_utf
+
+    property prefix:
+        "Namespace prefix for extension functions."
+        def __del__(self):
+            self._prefix = None # no prefix configured
+            self._prefix_utf = None
+        def __get__(self):
+            if self._prefix is None:
+                return ''
+            else:
+                return self._prefix
+        def __set__(self, prefix):
+            if prefix == '':
+                prefix = None # empty prefix
+            self._prefix_utf = _utf8(prefix) if prefix is not None else None
+            self._prefix = prefix
+
+cdef list _find_all_extension_prefixes():
+    "Internal lookup function to find all function prefixes for XSLT/XPath."
+    cdef _XPathFunctionNamespaceRegistry registry
+    cdef list ns_prefixes = []
+    for registry in __FUNCTION_NAMESPACE_REGISTRIES.itervalues():
+        if registry._prefix_utf is not None:
+            if registry._ns_uri_utf is not None:
+                ns_prefixes.append(
+                    (registry._prefix_utf, registry._ns_uri_utf))
+    return ns_prefixes
diff --git a/.venv/lib/python3.12/site-packages/lxml/objectify.cpython-312-x86_64-linux-gnu.so b/.venv/lib/python3.12/site-packages/lxml/objectify.cpython-312-x86_64-linux-gnu.so
new file mode 100755
index 00000000..885189c6
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/objectify.cpython-312-x86_64-linux-gnu.so
Binary files differdiff --git a/.venv/lib/python3.12/site-packages/lxml/objectify.pyx b/.venv/lib/python3.12/site-packages/lxml/objectify.pyx
new file mode 100644
index 00000000..0ff92226
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/objectify.pyx
@@ -0,0 +1,2145 @@
+# cython: binding=True
+# cython: auto_pickle=False
+# cython: language_level=3
+
+"""
+The ``lxml.objectify`` module implements a Python object API for XML.
+It is based on `lxml.etree`.
+"""
+
+cimport cython
+
+from lxml.includes.etreepublic cimport _Document, _Element, ElementBase, ElementClassLookup
+from lxml.includes.etreepublic cimport elementFactory, import_lxml__etree, textOf, pyunicode
+from lxml.includes.tree cimport const_xmlChar, _xcstr
+from lxml cimport python
+from lxml.includes cimport tree
+
+cimport lxml.includes.etreepublic as cetree
+cimport libc.string as cstring_h   # not to be confused with stdlib 'string'
+from libc.string cimport const_char
+
+__all__ = ['BoolElement', 'DataElement', 'E', 'Element', 'ElementMaker',
+           'FloatElement', 'IntElement', 'NoneElement',
+           'NumberElement', 'ObjectPath', 'ObjectifiedDataElement',
+           'ObjectifiedElement', 'ObjectifyElementClassLookup',
+           'PYTYPE_ATTRIBUTE', 'PyType', 'StringElement', 'SubElement',
+           'XML', 'annotate', 'deannotate', 'dump', 'enable_recursive_str',
+           'fromstring', 'getRegisteredTypes', 'makeparser', 'parse',
+           'pyannotate', 'pytypename', 'set_default_parser',
+           'set_pytype_attribute_tag', 'xsiannotate']
+
+cdef object etree
+from lxml import etree
+# initialize C-API of lxml.etree
+import_lxml__etree()
+
+__version__ = etree.__version__
+
+cdef object _float_is_inf, _float_is_nan
+from math import isinf as _float_is_inf, isnan as _float_is_nan
+
+cdef object re
+import re
+
+cdef tuple IGNORABLE_ERRORS = (ValueError, TypeError)
+cdef object is_special_method = re.compile('__.*__$').match
+
+
+cdef object _typename(object t):
+    cdef const_char* c_name
+    c_name = python._fqtypename(t)
+    s = cstring_h.strrchr(c_name, c'.')
+    if s is not NULL:
+        c_name = s + 1
+    return pyunicode(<const_xmlChar*>c_name)
+
+
+# namespace/name for "pytype" hint attribute
+cdef object PYTYPE_NAMESPACE
+cdef bytes PYTYPE_NAMESPACE_UTF8
+cdef const_xmlChar* _PYTYPE_NAMESPACE
+
+cdef object PYTYPE_ATTRIBUTE_NAME
+cdef bytes PYTYPE_ATTRIBUTE_NAME_UTF8
+cdef const_xmlChar* _PYTYPE_ATTRIBUTE_NAME
+
+PYTYPE_ATTRIBUTE = None
+
+cdef unicode TREE_PYTYPE_NAME = "TREE"
+
+cdef tuple _unicodeAndUtf8(s):
+    return s, python.PyUnicode_AsUTF8String(s)
+
+def set_pytype_attribute_tag(attribute_tag=None):
+    """set_pytype_attribute_tag(attribute_tag=None)
+    Change name and namespace of the XML attribute that holds Python type
+    information.
+
+    Do not use this unless you know what you are doing.
+
+    Reset by calling without argument.
+
+    Default: "{http://codespeak.net/lxml/objectify/pytype}pytype"
+    """
+    global PYTYPE_ATTRIBUTE, _PYTYPE_NAMESPACE, _PYTYPE_ATTRIBUTE_NAME
+    global PYTYPE_NAMESPACE, PYTYPE_NAMESPACE_UTF8
+    global PYTYPE_ATTRIBUTE_NAME, PYTYPE_ATTRIBUTE_NAME_UTF8
+    if attribute_tag is None:
+        PYTYPE_NAMESPACE, PYTYPE_NAMESPACE_UTF8 = \
+            _unicodeAndUtf8("http://codespeak.net/lxml/objectify/pytype")
+        PYTYPE_ATTRIBUTE_NAME, PYTYPE_ATTRIBUTE_NAME_UTF8 = \
+            _unicodeAndUtf8("pytype")
+    else:
+        PYTYPE_NAMESPACE_UTF8, PYTYPE_ATTRIBUTE_NAME_UTF8 = \
+            cetree.getNsTag(attribute_tag)
+        PYTYPE_NAMESPACE = PYTYPE_NAMESPACE_UTF8.decode('utf8')
+        PYTYPE_ATTRIBUTE_NAME = PYTYPE_ATTRIBUTE_NAME_UTF8.decode('utf8')
+
+    _PYTYPE_NAMESPACE      = PYTYPE_NAMESPACE_UTF8
+    _PYTYPE_ATTRIBUTE_NAME = PYTYPE_ATTRIBUTE_NAME_UTF8
+    PYTYPE_ATTRIBUTE = cetree.namespacedNameFromNsName(
+        _PYTYPE_NAMESPACE, _PYTYPE_ATTRIBUTE_NAME)
+
+set_pytype_attribute_tag()
+
+
+# namespaces for XML Schema
+cdef object XML_SCHEMA_NS, XML_SCHEMA_NS_UTF8
+XML_SCHEMA_NS, XML_SCHEMA_NS_UTF8 = \
+    _unicodeAndUtf8("http://www.w3.org/2001/XMLSchema")
+cdef const_xmlChar* _XML_SCHEMA_NS = _xcstr(XML_SCHEMA_NS_UTF8)
+
+cdef object XML_SCHEMA_INSTANCE_NS, XML_SCHEMA_INSTANCE_NS_UTF8
+XML_SCHEMA_INSTANCE_NS, XML_SCHEMA_INSTANCE_NS_UTF8 = \
+    _unicodeAndUtf8("http://www.w3.org/2001/XMLSchema-instance")
+cdef const_xmlChar* _XML_SCHEMA_INSTANCE_NS = _xcstr(XML_SCHEMA_INSTANCE_NS_UTF8)
+
+cdef object XML_SCHEMA_INSTANCE_NIL_ATTR = "{%s}nil" % XML_SCHEMA_INSTANCE_NS
+cdef object XML_SCHEMA_INSTANCE_TYPE_ATTR = "{%s}type" % XML_SCHEMA_INSTANCE_NS
+
+
+################################################################################
+# Element class for the main API
+
+cdef class ObjectifiedElement(ElementBase):
+    """Main XML Element class.
+
+    Element children are accessed as object attributes.  Multiple children
+    with the same name are available through a list index.  Example::
+
+       >>> root = XML("<root><c1><c2>0</c2><c2>1</c2></c1></root>")
+       >>> second_c2 = root.c1.c2[1]
+       >>> print(second_c2.text)
+       1
+
+    Note that you cannot (and must not) instantiate this class or its
+    subclasses.
+    """
+    def __iter__(self):
+        """Iterate over self and all siblings with the same tag.
+        """
+        parent = self.getparent()
+        if parent is None:
+            return iter([self])
+        return etree.ElementChildIterator(parent, tag=self.tag)
+
+    def __str__(self):
+        if __RECURSIVE_STR:
+            return _dump(self, 0)
+        else:
+            return textOf(self._c_node) or ''
+
+    # pickle support for objectified Element
+    def __reduce__(self):
+        return fromstring, (etree.tostring(self),)
+
+    @property
+    def text(self):
+        return textOf(self._c_node)
+
+    @property
+    def __dict__(self):
+        """A fake implementation for __dict__ to support dir() etc.
+
+        Note that this only considers the first child with a given name.
+        """
+        cdef _Element child
+        cdef dict children
+        c_ns = tree._getNs(self._c_node)
+        tag = "{%s}*" % pyunicode(c_ns) if c_ns is not NULL else None
+        children = {}
+        for child in etree.ElementChildIterator(self, tag=tag):
+            if c_ns is NULL and tree._getNs(child._c_node) is not NULL:
+                continue
+            name = pyunicode(child._c_node.name)
+            if name not in children:
+                children[name] = child
+        return children
+
+    def __len__(self):
+        """Count self and siblings with the same tag.
+        """
+        return _countSiblings(self._c_node)
+
+    def countchildren(self):
+        """countchildren(self)
+
+        Return the number of children of this element, regardless of their
+        name.
+        """
+        # copied from etree
+        cdef Py_ssize_t c
+        cdef tree.xmlNode* c_node
+        c = 0
+        c_node = self._c_node.children
+        while c_node is not NULL:
+            if tree._isElement(c_node):
+                c += 1
+            c_node = c_node.next
+        return c
+
+    def getchildren(self):
+        """getchildren(self)
+
+        Returns a sequence of all direct children.  The elements are
+        returned in document order.
+        """
+        cdef tree.xmlNode* c_node
+        result = []
+        c_node = self._c_node.children
+        while c_node is not NULL:
+            if tree._isElement(c_node):
+                result.append(cetree.elementFactory(self._doc, c_node))
+            c_node = c_node.next
+        return result
+
+    def __getattr__(self, tag):
+        """Return the (first) child with the given tag name.  If no namespace
+        is provided, the child will be looked up in the same one as self.
+        """
+        return _lookupChildOrRaise(self, tag)
+
+    def __setattr__(self, tag, value):
+        """Set the value of the (first) child with the given tag name.  If no
+        namespace is provided, the child will be looked up in the same one as
+        self.
+        """
+        cdef _Element element
+        # properties are looked up /after/ __setattr__, so we must emulate them
+        if tag == 'text' or tag == 'pyval':
+            # read-only !
+            raise TypeError, f"attribute '{tag}' of '{_typename(self)}' objects is not writable"
+        elif tag == 'tail':
+            cetree.setTailText(self._c_node, value)
+            return
+        elif tag == 'tag':
+            ElementBase.tag.__set__(self, value)
+            return
+        elif tag == 'base':
+            ElementBase.base.__set__(self, value)
+            return
+        tag = _buildChildTag(self, tag)
+        element = _lookupChild(self, tag)
+        if element is None:
+            _appendValue(self, tag, value)
+        else:
+            _replaceElement(element, value)
+
+    def __delattr__(self, tag):
+        child = _lookupChildOrRaise(self, tag)
+        self.remove(child)
+
+    def addattr(self, tag, value):
+        """addattr(self, tag, value)
+
+        Add a child value to the element.
+
+        As opposed to append(), it sets a data value, not an element.
+        """
+        _appendValue(self, _buildChildTag(self, tag), value)
+
+    def __getitem__(self, key):
+        """Return a sibling, counting from the first child of the parent.  The
+        method behaves like both a dict and a sequence.
+
+        * If argument is an integer, returns the sibling at that position.
+
+        * If argument is a string, does the same as getattr().  This can be
+          used to provide namespaces for element lookup, or to look up
+          children with special names (``text`` etc.).
+
+        * If argument is a slice object, returns the matching slice.
+        """
+        cdef tree.xmlNode* c_self_node
+        cdef tree.xmlNode* c_parent
+        cdef tree.xmlNode* c_node
+        cdef Py_ssize_t c_index
+        if python._isString(key):
+            return _lookupChildOrRaise(self, key)
+        elif isinstance(key, slice):
+            return list(self)[key]
+        # normal item access
+        c_index = key   # raises TypeError if necessary
+        c_self_node = self._c_node
+        c_parent = c_self_node.parent
+        if c_parent is NULL:
+            if c_index == 0 or c_index == -1:
+                return self
+            raise IndexError, unicode(key)
+        if c_index < 0:
+            c_node = c_parent.last
+        else:
+            c_node = c_parent.children
+        c_node = _findFollowingSibling(
+            c_node, tree._getNs(c_self_node), c_self_node.name, c_index)
+        if c_node is NULL:
+            raise IndexError, unicode(key)
+        return elementFactory(self._doc, c_node)
+
+    def __setitem__(self, key, value):
+        """Set the value of a sibling, counting from the first child of the
+        parent.  Implements key assignment, item assignment and slice
+        assignment.
+
+        * If argument is an integer, sets the sibling at that position.
+
+        * If argument is a string, does the same as setattr().  This is used
+          to provide namespaces for element lookup.
+
+        * If argument is a sequence (list, tuple, etc.), assign the contained
+          items to the siblings.
+        """
+        cdef _Element element
+        cdef tree.xmlNode* c_node
+        if python._isString(key):
+            key = _buildChildTag(self, key)
+            element = _lookupChild(self, key)
+            if element is None:
+                _appendValue(self, key, value)
+            else:
+                _replaceElement(element, value)
+            return
+
+        if self._c_node.parent is NULL:
+            # the 'root[i] = ...' case
+            raise TypeError, "assignment to root element is invalid"
+
+        if isinstance(key, slice):
+            # slice assignment
+            _setSlice(key, self, value)
+        else:
+            # normal index assignment
+            if key < 0:
+                c_node = self._c_node.parent.last
+            else:
+                c_node = self._c_node.parent.children
+            c_node = _findFollowingSibling(
+                c_node, tree._getNs(self._c_node), self._c_node.name, key)
+            if c_node is NULL:
+                raise IndexError, unicode(key)
+            element = elementFactory(self._doc, c_node)
+            _replaceElement(element, value)
+
+    def __delitem__(self, key):
+        parent = self.getparent()
+        if parent is None:
+            raise TypeError, "deleting items not supported by root element"
+        if isinstance(key, slice):
+            # slice deletion
+            del_items = list(self)[key]
+            remove = parent.remove
+            for el in del_items:
+                remove(el)
+        else:
+            # normal index deletion
+            sibling = self.__getitem__(key)
+            parent.remove(sibling)
+
+    def descendantpaths(self, prefix=None):
+        """descendantpaths(self, prefix=None)
+
+        Returns a list of object path expressions for all descendants.
+        """
+        if prefix is not None and not python._isString(prefix):
+            prefix = '.'.join(prefix)
+        return _build_descendant_paths(self._c_node, prefix)
+
+
+cdef inline bint _tagMatches(tree.xmlNode* c_node, const_xmlChar* c_href, const_xmlChar* c_name):
+    if c_node.name != c_name:
+        return 0
+    if c_href == NULL:
+        return 1
+    c_node_href = tree._getNs(c_node)
+    if c_node_href == NULL:
+        return c_href[0] == c'\0'
+    return tree.xmlStrcmp(c_node_href, c_href) == 0
+
+
+cdef Py_ssize_t _countSiblings(tree.xmlNode* c_start_node):
+    cdef tree.xmlNode* c_node
+    cdef Py_ssize_t count
+    c_tag  = c_start_node.name
+    c_href = tree._getNs(c_start_node)
+    count = 1
+    c_node = c_start_node.next
+    while c_node is not NULL:
+        if c_node.type == tree.XML_ELEMENT_NODE and \
+               _tagMatches(c_node, c_href, c_tag):
+            count += 1
+        c_node = c_node.next
+    c_node = c_start_node.prev
+    while c_node is not NULL:
+        if c_node.type == tree.XML_ELEMENT_NODE and \
+               _tagMatches(c_node, c_href, c_tag):
+            count += 1
+        c_node = c_node.prev
+    return count
+
+cdef tree.xmlNode* _findFollowingSibling(tree.xmlNode* c_node,
+                                         const_xmlChar* href, const_xmlChar* name,
+                                         Py_ssize_t index):
+    cdef tree.xmlNode* (*next)(tree.xmlNode*)
+    if index >= 0:
+        next = cetree.nextElement
+    else:
+        index = -1 - index
+        next = cetree.previousElement
+    while c_node is not NULL:
+        if c_node.type == tree.XML_ELEMENT_NODE and \
+               _tagMatches(c_node, href, name):
+            index = index - 1
+            if index < 0:
+                return c_node
+        c_node = next(c_node)
+    return NULL
+
+cdef object _lookupChild(_Element parent, tag):
+    cdef tree.xmlNode* c_result
+    cdef tree.xmlNode* c_node
+    c_node = parent._c_node
+    ns, tag = cetree.getNsTagWithEmptyNs(tag)
+    c_tag = tree.xmlDictExists(
+        c_node.doc.dict, _xcstr(tag), python.PyBytes_GET_SIZE(tag))
+    if c_tag is NULL:
+        return None # not in the hash map => not in the tree
+    if ns is None:
+        # either inherit ns from parent or use empty (i.e. no) namespace
+        c_href = tree._getNs(c_node) or <const_xmlChar*>''
+    else:
+        c_href = _xcstr(ns)
+    c_result = _findFollowingSibling(c_node.children, c_href, c_tag, 0)
+    if c_result is NULL:
+        return None
+    return elementFactory(parent._doc, c_result)
+
+cdef object _lookupChildOrRaise(_Element parent, tag):
+    element = _lookupChild(parent, tag)
+    if element is None:
+        raise AttributeError, "no such child: " + _buildChildTag(parent, tag)
+    return element
+
+cdef object _buildChildTag(_Element parent, tag):
+    ns, tag = cetree.getNsTag(tag)
+    c_tag = _xcstr(tag)
+    c_href = tree._getNs(parent._c_node) if ns is None else _xcstr(ns)
+    return cetree.namespacedNameFromNsName(c_href, c_tag)
+
+cdef _replaceElement(_Element element, value):
+    cdef _Element new_element
+    if isinstance(value, _Element):
+        # deep copy the new element
+        new_element = cetree.deepcopyNodeToDocument(
+            element._doc, (<_Element>value)._c_node)
+        new_element.tag = element.tag
+    elif isinstance(value, (list, tuple)):
+        element[:] = value
+        return
+    else:
+        new_element = element.makeelement(element.tag)
+        _setElementValue(new_element, value)
+    element.getparent().replace(element, new_element)
+
+cdef _appendValue(_Element parent, tag, value):
+    cdef _Element new_element
+    if isinstance(value, _Element):
+        # deep copy the new element
+        new_element = cetree.deepcopyNodeToDocument(
+            parent._doc, (<_Element>value)._c_node)
+        new_element.tag = tag
+        cetree.appendChildToElement(parent, new_element)
+    elif isinstance(value, (list, tuple)):
+        for item in value:
+            _appendValue(parent, tag, item)
+    else:
+        new_element = cetree.makeElement(
+            tag, parent._doc, None, None, None, None, None)
+        _setElementValue(new_element, value)
+        cetree.appendChildToElement(parent, new_element)
+
+cdef _setElementValue(_Element element, value):
+    if value is None:
+        cetree.setAttributeValue(
+            element, XML_SCHEMA_INSTANCE_NIL_ATTR, "true")
+    elif isinstance(value, _Element):
+        _replaceElement(element, value)
+        return
+    else:
+        cetree.delAttributeFromNsName(
+            element._c_node, _XML_SCHEMA_INSTANCE_NS, <unsigned char*>"nil")
+        if python._isString(value):
+            pytype_name = "str"
+            py_type = <PyType>_PYTYPE_DICT.get(pytype_name)
+        else:
+            pytype_name = _typename(value)
+            py_type = <PyType>_PYTYPE_DICT.get(pytype_name)
+            if py_type is not None:
+                value = py_type.stringify(value)
+            else:
+                value = unicode(value)
+        if py_type is not None:
+            cetree.setAttributeValue(element, PYTYPE_ATTRIBUTE, pytype_name)
+        else:
+            cetree.delAttributeFromNsName(
+                element._c_node, _PYTYPE_NAMESPACE, _PYTYPE_ATTRIBUTE_NAME)
+    cetree.setNodeText(element._c_node, value)
+
+cdef _setSlice(sliceobject, _Element target, items):
+    cdef _Element parent
+    cdef tree.xmlNode* c_node
+    cdef Py_ssize_t c_step, c_start, pos
+    # collect existing slice
+    if (<slice>sliceobject).step is None:
+        c_step = 1
+    else:
+        c_step = (<slice>sliceobject).step
+    if c_step == 0:
+        raise ValueError, "Invalid slice"
+    cdef list del_items = target[sliceobject]
+
+    # collect new values
+    new_items = []
+    tag = target.tag
+    for item in items:
+        if isinstance(item, _Element):
+            # deep copy the new element
+            new_element = cetree.deepcopyNodeToDocument(
+                target._doc, (<_Element>item)._c_node)
+            new_element.tag = tag
+        else:
+            new_element = cetree.makeElement(
+                tag, target._doc, None, None, None, None, None)
+            _setElementValue(new_element, item)
+        new_items.append(new_element)
+
+    # sanity check - raise what a list would raise
+    if c_step != 1 and len(del_items) != len(new_items):
+        raise ValueError, \
+            f"attempt to assign sequence of size {len(new_items)} to extended slice of size {len(del_items)}"
+
+    # replace existing items
+    pos = 0
+    parent = target.getparent()
+    replace = parent.replace
+    while pos < len(new_items) and pos < len(del_items):
+        replace(del_items[pos], new_items[pos])
+        pos += 1
+    # remove leftover items
+    if pos < len(del_items):
+        remove = parent.remove
+        while pos < len(del_items):
+            remove(del_items[pos])
+            pos += 1
+    # append remaining new items
+    if pos < len(new_items):
+        # the sanity check above guarantees (step == 1)
+        if pos > 0:
+            item = new_items[pos-1]
+        else:
+            if (<slice>sliceobject).start > 0:
+                c_node = parent._c_node.children
+            else:
+                c_node = parent._c_node.last
+            c_node = _findFollowingSibling(
+                c_node, tree._getNs(target._c_node), target._c_node.name,
+                (<slice>sliceobject).start - 1)
+            if c_node is NULL:
+                while pos < len(new_items):
+                    cetree.appendChildToElement(parent, new_items[pos])
+                    pos += 1
+                return
+            item = cetree.elementFactory(parent._doc, c_node)
+        while pos < len(new_items):
+            add = item.addnext
+            item = new_items[pos]
+            add(item)
+            pos += 1
+
+################################################################################
+# Data type support in subclasses
+
+cdef class ObjectifiedDataElement(ObjectifiedElement):
+    """This is the base class for all data type Elements.  Subclasses should
+    override the 'pyval' property and possibly the __str__ method.
+    """
+    @property
+    def pyval(self):
+        return textOf(self._c_node)
+
+    def __str__(self):
+        return textOf(self._c_node) or ''
+
+    def __repr__(self):
+        return textOf(self._c_node) or ''
+
+    def _setText(self, s):
+        """For use in subclasses only. Don't use unless you know what you are
+        doing.
+        """
+        cetree.setNodeText(self._c_node, s)
+
+
+cdef class NumberElement(ObjectifiedDataElement):
+    cdef object _parse_value
+
+    def _setValueParser(self, function):
+        """Set the function that parses the Python value from a string.
+
+        Do not use this unless you know what you are doing.
+        """
+        self._parse_value = function
+
+    @property
+    def pyval(self):
+        return _parseNumber(self)
+
+    def __int__(self):
+        return int(_parseNumber(self))
+
+    def __float__(self):
+        return float(_parseNumber(self))
+
+    def __complex__(self):
+        return complex(_parseNumber(self))
+
+    def __str__(self):
+        return unicode(_parseNumber(self))
+
+    def __repr__(self):
+        return repr(_parseNumber(self))
+
+    def __oct__(self):
+        return oct(_parseNumber(self))
+
+    def __hex__(self):
+        return hex(_parseNumber(self))
+
+    def __richcmp__(self, other, int op):
+        return _richcmpPyvals(self, other, op)
+
+    def __hash__(self):
+        return hash(_parseNumber(self))
+
+    def __add__(self, other):
+        return _numericValueOf(self) + _numericValueOf(other)
+
+    def __radd__(self, other):
+        return _numericValueOf(other) + _numericValueOf(self)
+
+    def __sub__(self, other):
+        return _numericValueOf(self) - _numericValueOf(other)
+
+    def __rsub__(self, other):
+        return _numericValueOf(other) - _numericValueOf(self)
+
+    def __mul__(self, other):
+        return _numericValueOf(self) * _numericValueOf(other)
+
+    def __rmul__(self, other):
+        return _numericValueOf(other) * _numericValueOf(self)
+
+    def __div__(self, other):
+        return _numericValueOf(self) / _numericValueOf(other)
+
+    def __rdiv__(self, other):
+        return _numericValueOf(other) / _numericValueOf(self)
+
+    def __truediv__(self, other):
+        return _numericValueOf(self) / _numericValueOf(other)
+
+    def __rtruediv__(self, other):
+        return _numericValueOf(other) / _numericValueOf(self)
+
+    def __floordiv__(self, other):
+        return _numericValueOf(self) // _numericValueOf(other)
+
+    def __rfloordiv__(self, other):
+        return _numericValueOf(other) // _numericValueOf(self)
+
+    def __mod__(self, other):
+        return _numericValueOf(self) % _numericValueOf(other)
+
+    def __rmod__(self, other):
+        return _numericValueOf(other) % _numericValueOf(self)
+
+    def __divmod__(self, other):
+        return divmod(_numericValueOf(self), _numericValueOf(other))
+
+    def __rdivmod__(self, other):
+        return divmod(_numericValueOf(other), _numericValueOf(self))
+
+    def __pow__(self, other, modulo):
+        if modulo is None:
+            return _numericValueOf(self) ** _numericValueOf(other)
+        else:
+            return pow(_numericValueOf(self), _numericValueOf(other), modulo)
+
+    def __rpow__(self, other, modulo):
+        if modulo is None:
+            return _numericValueOf(other) ** _numericValueOf(self)
+        else:
+            return pow(_numericValueOf(other), _numericValueOf(self), modulo)
+
+    def __neg__(self):
+        return - _numericValueOf(self)
+
+    def __pos__(self):
+        return + _numericValueOf(self)
+
+    def __abs__(self):
+        return abs( _numericValueOf(self) )
+
+    def __bool__(self):
+        return bool(_numericValueOf(self))
+
+    def __invert__(self):
+        return ~ _numericValueOf(self)
+
+    def __lshift__(self, other):
+        return _numericValueOf(self) << _numericValueOf(other)
+
+    def __rlshift__(self, other):
+        return _numericValueOf(other) << _numericValueOf(self)
+
+    def __rshift__(self, other):
+        return _numericValueOf(self) >> _numericValueOf(other)
+
+    def __rrshift__(self, other):
+        return _numericValueOf(other) >> _numericValueOf(self)
+
+    def __and__(self, other):
+        return _numericValueOf(self) & _numericValueOf(other)
+
+    def __rand__(self, other):
+        return _numericValueOf(other) & _numericValueOf(self)
+
+    def __or__(self, other):
+        return _numericValueOf(self) | _numericValueOf(other)
+
+    def __ror__(self, other):
+        return _numericValueOf(other) | _numericValueOf(self)
+
+    def __xor__(self, other):
+        return _numericValueOf(self) ^ _numericValueOf(other)
+
+    def __rxor__(self, other):
+        return _numericValueOf(other) ^ _numericValueOf(self)
+
+
+cdef class IntElement(NumberElement):
+    def _init(self):
+        self._parse_value = int
+
+    def __index__(self):
+        return int(_parseNumber(self))
+
+
+cdef class FloatElement(NumberElement):
+    def _init(self):
+        self._parse_value = float
+
+
+cdef class StringElement(ObjectifiedDataElement):
+    """String data class.
+
+    Note that this class does *not* support the sequence protocol of strings:
+    len(), iter(), str_attr[0], str_attr[0:1], etc. are *not* supported.
+    Instead, use the .text attribute to get a 'real' string.
+    """
+    @property
+    def pyval(self):
+        return textOf(self._c_node) or ''
+
+    def __repr__(self):
+        return repr(textOf(self._c_node) or '')
+
+    def strlen(self):
+        text = textOf(self._c_node)
+        if text is None:
+            return 0
+        else:
+            return len(text)
+
+    def __bool__(self):
+        return bool(textOf(self._c_node))
+
+    def __richcmp__(self, other, int op):
+        return _richcmpPyvals(self, other, op)
+
+    def __hash__(self):
+        return hash(textOf(self._c_node) or '')
+
+    def __add__(self, other):
+        text  = _strValueOf(self)
+        other = _strValueOf(other)
+        return text + other
+
+    def __radd__(self, other):
+        text  = _strValueOf(self)
+        other = _strValueOf(other)
+        return other + text
+
+    def __mul__(self, other):
+        if isinstance(self, StringElement):
+            return (textOf((<StringElement>self)._c_node) or '') * _numericValueOf(other)
+        elif isinstance(other, StringElement):
+            return _numericValueOf(self) * (textOf((<StringElement>other)._c_node) or '')
+        else:
+            return NotImplemented
+
+    def __rmul__(self, other):
+        return _numericValueOf(other) * (textOf((<StringElement>self)._c_node) or '')
+
+    def __mod__(self, other):
+        return (_strValueOf(self) or '') % other
+
+    def __int__(self):
+        return int(textOf(self._c_node))
+
+    def __float__(self):
+        return float(textOf(self._c_node))
+
+    def __complex__(self):
+        return complex(textOf(self._c_node))
+
+
+cdef class NoneElement(ObjectifiedDataElement):
+    def __str__(self):
+        return "None"
+
+    def __repr__(self):
+        return "None"
+
+    def __bool__(self):
+        return False
+
+    def __richcmp__(self, other, int op):
+        if other is None or self is None:
+            return python.PyObject_RichCompare(None, None, op)
+        if isinstance(self, NoneElement):
+            return python.PyObject_RichCompare(None, other, op)
+        else:
+            return python.PyObject_RichCompare(self, None, op)
+
+    def __hash__(self):
+        return hash(None)
+
+    @property
+    def pyval(self):
+        return None
+
+
+cdef class BoolElement(IntElement):
+    """Boolean type base on string values: 'true' or 'false'.
+
+    Note that this inherits from IntElement to mimic the behaviour of
+    Python's bool type.
+    """
+    def _init(self):
+        self._parse_value = _parseBool  # wraps as Python callable
+
+    def __bool__(self):
+        return _parseBool(textOf(self._c_node))
+
+    def __int__(self):
+        return 0 + _parseBool(textOf(self._c_node))
+
+    def __float__(self):
+        return 0.0 + _parseBool(textOf(self._c_node))
+
+    def __richcmp__(self, other, int op):
+        return _richcmpPyvals(self, other, op)
+
+    def __hash__(self):
+        return hash(_parseBool(textOf(self._c_node)))
+
+    def __str__(self):
+        return unicode(_parseBool(textOf(self._c_node)))
+
+    def __repr__(self):
+        return repr(_parseBool(textOf(self._c_node)))
+
+    @property
+    def pyval(self):
+        return _parseBool(textOf(self._c_node))
+
+
+cdef _checkBool(s):
+    cdef int value = -1
+    if s is not None:
+        value = __parseBoolAsInt(s)
+    if value == -1:
+        raise ValueError
+
+
+cdef bint _parseBool(s) except -1:
+    cdef int value
+    if s is None:
+        return False
+    value = __parseBoolAsInt(s)
+    if value == -1:
+        raise ValueError, f"Invalid boolean value: '{s}'"
+    return value
+
+
+cdef inline int __parseBoolAsInt(text) except -2:
+    if text == 'false':
+        return 0
+    elif text == 'true':
+        return 1
+    elif text == '0':
+        return 0
+    elif text == '1':
+        return 1
+    return -1
+
+
+cdef object _parseNumber(NumberElement element):
+    return element._parse_value(textOf(element._c_node))
+
+
+cdef enum NumberParserState:
+    NPS_SPACE_PRE = 0
+    NPS_SIGN = 1
+    NPS_DIGITS = 2
+    NPS_POINT_LEAD = 3
+    NPS_POINT = 4
+    NPS_FRACTION = 5
+    NPS_EXP = 6
+    NPS_EXP_SIGN = 7
+    NPS_DIGITS_EXP = 8
+    NPS_SPACE_TAIL = 9
+    NPS_INF1 = 20
+    NPS_INF2 = 21
+    NPS_INF3 = 22
+    NPS_NAN1 = 23
+    NPS_NAN2 = 24
+    NPS_NAN3 = 25
+    NPS_ERROR = 99
+
+
+ctypedef fused bytes_unicode:
+    bytes
+    unicode
+
+
+cdef _checkNumber(bytes_unicode s, bint allow_float):
+    cdef Py_UCS4 c
+    cdef NumberParserState state = NPS_SPACE_PRE
+
+    for c in s:
+        if c in '0123456789':
+            if state in (NPS_DIGITS, NPS_FRACTION, NPS_DIGITS_EXP):
+                pass
+            elif state in (NPS_SPACE_PRE, NPS_SIGN):
+                state = NPS_DIGITS
+            elif state in (NPS_POINT_LEAD, NPS_POINT):
+                state = NPS_FRACTION
+            elif state in (NPS_EXP, NPS_EXP_SIGN):
+                state = NPS_DIGITS_EXP
+            else:
+                state = NPS_ERROR
+        else:
+            if c == '.':
+                if state in (NPS_SPACE_PRE, NPS_SIGN):
+                    state = NPS_POINT_LEAD
+                elif state == NPS_DIGITS:
+                    state = NPS_POINT
+                else:
+                    state = NPS_ERROR
+                if not allow_float:
+                    state = NPS_ERROR
+            elif c in '-+':
+                if state == NPS_SPACE_PRE:
+                    state = NPS_SIGN
+                elif state == NPS_EXP:
+                    state = NPS_EXP_SIGN
+                else:
+                    state = NPS_ERROR
+            elif c == 'E':
+                if state in (NPS_DIGITS, NPS_POINT, NPS_FRACTION):
+                    state = NPS_EXP
+                else:
+                    state = NPS_ERROR
+                if not allow_float:
+                    state = NPS_ERROR
+            # Allow INF and NaN. XMLSchema requires case, we don't, like Python.
+            elif c in 'iI':
+                state = NPS_INF1 if allow_float and state in (NPS_SPACE_PRE, NPS_SIGN) else NPS_ERROR
+            elif c in 'fF':
+                state = NPS_INF3 if state == NPS_INF2 else NPS_ERROR
+            elif c in 'aA':
+                state = NPS_NAN2 if state == NPS_NAN1 else NPS_ERROR
+            elif c in 'nN':
+                # Python also allows [+-]NaN, so let's accept that.
+                if state in (NPS_SPACE_PRE, NPS_SIGN):
+                    state = NPS_NAN1 if allow_float else NPS_ERROR
+                elif state == NPS_NAN2:
+                    state = NPS_NAN3
+                elif state == NPS_INF1:
+                    state = NPS_INF2
+                else:
+                    state = NPS_ERROR
+            # Allow spaces around text values.
+            else:
+                if c.isspace() if (bytes_unicode is unicode) else c in b'\x09\x0a\x0b\x0c\x0d\x20':
+                    if state in (NPS_SPACE_PRE, NPS_SPACE_TAIL):
+                        pass
+                    elif state in (NPS_DIGITS, NPS_POINT, NPS_FRACTION, NPS_DIGITS_EXP, NPS_INF3, NPS_NAN3):
+                        state = NPS_SPACE_TAIL
+                    else:
+                        state = NPS_ERROR
+                else:
+                    state = NPS_ERROR
+
+            if state == NPS_ERROR:
+                break
+
+    if state not in (NPS_DIGITS, NPS_FRACTION, NPS_POINT, NPS_DIGITS_EXP, NPS_INF3, NPS_NAN3, NPS_SPACE_TAIL):
+        raise ValueError
+
+
+cdef _checkInt(s):
+    return _checkNumber(<unicode>s, allow_float=False)
+
+
+cdef _checkFloat(s):
+    return _checkNumber(<unicode>s, allow_float=True)
+
+
+cdef object _strValueOf(obj):
+    if python._isString(obj):
+        return obj
+    if isinstance(obj, _Element):
+        return textOf((<_Element>obj)._c_node) or ''
+    if obj is None:
+        return ''
+    return unicode(obj)
+
+
+cdef object _numericValueOf(obj):
+    if isinstance(obj, NumberElement):
+        return _parseNumber(<NumberElement>obj)
+    try:
+        # not always numeric, but Python will raise the right exception
+        return obj.pyval
+    except AttributeError:
+        pass
+    return obj
+
+
+cdef _richcmpPyvals(left, right, int op):
+    left  = getattr(left,  'pyval', left)
+    right = getattr(right, 'pyval', right)
+    return python.PyObject_RichCompare(left, right, op)
+
+
+################################################################################
+# Python type registry
+
+cdef class PyType:
+    """PyType(self, name, type_check, type_class, stringify=None)
+    User defined type.
+
+    Named type that contains a type check function, a type class that
+    inherits from ObjectifiedDataElement and an optional "stringification"
+    function.  The type check must take a string as argument and raise
+    ValueError or TypeError if it cannot handle the string value.  It may be
+    None in which case it is not considered for type guessing.  For registered
+    named types, the 'stringify' function (or unicode() if None) is used to
+    convert a Python object with type name 'name' to the string representation
+    stored in the XML tree.
+
+    Example::
+
+        PyType('int', int, MyIntClass).register()
+
+    Note that the order in which types are registered matters.  The first
+    matching type will be used.
+    """
+    cdef readonly object name
+    cdef readonly object type_check
+    cdef readonly object stringify
+    cdef object _type
+    cdef list _schema_types
+    def __init__(self, name, type_check, type_class, stringify=None):
+        if isinstance(name, bytes):
+            name = (<bytes>name).decode('ascii')
+        elif not isinstance(name, unicode):
+            raise TypeError, "Type name must be a string"
+        if type_check is not None and not callable(type_check):
+            raise TypeError, "Type check function must be callable (or None)"
+        if name != TREE_PYTYPE_NAME and \
+               not issubclass(type_class, ObjectifiedDataElement):
+            raise TypeError, \
+                "Data classes must inherit from ObjectifiedDataElement"
+        self.name  = name
+        self._type = type_class
+        self.type_check = type_check
+        if stringify is None:
+            stringify = unicode
+        self.stringify = stringify
+        self._schema_types = []
+
+    def __repr__(self):
+        return "PyType(%s, %s)" % (self.name, self._type.__name__)
+
+    def register(self, before=None, after=None):
+        """register(self, before=None, after=None)
+
+        Register the type.
+
+        The additional keyword arguments 'before' and 'after' accept a
+        sequence of type names that must appear before/after the new type in
+        the type list.  If any of them is not currently known, it is simply
+        ignored.  Raises ValueError if the dependencies cannot be fulfilled.
+        """
+        if self.name == TREE_PYTYPE_NAME:
+            raise ValueError, "Cannot register tree type"
+        if self.type_check is not None:
+            for item in _TYPE_CHECKS:
+                if item[0] is self.type_check:
+                    _TYPE_CHECKS.remove(item)
+                    break
+            entry = (self.type_check, self)
+            first_pos = 0
+            last_pos = -1
+            if before or after:
+                if before is None:
+                    before = ()
+                elif after is None:
+                    after = ()
+                for i, (check, pytype) in enumerate(_TYPE_CHECKS):
+                    if last_pos == -1 and pytype.name in before:
+                        last_pos = i
+                    if pytype.name in after:
+                        first_pos = i+1
+            if last_pos == -1:
+                _TYPE_CHECKS.append(entry)
+            elif first_pos > last_pos:
+                raise ValueError, "inconsistent before/after dependencies"
+            else:
+                _TYPE_CHECKS.insert(last_pos, entry)
+
+        _PYTYPE_DICT[self.name] = self
+        for xs_type in self._schema_types:
+            _SCHEMA_TYPE_DICT[xs_type] = self
+
+    def unregister(self):
+        "unregister(self)"
+        if _PYTYPE_DICT.get(self.name) is self:
+            del _PYTYPE_DICT[self.name]
+        for xs_type, pytype in list(_SCHEMA_TYPE_DICT.items()):
+            if pytype is self:
+                del _SCHEMA_TYPE_DICT[xs_type]
+        if self.type_check is None:
+            return
+        try:
+            _TYPE_CHECKS.remove( (self.type_check, self) )
+        except ValueError:
+            pass
+
+    property xmlSchemaTypes:
+        """The list of XML Schema datatypes this Python type maps to.
+
+        Note that this must be set before registering the type!
+        """
+        def __get__(self):
+            return self._schema_types
+        def __set__(self, types):
+            self._schema_types = list(map(unicode, types))
+
+
+cdef dict _PYTYPE_DICT = {}
+cdef dict _SCHEMA_TYPE_DICT = {}
+cdef list _TYPE_CHECKS = []
+
+cdef unicode _xml_bool(value):
+    return "true" if value else "false"
+
+cdef unicode _xml_float(value):
+    if _float_is_inf(value):
+        if value > 0:
+            return "INF"
+        return "-INF"
+    if _float_is_nan(value):
+        return "NaN"
+    return unicode(repr(value))
+
+cdef _pytypename(obj):
+    return "str" if python._isString(obj) else _typename(obj)
+
+def pytypename(obj):
+    """pytypename(obj)
+
+    Find the name of the corresponding PyType for a Python object.
+    """
+    return _pytypename(obj)
+
+cdef _registerPyTypes():
+    pytype = PyType('int', _checkInt, IntElement)  # wraps functions for Python
+    pytype.xmlSchemaTypes = ("integer", "int", "short", "byte", "unsignedShort",
+                             "unsignedByte", "nonPositiveInteger",
+                             "negativeInteger", "long", "nonNegativeInteger",
+                             "unsignedLong", "unsignedInt", "positiveInteger",)
+    pytype.register()
+
+    # 'long' type just for backwards compatibility
+    pytype = PyType('long', None, IntElement)
+    pytype.register()
+
+    pytype = PyType('float', _checkFloat, FloatElement, _xml_float)  # wraps functions for Python
+    pytype.xmlSchemaTypes = ("double", "float")
+    pytype.register()
+
+    pytype = PyType('bool', _checkBool, BoolElement, _xml_bool)  # wraps functions for Python
+    pytype.xmlSchemaTypes = ("boolean",)
+    pytype.register()
+
+    pytype = PyType('str', None, StringElement)
+    pytype.xmlSchemaTypes = ("string", "normalizedString", "token", "language",
+                             "Name", "NCName", "ID", "IDREF", "ENTITY",
+                             "NMTOKEN", )
+    pytype.register()
+
+    # since lxml 2.0
+    pytype = PyType('NoneType', None, NoneElement)
+    pytype.register()
+
+    # backwards compatibility
+    pytype = PyType('none', None, NoneElement)
+    pytype.register()
+
+# non-registered PyType for inner tree elements
+cdef PyType TREE_PYTYPE = PyType(TREE_PYTYPE_NAME, None, ObjectifiedElement)
+
+_registerPyTypes()
+
+def getRegisteredTypes():
+    """getRegisteredTypes()
+
+    Returns a list of the currently registered PyType objects.
+
+    To add a new type, retrieve this list and call unregister() for all
+    entries.  Then add the new type at a suitable position (possibly replacing
+    an existing one) and call register() for all entries.
+
+    This is necessary if the new type interferes with the type check functions
+    of existing ones (normally only int/float/bool) and must the tried before
+    other types.  To add a type that is not yet parsable by the current type
+    check functions, you can simply register() it, which will append it to the
+    end of the type list.
+    """
+    cdef list types = []
+    cdef set known = set()
+    for check, pytype in _TYPE_CHECKS:
+        name = pytype.name
+        if name not in known:
+            known.add(name)
+            types.append(pytype)
+    for pytype in _PYTYPE_DICT.values():
+        name = pytype.name
+        if name not in known:
+            known.add(name)
+            types.append(pytype)
+    return types
+
+cdef PyType _guessPyType(value, PyType defaulttype):
+    if value is None:
+        return None
+    for type_check, tested_pytype in _TYPE_CHECKS:
+        try:
+            type_check(value)
+            return <PyType>tested_pytype
+        except IGNORABLE_ERRORS:
+            # could not be parsed as the specified type => ignore
+            pass
+    return defaulttype
+
+cdef object _guessElementClass(tree.xmlNode* c_node):
+    value = textOf(c_node)
+    if value is None:
+        return None
+    if value == '':
+        return StringElement
+    
+    for type_check, pytype in _TYPE_CHECKS:
+        try:
+            type_check(value)
+            return (<PyType>pytype)._type
+        except IGNORABLE_ERRORS:
+            pass
+    return None
+
+################################################################################
+# adapted ElementMaker supports registered PyTypes
+
+@cython.final
+@cython.internal
+cdef class _ObjectifyElementMakerCaller:
+    cdef object _tag
+    cdef object _nsmap
+    cdef object _element_factory
+    cdef bint _annotate
+
+    def __call__(self, *children, **attrib):
+        "__call__(self, *children, **attrib)"
+        cdef _ObjectifyElementMakerCaller elementMaker
+        cdef _Element element
+        cdef _Element childElement
+        cdef bint has_children
+        cdef bint has_string_value
+        if self._element_factory is None:
+            element = _makeElement(self._tag, None, attrib, self._nsmap)
+        else:
+            element = self._element_factory(self._tag, attrib, self._nsmap)
+
+        pytype_name = None
+        has_children = False
+        has_string_value = False
+        for child in children:
+            if child is None:
+                if len(children) == 1:
+                    cetree.setAttributeValue(
+                        element, XML_SCHEMA_INSTANCE_NIL_ATTR, "true")
+            elif python._isString(child):
+                _add_text(element, child)
+                has_string_value = True
+            elif isinstance(child, _Element):
+                cetree.appendChildToElement(element, <_Element>child)
+                has_children = True
+            elif isinstance(child, _ObjectifyElementMakerCaller):
+                elementMaker = <_ObjectifyElementMakerCaller>child
+                if elementMaker._element_factory is None:
+                    cetree.makeSubElement(element, elementMaker._tag,
+                                          None, None, None, None)
+                else:
+                    childElement = elementMaker._element_factory(
+                        elementMaker._tag)
+                    cetree.appendChildToElement(element, childElement)
+                has_children = True
+            elif isinstance(child, dict):
+                for name, value in child.items():
+                    # keyword arguments in attrib take precedence
+                    if name in attrib:
+                        continue
+                    pytype = _PYTYPE_DICT.get(_typename(value))
+                    if pytype is not None:
+                        value = (<PyType>pytype).stringify(value)
+                    elif not python._isString(value):
+                        value = unicode(value)
+                    cetree.setAttributeValue(element, name, value)
+            else:
+                if pytype_name is not None:
+                    # concatenation always makes the result a string
+                    has_string_value = True
+                pytype_name = _typename(child)
+                pytype = _PYTYPE_DICT.get(_typename(child))
+                if pytype is not None:
+                    _add_text(element, (<PyType>pytype).stringify(child))
+                else:
+                    has_string_value = True
+                    child = unicode(child)
+                    _add_text(element, child)
+
+        if self._annotate and not has_children:
+            if has_string_value:
+                cetree.setAttributeValue(element, PYTYPE_ATTRIBUTE, "str")
+            elif pytype_name is not None:
+                cetree.setAttributeValue(element, PYTYPE_ATTRIBUTE, pytype_name)
+
+        return element
+
+cdef _add_text(_Element elem, text):
+    # add text to the tree in construction, either as element text or
+    # tail text, depending on the current tree state
+    cdef tree.xmlNode* c_child
+    c_child = cetree.findChildBackwards(elem._c_node, 0)
+    if c_child is not NULL:
+        old = cetree.tailOf(c_child)
+        if old is not None:
+            text = old + text
+        cetree.setTailText(c_child, text)
+    else:
+        old = cetree.textOf(elem._c_node)
+        if old is not None:
+            text = old + text
+        cetree.setNodeText(elem._c_node, text)
+
+cdef class ElementMaker:
+    """ElementMaker(self, namespace=None, nsmap=None, annotate=True, makeelement=None)
+
+    An ElementMaker that can be used for constructing trees.
+
+    Example::
+
+      >>> M = ElementMaker(annotate=False)
+      >>> attributes = {'class': 'par'}
+      >>> html = M.html( M.body( M.p('hello', attributes, M.br, 'objectify', style="font-weight: bold") ) )
+
+      >>> from lxml.etree import tostring
+      >>> print(tostring(html, method='html').decode('ascii'))
+      <html><body><p style="font-weight: bold" class="par">hello<br>objectify</p></body></html>
+
+    To create tags that are not valid Python identifiers, call the factory
+    directly and pass the tag name as first argument::
+
+      >>> root = M('tricky-tag', 'some text')
+      >>> print(root.tag)
+      tricky-tag
+      >>> print(root.text)
+      some text
+
+    Note that this module has a predefined ElementMaker instance called ``E``.
+    """
+    cdef object _makeelement
+    cdef object _namespace
+    cdef object _nsmap
+    cdef bint _annotate
+    cdef dict _cache
+    def __init__(self, *, namespace=None, nsmap=None, annotate=True,
+                 makeelement=None):
+        if nsmap is None:
+            nsmap = _DEFAULT_NSMAP if annotate else {}
+        self._nsmap = nsmap
+        self._namespace = None if namespace is None else "{%s}" % namespace
+        self._annotate = annotate
+        if makeelement is not None:
+            if not callable(makeelement):
+                raise TypeError(
+                    f"argument of 'makeelement' parameter must be callable, got {type(makeelement)}")
+            self._makeelement = makeelement
+        else:
+            self._makeelement = None
+        self._cache = {}
+
+    @cython.final
+    cdef _build_element_maker(self, tag, bint caching):
+        cdef _ObjectifyElementMakerCaller element_maker
+        element_maker = _ObjectifyElementMakerCaller.__new__(_ObjectifyElementMakerCaller)
+        if self._namespace is not None and tag[0] != "{":
+            element_maker._tag = self._namespace + tag
+        else:
+            element_maker._tag = tag
+        element_maker._nsmap = self._nsmap
+        element_maker._annotate = self._annotate
+        element_maker._element_factory = self._makeelement
+        if caching:
+            if len(self._cache) > 200:
+                self._cache.clear()
+            self._cache[tag] = element_maker
+        return element_maker
+
+    def __getattr__(self, tag):
+        element_maker = self._cache.get(tag)
+        if element_maker is None:
+            return self._build_element_maker(tag, caching=True)
+        return element_maker
+
+    def __call__(self, tag, *args, **kwargs):
+        element_maker = self._cache.get(tag)
+        if element_maker is None:
+            element_maker = self._build_element_maker(
+                tag, caching=not is_special_method(tag))
+        return element_maker(*args, **kwargs)
+
+################################################################################
+# Recursive element dumping
+
+cdef bint __RECURSIVE_STR = 0 # default: off
+
+def enable_recursive_str(on=True):
+    """enable_recursive_str(on=True)
+
+    Enable a recursively generated tree representation for str(element),
+    based on objectify.dump(element).
+    """
+    global __RECURSIVE_STR
+    __RECURSIVE_STR = on
+
+def dump(_Element element not None):
+    """dump(_Element element not None)
+
+    Return a recursively generated string representation of an element.
+    """
+    return _dump(element, 0)
+
+cdef object _dump(_Element element, int indent):
+    indentstr = "    " * indent
+    if isinstance(element, ObjectifiedDataElement):
+        value = repr(element)
+    else:
+        value = textOf(element._c_node)
+        if value is not None:
+            if not value.strip():
+                value = None
+            else:
+                value = repr(value)
+    result = f"{indentstr}{element.tag} = {value} [{_typename(element)}]\n"
+    xsi_ns    = "{%s}" % XML_SCHEMA_INSTANCE_NS
+    pytype_ns = "{%s}" % PYTYPE_NAMESPACE
+    for name, value in sorted(cetree.iterattributes(element, 3)):
+        if '{' in name:
+            if name == PYTYPE_ATTRIBUTE:
+                if value == TREE_PYTYPE_NAME:
+                    continue
+                else:
+                    name = name.replace(pytype_ns, 'py:')
+            name = name.replace(xsi_ns, 'xsi:')
+        result += f"{indentstr}  * {name} = {value!r}\n"
+
+    indent += 1
+    for child in element.iterchildren():
+        result += _dump(child, indent)
+    if indent == 1:
+        return result[:-1] # strip last '\n'
+    else:
+        return result
+
+
+################################################################################
+# Pickle support for objectified ElementTree
+
+def __unpickleElementTree(data):
+    return etree.ElementTree(fromstring(data))
+
+cdef _setupPickle(elementTreeReduceFunction):
+    import copyreg
+    copyreg.pickle(etree._ElementTree,
+                   elementTreeReduceFunction, __unpickleElementTree)
+
+def pickleReduceElementTree(obj):
+    return __unpickleElementTree, (etree.tostring(obj),)
+
+_setupPickle(pickleReduceElementTree)
+del pickleReduceElementTree
+
+################################################################################
+# Element class lookup
+
+cdef class ObjectifyElementClassLookup(ElementClassLookup):
+    """ObjectifyElementClassLookup(self, tree_class=None, empty_data_class=None)
+    Element class lookup method that uses the objectify classes.
+    """
+    cdef object empty_data_class
+    cdef object tree_class
+    def __init__(self, tree_class=None, empty_data_class=None):
+        """Lookup mechanism for objectify.
+
+        The default Element classes can be replaced by passing subclasses of
+        ObjectifiedElement and ObjectifiedDataElement as keyword arguments.
+        'tree_class' defines inner tree classes (defaults to
+        ObjectifiedElement), 'empty_data_class' defines the default class for
+        empty data elements (defaults to StringElement).
+        """
+        self._lookup_function = _lookupElementClass
+        if tree_class is None:
+            tree_class = ObjectifiedElement
+        self.tree_class = tree_class
+        if empty_data_class is None:
+            empty_data_class = StringElement
+        self.empty_data_class = empty_data_class
+
+cdef object _lookupElementClass(state, _Document doc, tree.xmlNode* c_node):
+    cdef ObjectifyElementClassLookup lookup
+    lookup = <ObjectifyElementClassLookup>state
+    # if element has children => no data class
+    if cetree.hasChild(c_node):
+        return lookup.tree_class
+
+    # if element is defined as xsi:nil, return NoneElement class
+    if "true" == cetree.attributeValueFromNsName(
+        c_node, _XML_SCHEMA_INSTANCE_NS, <unsigned char*>"nil"):
+        return NoneElement
+
+    # check for Python type hint
+    value = cetree.attributeValueFromNsName(
+        c_node, _PYTYPE_NAMESPACE, _PYTYPE_ATTRIBUTE_NAME)
+    if value is not None:
+        if value == TREE_PYTYPE_NAME:
+            return lookup.tree_class
+        py_type = <PyType>_PYTYPE_DICT.get(value)
+        if py_type is not None:
+            return py_type._type
+        # unknown 'pyval' => try to figure it out ourself, just go on
+
+    # check for XML Schema type hint
+    value = cetree.attributeValueFromNsName(
+        c_node, _XML_SCHEMA_INSTANCE_NS, <unsigned char*>"type")
+
+    if value is not None:
+        schema_type = <PyType>_SCHEMA_TYPE_DICT.get(value)
+        if schema_type is None and ':' in value:
+            prefix, value = value.split(':', 1)
+            schema_type = <PyType>_SCHEMA_TYPE_DICT.get(value)
+        if schema_type is not None:
+            return schema_type._type
+
+    # otherwise determine class based on text content type
+    el_class = _guessElementClass(c_node)
+    if el_class is not None:
+        return el_class
+
+    # if element is a root node => default to tree node
+    if c_node.parent is NULL or not tree._isElement(c_node.parent):
+        return lookup.tree_class
+
+    return lookup.empty_data_class
+
+
+################################################################################
+# Type annotations
+
+cdef PyType _check_type(tree.xmlNode* c_node, PyType pytype):
+    if pytype is None:
+        return None
+    value = textOf(c_node)
+    try:
+        pytype.type_check(value)
+        return pytype
+    except IGNORABLE_ERRORS:
+        # could not be parsed as the specified type => ignore
+        pass
+    return None
+
+def pyannotate(element_or_tree, *, ignore_old=False, ignore_xsi=False,
+             empty_pytype=None):
+    """pyannotate(element_or_tree, ignore_old=False, ignore_xsi=False, empty_pytype=None)
+
+    Recursively annotates the elements of an XML tree with 'pytype'
+    attributes.
+
+    If the 'ignore_old' keyword argument is True (the default), current 'pytype'
+    attributes will be ignored and replaced.  Otherwise, they will be checked
+    and only replaced if they no longer fit the current text value.
+
+    Setting the keyword argument ``ignore_xsi`` to True makes the function
+    additionally ignore existing ``xsi:type`` annotations.  The default is to
+    use them as a type hint.
+
+    The default annotation of empty elements can be set with the
+    ``empty_pytype`` keyword argument.  The default is not to annotate empty
+    elements.  Pass 'str', for example, to make string values the default.
+    """
+    cdef _Element  element
+    element = cetree.rootNodeOrRaise(element_or_tree)
+    _annotate(element, 0, 1, ignore_xsi, ignore_old, None, empty_pytype)
+
+def xsiannotate(element_or_tree, *, ignore_old=False, ignore_pytype=False,
+                empty_type=None):
+    """xsiannotate(element_or_tree, ignore_old=False, ignore_pytype=False, empty_type=None)
+
+    Recursively annotates the elements of an XML tree with 'xsi:type'
+    attributes.
+
+    If the 'ignore_old' keyword argument is True (the default), current
+    'xsi:type' attributes will be ignored and replaced.  Otherwise, they will be
+    checked and only replaced if they no longer fit the current text value.
+
+    Note that the mapping from Python types to XSI types is usually ambiguous.
+    Currently, only the first XSI type name in the corresponding PyType
+    definition will be used for annotation.  Thus, you should consider naming
+    the widest type first if you define additional types.
+
+    Setting the keyword argument ``ignore_pytype`` to True makes the function
+    additionally ignore existing ``pytype`` annotations.  The default is to
+    use them as a type hint.
+
+    The default annotation of empty elements can be set with the
+    ``empty_type`` keyword argument.  The default is not to annotate empty
+    elements.  Pass 'string', for example, to make string values the default.
+    """
+    cdef _Element  element
+    element = cetree.rootNodeOrRaise(element_or_tree)
+    _annotate(element, 1, 0, ignore_old, ignore_pytype, empty_type, None)
+
+def annotate(element_or_tree, *, ignore_old=True, ignore_xsi=False,
+             empty_pytype=None, empty_type=None, annotate_xsi=0,
+             annotate_pytype=1):
+    """annotate(element_or_tree, ignore_old=True, ignore_xsi=False, empty_pytype=None, empty_type=None, annotate_xsi=0, annotate_pytype=1)
+
+    Recursively annotates the elements of an XML tree with 'xsi:type'
+    and/or 'py:pytype' attributes.
+
+    If the 'ignore_old' keyword argument is True (the default), current
+    'py:pytype' attributes will be ignored for the type annotation. Set to False
+    if you want reuse existing 'py:pytype' information (iff appropriate for the
+    element text value).
+
+    If the 'ignore_xsi' keyword argument is False (the default), existing
+    'xsi:type' attributes will be used for the type annotation, if they fit the
+    element text values. 
+    
+    Note that the mapping from Python types to XSI types is usually ambiguous.
+    Currently, only the first XSI type name in the corresponding PyType
+    definition will be used for annotation.  Thus, you should consider naming
+    the widest type first if you define additional types.
+
+    The default 'py:pytype' annotation of empty elements can be set with the
+    ``empty_pytype`` keyword argument. Pass 'str', for example, to make
+    string values the default.
+
+    The default 'xsi:type' annotation of empty elements can be set with the
+    ``empty_type`` keyword argument.  The default is not to annotate empty
+    elements.  Pass 'string', for example, to make string values the default.
+
+    The keyword arguments 'annotate_xsi' (default: 0) and 'annotate_pytype'
+    (default: 1) control which kind(s) of annotation to use. 
+    """
+    cdef _Element  element
+    element = cetree.rootNodeOrRaise(element_or_tree)
+    _annotate(element, annotate_xsi, annotate_pytype, ignore_xsi,
+              ignore_old, empty_type, empty_pytype)
+
+
+cdef _annotate(_Element element, bint annotate_xsi, bint annotate_pytype,
+               bint ignore_xsi, bint ignore_pytype,
+               empty_type_name, empty_pytype_name):
+    cdef _Document doc
+    cdef tree.xmlNode* c_node
+    cdef PyType empty_pytype, StrType, NoneType
+
+    if not annotate_xsi and not annotate_pytype:
+        return
+
+    if empty_type_name is not None:
+        if isinstance(empty_type_name, bytes):
+            empty_type_name = (<bytes>empty_type_name).decode("ascii")
+        empty_pytype = <PyType>_SCHEMA_TYPE_DICT.get(empty_type_name)
+    elif empty_pytype_name is not None:
+        if isinstance(empty_pytype_name, bytes):
+            empty_pytype_name = (<bytes>empty_pytype_name).decode("ascii")
+        empty_pytype = <PyType>_PYTYPE_DICT.get(empty_pytype_name)
+    else:
+        empty_pytype = None
+
+    StrType  = <PyType>_PYTYPE_DICT.get('str')
+    NoneType = <PyType>_PYTYPE_DICT.get('NoneType')
+
+    doc = element._doc
+    c_node = element._c_node
+    tree.BEGIN_FOR_EACH_ELEMENT_FROM(c_node, c_node, 1)
+    if c_node.type == tree.XML_ELEMENT_NODE:
+        _annotate_element(c_node, doc, annotate_xsi, annotate_pytype,
+                          ignore_xsi, ignore_pytype,
+                          empty_type_name, empty_pytype, StrType, NoneType)
+    tree.END_FOR_EACH_ELEMENT_FROM(c_node)
+
+cdef int _annotate_element(tree.xmlNode* c_node, _Document doc,
+                           bint annotate_xsi, bint annotate_pytype,
+                           bint ignore_xsi, bint ignore_pytype,
+                           empty_type_name, PyType empty_pytype,
+                           PyType StrType, PyType NoneType) except -1:
+    cdef tree.xmlNs*   c_ns
+    cdef PyType pytype = None
+    typename = None
+    istree = 0
+
+    # if element is defined as xsi:nil, represent it as None
+    if cetree.attributeValueFromNsName(
+        c_node, _XML_SCHEMA_INSTANCE_NS, <unsigned char*>"nil") == "true":
+        pytype = NoneType
+
+    if pytype is None and not ignore_xsi:
+        # check that old xsi type value is valid
+        typename = cetree.attributeValueFromNsName(
+            c_node, _XML_SCHEMA_INSTANCE_NS, <unsigned char*>"type")
+        if typename is not None:
+            pytype = <PyType>_SCHEMA_TYPE_DICT.get(typename)
+            if pytype is None and ':' in typename:
+                prefix, typename = typename.split(':', 1)
+                pytype = <PyType>_SCHEMA_TYPE_DICT.get(typename)
+            if pytype is not None and pytype is not StrType:
+                # StrType does not have a typecheck but is the default
+                # anyway, so just accept it if given as type
+                # information
+                pytype = _check_type(c_node, pytype)
+                if pytype is None:
+                    typename = None
+
+    if pytype is None and not ignore_pytype:
+        # check that old pytype value is valid
+        old_pytypename = cetree.attributeValueFromNsName(
+            c_node, _PYTYPE_NAMESPACE, _PYTYPE_ATTRIBUTE_NAME)
+        if old_pytypename is not None:
+            if old_pytypename == TREE_PYTYPE_NAME:
+                if not cetree.hasChild(c_node):
+                    # only case where we should keep it,
+                    # everything else is clear enough
+                    pytype = TREE_PYTYPE
+            else:
+                if old_pytypename == 'none':
+                    # transition from lxml 1.x
+                    old_pytypename = "NoneType"
+                pytype = <PyType>_PYTYPE_DICT.get(old_pytypename)
+                if pytype is not None and pytype is not StrType:
+                    # StrType does not have a typecheck but is the
+                    # default anyway, so just accept it if given as
+                    # type information
+                    pytype = _check_type(c_node, pytype)
+
+    if pytype is None:
+        # try to guess type
+        if not cetree.hasChild(c_node):
+            # element has no children => data class
+            pytype = _guessPyType(textOf(c_node), StrType)
+        else:
+            istree = 1
+
+    if pytype is None:
+        # use default type for empty elements
+        if cetree.hasText(c_node):
+            pytype = StrType
+        else:
+            pytype = empty_pytype
+            if typename is None:
+                typename = empty_type_name
+
+    if pytype is not None:
+        if typename is None:
+            if not istree:
+                if pytype._schema_types:
+                    # pytype->xsi:type is a 1:n mapping
+                    # simply take the first
+                    typename = pytype._schema_types[0]
+        elif typename not in pytype._schema_types:
+            typename = pytype._schema_types[0]
+
+    if annotate_xsi:
+        if typename is None or istree:
+            cetree.delAttributeFromNsName(
+                c_node, _XML_SCHEMA_INSTANCE_NS, <unsigned char*>"type")
+        else:
+            # update or create attribute
+            typename_utf8 = cetree.utf8(typename)
+            c_ns = cetree.findOrBuildNodeNsPrefix(
+                doc, c_node, _XML_SCHEMA_NS, <unsigned char*>'xsd')
+            if c_ns is not NULL:
+                if b':' in typename_utf8:
+                    prefix, name = typename_utf8.split(b':', 1)
+                    if c_ns.prefix is NULL or c_ns.prefix[0] == c'\0':
+                        typename_utf8 = name
+                    elif tree.xmlStrcmp(_xcstr(prefix), c_ns.prefix) != 0:
+                        typename_utf8 = (<unsigned char*>c_ns.prefix) + b':' + name
+                elif c_ns.prefix is not NULL and c_ns.prefix[0] != c'\0':
+                    typename_utf8 = (<unsigned char*>c_ns.prefix) + b':' + typename_utf8
+            c_ns = cetree.findOrBuildNodeNsPrefix(
+                doc, c_node, _XML_SCHEMA_INSTANCE_NS, <unsigned char*>'xsi')
+            tree.xmlSetNsProp(c_node, c_ns, <unsigned char*>"type", _xcstr(typename_utf8))
+
+    if annotate_pytype:
+        if pytype is None:
+            # delete attribute if it exists
+            cetree.delAttributeFromNsName(
+                c_node, _PYTYPE_NAMESPACE, _PYTYPE_ATTRIBUTE_NAME)
+        else:
+            # update or create attribute
+            c_ns = cetree.findOrBuildNodeNsPrefix(
+                doc, c_node, _PYTYPE_NAMESPACE, <unsigned char*>'py')
+            pytype_name = cetree.utf8(pytype.name)
+            tree.xmlSetNsProp(c_node, c_ns, _PYTYPE_ATTRIBUTE_NAME,
+                              _xcstr(pytype_name))
+            if pytype is NoneType:
+                c_ns = cetree.findOrBuildNodeNsPrefix(
+                    doc, c_node, _XML_SCHEMA_INSTANCE_NS, <unsigned char*>'xsi')
+                tree.xmlSetNsProp(c_node, c_ns, <unsigned char*>"nil", <unsigned char*>"true")
+
+    return 0
+
+cdef object _strip_attributes = etree.strip_attributes
+cdef object _cleanup_namespaces = etree.cleanup_namespaces
+
+def deannotate(element_or_tree, *, bint pytype=True, bint xsi=True,
+               bint xsi_nil=False, bint cleanup_namespaces=False):
+    """deannotate(element_or_tree, pytype=True, xsi=True, xsi_nil=False, cleanup_namespaces=False)
+
+    Recursively de-annotate the elements of an XML tree by removing 'py:pytype'
+    and/or 'xsi:type' attributes and/or 'xsi:nil' attributes.
+
+    If the 'pytype' keyword argument is True (the default), 'py:pytype'
+    attributes will be removed. If the 'xsi' keyword argument is True (the 
+    default), 'xsi:type' attributes will be removed.
+    If the 'xsi_nil' keyword argument is True (default: False), 'xsi:nil'
+    attributes will be removed.
+
+    Note that this does not touch the namespace declarations by
+    default.  If you want to remove unused namespace declarations from
+    the tree, pass the option ``cleanup_namespaces=True``.
+    """
+    cdef list attribute_names = []
+
+    if pytype:
+        attribute_names.append(PYTYPE_ATTRIBUTE)
+    if xsi:
+        attribute_names.append(XML_SCHEMA_INSTANCE_TYPE_ATTR)
+    if xsi_nil:
+        attribute_names.append(XML_SCHEMA_INSTANCE_NIL_ATTR)
+
+    _strip_attributes(element_or_tree, *attribute_names)
+    if cleanup_namespaces:
+        _cleanup_namespaces(element_or_tree)
+
+################################################################################
+# Module level parser setup
+
+cdef object __DEFAULT_PARSER
+__DEFAULT_PARSER = etree.XMLParser(remove_blank_text=True)
+__DEFAULT_PARSER.set_element_class_lookup( ObjectifyElementClassLookup() )
+
+cdef object objectify_parser
+objectify_parser = __DEFAULT_PARSER
+
+def set_default_parser(new_parser = None):
+    """set_default_parser(new_parser = None)
+
+    Replace the default parser used by objectify's Element() and
+    fromstring() functions.
+
+    The new parser must be an etree.XMLParser.
+
+    Call without arguments to reset to the original parser.
+    """
+    global objectify_parser
+    if new_parser is None:
+        objectify_parser = __DEFAULT_PARSER
+    elif isinstance(new_parser, etree.XMLParser):
+        objectify_parser = new_parser
+    else:
+        raise TypeError, "parser must inherit from lxml.etree.XMLParser"
+
+def makeparser(**kw):
+    """makeparser(remove_blank_text=True, **kw)
+
+    Create a new XML parser for objectify trees.
+
+    You can pass all keyword arguments that are supported by
+    ``etree.XMLParser()``.  Note that this parser defaults to removing
+    blank text.  You can disable this by passing the
+    ``remove_blank_text`` boolean keyword option yourself.
+    """
+    if 'remove_blank_text' not in kw:
+        kw['remove_blank_text'] = True
+    parser = etree.XMLParser(**kw)
+    parser.set_element_class_lookup( ObjectifyElementClassLookup() )
+    return parser
+
+cdef _Element _makeElement(tag, text, attrib, nsmap):
+    return cetree.makeElement(tag, None, objectify_parser, text, None, attrib, nsmap)
+
+################################################################################
+# Module level factory functions
+
+cdef object _fromstring
+_fromstring = etree.fromstring
+
+SubElement = etree.SubElement
+
+def fromstring(xml, parser=None, *, base_url=None):
+    """fromstring(xml, parser=None, base_url=None)
+
+    Objectify specific version of the lxml.etree fromstring() function
+    that uses the objectify parser.
+
+    You can pass a different parser as second argument.
+
+    The ``base_url`` keyword argument allows to set the original base URL of
+    the document to support relative Paths when looking up external entities
+    (DTD, XInclude, ...).
+    """
+    if parser is None:
+        parser = objectify_parser
+    return _fromstring(xml, parser, base_url=base_url)
+
+def XML(xml, parser=None, *, base_url=None):
+    """XML(xml, parser=None, base_url=None)
+
+    Objectify specific version of the lxml.etree XML() literal factory
+    that uses the objectify parser.
+
+    You can pass a different parser as second argument.
+
+    The ``base_url`` keyword argument allows to set the original base URL of
+    the document to support relative Paths when looking up external entities
+    (DTD, XInclude, ...).
+    """
+    if parser is None:
+        parser = objectify_parser
+    return _fromstring(xml, parser, base_url=base_url)
+
+cdef object _parse
+_parse = etree.parse
+
+def parse(f, parser=None, *, base_url=None):
+    """parse(f, parser=None, base_url=None)
+
+    Parse a file or file-like object with the objectify parser.
+
+    You can pass a different parser as second argument.
+
+    The ``base_url`` keyword allows setting a URL for the document
+    when parsing from a file-like object.  This is needed when looking
+    up external entities (DTD, XInclude, ...) with relative paths.
+    """
+    if parser is None:
+        parser = objectify_parser
+    return _parse(f, parser, base_url=base_url)
+
+cdef dict _DEFAULT_NSMAP = {
+    "py"  : PYTYPE_NAMESPACE,
+    "xsi" : XML_SCHEMA_INSTANCE_NS,
+    "xsd" : XML_SCHEMA_NS
+}
+
+E = ElementMaker()
+
+def Element(_tag, attrib=None, nsmap=None, *, _pytype=None, **_attributes):
+    """Element(_tag, attrib=None, nsmap=None, _pytype=None, **_attributes)
+
+    Objectify specific version of the lxml.etree Element() factory that
+    always creates a structural (tree) element.
+
+    NOTE: requires parser based element class lookup activated in lxml.etree!
+    """
+    if attrib is not None:
+        if _attributes:
+            attrib = dict(attrib)
+            attrib.update(_attributes)
+        _attributes = attrib
+    if _pytype is None:
+        _pytype = TREE_PYTYPE_NAME
+    if nsmap is None:
+        nsmap = _DEFAULT_NSMAP
+    _attributes[PYTYPE_ATTRIBUTE] = _pytype
+    return _makeElement(_tag, None, _attributes, nsmap)
+
+def DataElement(_value, attrib=None, nsmap=None, *, _pytype=None, _xsi=None,
+                **_attributes):
+    """DataElement(_value, attrib=None, nsmap=None, _pytype=None, _xsi=None, **_attributes)
+
+    Create a new element from a Python value and XML attributes taken from
+    keyword arguments or a dictionary passed as second argument.
+
+    Automatically adds a 'pytype' attribute for the Python type of the value,
+    if the type can be identified.  If '_pytype' or '_xsi' are among the
+    keyword arguments, they will be used instead.
+
+    If the _value argument is an ObjectifiedDataElement instance, its py:pytype,
+    xsi:type and other attributes and nsmap are reused unless they are redefined
+    in attrib and/or keyword arguments.
+    """
+    if nsmap is None:
+        nsmap = _DEFAULT_NSMAP
+    if attrib is not None and attrib:
+        if _attributes:
+            attrib = dict(attrib)
+            attrib.update(_attributes)
+        _attributes = attrib
+    if isinstance(_value, ObjectifiedElement):
+        if _pytype is None:
+            if _xsi is None and not _attributes and nsmap is _DEFAULT_NSMAP:
+                # special case: no change!
+                return _value.__copy__()
+    if isinstance(_value, ObjectifiedDataElement):
+        # reuse existing nsmap unless redefined in nsmap parameter
+        temp = _value.nsmap
+        if temp is not None and temp:
+            temp = dict(temp)
+            temp.update(nsmap)
+            nsmap = temp
+        # reuse existing attributes unless redefined in attrib/_attributes
+        temp = _value.attrib
+        if temp is not None and temp:
+            temp = dict(temp)
+            temp.update(_attributes)
+            _attributes = temp
+        # reuse existing xsi:type or py:pytype attributes, unless provided as
+        # arguments
+        if _xsi is None and _pytype is None:
+            _xsi = _attributes.get(XML_SCHEMA_INSTANCE_TYPE_ATTR)
+            _pytype = _attributes.get(PYTYPE_ATTRIBUTE)
+
+    if _xsi is not None:
+        if ':' in _xsi:
+            prefix, name = _xsi.split(':', 1)
+            ns = nsmap.get(prefix)
+            if ns != XML_SCHEMA_NS:
+                raise ValueError, "XSD types require the XSD namespace"
+        elif nsmap is _DEFAULT_NSMAP:
+            name = _xsi
+            _xsi = 'xsd:' + _xsi
+        else:
+            name = _xsi
+            for prefix, ns in nsmap.items():
+                if ns == XML_SCHEMA_NS:
+                    if prefix is not None and prefix:
+                        _xsi = prefix + ':' + _xsi
+                    break
+            else:
+                raise ValueError, "XSD types require the XSD namespace"
+        _attributes[XML_SCHEMA_INSTANCE_TYPE_ATTR] = _xsi
+        if _pytype is None:
+            # allow using unregistered or even wrong xsi:type names
+            py_type = <PyType>_SCHEMA_TYPE_DICT.get(_xsi)
+            if py_type is None:
+                py_type = <PyType>_SCHEMA_TYPE_DICT.get(name)
+            if py_type is not None:
+                _pytype = py_type.name
+
+    if _pytype is None:
+        _pytype = _pytypename(_value)
+
+    if _value is None and _pytype != "str":
+        _pytype = _pytype or "NoneType"
+        strval = None
+    elif python._isString(_value):
+        strval = _value
+    elif isinstance(_value, bool):
+        if _value:
+            strval = "true"
+        else:
+            strval = "false"
+    else:
+        py_type = <PyType>_PYTYPE_DICT.get(_pytype)
+        stringify = unicode if py_type is None else py_type.stringify
+        strval = stringify(_value)
+
+    if _pytype is not None: 
+        if _pytype == "NoneType" or _pytype == "none":
+            strval = None
+            _attributes[XML_SCHEMA_INSTANCE_NIL_ATTR] = "true"
+        else:
+            # check if type information from arguments is valid
+            py_type = <PyType>_PYTYPE_DICT.get(_pytype)
+            if py_type is not None:
+                if py_type.type_check is not None:
+                    py_type.type_check(strval)
+                _attributes[PYTYPE_ATTRIBUTE] = _pytype
+
+    return _makeElement("value", strval, _attributes, nsmap)
+
+
+################################################################################
+# ObjectPath
+
+include "objectpath.pxi"
diff --git a/.venv/lib/python3.12/site-packages/lxml/objectpath.pxi b/.venv/lib/python3.12/site-packages/lxml/objectpath.pxi
new file mode 100644
index 00000000..e562a365
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/objectpath.pxi
@@ -0,0 +1,332 @@
+################################################################################
+# ObjectPath
+
+ctypedef struct _ObjectPath:
+    const_xmlChar* href
+    const_xmlChar* name
+    Py_ssize_t index
+
+
+cdef object _NO_DEFAULT = object()
+
+
+cdef class ObjectPath:
+    """ObjectPath(path)
+    Immutable object that represents a compiled object path.
+
+    Example for a path: 'root.child[1].{other}child[25]'
+    """
+    cdef readonly object find
+    cdef list _path
+    cdef object _path_str
+    cdef _ObjectPath*  _c_path
+    cdef Py_ssize_t _path_len
+    def __init__(self, path):
+        if python._isString(path):
+            self._path = _parse_object_path_string(path)
+            self._path_str = path
+        else:
+            self._path = _parse_object_path_list(path)
+            self._path_str = '.'.join(path)
+        self._path_len = len(self._path)
+        self._c_path = _build_object_path_segments(self._path)
+        self.find = self.__call__
+
+    def __dealloc__(self):
+        if self._c_path is not NULL:
+            python.lxml_free(self._c_path)
+
+    def __str__(self):
+        return self._path_str
+
+    def __call__(self, _Element root not None, *_default):
+        """Follow the attribute path in the object structure and return the
+        target attribute value.
+
+        If it it not found, either returns a default value (if one was passed
+        as second argument) or raises AttributeError.
+        """
+        if _default:
+            if len(_default) > 1:
+                raise TypeError, "invalid number of arguments: needs one or two"
+            default = _default[0]
+        else:
+            default = _NO_DEFAULT
+        return _find_object_path(root, self._c_path, self._path_len, default)
+
+    def hasattr(self, _Element root not None):
+        "hasattr(self, root)"
+        try:
+            _find_object_path(root, self._c_path, self._path_len, _NO_DEFAULT)
+        except AttributeError:
+            return False
+        return True
+
+    def setattr(self, _Element root not None, value):
+        """setattr(self, root, value)
+
+        Set the value of the target element in a subtree.
+
+        If any of the children on the path does not exist, it is created.
+        """
+        _create_object_path(root, self._c_path, self._path_len, 1, value)
+
+    def addattr(self, _Element root not None, value):
+        """addattr(self, root, value)
+
+        Append a value to the target element in a subtree.
+
+        If any of the children on the path does not exist, it is created.
+        """
+        _create_object_path(root, self._c_path, self._path_len, 0, value)
+
+
+cdef object __MATCH_PATH_SEGMENT = re.compile(
+    r"(\.?)\s*(?:\{([^}]*)\})?\s*([^.{}\[\]\s]+)\s*(?:\[\s*([-0-9]+)\s*\])?",
+    re.U).match
+
+cdef tuple _RELATIVE_PATH_SEGMENT = (None, None, 0)
+
+
+cdef list _parse_object_path_string(_path):
+    """Parse object path string into a (ns, name, index) list.
+    """
+    cdef bint has_dot
+    cdef unicode path
+    new_path = []
+    if isinstance(_path, bytes):
+        path = (<bytes>_path).decode('ascii')
+    elif type(_path) is not unicode:
+        path = unicode(_path)
+    else:
+        path = _path
+    path = path.strip()
+    if path == '.':
+        return [_RELATIVE_PATH_SEGMENT]
+    path_pos = 0
+    while path:
+        match = __MATCH_PATH_SEGMENT(path, path_pos)
+        if match is None:
+            break
+
+        dot, ns, name, index = match.groups()
+        index = int(index) if index else 0
+        has_dot = dot == '.'
+        if not new_path:
+            if has_dot:
+                # path '.child' => ignore root
+                new_path.append(_RELATIVE_PATH_SEGMENT)
+            elif index:
+                raise ValueError, "index not allowed on root node"
+        elif not has_dot:
+            raise ValueError, "invalid path"
+        if ns is not None:
+            ns = python.PyUnicode_AsUTF8String(ns)
+        name = python.PyUnicode_AsUTF8String(name)
+        new_path.append( (ns, name, index) )
+
+        path_pos = match.end()
+    if not new_path or len(path) > path_pos:
+        raise ValueError, "invalid path"
+    return new_path
+
+
+cdef list _parse_object_path_list(path):
+    """Parse object path sequence into a (ns, name, index) list.
+    """
+    new_path = []
+    for item in path:
+        item = item.strip()
+        if not new_path and item == '':
+            # path '.child' => ignore root
+            ns = name = None
+            index = 0
+        else:
+            ns, name = cetree.getNsTag(item)
+            c_name = _xcstr(name)
+            index_pos = tree.xmlStrchr(c_name, c'[')
+            if index_pos is NULL:
+                index = 0
+            else:
+                index_end = tree.xmlStrchr(index_pos + 1, c']')
+                if index_end is NULL:
+                    raise ValueError, "index must be enclosed in []"
+                index = int(index_pos[1:index_end - index_pos])
+                if not new_path and index != 0:
+                    raise ValueError, "index not allowed on root node"
+                name = <bytes>c_name[:index_pos - c_name]
+        new_path.append( (ns, name, index) )
+    if not new_path:
+        raise ValueError, "invalid path"
+    return new_path
+
+
+cdef _ObjectPath* _build_object_path_segments(list path_list) except NULL:
+    cdef _ObjectPath* c_path
+    cdef _ObjectPath* c_path_segments
+    c_path_segments = <_ObjectPath*>python.lxml_malloc(len(path_list), sizeof(_ObjectPath))
+    if c_path_segments is NULL:
+        raise MemoryError()
+    c_path = c_path_segments
+    for href, name, index in path_list:
+        c_path[0].href = _xcstr(href) if href is not None else NULL
+        c_path[0].name = _xcstr(name) if name is not None else NULL
+        c_path[0].index = index
+        c_path += 1
+    return c_path_segments
+
+
+cdef _find_object_path(_Element root, _ObjectPath* c_path, Py_ssize_t c_path_len, default_value):
+    """Follow the path to find the target element.
+    """
+    cdef tree.xmlNode* c_node
+    cdef Py_ssize_t c_index
+    c_node = root._c_node
+    c_name = c_path[0].name
+    c_href = c_path[0].href
+    if c_href is NULL or c_href[0] == c'\0':
+        c_href = tree._getNs(c_node)
+    if not cetree.tagMatches(c_node, c_href, c_name):
+        if default_value is not _NO_DEFAULT:
+            return default_value
+        else:
+            raise ValueError(
+                f"root element does not match: need {cetree.namespacedNameFromNsName(c_href, c_name)}, got {root.tag}")
+
+    while c_node is not NULL:
+        c_path_len -= 1
+        if c_path_len <= 0:
+            break
+
+        c_path += 1
+        if c_path[0].href is not NULL:
+            c_href = c_path[0].href # otherwise: keep parent namespace
+        c_name = tree.xmlDictExists(c_node.doc.dict, c_path[0].name, -1)
+        if c_name is NULL:
+            c_name = c_path[0].name
+            c_node = NULL
+            break
+        c_index = c_path[0].index
+        c_node = c_node.last if c_index < 0 else c_node.children
+        c_node = _findFollowingSibling(c_node, c_href, c_name, c_index)
+
+    if c_node is not NULL:
+        return cetree.elementFactory(root._doc, c_node)
+    elif default_value is not _NO_DEFAULT:
+        return default_value
+    else:
+        tag = cetree.namespacedNameFromNsName(c_href, c_name)
+        raise AttributeError, f"no such child: {tag}"
+
+
+cdef _create_object_path(_Element root, _ObjectPath* c_path,
+                         Py_ssize_t c_path_len, int replace, value):
+    """Follow the path to find the target element, build the missing children
+    as needed and set the target element to 'value'.  If replace is true, an
+    existing value is replaced, otherwise the new value is added.
+    """
+    cdef _Element child
+    cdef tree.xmlNode* c_node
+    cdef tree.xmlNode* c_child
+    cdef Py_ssize_t c_index
+    if c_path_len == 1:
+        raise TypeError, "cannot update root node"
+
+    c_node = root._c_node
+    c_name = c_path[0].name
+    c_href = c_path[0].href
+    if c_href is NULL or c_href[0] == c'\0':
+        c_href = tree._getNs(c_node)
+    if not cetree.tagMatches(c_node, c_href, c_name):
+        raise ValueError(
+            f"root element does not match: need {cetree.namespacedNameFromNsName(c_href, c_name)}, got {root.tag}")
+
+    while c_path_len > 1:
+        c_path_len -= 1
+        c_path += 1
+        if c_path[0].href is not NULL:
+            c_href = c_path[0].href # otherwise: keep parent namespace
+        c_index = c_path[0].index
+        c_name = tree.xmlDictExists(c_node.doc.dict, c_path[0].name, -1)
+        if c_name is NULL:
+            c_name = c_path[0].name
+            c_child = NULL
+        else:
+            c_child = c_node.last if c_index < 0 else c_node.children
+            c_child = _findFollowingSibling(c_child, c_href, c_name, c_index)
+
+        if c_child is not NULL:
+            c_node = c_child
+        elif c_index != 0:
+            raise TypeError, "creating indexed path attributes is not supported"
+        elif c_path_len == 1:
+            _appendValue(cetree.elementFactory(root._doc, c_node),
+                         cetree.namespacedNameFromNsName(c_href, c_name),
+                         value)
+            return
+        else:
+            child = cetree.makeSubElement(
+                cetree.elementFactory(root._doc, c_node),
+                cetree.namespacedNameFromNsName(c_href, c_name),
+                None, None, None, None)
+            c_node = child._c_node
+
+    # if we get here, the entire path was already there
+    if replace:
+        element = cetree.elementFactory(root._doc, c_node)
+        _replaceElement(element, value)
+    else:
+        _appendValue(cetree.elementFactory(root._doc, c_node.parent),
+                     cetree.namespacedName(c_node), value)
+
+
+cdef list _build_descendant_paths(tree.xmlNode* c_node, prefix_string):
+    """Returns a list of all descendant paths.
+    """
+    cdef list path, path_list
+    tag = cetree.namespacedName(c_node)
+    if prefix_string:
+        if prefix_string[-1] != '.':
+            prefix_string += '.'
+        prefix_string = prefix_string + tag
+    else:
+        prefix_string = tag
+    path = [prefix_string]
+    path_list = []
+    _recursive_build_descendant_paths(c_node, path, path_list)
+    return path_list
+
+
+cdef int _recursive_build_descendant_paths(tree.xmlNode* c_node,
+                                           list path, list path_list) except -1:
+    """Fills the list 'path_list' with all descendant paths, initial prefix
+    being in the list 'path'.
+    """
+    cdef tree.xmlNode* c_child
+    tags = {}
+    path_list.append('.'.join(path))
+    c_href = tree._getNs(c_node)
+    c_child = c_node.children
+    while c_child is not NULL:
+        while c_child.type != tree.XML_ELEMENT_NODE:
+            c_child = c_child.next
+            if c_child is NULL:
+                return 0
+        if c_href is tree._getNs(c_child):
+            tag = pyunicode(c_child.name)
+        elif c_href is not NULL and tree._getNs(c_child) is NULL:
+            # special case: parent has namespace, child does not
+            tag = '{}' + pyunicode(c_child.name)
+        else:
+            tag = cetree.namespacedName(c_child)
+        count = tags.get(tag)
+        if count is None:
+            tags[tag] = 1
+        else:
+            tags[tag] = count + 1
+            tag += f'[{count}]'
+        path.append(tag)
+        _recursive_build_descendant_paths(c_child, path, path_list)
+        del path[-1]
+        c_child = c_child.next
+    return 0
diff --git a/.venv/lib/python3.12/site-packages/lxml/parser.pxi b/.venv/lib/python3.12/site-packages/lxml/parser.pxi
new file mode 100644
index 00000000..70337d87
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/parser.pxi
@@ -0,0 +1,2000 @@
+# Parsers for XML and HTML
+
+from lxml.includes cimport xmlparser
+from lxml.includes cimport htmlparser
+
+
+class ParseError(LxmlSyntaxError):
+    """Syntax error while parsing an XML document.
+
+    For compatibility with ElementTree 1.3 and later.
+    """
+    def __init__(self, message, code, line, column, filename=None):
+        super(_ParseError, self).__init__(message)
+        self.lineno, self.offset = (line, column - 1)
+        self.code = code
+        self.filename = filename
+
+    @property
+    def position(self):
+        return self.lineno, self.offset + 1
+
+    @position.setter
+    def position(self, new_pos):
+        self.lineno, column = new_pos
+        self.offset = column - 1
+
+cdef object _ParseError = ParseError
+
+
+class XMLSyntaxError(ParseError):
+    """Syntax error while parsing an XML document.
+    """
+
+cdef class ParserError(LxmlError):
+    """Internal lxml parser error.
+    """
+
+
+@cython.final
+@cython.internal
+cdef class _ParserDictionaryContext:
+    # Global parser context to share the string dictionary.
+    #
+    # This class is a delegate singleton!
+    #
+    # It creates _ParserDictionaryContext objects for each thread to keep thread state,
+    # but those must never be used directly.  Always stick to using the static
+    # __GLOBAL_PARSER_CONTEXT as defined below the class.
+    #
+
+    cdef tree.xmlDict* _c_dict
+    cdef _BaseParser _default_parser
+    cdef list _implied_parser_contexts
+
+    def __cinit__(self):
+        self._c_dict = NULL
+        self._implied_parser_contexts = []
+
+    def __dealloc__(self):
+        if self._c_dict is not NULL:
+            xmlparser.xmlDictFree(self._c_dict)
+
+    cdef int initMainParserContext(self) except -1:
+        """Put the global context into the thread dictionary of the main
+        thread.  To be called once and only in the main thread."""
+        thread_dict = python.PyThreadState_GetDict()
+        if thread_dict is not NULL:
+            (<dict>thread_dict)["_ParserDictionaryContext"] = self
+
+    cdef _ParserDictionaryContext _findThreadParserContext(self):
+        "Find (or create) the _ParserDictionaryContext object for the current thread"
+        cdef _ParserDictionaryContext context
+        thread_dict = python.PyThreadState_GetDict()
+        if thread_dict is NULL:
+            return self
+        d = <dict>thread_dict
+        result = python.PyDict_GetItem(d, "_ParserDictionaryContext")
+        if result is not NULL:
+            return <object>result
+        context = <_ParserDictionaryContext>_ParserDictionaryContext.__new__(_ParserDictionaryContext)
+        d["_ParserDictionaryContext"] = context
+        return context
+
+    cdef int setDefaultParser(self, _BaseParser parser) except -1:
+        "Set the default parser for the current thread"
+        cdef _ParserDictionaryContext context
+        context = self._findThreadParserContext()
+        context._default_parser = parser
+
+    cdef _BaseParser getDefaultParser(self):
+        "Return (or create) the default parser of the current thread"
+        cdef _ParserDictionaryContext context
+        context = self._findThreadParserContext()
+        if context._default_parser is None:
+            if self._default_parser is None:
+                self._default_parser = __DEFAULT_XML_PARSER._copy()
+            if context is not self:
+                context._default_parser = self._default_parser._copy()
+        return context._default_parser
+
+    cdef tree.xmlDict* _getThreadDict(self, tree.xmlDict* default):
+        "Return the thread-local dict or create a new one if necessary."
+        cdef _ParserDictionaryContext context
+        context = self._findThreadParserContext()
+        if context._c_dict is NULL:
+            # thread dict not yet set up => use default or create a new one
+            if default is not NULL:
+                context._c_dict = default
+                xmlparser.xmlDictReference(default)
+                return default
+            if self._c_dict is NULL:
+                self._c_dict = xmlparser.xmlDictCreate()
+            if context is not self:
+                context._c_dict = xmlparser.xmlDictCreateSub(self._c_dict)
+        return context._c_dict
+
+    cdef int initThreadDictRef(self, tree.xmlDict** c_dict_ref) except -1:
+        c_dict = c_dict_ref[0]
+        c_thread_dict = self._getThreadDict(c_dict)
+        if c_dict is c_thread_dict:
+            return 0
+        if c_dict is not NULL:
+            xmlparser.xmlDictFree(c_dict)
+        c_dict_ref[0] = c_thread_dict
+        xmlparser.xmlDictReference(c_thread_dict)
+
+    cdef int initParserDict(self, xmlparser.xmlParserCtxt* pctxt) except -1:
+        "Assure we always use the same string dictionary."
+        self.initThreadDictRef(&pctxt.dict)
+        pctxt.dictNames = 1
+
+    cdef int initXPathParserDict(self, xpath.xmlXPathContext* pctxt) except -1:
+        "Assure we always use the same string dictionary."
+        self.initThreadDictRef(&pctxt.dict)
+
+    cdef int initDocDict(self, xmlDoc* result) except -1:
+        "Store dict of last object parsed if no shared dict yet"
+        # XXX We also free the result dict here if there already was one.
+        # This case should only occur for new documents with empty dicts,
+        # otherwise we'd free data that's in use => segfault
+        self.initThreadDictRef(&result.dict)
+
+    cdef _ParserContext findImpliedContext(self):
+        """Return any current implied xml parser context for the current
+        thread.  This is used when the resolver functions are called
+        with an xmlParserCtxt that was generated from within libxml2
+        (i.e. without a _ParserContext) - which happens when parsing
+        schema and xinclude external references."""
+        cdef _ParserDictionaryContext context
+        cdef _ParserContext implied_context
+
+        # see if we have a current implied parser
+        context = self._findThreadParserContext()
+        if context._implied_parser_contexts:
+            implied_context = context._implied_parser_contexts[-1]
+            return implied_context
+        return None
+
+    cdef int pushImpliedContextFromParser(self, _BaseParser parser) except -1:
+        "Push a new implied context object taken from the parser."
+        if parser is not None:
+            self.pushImpliedContext(parser._getParserContext())
+        else:
+            self.pushImpliedContext(None)
+
+    cdef int pushImpliedContext(self, _ParserContext parser_context) except -1:
+        "Push a new implied context object."
+        cdef _ParserDictionaryContext context
+        context = self._findThreadParserContext()
+        context._implied_parser_contexts.append(parser_context)
+
+    cdef int popImpliedContext(self) except -1:
+        "Pop the current implied context object."
+        cdef _ParserDictionaryContext context
+        context = self._findThreadParserContext()
+        context._implied_parser_contexts.pop()
+
+cdef _ParserDictionaryContext __GLOBAL_PARSER_CONTEXT = _ParserDictionaryContext()
+__GLOBAL_PARSER_CONTEXT.initMainParserContext()
+
+############################################################
+## support for Python unicode I/O
+############################################################
+
+# name of Python Py_UNICODE encoding as known to libxml2
+cdef const_char* _PY_UNICODE_ENCODING = NULL
+
+cdef int _setupPythonUnicode() except -1:
+    """Sets _PY_UNICODE_ENCODING to the internal encoding name of Python unicode
+    strings if libxml2 supports reading native Python unicode.  This depends
+    on iconv and the local Python installation, so we simply check if we find
+    a matching encoding handler.
+    """
+    cdef tree.xmlCharEncodingHandler* enchandler
+    cdef Py_ssize_t l
+    cdef const_char* enc
+    cdef Py_UNICODE *uchars = [c'<', c't', c'e', c's', c't', c'/', c'>']
+    cdef const_xmlChar* buffer = <const_xmlChar*>uchars
+    # apparently, libxml2 can't detect UTF-16 on some systems
+    if (buffer[0] == c'<' and buffer[1] == c'\0' and
+            buffer[2] == c't' and buffer[3] == c'\0'):
+        enc = "UTF-16LE"
+    elif (buffer[0] == c'\0' and buffer[1] == c'<' and
+            buffer[2] == c'\0' and buffer[3] == c't'):
+        enc = "UTF-16BE"
+    else:
+        # let libxml2 give it a try
+        enc = _findEncodingName(buffer, sizeof(Py_UNICODE) * 7)
+        if enc is NULL:
+            # not my fault, it's YOUR broken system :)
+            return 0
+    enchandler = tree.xmlFindCharEncodingHandler(enc)
+    if enchandler is not NULL:
+        global _PY_UNICODE_ENCODING
+        tree.xmlCharEncCloseFunc(enchandler)
+        _PY_UNICODE_ENCODING = enc
+    return 0
+
+cdef const_char* _findEncodingName(const_xmlChar* buffer, int size):
+    "Work around bug in libxml2: find iconv name of encoding on our own."
+    cdef tree.xmlCharEncoding enc
+    enc = tree.xmlDetectCharEncoding(buffer, size)
+    if enc == tree.XML_CHAR_ENCODING_UTF16LE:
+        if size >= 4 and (buffer[0] == <const_xmlChar> b'\xFF' and
+                          buffer[1] == <const_xmlChar> b'\xFE' and
+                          buffer[2] == 0 and buffer[3] == 0):
+            return "UTF-32LE"  # according to BOM
+        else:
+            return "UTF-16LE"
+    elif enc == tree.XML_CHAR_ENCODING_UTF16BE:
+        return "UTF-16BE"
+    elif enc == tree.XML_CHAR_ENCODING_UCS4LE:
+        return "UCS-4LE"
+    elif enc == tree.XML_CHAR_ENCODING_UCS4BE:
+        return "UCS-4BE"
+    elif enc == tree.XML_CHAR_ENCODING_NONE:
+        return NULL
+    else:
+        # returns a constant char*, no need to free it
+        return tree.xmlGetCharEncodingName(enc)
+
+# Python 3.12 removed support for "Py_UNICODE".
+if python.PY_VERSION_HEX < 0x030C0000:
+    _setupPythonUnicode()
+
+
+cdef unicode _find_PyUCS4EncodingName():
+    """
+    Find a suitable encoding for Py_UCS4 PyUnicode strings in libxml2.
+    """
+    ustring = "<xml>\U0001F92A</xml>"
+    cdef const xmlChar* buffer = <const xmlChar*> python.PyUnicode_DATA(ustring)
+    cdef Py_ssize_t py_buffer_len = python.PyUnicode_GET_LENGTH(ustring)
+
+    encoding_name = ''
+    cdef tree.xmlCharEncoding enc = tree.xmlDetectCharEncoding(buffer, py_buffer_len)
+    enchandler = tree.xmlGetCharEncodingHandler(enc)
+    if enchandler is not NULL:
+        try:
+            if enchandler.name:
+                encoding_name = enchandler.name.decode('UTF-8')
+        finally:
+            tree.xmlCharEncCloseFunc(enchandler)
+    else:
+        c_name = tree.xmlGetCharEncodingName(enc)
+        if c_name:
+            encoding_name = c_name.decode('UTF-8')
+
+
+    if encoding_name and not encoding_name.endswith('LE') and not encoding_name.endswith('BE'):
+        encoding_name += 'BE' if python.PY_BIG_ENDIAN else 'LE'
+    return encoding_name or None
+
+_pyucs4_encoding_name = _find_PyUCS4EncodingName()
+
+
+############################################################
+## support for file-like objects
+############################################################
+
+@cython.final
+@cython.internal
+cdef class _FileReaderContext:
+    cdef object _filelike
+    cdef object _encoding
+    cdef object _url
+    cdef object _bytes
+    cdef _ExceptionContext _exc_context
+    cdef Py_ssize_t _bytes_read
+    cdef char* _c_url
+    cdef bint _close_file_after_read
+
+    def __cinit__(self, filelike, exc_context not None, url, encoding=None, bint close_file=False):
+        self._exc_context = exc_context
+        self._filelike = filelike
+        self._close_file_after_read = close_file
+        self._encoding = encoding
+        if url is None:
+            self._c_url = NULL
+        else:
+            url = _encodeFilename(url)
+            self._c_url = _cstr(url)
+        self._url = url
+        self._bytes  = b''
+        self._bytes_read = 0
+
+    cdef _close_file(self):
+        if self._filelike is None or not self._close_file_after_read:
+            return
+        try:
+            close = self._filelike.close
+        except AttributeError:
+            close = None
+        finally:
+            self._filelike = None
+        if close is not None:
+            close()
+
+    cdef xmlparser.xmlParserInputBuffer* _createParserInputBuffer(self) noexcept:
+        cdef xmlparser.xmlParserInputBuffer* c_buffer = xmlparser.xmlAllocParserInputBuffer(0)
+        if c_buffer:
+            c_buffer.readcallback  = _readFilelikeParser
+            c_buffer.context = <python.PyObject*> self
+        return c_buffer
+
+    cdef xmlparser.xmlParserInput* _createParserInput(
+            self, xmlparser.xmlParserCtxt* ctxt) noexcept:
+        cdef xmlparser.xmlParserInputBuffer* c_buffer = self._createParserInputBuffer()
+        if not c_buffer:
+            return NULL
+        return xmlparser.xmlNewIOInputStream(ctxt, c_buffer, 0)
+
+    cdef tree.xmlDtd* _readDtd(self) noexcept:
+        cdef xmlparser.xmlParserInputBuffer* c_buffer = self._createParserInputBuffer()
+        if not c_buffer:
+            return NULL
+        with nogil:
+            return xmlparser.xmlIOParseDTD(NULL, c_buffer, 0)
+
+    cdef xmlDoc* _readDoc(self, xmlparser.xmlParserCtxt* ctxt, int options) noexcept:
+        cdef xmlDoc* result
+        cdef void* c_callback_context = <python.PyObject*> self
+        cdef char* c_encoding = _cstr(self._encoding) if self._encoding is not None else NULL
+
+        orig_options = ctxt.options
+        with nogil:
+            if ctxt.html:
+                result = htmlparser.htmlCtxtReadIO(
+                        ctxt, _readFilelikeParser, NULL, c_callback_context,
+                        self._c_url, c_encoding, options)
+                if result is not NULL:
+                    if _fixHtmlDictNames(ctxt.dict, result) < 0:
+                        tree.xmlFreeDoc(result)
+                        result = NULL
+            else:
+                result = xmlparser.xmlCtxtReadIO(
+                    ctxt, _readFilelikeParser, NULL, c_callback_context,
+                    self._c_url, c_encoding, options)
+        ctxt.options = orig_options # work around libxml2 problem
+
+        try:
+            self._close_file()
+        except:
+            self._exc_context._store_raised()
+        finally:
+            return result  # swallow any exceptions
+
+    cdef int copyToBuffer(self, char* c_buffer, int c_requested) noexcept:
+        cdef int c_byte_count = 0
+        cdef char* c_start
+        cdef Py_ssize_t byte_count, remaining
+        if self._bytes_read < 0:
+            return 0
+        try:
+            byte_count = python.PyBytes_GET_SIZE(self._bytes)
+            remaining  = byte_count - self._bytes_read
+            while c_requested > remaining:
+                c_start = _cstr(self._bytes) + self._bytes_read
+                cstring_h.memcpy(c_buffer, c_start, remaining)
+                c_byte_count += remaining
+                c_buffer += remaining
+                c_requested -= remaining
+
+                self._bytes = self._filelike.read(c_requested)
+                if not isinstance(self._bytes, bytes):
+                    if isinstance(self._bytes, unicode):
+                        if self._encoding is None:
+                            self._bytes = (<unicode>self._bytes).encode('utf8')
+                        else:
+                            self._bytes = python.PyUnicode_AsEncodedString(
+                                self._bytes, _cstr(self._encoding), NULL)
+                    else:
+                        self._close_file()
+                        raise TypeError, \
+                            "reading from file-like objects must return byte strings or unicode strings"
+
+                remaining = python.PyBytes_GET_SIZE(self._bytes)
+                if remaining == 0:
+                    self._bytes_read = -1
+                    self._close_file()
+                    return c_byte_count
+                self._bytes_read = 0
+
+            if c_requested > 0:
+                c_start = _cstr(self._bytes) + self._bytes_read
+                cstring_h.memcpy(c_buffer, c_start, c_requested)
+                c_byte_count += c_requested
+                self._bytes_read += c_requested
+        except:
+            c_byte_count = -1
+            self._exc_context._store_raised()
+            try:
+                self._close_file()
+            except:
+                self._exc_context._store_raised()
+        finally:
+            return c_byte_count  # swallow any exceptions
+
+cdef int _readFilelikeParser(void* ctxt, char* c_buffer, int c_size) noexcept with gil:
+    return (<_FileReaderContext>ctxt).copyToBuffer(c_buffer, c_size)
+
+cdef int _readFileParser(void* ctxt, char* c_buffer, int c_size) noexcept nogil:
+    return stdio.fread(c_buffer, 1,  c_size, <stdio.FILE*>ctxt)
+
+############################################################
+## support for custom document loaders
+############################################################
+
+cdef xmlparser.xmlParserInput* _local_resolver(const_char* c_url, const_char* c_pubid,
+                                               xmlparser.xmlParserCtxt* c_context) noexcept with gil:
+    cdef _ResolverContext context
+    cdef xmlparser.xmlParserInput* c_input
+    cdef _InputDocument doc_ref
+    cdef _FileReaderContext file_context
+    # if there is no _ParserContext associated with the xmlParserCtxt
+    # passed, check to see if the thread state object has an implied
+    # context.
+    if c_context._private is not NULL:
+        context = <_ResolverContext>c_context._private
+    else:
+        context = __GLOBAL_PARSER_CONTEXT.findImpliedContext()
+
+    if context is None:
+        if __DEFAULT_ENTITY_LOADER is NULL:
+            return NULL
+        with nogil:
+            # free the GIL as we might do serious I/O here (e.g. HTTP)
+            c_input = __DEFAULT_ENTITY_LOADER(c_url, c_pubid, c_context)
+        return c_input
+
+    try:
+        if c_url is NULL:
+            url = None
+        else:
+            # parsing a related document (DTD etc.) => UTF-8 encoded URL?
+            url = _decodeFilename(<const_xmlChar*>c_url)
+        if c_pubid is NULL:
+            pubid = None
+        else:
+            pubid = funicode(<const_xmlChar*>c_pubid) # always UTF-8
+
+        doc_ref = context._resolvers.resolve(url, pubid, context)
+    except:
+        context._store_raised()
+        return NULL
+
+    if doc_ref is not None:
+        if doc_ref._type == PARSER_DATA_STRING:
+            data = doc_ref._data_bytes
+            filename = doc_ref._filename
+            if not filename:
+                filename = None
+            elif not isinstance(filename, bytes):
+                # most likely a text URL
+                filename = filename.encode('utf8')
+                if not isinstance(filename, bytes):
+                    filename = None
+
+            c_input = xmlparser.xmlNewInputStream(c_context)
+            if c_input is not NULL:
+                if filename is not None:
+                    c_input.filename = <char *>tree.xmlStrdup(_xcstr(filename))
+                c_input.base = _xcstr(data)
+                c_input.length = python.PyBytes_GET_SIZE(data)
+                c_input.cur = c_input.base
+                c_input.end = c_input.base + c_input.length
+        elif doc_ref._type == PARSER_DATA_FILENAME:
+            data = None
+            c_filename = _cstr(doc_ref._filename)
+            with nogil:
+                # free the GIL as we might do serious I/O here
+                c_input = xmlparser.xmlNewInputFromFile(
+                    c_context, c_filename)
+        elif doc_ref._type == PARSER_DATA_FILE:
+            file_context = _FileReaderContext(doc_ref._file, context, url,
+                                              None, doc_ref._close_file)
+            c_input = file_context._createParserInput(c_context)
+            data = file_context
+        else:
+            data = None
+            c_input = NULL
+
+        if data is not None:
+            context._storage.add(data)
+        if c_input is not NULL:
+            return c_input
+
+    if __DEFAULT_ENTITY_LOADER is NULL:
+        return NULL
+
+    with nogil:
+        # free the GIL as we might do serious I/O here (e.g. HTTP)
+        c_input = __DEFAULT_ENTITY_LOADER(c_url, c_pubid, c_context)
+    return c_input
+
+cdef xmlparser.xmlExternalEntityLoader __DEFAULT_ENTITY_LOADER
+__DEFAULT_ENTITY_LOADER = xmlparser.xmlGetExternalEntityLoader()
+
+
+cdef xmlparser.xmlExternalEntityLoader _register_document_loader() noexcept nogil:
+    cdef xmlparser.xmlExternalEntityLoader old = xmlparser.xmlGetExternalEntityLoader()
+    xmlparser.xmlSetExternalEntityLoader(<xmlparser.xmlExternalEntityLoader>_local_resolver)
+    return old
+
+cdef void _reset_document_loader(xmlparser.xmlExternalEntityLoader old) noexcept nogil:
+    xmlparser.xmlSetExternalEntityLoader(old)
+
+
+############################################################
+## Parsers
+############################################################
+
+@cython.no_gc_clear  # May have to call "self._validator.disconnect()" on dealloc.
+@cython.internal
+cdef class _ParserContext(_ResolverContext):
+    cdef _ErrorLog _error_log
+    cdef _ParserSchemaValidationContext _validator
+    cdef xmlparser.xmlParserCtxt* _c_ctxt
+    cdef xmlparser.xmlExternalEntityLoader _orig_loader
+    cdef python.PyThread_type_lock _lock
+    cdef _Document _doc
+    cdef bint _collect_ids
+
+    def __cinit__(self):
+        self._c_ctxt = NULL
+        self._collect_ids = True
+        if not config.ENABLE_THREADING:
+            self._lock = NULL
+        else:
+            self._lock = python.PyThread_allocate_lock()
+        self._error_log = _ErrorLog()
+
+    def __dealloc__(self):
+        if config.ENABLE_THREADING and self._lock is not NULL:
+            python.PyThread_free_lock(self._lock)
+            self._lock = NULL
+        if self._c_ctxt is not NULL:
+            if <void*>self._validator is not NULL and self._validator is not None:
+                # If the parser was not closed correctly (e.g. interrupted iterparse()),
+                # and the schema validator wasn't freed and cleaned up yet, the libxml2 SAX
+                # validator plug might still be in place, which will make xmlFreeParserCtxt()
+                # crash when trying to xmlFree() a static SAX handler.
+                # Thus, make sure we disconnect the handler interceptor here at the latest.
+                self._validator.disconnect()
+            xmlparser.xmlFreeParserCtxt(self._c_ctxt)
+
+    cdef _ParserContext _copy(self):
+        cdef _ParserContext context
+        context = self.__class__()
+        context._collect_ids = self._collect_ids
+        context._validator = self._validator.copy()
+        _initParserContext(context, self._resolvers._copy(), NULL)
+        return context
+
+    cdef void _initParserContext(self, xmlparser.xmlParserCtxt* c_ctxt) noexcept:
+        self._c_ctxt = c_ctxt
+        c_ctxt._private = <void*>self
+
+    cdef void _resetParserContext(self) noexcept:
+        if self._c_ctxt is not NULL:
+            if self._c_ctxt.html:
+                htmlparser.htmlCtxtReset(self._c_ctxt)
+                self._c_ctxt.disableSAX = 0 # work around bug in libxml2
+            else:
+                xmlparser.xmlClearParserCtxt(self._c_ctxt)
+                # work around bug in libxml2 [2.9.10 .. 2.9.14]:
+                # https://gitlab.gnome.org/GNOME/libxml2/-/issues/378
+                self._c_ctxt.nsNr = 0
+
+    cdef int prepare(self, bint set_document_loader=True) except -1:
+        cdef int result
+        if config.ENABLE_THREADING and self._lock is not NULL:
+            with nogil:
+                result = python.PyThread_acquire_lock(
+                    self._lock, python.WAIT_LOCK)
+            if result == 0:
+                raise ParserError, "parser locking failed"
+        self._error_log.clear()
+        self._doc = None
+        # Need a cast here because older libxml2 releases do not use 'const' in the functype.
+        self._c_ctxt.sax.serror = <xmlerror.xmlStructuredErrorFunc> _receiveParserError
+        self._orig_loader = _register_document_loader() if set_document_loader else NULL
+        if self._validator is not None:
+            self._validator.connect(self._c_ctxt, self._error_log)
+        return 0
+
+    cdef int cleanup(self) except -1:
+        if self._orig_loader is not NULL:
+            _reset_document_loader(self._orig_loader)
+        try:
+            if self._validator is not None:
+                self._validator.disconnect()
+            self._resetParserContext()
+            self.clear()
+            self._doc = None
+            self._c_ctxt.sax.serror = NULL
+        finally:
+            if config.ENABLE_THREADING and self._lock is not NULL:
+                python.PyThread_release_lock(self._lock)
+        return 0
+
+    cdef object _handleParseResult(self, _BaseParser parser,
+                                   xmlDoc* result, filename):
+        c_doc = self._handleParseResultDoc(parser, result, filename)
+        if self._doc is not None and self._doc._c_doc is c_doc:
+            return self._doc
+        else:
+            return _documentFactory(c_doc, parser)
+
+    cdef xmlDoc* _handleParseResultDoc(self, _BaseParser parser,
+                                       xmlDoc* result, filename) except NULL:
+        recover = parser._parse_options & xmlparser.XML_PARSE_RECOVER
+        return _handleParseResult(self, self._c_ctxt, result,
+                                  filename, recover,
+                                  free_doc=self._doc is None)
+
+cdef _initParserContext(_ParserContext context,
+                        _ResolverRegistry resolvers,
+                        xmlparser.xmlParserCtxt* c_ctxt):
+    _initResolverContext(context, resolvers)
+    if c_ctxt is not NULL:
+        context._initParserContext(c_ctxt)
+
+cdef void _forwardParserError(xmlparser.xmlParserCtxt* _parser_context, const xmlerror.xmlError* error) noexcept with gil:
+    (<_ParserContext>_parser_context._private)._error_log._receive(error)
+
+cdef void _receiveParserError(void* c_context, const xmlerror.xmlError* error) noexcept nogil:
+    if __DEBUG:
+        if c_context is NULL or (<xmlparser.xmlParserCtxt*>c_context)._private is NULL:
+            _forwardError(NULL, error)
+        else:
+            _forwardParserError(<xmlparser.xmlParserCtxt*>c_context, error)
+
+cdef int _raiseParseError(xmlparser.xmlParserCtxt* ctxt, filename,
+                          _ErrorLog error_log) except -1:
+    if filename is not None and \
+           ctxt.lastError.domain == xmlerror.XML_FROM_IO:
+        if isinstance(filename, bytes):
+            filename = _decodeFilenameWithLength(
+                <bytes>filename, len(<bytes>filename))
+        if ctxt.lastError.message is not NULL:
+            try:
+                message = ctxt.lastError.message.decode('utf-8')
+            except UnicodeDecodeError:
+                # the filename may be in there => play it safe
+                message = ctxt.lastError.message.decode('iso8859-1')
+            message = f"Error reading file '{filename}': {message.strip()}"
+        else:
+            message = f"Error reading '{filename}'"
+        raise IOError, message
+    elif error_log:
+        raise error_log._buildParseException(
+            XMLSyntaxError, "Document is not well formed")
+    elif ctxt.lastError.message is not NULL:
+        message = ctxt.lastError.message.strip()
+        code = ctxt.lastError.code
+        line = ctxt.lastError.line
+        column = ctxt.lastError.int2
+        if ctxt.lastError.line > 0:
+            message = f"line {line}: {message}"
+        raise XMLSyntaxError(message, code, line, column, filename)
+    else:
+        raise XMLSyntaxError(None, xmlerror.XML_ERR_INTERNAL_ERROR, 0, 0,
+                             filename)
+
+cdef xmlDoc* _handleParseResult(_ParserContext context,
+                                xmlparser.xmlParserCtxt* c_ctxt,
+                                xmlDoc* result, filename,
+                                bint recover, bint free_doc) except NULL:
+    cdef bint well_formed
+    if result is not NULL:
+        __GLOBAL_PARSER_CONTEXT.initDocDict(result)
+
+    if c_ctxt.myDoc is not NULL:
+        if c_ctxt.myDoc is not result:
+            __GLOBAL_PARSER_CONTEXT.initDocDict(c_ctxt.myDoc)
+            tree.xmlFreeDoc(c_ctxt.myDoc)
+        c_ctxt.myDoc = NULL
+
+    if result is not NULL:
+        if (context._validator is not None and
+                not context._validator.isvalid()):
+            well_formed = 0  # actually not 'valid', but anyway ...
+        elif (not c_ctxt.wellFormed and not c_ctxt.html and
+                c_ctxt.charset == tree.XML_CHAR_ENCODING_8859_1 and
+                [1 for error in context._error_log
+                 if error.type == ErrorTypes.ERR_INVALID_CHAR]):
+            # An encoding error occurred and libxml2 switched from UTF-8
+            # input to (undecoded) Latin-1, at some arbitrary point in the
+            # document.  Better raise an error than allowing for a broken
+            # tree with mixed encodings. This is fixed in libxml2 2.12.
+            well_formed = 0
+        elif recover or (c_ctxt.wellFormed and
+                         c_ctxt.lastError.level < xmlerror.XML_ERR_ERROR):
+            well_formed = 1
+        elif not c_ctxt.replaceEntities and not c_ctxt.validate \
+                 and context is not None:
+            # in this mode, we ignore errors about undefined entities
+            for error in context._error_log.filter_from_errors():
+                if error.type != ErrorTypes.WAR_UNDECLARED_ENTITY and \
+                       error.type != ErrorTypes.ERR_UNDECLARED_ENTITY:
+                    well_formed = 0
+                    break
+            else:
+                well_formed = 1
+        else:
+            well_formed = 0
+
+        if not well_formed:
+            if free_doc:
+                tree.xmlFreeDoc(result)
+            result = NULL
+
+    if context is not None and context._has_raised():
+        if result is not NULL:
+            if free_doc:
+                tree.xmlFreeDoc(result)
+            result = NULL
+        context._raise_if_stored()
+
+    if result is NULL:
+        if context is not None:
+            _raiseParseError(c_ctxt, filename, context._error_log)
+        else:
+            _raiseParseError(c_ctxt, filename, None)
+    else:
+        if result.URL is NULL and filename is not None:
+            result.URL = tree.xmlStrdup(_xcstr(filename))
+        if result.encoding is NULL:
+            result.encoding = tree.xmlStrdup(<unsigned char*>"UTF-8")
+
+    if context._validator is not None and \
+           context._validator._add_default_attributes:
+        # we currently need to do this here as libxml2 does not
+        # support inserting default attributes during parse-time
+        # validation
+        context._validator.inject_default_attributes(result)
+
+    return result
+
+cdef int _fixHtmlDictNames(tree.xmlDict* c_dict, xmlDoc* c_doc) noexcept nogil:
+    cdef xmlNode* c_node
+    if c_doc is NULL:
+        return 0
+    c_node = c_doc.children
+    tree.BEGIN_FOR_EACH_ELEMENT_FROM(<xmlNode*>c_doc, c_node, 1)
+    if c_node.type == tree.XML_ELEMENT_NODE:
+        if _fixHtmlDictNodeNames(c_dict, c_node) < 0:
+            return -1
+    tree.END_FOR_EACH_ELEMENT_FROM(c_node)
+    return 0
+
+cdef int _fixHtmlDictSubtreeNames(tree.xmlDict* c_dict, xmlDoc* c_doc,
+                                  xmlNode* c_start_node) noexcept nogil:
+    """
+    Move names to the dict, iterating in document order, starting at
+    c_start_node. This is used in incremental parsing after each chunk.
+    """
+    cdef xmlNode* c_node
+    if not c_doc:
+        return 0
+    if not c_start_node:
+        return _fixHtmlDictNames(c_dict, c_doc)
+    c_node = c_start_node
+    tree.BEGIN_FOR_EACH_ELEMENT_FROM(<xmlNode*>c_doc, c_node, 1)
+    if c_node.type == tree.XML_ELEMENT_NODE:
+        if _fixHtmlDictNodeNames(c_dict, c_node) < 0:
+            return -1
+    tree.END_FOR_EACH_ELEMENT_FROM(c_node)
+    return 0
+
+cdef inline int _fixHtmlDictNodeNames(tree.xmlDict* c_dict,
+                                      xmlNode* c_node) noexcept nogil:
+    cdef xmlNode* c_attr
+    c_name = tree.xmlDictLookup(c_dict, c_node.name, -1)
+    if c_name is NULL:
+        return -1
+    if c_name is not c_node.name:
+        tree.xmlFree(<char*>c_node.name)
+        c_node.name = c_name
+    c_attr = <xmlNode*>c_node.properties
+    while c_attr is not NULL:
+        c_name = tree.xmlDictLookup(c_dict, c_attr.name, -1)
+        if c_name is NULL:
+            return -1
+        if c_name is not c_attr.name:
+            tree.xmlFree(<char*>c_attr.name)
+            c_attr.name = c_name
+        c_attr = c_attr.next
+    return 0
+
+
+@cython.internal
+cdef class _BaseParser:
+    cdef ElementClassLookup _class_lookup
+    cdef _ResolverRegistry _resolvers
+    cdef _ParserContext _parser_context
+    cdef _ParserContext _push_parser_context
+    cdef int _parse_options
+    cdef bint _for_html
+    cdef bint _remove_comments
+    cdef bint _remove_pis
+    cdef bint _strip_cdata
+    cdef bint _collect_ids
+    cdef bint _resolve_external_entities
+    cdef XMLSchema _schema
+    cdef bytes _filename
+    cdef readonly object target
+    cdef object _default_encoding
+    cdef tuple _events_to_collect  # (event_types, tag)
+
+    def __init__(self, int parse_options, bint for_html, XMLSchema schema,
+                 remove_comments, remove_pis, strip_cdata, collect_ids,
+                 target, encoding, bint resolve_external_entities=True):
+        cdef tree.xmlCharEncodingHandler* enchandler
+        cdef int c_encoding
+        if not isinstance(self, (XMLParser, HTMLParser)):
+            raise TypeError, "This class cannot be instantiated"
+
+        self._parse_options = parse_options
+        self.target = target
+        self._for_html = for_html
+        self._remove_comments = remove_comments
+        self._remove_pis = remove_pis
+        self._strip_cdata = strip_cdata
+        self._collect_ids = collect_ids
+        self._resolve_external_entities = resolve_external_entities
+        self._schema = schema
+
+        self._resolvers = _ResolverRegistry()
+
+        if encoding is None:
+            self._default_encoding = None
+        else:
+            encoding = _utf8(encoding)
+            enchandler = tree.xmlFindCharEncodingHandler(_cstr(encoding))
+            if enchandler is NULL:
+                raise LookupError, f"unknown encoding: '{encoding}'"
+            tree.xmlCharEncCloseFunc(enchandler)
+            self._default_encoding = encoding
+
+    cdef _setBaseURL(self, base_url):
+        self._filename = _encodeFilename(base_url)
+
+    cdef _collectEvents(self, event_types, tag):
+        if event_types is None:
+            event_types = ()
+        else:
+            event_types = tuple(set(event_types))
+            _buildParseEventFilter(event_types)  # purely for validation
+        self._events_to_collect = (event_types, tag)
+
+    cdef _ParserContext _getParserContext(self):
+        cdef xmlparser.xmlParserCtxt* pctxt
+        if self._parser_context is None:
+            self._parser_context = self._createContext(self.target, None)
+            self._parser_context._collect_ids = self._collect_ids
+            if self._schema is not None:
+                self._parser_context._validator = \
+                    self._schema._newSaxValidator(
+                        self._parse_options & xmlparser.XML_PARSE_DTDATTR)
+            pctxt = self._newParserCtxt()
+            _initParserContext(self._parser_context, self._resolvers, pctxt)
+            self._configureSaxContext(pctxt)
+        return self._parser_context
+
+    cdef _ParserContext _getPushParserContext(self):
+        cdef xmlparser.xmlParserCtxt* pctxt
+        if self._push_parser_context is None:
+            self._push_parser_context = self._createContext(
+                self.target, self._events_to_collect)
+            self._push_parser_context._collect_ids = self._collect_ids
+            if self._schema is not None:
+                self._push_parser_context._validator = \
+                    self._schema._newSaxValidator(
+                        self._parse_options & xmlparser.XML_PARSE_DTDATTR)
+            pctxt = self._newPushParserCtxt()
+            _initParserContext(
+                self._push_parser_context, self._resolvers, pctxt)
+            self._configureSaxContext(pctxt)
+        return self._push_parser_context
+
+    cdef _ParserContext _createContext(self, target, events_to_collect):
+        cdef _SaxParserContext sax_context
+        if target is not None:
+            sax_context = _TargetParserContext(self)
+            (<_TargetParserContext>sax_context)._setTarget(target)
+        elif events_to_collect:
+            sax_context = _SaxParserContext(self)
+        else:
+            # nothing special to configure
+            return _ParserContext()
+        if events_to_collect:
+            events, tag = events_to_collect
+            sax_context._setEventFilter(events, tag)
+        return sax_context
+
+    @cython.final
+    cdef int _configureSaxContext(self, xmlparser.xmlParserCtxt* pctxt) except -1:
+        if self._remove_comments:
+            pctxt.sax.comment = NULL
+        if self._remove_pis:
+            pctxt.sax.processingInstruction = NULL
+        if self._strip_cdata:
+            # hard switch-off for CDATA nodes => makes them plain text
+            pctxt.sax.cdataBlock = NULL
+        if not self._resolve_external_entities:
+            pctxt.sax.getEntity = _getInternalEntityOnly
+
+    cdef int _registerHtmlErrorHandler(self, xmlparser.xmlParserCtxt* c_ctxt) except -1:
+        cdef xmlparser.xmlSAXHandler* sax = c_ctxt.sax
+        if sax is not NULL and sax.initialized and sax.initialized != xmlparser.XML_SAX2_MAGIC:
+            # need to extend SAX1 context to SAX2 to get proper error reports
+            if <xmlparser.xmlSAXHandlerV1*>sax is &htmlparser.htmlDefaultSAXHandler:
+                sax = <xmlparser.xmlSAXHandler*> tree.xmlMalloc(sizeof(xmlparser.xmlSAXHandler))
+                if sax is NULL:
+                    raise MemoryError()
+                cstring_h.memcpy(sax, &htmlparser.htmlDefaultSAXHandler,
+                                 sizeof(htmlparser.htmlDefaultSAXHandler))
+                c_ctxt.sax = sax
+            sax.initialized = xmlparser.XML_SAX2_MAGIC
+            # Need a cast here because older libxml2 releases do not use 'const' in the functype.
+            sax.serror = <xmlerror.xmlStructuredErrorFunc> _receiveParserError
+            sax.startElementNs = NULL
+            sax.endElementNs = NULL
+            sax._private = NULL
+        return 0
+
+    cdef xmlparser.xmlParserCtxt* _newParserCtxt(self) except NULL:
+        cdef xmlparser.xmlParserCtxt* c_ctxt
+        if self._for_html:
+            c_ctxt = htmlparser.htmlCreateMemoryParserCtxt('dummy', 5)
+            if c_ctxt is not NULL:
+                self._registerHtmlErrorHandler(c_ctxt)
+        else:
+            c_ctxt = xmlparser.xmlNewParserCtxt()
+        if c_ctxt is NULL:
+            raise MemoryError
+        c_ctxt.sax.startDocument = _initSaxDocument
+        return c_ctxt
+
+    cdef xmlparser.xmlParserCtxt* _newPushParserCtxt(self) except NULL:
+        cdef xmlparser.xmlParserCtxt* c_ctxt
+        cdef char* c_filename = _cstr(self._filename) if self._filename is not None else NULL
+        if self._for_html:
+            c_ctxt = htmlparser.htmlCreatePushParserCtxt(
+                NULL, NULL, NULL, 0, c_filename, tree.XML_CHAR_ENCODING_NONE)
+            if c_ctxt is not NULL:
+                self._registerHtmlErrorHandler(c_ctxt)
+                htmlparser.htmlCtxtUseOptions(c_ctxt, self._parse_options)
+        else:
+            c_ctxt = xmlparser.xmlCreatePushParserCtxt(
+                NULL, NULL, NULL, 0, c_filename)
+            if c_ctxt is not NULL:
+                xmlparser.xmlCtxtUseOptions(c_ctxt, self._parse_options)
+        if c_ctxt is NULL:
+            raise MemoryError()
+        c_ctxt.sax.startDocument = _initSaxDocument
+        return c_ctxt
+
+    @property
+    def error_log(self):
+        """The error log of the last parser run.
+        """
+        cdef _ParserContext context
+        context = self._getParserContext()
+        return context._error_log.copy()
+
+    @property
+    def resolvers(self):
+        """The custom resolver registry of this parser."""
+        return self._resolvers
+
+    @property
+    def version(self):
+        """The version of the underlying XML parser."""
+        return "libxml2 %d.%d.%d" % LIBXML_VERSION
+
+    def set_element_class_lookup(self, ElementClassLookup lookup = None):
+        """set_element_class_lookup(self, lookup = None)
+
+        Set a lookup scheme for element classes generated from this parser.
+
+        Reset it by passing None or nothing.
+        """
+        self._class_lookup = lookup
+
+    cdef _BaseParser _copy(self):
+        "Create a new parser with the same configuration."
+        cdef _BaseParser parser
+        parser = self.__class__()
+        parser._parse_options = self._parse_options
+        parser._for_html = self._for_html
+        parser._remove_comments = self._remove_comments
+        parser._remove_pis = self._remove_pis
+        parser._strip_cdata = self._strip_cdata
+        parser._filename = self._filename
+        parser._resolvers = self._resolvers
+        parser.target = self.target
+        parser._class_lookup  = self._class_lookup
+        parser._default_encoding = self._default_encoding
+        parser._schema = self._schema
+        parser._events_to_collect = self._events_to_collect
+        return parser
+
+    def copy(self):
+        """copy(self)
+
+        Create a new parser with the same configuration.
+        """
+        return self._copy()
+
+    def makeelement(self, _tag, attrib=None, nsmap=None, **_extra):
+        """makeelement(self, _tag, attrib=None, nsmap=None, **_extra)
+
+        Creates a new element associated with this parser.
+        """
+        return _makeElement(_tag, NULL, None, self, None, None,
+                            attrib, nsmap, _extra)
+
+    # internal parser methods
+
+    cdef xmlDoc* _parseUnicodeDoc(self, utext, char* c_filename) except NULL:
+        """Parse unicode document, share dictionary if possible.
+        """
+        cdef _ParserContext context
+        cdef xmlDoc* result
+        cdef xmlparser.xmlParserCtxt* pctxt
+        cdef Py_ssize_t py_buffer_len
+        cdef int buffer_len, c_kind
+        cdef const_char* c_text
+        cdef const_char* c_encoding = _PY_UNICODE_ENCODING
+        if python.PyUnicode_IS_READY(utext):
+            # PEP-393 string
+            c_text = <const_char*>python.PyUnicode_DATA(utext)
+            py_buffer_len = python.PyUnicode_GET_LENGTH(utext)
+            c_kind = python.PyUnicode_KIND(utext)
+            if c_kind == 1:
+                if python.PyUnicode_MAX_CHAR_VALUE(utext) <= 127:
+                    c_encoding = 'UTF-8'
+                else:
+                    c_encoding = 'ISO-8859-1'
+            elif c_kind == 2:
+                py_buffer_len *= 2
+                if python.PY_BIG_ENDIAN:
+                    c_encoding = 'UTF-16BE'  # actually UCS-2
+                else:
+                    c_encoding = 'UTF-16LE'  # actually UCS-2
+            elif c_kind == 4:
+                py_buffer_len *= 4
+                if python.PY_BIG_ENDIAN:
+                    c_encoding = 'UTF-32BE'  # actually UCS-4
+                else:
+                    c_encoding = 'UTF-32LE'  # actually UCS-4
+            else:
+                assert False, f"Illegal Unicode kind {c_kind}"
+        else:
+            # old Py_UNICODE string
+            py_buffer_len = python.PyUnicode_GET_DATA_SIZE(utext)
+            c_text = python.PyUnicode_AS_DATA(utext)
+        assert 0 <= py_buffer_len <= limits.INT_MAX
+        buffer_len = py_buffer_len
+
+        context = self._getParserContext()
+        context.prepare()
+        try:
+            pctxt = context._c_ctxt
+            __GLOBAL_PARSER_CONTEXT.initParserDict(pctxt)
+            orig_options = pctxt.options
+            with nogil:
+                if self._for_html:
+                    result = htmlparser.htmlCtxtReadMemory(
+                        pctxt, c_text, buffer_len, c_filename, c_encoding,
+                        self._parse_options)
+                    if result is not NULL:
+                        if _fixHtmlDictNames(pctxt.dict, result) < 0:
+                            tree.xmlFreeDoc(result)
+                            result = NULL
+                else:
+                    result = xmlparser.xmlCtxtReadMemory(
+                        pctxt, c_text, buffer_len, c_filename, c_encoding,
+                        self._parse_options)
+            pctxt.options = orig_options # work around libxml2 problem
+
+            return context._handleParseResultDoc(self, result, None)
+        finally:
+            context.cleanup()
+
+    cdef xmlDoc* _parseDoc(self, char* c_text, int c_len,
+                           char* c_filename) except NULL:
+        """Parse document, share dictionary if possible.
+        """
+        cdef _ParserContext context
+        cdef xmlDoc* result
+        cdef xmlparser.xmlParserCtxt* pctxt
+        cdef char* c_encoding
+        cdef tree.xmlCharEncoding enc
+        context = self._getParserContext()
+        context.prepare()
+        try:
+            pctxt = context._c_ctxt
+            __GLOBAL_PARSER_CONTEXT.initParserDict(pctxt)
+
+            if self._default_encoding is None:
+                c_encoding = NULL
+                # libxml2 (at least 2.9.3) does not recognise UTF-32 BOMs
+                # NOTE: limit to problematic cases because it changes character offsets
+                if c_len >= 4 and (c_text[0] == b'\xFF' and c_text[1] == b'\xFE' and
+                                   c_text[2] == 0 and c_text[3] == 0):
+                    c_encoding = "UTF-32LE"
+                    c_text += 4
+                    c_len -= 4
+                elif c_len >= 4 and (c_text[0] == 0 and c_text[1] == 0 and
+                                     c_text[2] == b'\xFE' and c_text[3] == b'\xFF'):
+                    c_encoding = "UTF-32BE"
+                    c_text += 4
+                    c_len -= 4
+                else:
+                    # no BOM => try to determine encoding
+                    enc = tree.xmlDetectCharEncoding(<const_xmlChar*>c_text, c_len)
+                    if enc == tree.XML_CHAR_ENCODING_UCS4LE:
+                        c_encoding = 'UTF-32LE'
+                    elif enc == tree.XML_CHAR_ENCODING_UCS4BE:
+                        c_encoding = 'UTF-32BE'
+            else:
+                c_encoding = _cstr(self._default_encoding)
+
+            orig_options = pctxt.options
+            with nogil:
+                if self._for_html:
+                    result = htmlparser.htmlCtxtReadMemory(
+                        pctxt, c_text, c_len, c_filename,
+                        c_encoding, self._parse_options)
+                    if result is not NULL:
+                        if _fixHtmlDictNames(pctxt.dict, result) < 0:
+                            tree.xmlFreeDoc(result)
+                            result = NULL
+                else:
+                    result = xmlparser.xmlCtxtReadMemory(
+                        pctxt, c_text, c_len, c_filename,
+                        c_encoding, self._parse_options)
+            pctxt.options = orig_options # work around libxml2 problem
+
+            return context._handleParseResultDoc(self, result, None)
+        finally:
+            context.cleanup()
+
+    cdef xmlDoc* _parseDocFromFile(self, char* c_filename) except NULL:
+        cdef _ParserContext context
+        cdef xmlDoc* result
+        cdef xmlparser.xmlParserCtxt* pctxt
+        cdef char* c_encoding
+        result = NULL
+
+        context = self._getParserContext()
+        context.prepare()
+        try:
+            pctxt = context._c_ctxt
+            __GLOBAL_PARSER_CONTEXT.initParserDict(pctxt)
+
+            if self._default_encoding is None:
+                c_encoding = NULL
+            else:
+                c_encoding = _cstr(self._default_encoding)
+
+            orig_options = pctxt.options
+            with nogil:
+                if self._for_html:
+                    result = htmlparser.htmlCtxtReadFile(
+                        pctxt, c_filename, c_encoding, self._parse_options)
+                    if result is not NULL:
+                        if _fixHtmlDictNames(pctxt.dict, result) < 0:
+                            tree.xmlFreeDoc(result)
+                            result = NULL
+                else:
+                    result = xmlparser.xmlCtxtReadFile(
+                        pctxt, c_filename, c_encoding, self._parse_options)
+            pctxt.options = orig_options # work around libxml2 problem
+
+            return context._handleParseResultDoc(self, result, c_filename)
+        finally:
+            context.cleanup()
+
+    cdef xmlDoc* _parseDocFromFilelike(self, filelike, filename,
+                                       encoding) except NULL:
+        cdef _ParserContext context
+        cdef _FileReaderContext file_context
+        cdef xmlDoc* result
+        cdef xmlparser.xmlParserCtxt* pctxt
+        cdef char* c_filename
+        if not filename:
+            filename = None
+
+        context = self._getParserContext()
+        context.prepare()
+        try:
+            pctxt = context._c_ctxt
+            __GLOBAL_PARSER_CONTEXT.initParserDict(pctxt)
+            file_context = _FileReaderContext(
+                filelike, context, filename,
+                encoding or self._default_encoding)
+            result = file_context._readDoc(pctxt, self._parse_options)
+
+            return context._handleParseResultDoc(
+                self, result, filename)
+        finally:
+            context.cleanup()
+
+
+cdef tree.xmlEntity* _getInternalEntityOnly(void* ctxt, const_xmlChar* name) noexcept nogil:
+    """
+    Callback function to intercept the entity resolution when external entity loading is disabled.
+    """
+    cdef tree.xmlEntity* entity = xmlparser.xmlSAX2GetEntity(ctxt, name)
+    if not entity:
+        return NULL
+    if entity.etype not in (
+            tree.xmlEntityType.XML_EXTERNAL_GENERAL_PARSED_ENTITY,
+            tree.xmlEntityType.XML_EXTERNAL_GENERAL_UNPARSED_ENTITY,
+            tree.xmlEntityType.XML_EXTERNAL_PARAMETER_ENTITY):
+        return entity
+
+    # Reject all external entities and fail the parsing instead. There is currently
+    # no way in libxml2 to just prevent the entity resolution in this case.
+    cdef xmlerror.xmlError c_error
+    cdef xmlerror.xmlStructuredErrorFunc err_func
+    cdef xmlparser.xmlParserInput* parser_input
+    cdef void* err_context
+
+    c_ctxt = <xmlparser.xmlParserCtxt *> ctxt
+    err_func = xmlerror.xmlStructuredError
+    if err_func:
+        parser_input = c_ctxt.input
+        # Copied from xmlVErrParser() in libxml2: get current input from stack.
+        if parser_input and parser_input.filename is NULL and c_ctxt.inputNr > 1:
+            parser_input = c_ctxt.inputTab[c_ctxt.inputNr - 2]
+
+        c_error = xmlerror.xmlError(
+            domain=xmlerror.xmlErrorDomain.XML_FROM_PARSER,
+            code=xmlerror.xmlParserErrors.XML_ERR_EXT_ENTITY_STANDALONE,
+            level=xmlerror.xmlErrorLevel.XML_ERR_FATAL,
+            message=b"External entity resolution is disabled for security reasons "
+                    b"when resolving '&%s;'. Use 'XMLParser(resolve_entities=True)' "
+                    b"if you consider it safe to enable it.",
+            file=parser_input.filename,
+            node=entity,
+            str1=<char*> name,
+            str2=NULL,
+            str3=NULL,
+            line=parser_input.line if parser_input else 0,
+            int1=0,
+            int2=parser_input.col if parser_input else 0,
+        )
+        err_context = xmlerror.xmlStructuredErrorContext
+        err_func(err_context, &c_error)
+
+    c_ctxt.wellFormed = 0
+    # The entity was looked up and does not need to be freed.
+    return NULL
+
+
+cdef void _initSaxDocument(void* ctxt) noexcept with gil:
+    xmlparser.xmlSAX2StartDocument(ctxt)
+    c_ctxt = <xmlparser.xmlParserCtxt*>ctxt
+    c_doc = c_ctxt.myDoc
+
+    # set up document dict
+    if c_doc and c_ctxt.dict and not c_doc.dict:
+        # I have no idea why libxml2 disables this - we need it
+        c_ctxt.dictNames = 1
+        c_doc.dict = c_ctxt.dict
+        xmlparser.xmlDictReference(c_ctxt.dict)
+
+    # set up XML ID hash table
+    if c_ctxt._private:
+        context = <_ParserContext>c_ctxt._private
+        if context._collect_ids:
+            # keep the global parser dict from filling up with XML IDs
+            if c_doc and not c_doc.ids:
+                # memory errors are not fatal here
+                c_dict = xmlparser.xmlDictCreate()
+                if c_dict:
+                    c_doc.ids = tree.xmlHashCreateDict(0, c_dict)
+                    xmlparser.xmlDictFree(c_dict)
+                else:
+                    c_doc.ids = tree.xmlHashCreate(0)
+        else:
+            c_ctxt.loadsubset |= xmlparser.XML_SKIP_IDS
+            if c_doc and c_doc.ids and not tree.xmlHashSize(c_doc.ids):
+                # already initialised but empty => clear
+                tree.xmlHashFree(c_doc.ids, NULL)
+                c_doc.ids = NULL
+
+
+############################################################
+## ET feed parser
+############################################################
+
+cdef class _FeedParser(_BaseParser):
+    cdef bint _feed_parser_running
+
+    @property
+    def feed_error_log(self):
+        """The error log of the last (or current) run of the feed parser.
+
+        Note that this is local to the feed parser and thus is
+        different from what the ``error_log`` property returns.
+        """
+        return self._getPushParserContext()._error_log.copy()
+
+    cpdef feed(self, data):
+        """feed(self, data)
+
+        Feeds data to the parser.  The argument should be an 8-bit string
+        buffer containing encoded data, although Unicode is supported as long
+        as both string types are not mixed.
+
+        This is the main entry point to the consumer interface of a
+        parser.  The parser will parse as much of the XML stream as it
+        can on each call.  To finish parsing or to reset the parser,
+        call the ``close()`` method.  Both methods may raise
+        ParseError if errors occur in the input data.  If an error is
+        raised, there is no longer a need to call ``close()``.
+
+        The feed parser interface is independent of the normal parser
+        usage.  You can use the same parser as a feed parser and in
+        the ``parse()`` function concurrently.
+        """
+        cdef _ParserContext context
+        cdef bytes bstring
+        cdef xmlparser.xmlParserCtxt* pctxt
+        cdef Py_ssize_t py_buffer_len, ustart
+        cdef const_char* char_data
+        cdef const_char* c_encoding
+        cdef int buffer_len
+        cdef int error
+        cdef bint recover = self._parse_options & xmlparser.XML_PARSE_RECOVER
+
+        if isinstance(data, bytes):
+            if self._default_encoding is None:
+                c_encoding = NULL
+            else:
+                c_encoding = self._default_encoding
+            char_data = _cstr(data)
+            py_buffer_len = python.PyBytes_GET_SIZE(data)
+            ustart = 0
+        elif isinstance(data, unicode):
+            c_encoding = b"UTF-8"
+            char_data = NULL
+            py_buffer_len = len(<unicode> data)
+            ustart = 0
+        else:
+            raise TypeError, "Parsing requires string data"
+
+        context = self._getPushParserContext()
+        pctxt = context._c_ctxt
+        error = 0
+        if not self._feed_parser_running:
+            context.prepare(set_document_loader=False)
+            self._feed_parser_running = 1
+            c_filename = (_cstr(self._filename)
+                          if self._filename is not None else NULL)
+
+            # We have to give *mlCtxtResetPush() enough input to figure
+            # out the character encoding (at least four bytes),
+            # however if we give it all we got, we'll have nothing for
+            # *mlParseChunk() and things go wrong.
+            buffer_len = 0
+            if char_data is not NULL:
+                buffer_len = 4 if py_buffer_len > 4 else <int>py_buffer_len
+            orig_loader = _register_document_loader()
+            if self._for_html:
+                error = _htmlCtxtResetPush(
+                    pctxt, char_data, buffer_len, c_filename, c_encoding,
+                    self._parse_options)
+            else:
+                xmlparser.xmlCtxtUseOptions(pctxt, self._parse_options)
+                error = xmlparser.xmlCtxtResetPush(
+                    pctxt, char_data, buffer_len, c_filename, c_encoding)
+            _reset_document_loader(orig_loader)
+            py_buffer_len -= buffer_len
+            char_data += buffer_len
+            if error:
+                raise MemoryError()
+            __GLOBAL_PARSER_CONTEXT.initParserDict(pctxt)
+
+        #print pctxt.charset, 'NONE' if c_encoding is NULL else c_encoding
+
+        fixup_error = 0
+        while py_buffer_len > 0 and (error == 0 or recover):
+            if char_data is NULL:
+                # Unicode parsing by converting chunks to UTF-8
+                buffer_len = 2**19  # len(bytes) <= 4 * (2**19) == 2 MiB
+                bstring = (<unicode> data)[ustart : ustart+buffer_len].encode('UTF-8')
+                ustart += buffer_len
+                py_buffer_len -= buffer_len  # may end up < 0
+                error, fixup_error = _parse_data_chunk(pctxt, <const char*> bstring, <int> len(bstring))
+            else:
+                # Direct byte string parsing.
+                buffer_len = <int>py_buffer_len if py_buffer_len <= limits.INT_MAX else limits.INT_MAX
+                error, fixup_error = _parse_data_chunk(pctxt, char_data, buffer_len)
+                py_buffer_len -= buffer_len
+                char_data += buffer_len
+
+            if fixup_error:
+                context.store_exception(MemoryError())
+
+            if context._has_raised():
+                # propagate Python exceptions immediately
+                recover = 0
+                error = 1
+                break
+
+            if error and not pctxt.replaceEntities and not pctxt.validate:
+                # in this mode, we ignore errors about undefined entities
+                for entry in context._error_log.filter_from_errors():
+                    if entry.type != ErrorTypes.WAR_UNDECLARED_ENTITY and \
+                           entry.type != ErrorTypes.ERR_UNDECLARED_ENTITY:
+                        break
+                else:
+                    error = 0
+
+        if not pctxt.wellFormed and pctxt.disableSAX and context._has_raised():
+            # propagate Python exceptions immediately
+            recover = 0
+            error = 1
+
+        if fixup_error or not recover and (error or not pctxt.wellFormed):
+            self._feed_parser_running = 0
+            try:
+                context._handleParseResult(self, pctxt.myDoc, None)
+            finally:
+                context.cleanup()
+
+    cpdef close(self):
+        """close(self)
+
+        Terminates feeding data to this parser.  This tells the parser to
+        process any remaining data in the feed buffer, and then returns the
+        root Element of the tree that was parsed.
+
+        This method must be called after passing the last chunk of data into
+        the ``feed()`` method.  It should only be called when using the feed
+        parser interface, all other usage is undefined.
+        """
+        if not self._feed_parser_running:
+            raise XMLSyntaxError("no element found",
+                                 xmlerror.XML_ERR_INTERNAL_ERROR, 0, 0,
+                                 self._filename)
+
+        context = self._getPushParserContext()
+        pctxt = context._c_ctxt
+
+        self._feed_parser_running = 0
+        if self._for_html:
+            htmlparser.htmlParseChunk(pctxt, NULL, 0, 1)
+        else:
+            xmlparser.xmlParseChunk(pctxt, NULL, 0, 1)
+
+        if (pctxt.recovery and not pctxt.disableSAX and
+                isinstance(context, _SaxParserContext)):
+            # apply any left-over 'end' events
+            (<_SaxParserContext>context).flushEvents()
+
+        try:
+            result = context._handleParseResult(self, pctxt.myDoc, None)
+        finally:
+            context.cleanup()
+
+        if isinstance(result, _Document):
+            return (<_Document>result).getroot()
+        else:
+            return result
+
+
+cdef (int, int) _parse_data_chunk(xmlparser.xmlParserCtxt* c_ctxt,
+                                  const char* char_data, int buffer_len):
+    fixup_error = 0
+    with nogil:
+        if c_ctxt.html:
+            c_node = c_ctxt.node  # last node where the parser stopped
+            orig_loader = _register_document_loader()
+            error = htmlparser.htmlParseChunk(c_ctxt, char_data, buffer_len, 0)
+            _reset_document_loader(orig_loader)
+            # and now for the fun part: move node names to the dict
+            if c_ctxt.myDoc:
+                fixup_error = _fixHtmlDictSubtreeNames(
+                    c_ctxt.dict, c_ctxt.myDoc, c_node)
+                if c_ctxt.myDoc.dict and c_ctxt.myDoc.dict is not c_ctxt.dict:
+                    xmlparser.xmlDictFree(c_ctxt.myDoc.dict)
+                    c_ctxt.myDoc.dict = c_ctxt.dict
+                    xmlparser.xmlDictReference(c_ctxt.dict)
+        else:
+            orig_loader = _register_document_loader()
+            error = xmlparser.xmlParseChunk(c_ctxt, char_data, buffer_len, 0)
+            _reset_document_loader(orig_loader)
+    return (error, fixup_error)
+
+
+cdef int _htmlCtxtResetPush(xmlparser.xmlParserCtxt* c_ctxt,
+                             const_char* c_data, int buffer_len,
+                             const_char* c_filename, const_char* c_encoding,
+                             int parse_options) except -1:
+    cdef xmlparser.xmlParserInput* c_input_stream
+    # libxml2 lacks an HTML push parser setup function
+    error = xmlparser.xmlCtxtResetPush(
+        c_ctxt, c_data, buffer_len, c_filename, c_encoding)
+    if error:
+        return error
+
+    # fix libxml2 setup for HTML
+    c_ctxt.progressive = 1
+    c_ctxt.html = 1
+    htmlparser.htmlCtxtUseOptions(c_ctxt, parse_options)
+
+    return 0
+
+
+############################################################
+## XML parser
+############################################################
+
+cdef int _XML_DEFAULT_PARSE_OPTIONS
+_XML_DEFAULT_PARSE_OPTIONS = (
+    xmlparser.XML_PARSE_NOENT   |
+    xmlparser.XML_PARSE_NOCDATA |
+    xmlparser.XML_PARSE_NONET   |
+    xmlparser.XML_PARSE_COMPACT |
+    xmlparser.XML_PARSE_BIG_LINES
+    )
+
+cdef class XMLParser(_FeedParser):
+    """XMLParser(self, encoding=None, attribute_defaults=False, dtd_validation=False, load_dtd=False, no_network=True, ns_clean=False, recover=False, schema: XMLSchema =None, huge_tree=False, remove_blank_text=False, resolve_entities=True, remove_comments=False, remove_pis=False, strip_cdata=True, collect_ids=True, target=None, compact=True)
+
+    The XML parser.
+
+    Parsers can be supplied as additional argument to various parse
+    functions of the lxml API.  A default parser is always available
+    and can be replaced by a call to the global function
+    'set_default_parser'.  New parsers can be created at any time
+    without a major run-time overhead.
+
+    The keyword arguments in the constructor are mainly based on the
+    libxml2 parser configuration.  A DTD will also be loaded if DTD
+    validation or attribute default values are requested (unless you
+    additionally provide an XMLSchema from which the default
+    attributes can be read).
+
+    Available boolean keyword arguments:
+
+    - attribute_defaults - inject default attributes from DTD or XMLSchema
+    - dtd_validation     - validate against a DTD referenced by the document
+    - load_dtd           - use DTD for parsing
+    - no_network         - prevent network access for related files (default: True)
+    - ns_clean           - clean up redundant namespace declarations
+    - recover            - try hard to parse through broken XML
+    - remove_blank_text  - discard blank text nodes that appear ignorable
+    - remove_comments    - discard comments
+    - remove_pis         - discard processing instructions
+    - strip_cdata        - replace CDATA sections by normal text content (default: True)
+    - compact            - save memory for short text content (default: True)
+    - collect_ids        - use a hash table of XML IDs for fast access (default: True, always True with DTD validation)
+    - huge_tree          - disable security restrictions and support very deep trees
+                           and very long text content (only affects libxml2 2.7+)
+
+    Other keyword arguments:
+
+    - resolve_entities - replace entities by their text value: False for keeping the
+          entity references, True for resolving them, and 'internal' for resolving
+          internal definitions only (no external file/URL access).
+          The default used to be True and was changed to 'internal' in lxml 5.0.
+    - encoding - override the document encoding (note: libiconv encoding name)
+    - target   - a parser target object that will receive the parse events
+    - schema   - an XMLSchema to validate against
+
+    Note that you should avoid sharing parsers between threads.  While this is
+    not harmful, it is more efficient to use separate parsers.  This does not
+    apply to the default parser.
+    """
+    def __init__(self, *, encoding=None, attribute_defaults=False,
+                 dtd_validation=False, load_dtd=False, no_network=True,
+                 ns_clean=False, recover=False, XMLSchema schema=None,
+                 huge_tree=False, remove_blank_text=False, resolve_entities='internal',
+                 remove_comments=False, remove_pis=False, strip_cdata=True,
+                 collect_ids=True, target=None, compact=True):
+        cdef int parse_options
+        cdef bint resolve_external = True
+        parse_options = _XML_DEFAULT_PARSE_OPTIONS
+        if load_dtd:
+            parse_options = parse_options | xmlparser.XML_PARSE_DTDLOAD
+        if dtd_validation:
+            parse_options = parse_options | xmlparser.XML_PARSE_DTDVALID | \
+                            xmlparser.XML_PARSE_DTDLOAD
+        if attribute_defaults:
+            parse_options = parse_options | xmlparser.XML_PARSE_DTDATTR
+            if schema is None:
+                parse_options = parse_options | xmlparser.XML_PARSE_DTDLOAD
+        if ns_clean:
+            parse_options = parse_options | xmlparser.XML_PARSE_NSCLEAN
+        if recover:
+            parse_options = parse_options | xmlparser.XML_PARSE_RECOVER
+        if remove_blank_text:
+            parse_options = parse_options | xmlparser.XML_PARSE_NOBLANKS
+        if huge_tree:
+            parse_options = parse_options | xmlparser.XML_PARSE_HUGE
+        if not no_network:
+            parse_options = parse_options ^ xmlparser.XML_PARSE_NONET
+        if not compact:
+            parse_options = parse_options ^ xmlparser.XML_PARSE_COMPACT
+        if not resolve_entities:
+            parse_options = parse_options ^ xmlparser.XML_PARSE_NOENT
+        elif resolve_entities == 'internal':
+            resolve_external = False
+        if not strip_cdata:
+            parse_options = parse_options ^ xmlparser.XML_PARSE_NOCDATA
+
+        _BaseParser.__init__(self, parse_options, False, schema,
+                             remove_comments, remove_pis, strip_cdata,
+                             collect_ids, target, encoding, resolve_external)
+
+
+cdef class XMLPullParser(XMLParser):
+    """XMLPullParser(self, events=None, *, tag=None, **kwargs)
+
+    XML parser that collects parse events in an iterator.
+
+    The collected events are the same as for iterparse(), but the
+    parser itself is non-blocking in the sense that it receives
+    data chunks incrementally through its .feed() method, instead
+    of reading them directly from a file(-like) object all by itself.
+
+    By default, it collects Element end events.  To change that,
+    pass any subset of the available events into the ``events``
+    argument: ``'start'``, ``'end'``, ``'start-ns'``,
+    ``'end-ns'``, ``'comment'``, ``'pi'``.
+
+    To support loading external dependencies relative to the input
+    source, you can pass the ``base_url``.
+    """
+    def __init__(self, events=None, *, tag=None, base_url=None, **kwargs):
+        XMLParser.__init__(self, **kwargs)
+        if events is None:
+            events = ('end',)
+        self._setBaseURL(base_url)
+        self._collectEvents(events, tag)
+
+    def read_events(self):
+        return (<_SaxParserContext?>self._getPushParserContext()).events_iterator
+
+
+cdef class ETCompatXMLParser(XMLParser):
+    """ETCompatXMLParser(self, encoding=None, attribute_defaults=False, \
+                 dtd_validation=False, load_dtd=False, no_network=True, \
+                 ns_clean=False, recover=False, schema=None, \
+                 huge_tree=False, remove_blank_text=False, resolve_entities=True, \
+                 remove_comments=True, remove_pis=True, strip_cdata=True, \
+                 target=None, compact=True)
+
+    An XML parser with an ElementTree compatible default setup.
+
+    See the XMLParser class for details.
+
+    This parser has ``remove_comments`` and ``remove_pis`` enabled by default
+    and thus ignores comments and processing instructions.
+    """
+    def __init__(self, *, encoding=None, attribute_defaults=False,
+                 dtd_validation=False, load_dtd=False, no_network=True,
+                 ns_clean=False, recover=False, schema=None,
+                 huge_tree=False, remove_blank_text=False, resolve_entities=True,
+                 remove_comments=True, remove_pis=True, strip_cdata=True,
+                 target=None, compact=True):
+        XMLParser.__init__(self,
+                           attribute_defaults=attribute_defaults,
+                           dtd_validation=dtd_validation,
+                           load_dtd=load_dtd,
+                           no_network=no_network,
+                           ns_clean=ns_clean,
+                           recover=recover,
+                           remove_blank_text=remove_blank_text,
+                           huge_tree=huge_tree,
+                           compact=compact,
+                           resolve_entities=resolve_entities,
+                           remove_comments=remove_comments,
+                           remove_pis=remove_pis,
+                           strip_cdata=strip_cdata,
+                           target=target,
+                           encoding=encoding,
+                           schema=schema)
+
+# ET 1.2 compatible name
+XMLTreeBuilder = ETCompatXMLParser
+
+
+cdef XMLParser __DEFAULT_XML_PARSER
+__DEFAULT_XML_PARSER = XMLParser()
+
+__GLOBAL_PARSER_CONTEXT.setDefaultParser(__DEFAULT_XML_PARSER)
+
+def set_default_parser(_BaseParser parser=None):
+    """set_default_parser(parser=None)
+
+    Set a default parser for the current thread.  This parser is used
+    globally whenever no parser is supplied to the various parse functions of
+    the lxml API.  If this function is called without a parser (or if it is
+    None), the default parser is reset to the original configuration.
+
+    Note that the pre-installed default parser is not thread-safe.  Avoid the
+    default parser in multi-threaded environments.  You can create a separate
+    parser for each thread explicitly or use a parser pool.
+    """
+    if parser is None:
+        parser = __DEFAULT_XML_PARSER
+    __GLOBAL_PARSER_CONTEXT.setDefaultParser(parser)
+
+def get_default_parser():
+    "get_default_parser()"
+    return __GLOBAL_PARSER_CONTEXT.getDefaultParser()
+
+############################################################
+## HTML parser
+############################################################
+
+cdef int _HTML_DEFAULT_PARSE_OPTIONS
+_HTML_DEFAULT_PARSE_OPTIONS = (
+    htmlparser.HTML_PARSE_RECOVER |
+    htmlparser.HTML_PARSE_NONET   |
+    htmlparser.HTML_PARSE_COMPACT
+    )
+
+cdef object _UNUSED = object()
+
+cdef class HTMLParser(_FeedParser):
+    """HTMLParser(self, encoding=None, remove_blank_text=False, \
+                   remove_comments=False, remove_pis=False, \
+                   no_network=True, target=None, schema: XMLSchema =None, \
+                   recover=True, compact=True, collect_ids=True, huge_tree=False)
+
+    The HTML parser.
+
+    This parser allows reading HTML into a normal XML tree.  By
+    default, it can read broken (non well-formed) HTML, depending on
+    the capabilities of libxml2.  Use the 'recover' option to switch
+    this off.
+
+    Available boolean keyword arguments:
+
+    - recover            - try hard to parse through broken HTML (default: True)
+    - no_network         - prevent network access for related files (default: True)
+    - remove_blank_text  - discard empty text nodes that are ignorable (i.e. not actual text content)
+    - remove_comments    - discard comments
+    - remove_pis         - discard processing instructions
+    - compact            - save memory for short text content (default: True)
+    - default_doctype    - add a default doctype even if it is not found in the HTML (default: True)
+    - collect_ids        - use a hash table of XML IDs for fast access (default: True)
+    - huge_tree          - disable security restrictions and support very deep trees
+                           and very long text content (only affects libxml2 2.7+)
+
+    Other keyword arguments:
+
+    - encoding - override the document encoding (note: libiconv encoding name)
+    - target   - a parser target object that will receive the parse events
+    - schema   - an XMLSchema to validate against
+
+    Note that you should avoid sharing parsers between threads for performance
+    reasons.
+    """
+    def __init__(self, *, encoding=None, remove_blank_text=False,
+                 remove_comments=False, remove_pis=False, strip_cdata=_UNUSED,
+                 no_network=True, target=None, XMLSchema schema=None,
+                 recover=True, compact=True, default_doctype=True,
+                 collect_ids=True, huge_tree=False):
+        cdef int parse_options
+        parse_options = _HTML_DEFAULT_PARSE_OPTIONS
+        if remove_blank_text:
+            parse_options = parse_options | htmlparser.HTML_PARSE_NOBLANKS
+        if not recover:
+            parse_options = parse_options ^ htmlparser.HTML_PARSE_RECOVER
+        if not no_network:
+            parse_options = parse_options ^ htmlparser.HTML_PARSE_NONET
+        if not compact:
+            parse_options = parse_options ^ htmlparser.HTML_PARSE_COMPACT
+        if not default_doctype:
+            parse_options = parse_options ^ htmlparser.HTML_PARSE_NODEFDTD
+        if huge_tree:
+            parse_options = parse_options | xmlparser.XML_PARSE_HUGE
+
+        if strip_cdata is not _UNUSED:
+            import warnings
+            warnings.warn(
+                "The 'strip_cdata' option of HTMLParser() has never done anything and will eventually be removed.",
+                DeprecationWarning)
+        _BaseParser.__init__(self, parse_options, True, schema,
+                             remove_comments, remove_pis, strip_cdata,
+                             collect_ids, target, encoding)
+
+
+cdef HTMLParser __DEFAULT_HTML_PARSER
+__DEFAULT_HTML_PARSER = HTMLParser()
+
+
+cdef class HTMLPullParser(HTMLParser):
+    """HTMLPullParser(self, events=None, *, tag=None, base_url=None, **kwargs)
+
+    HTML parser that collects parse events in an iterator.
+
+    The collected events are the same as for iterparse(), but the
+    parser itself is non-blocking in the sense that it receives
+    data chunks incrementally through its .feed() method, instead
+    of reading them directly from a file(-like) object all by itself.
+
+    By default, it collects Element end events.  To change that,
+    pass any subset of the available events into the ``events``
+    argument: ``'start'``, ``'end'``, ``'start-ns'``,
+    ``'end-ns'``, ``'comment'``, ``'pi'``.
+
+    To support loading external dependencies relative to the input
+    source, you can pass the ``base_url``.
+    """
+    def __init__(self, events=None, *, tag=None, base_url=None, **kwargs):
+        HTMLParser.__init__(self, **kwargs)
+        if events is None:
+            events = ('end',)
+        self._setBaseURL(base_url)
+        self._collectEvents(events, tag)
+
+    def read_events(self):
+        return (<_SaxParserContext?>self._getPushParserContext()).events_iterator
+
+
+############################################################
+## helper functions for document creation
+############################################################
+
+cdef xmlDoc* _parseDoc(text, filename, _BaseParser parser) except NULL:
+    cdef char* c_filename
+    cdef char* c_text
+    cdef Py_ssize_t c_len
+    if parser is None:
+        parser = __GLOBAL_PARSER_CONTEXT.getDefaultParser()
+    if not filename:
+        c_filename = NULL
+    else:
+        filename_utf = _encodeFilenameUTF8(filename)
+        c_filename = _cstr(filename_utf)
+    if isinstance(text, unicode):
+        if python.PyUnicode_IS_READY(text):
+            # PEP-393 Unicode string
+            c_len = python.PyUnicode_GET_LENGTH(text) * python.PyUnicode_KIND(text)
+        else:
+            # old Py_UNICODE string
+            c_len = python.PyUnicode_GET_DATA_SIZE(text)
+        if c_len > limits.INT_MAX:
+            return (<_BaseParser>parser)._parseDocFromFilelike(
+                StringIO(text), filename, None)
+        return (<_BaseParser>parser)._parseUnicodeDoc(text, c_filename)
+    else:
+        c_len = python.PyBytes_GET_SIZE(text)
+        if c_len > limits.INT_MAX:
+            return (<_BaseParser>parser)._parseDocFromFilelike(
+                BytesIO(text), filename, None)
+        c_text = _cstr(text)
+        return (<_BaseParser>parser)._parseDoc(c_text, c_len, c_filename)
+
+cdef xmlDoc* _parseDocFromFile(filename8, _BaseParser parser) except NULL:
+    if parser is None:
+        parser = __GLOBAL_PARSER_CONTEXT.getDefaultParser()
+    return (<_BaseParser>parser)._parseDocFromFile(_cstr(filename8))
+
+cdef xmlDoc* _parseDocFromFilelike(source, filename,
+                                   _BaseParser parser) except NULL:
+    if parser is None:
+        parser = __GLOBAL_PARSER_CONTEXT.getDefaultParser()
+    return (<_BaseParser>parser)._parseDocFromFilelike(source, filename, None)
+
+cdef xmlDoc* _newXMLDoc() except NULL:
+    cdef xmlDoc* result
+    result = tree.xmlNewDoc(NULL)
+    if result is NULL:
+        raise MemoryError()
+    if result.encoding is NULL:
+        result.encoding = tree.xmlStrdup(<unsigned char*>"UTF-8")
+    __GLOBAL_PARSER_CONTEXT.initDocDict(result)
+    return result
+
+cdef xmlDoc* _newHTMLDoc() except NULL:
+    cdef xmlDoc* result
+    result = tree.htmlNewDoc(NULL, NULL)
+    if result is NULL:
+        raise MemoryError()
+    __GLOBAL_PARSER_CONTEXT.initDocDict(result)
+    return result
+
+cdef xmlDoc* _copyDoc(xmlDoc* c_doc, int recursive) except NULL:
+    cdef xmlDoc* result
+    if recursive:
+        with nogil:
+            result = tree.xmlCopyDoc(c_doc, recursive)
+    else:
+        result = tree.xmlCopyDoc(c_doc, 0)
+    if result is NULL:
+        raise MemoryError()
+    __GLOBAL_PARSER_CONTEXT.initDocDict(result)
+    return result
+
+cdef xmlDoc* _copyDocRoot(xmlDoc* c_doc, xmlNode* c_new_root) except NULL:
+    "Recursively copy the document and make c_new_root the new root node."
+    cdef xmlDoc* result
+    cdef xmlNode* c_node
+    result = tree.xmlCopyDoc(c_doc, 0) # non recursive
+    __GLOBAL_PARSER_CONTEXT.initDocDict(result)
+    with nogil:
+        c_node = tree.xmlDocCopyNode(c_new_root, result, 1) # recursive
+    if c_node is NULL:
+        raise MemoryError()
+    tree.xmlDocSetRootElement(result, c_node)
+    _copyTail(c_new_root.next, c_node)
+    return result
+
+cdef xmlNode* _copyNodeToDoc(xmlNode* c_node, xmlDoc* c_doc) except NULL:
+    "Recursively copy the element into the document. c_doc is not modified."
+    cdef xmlNode* c_root
+    c_root = tree.xmlDocCopyNode(c_node, c_doc, 1) # recursive
+    if c_root is NULL:
+        raise MemoryError()
+    _copyTail(c_node.next, c_root)
+    return c_root
+
+
+############################################################
+## API level helper functions for _Document creation
+############################################################
+
+cdef _Document _parseDocument(source, _BaseParser parser, base_url):
+    cdef _Document doc
+    source = _getFSPathOrObject(source)
+    if _isString(source):
+        # parse the file directly from the filesystem
+        doc = _parseDocumentFromURL(_encodeFilename(source), parser)
+        # fix base URL if requested
+        if base_url is not None:
+            base_url = _encodeFilenameUTF8(base_url)
+            if doc._c_doc.URL is not NULL:
+                tree.xmlFree(<char*>doc._c_doc.URL)
+            doc._c_doc.URL = tree.xmlStrdup(_xcstr(base_url))
+        return doc
+
+    if base_url is not None:
+        url = base_url
+    else:
+        url = _getFilenameForFile(source)
+
+    if hasattr(source, 'getvalue') and hasattr(source, 'tell'):
+        # StringIO - reading from start?
+        if source.tell() == 0:
+            return _parseMemoryDocument(source.getvalue(), url, parser)
+
+    # Support for file-like objects (urlgrabber.urlopen, ...)
+    if hasattr(source, 'read'):
+        return _parseFilelikeDocument(source, url, parser)
+
+    raise TypeError, f"cannot parse from '{python._fqtypename(source).decode('UTF-8')}'"
+
+cdef _Document _parseDocumentFromURL(url, _BaseParser parser):
+    c_doc = _parseDocFromFile(url, parser)
+    return _documentFactory(c_doc, parser)
+
+cdef _Document _parseMemoryDocument(text, url, _BaseParser parser):
+    if isinstance(text, unicode):
+        if _hasEncodingDeclaration(text):
+            raise ValueError(
+                "Unicode strings with encoding declaration are not supported. "
+                "Please use bytes input or XML fragments without declaration.")
+    elif not isinstance(text, bytes):
+        raise ValueError, "can only parse strings"
+    c_doc = _parseDoc(text, url, parser)
+    return _documentFactory(c_doc, parser)
+
+cdef _Document _parseFilelikeDocument(source, url, _BaseParser parser):
+    c_doc = _parseDocFromFilelike(source, url, parser)
+    return _documentFactory(c_doc, parser)
diff --git a/.venv/lib/python3.12/site-packages/lxml/parsertarget.pxi b/.venv/lib/python3.12/site-packages/lxml/parsertarget.pxi
new file mode 100644
index 00000000..37c29957
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/parsertarget.pxi
@@ -0,0 +1,180 @@
+# Parser target context (ET target interface)
+
+cdef object inspect_getargspec
+try:
+    from inspect import getfullargspec as inspect_getargspec
+except ImportError:
+    from inspect import getargspec as inspect_getargspec
+
+
+class _TargetParserResult(Exception):
+    # Admittedly, this is somewhat ugly, but it's the easiest way
+    # to push the Python level parser result through the parser
+    # machinery towards the API level functions
+    def __init__(self, result):
+        self.result = result
+
+
+@cython.final
+@cython.internal
+cdef class _PythonSaxParserTarget(_SaxParserTarget):
+    cdef object _target_start
+    cdef object _target_end
+    cdef object _target_data
+    cdef object _target_start_ns
+    cdef object _target_end_ns
+    cdef object _target_doctype
+    cdef object _target_pi
+    cdef object _target_comment
+    cdef bint _start_takes_nsmap
+
+    def __cinit__(self, target):
+        cdef int event_filter
+        event_filter = 0
+        self._start_takes_nsmap = 0
+        try:
+            self._target_start = target.start
+            if self._target_start is not None:
+                event_filter |= SAX_EVENT_START
+        except AttributeError:
+            pass
+        else:
+            try:
+                arguments = inspect_getargspec(self._target_start)
+                if len(arguments[0]) > 3 or arguments[1] is not None:
+                    self._start_takes_nsmap = 1
+            except TypeError:
+                pass
+        try:
+            self._target_end = target.end
+            if self._target_end is not None:
+                event_filter |= SAX_EVENT_END
+        except AttributeError:
+            pass
+        try:
+            self._target_start_ns = target.start_ns
+            if self._target_start_ns is not None:
+                event_filter |= SAX_EVENT_START_NS
+        except AttributeError:
+            pass
+        try:
+            self._target_end_ns = target.end_ns
+            if self._target_end_ns is not None:
+                event_filter |= SAX_EVENT_END_NS
+        except AttributeError:
+            pass
+        try:
+            self._target_data = target.data
+            if self._target_data is not None:
+                event_filter |= SAX_EVENT_DATA
+        except AttributeError:
+            pass
+        try:
+            self._target_doctype = target.doctype
+            if self._target_doctype is not None:
+                event_filter |= SAX_EVENT_DOCTYPE
+        except AttributeError:
+            pass
+        try:
+            self._target_pi = target.pi
+            if self._target_pi is not None:
+                event_filter |= SAX_EVENT_PI
+        except AttributeError:
+            pass
+        try:
+            self._target_comment = target.comment
+            if self._target_comment is not None:
+                event_filter |= SAX_EVENT_COMMENT
+        except AttributeError:
+            pass
+        self._sax_event_filter = event_filter
+
+    cdef _handleSaxStart(self, tag, attrib, nsmap):
+        if self._start_takes_nsmap:
+            return self._target_start(tag, attrib, nsmap)
+        else:
+            return self._target_start(tag, attrib)
+
+    cdef _handleSaxEnd(self, tag):
+        return self._target_end(tag)
+
+    cdef _handleSaxStartNs(self, prefix, uri):
+        return self._target_start_ns(prefix, uri)
+
+    cdef _handleSaxEndNs(self, prefix):
+        return self._target_end_ns(prefix)
+
+    cdef int _handleSaxData(self, data) except -1:
+        self._target_data(data)
+
+    cdef int _handleSaxDoctype(self, root_tag, public_id, system_id) except -1:
+        self._target_doctype(root_tag, public_id, system_id)
+
+    cdef _handleSaxPi(self, target, data):
+        return self._target_pi(target, data)
+
+    cdef _handleSaxComment(self, comment):
+        return self._target_comment(comment)
+
+
+@cython.final
+@cython.internal
+@cython.no_gc_clear  # Required because parent class uses it - Cython bug.
+cdef class _TargetParserContext(_SaxParserContext):
+    """This class maps SAX2 events to the ET parser target interface.
+    """
+    cdef object _python_target
+    cdef int _setTarget(self, target) except -1:
+        self._python_target = target
+        if not isinstance(target, _SaxParserTarget) or \
+                hasattr(target, '__dict__'):
+            target = _PythonSaxParserTarget(target)
+        self._setSaxParserTarget(target)
+        return 0
+
+    cdef _ParserContext _copy(self):
+        cdef _TargetParserContext context
+        context = _ParserContext._copy(self)
+        context._setTarget(self._python_target)
+        return context
+
+    cdef void _cleanupTargetParserContext(self, xmlDoc* result) noexcept:
+        if self._c_ctxt.myDoc is not NULL:
+            if self._c_ctxt.myDoc is not result and \
+                    self._c_ctxt.myDoc._private is NULL:
+                # no _Document proxy => orphen
+                tree.xmlFreeDoc(self._c_ctxt.myDoc)
+            self._c_ctxt.myDoc = NULL
+
+    cdef object _handleParseResult(self, _BaseParser parser, xmlDoc* result,
+                                   filename):
+        cdef bint recover
+        recover = parser._parse_options & xmlparser.XML_PARSE_RECOVER
+        try:
+            if self._has_raised():
+                self._cleanupTargetParserContext(result)
+                self._raise_if_stored()
+            if not self._c_ctxt.wellFormed and not recover:
+                _raiseParseError(self._c_ctxt, filename, self._error_log)
+        except:
+            self._python_target.close()
+            raise
+        return self._python_target.close()
+
+    cdef xmlDoc* _handleParseResultDoc(self, _BaseParser parser,
+                                       xmlDoc* result, filename) except NULL:
+        cdef bint recover
+        recover = parser._parse_options & xmlparser.XML_PARSE_RECOVER
+        if result is not NULL and result._private is NULL:
+            # no _Document proxy => orphen
+            tree.xmlFreeDoc(result)
+        try:
+            self._cleanupTargetParserContext(result)
+            self._raise_if_stored()
+            if not self._c_ctxt.wellFormed and not recover:
+                _raiseParseError(self._c_ctxt, filename, self._error_log)
+        except:
+            self._python_target.close()
+            raise
+        parse_result = self._python_target.close()
+        raise _TargetParserResult(parse_result)
diff --git a/.venv/lib/python3.12/site-packages/lxml/proxy.pxi b/.venv/lib/python3.12/site-packages/lxml/proxy.pxi
new file mode 100644
index 00000000..f7b47a73
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/proxy.pxi
@@ -0,0 +1,619 @@
+# Proxy functions and low level node allocation stuff
+
+# Proxies represent elements, their reference is stored in the C
+# structure of the respective node to avoid multiple instantiation of
+# the Python class.
+
+@cython.linetrace(False)
+@cython.profile(False)
+cdef inline _Element getProxy(xmlNode* c_node):
+    """Get a proxy for a given node.
+    """
+    #print "getProxy for:", <int>c_node
+    if c_node is not NULL and c_node._private is not NULL:
+        return <_Element>c_node._private
+    else:
+        return None
+
+
+@cython.linetrace(False)
+@cython.profile(False)
+cdef inline bint hasProxy(xmlNode* c_node):
+    if c_node._private is NULL:
+        return False
+    return True
+
+
+@cython.linetrace(False)
+@cython.profile(False)
+cdef inline int _registerProxy(_Element proxy, _Document doc,
+                               xmlNode* c_node) except -1:
+    """Register a proxy and type for the node it's proxying for.
+    """
+    #print "registering for:", <int>proxy._c_node
+    assert not hasProxy(c_node), "double registering proxy!"
+    proxy._doc = doc
+    proxy._c_node = c_node
+    c_node._private = <void*>proxy
+    return 0
+
+
+@cython.linetrace(False)
+@cython.profile(False)
+cdef inline int _unregisterProxy(_Element proxy) except -1:
+    """Unregister a proxy for the node it's proxying for.
+    """
+    cdef xmlNode* c_node = proxy._c_node
+    assert c_node._private is <void*>proxy, "Tried to unregister unknown proxy"
+    c_node._private = NULL
+    return 0
+
+
+################################################################################
+# temporarily make a node the root node of its document
+
+cdef xmlDoc* _fakeRootDoc(xmlDoc* c_base_doc, xmlNode* c_node) except NULL:
+    return _plainFakeRootDoc(c_base_doc, c_node, 1)
+
+cdef xmlDoc* _plainFakeRootDoc(xmlDoc* c_base_doc, xmlNode* c_node,
+                               bint with_siblings) except NULL:
+    # build a temporary document that has the given node as root node
+    # note that copy and original must not be modified during its lifetime!!
+    # always call _destroyFakeDoc() after use!
+    cdef xmlNode* c_child
+    cdef xmlNode* c_root
+    cdef xmlNode* c_new_root
+    cdef xmlDoc*  c_doc
+    if with_siblings or (c_node.prev is NULL and c_node.next is NULL):
+        c_root = tree.xmlDocGetRootElement(c_base_doc)
+        if c_root is c_node:
+            # already the root node, no siblings
+            return c_base_doc
+
+    c_doc  = _copyDoc(c_base_doc, 0)                   # non recursive!
+    c_new_root = tree.xmlDocCopyNode(c_node, c_doc, 2) # non recursive!
+    tree.xmlDocSetRootElement(c_doc, c_new_root)
+    _copyParentNamespaces(c_node, c_new_root)
+
+    c_new_root.children = c_node.children
+    c_new_root.last = c_node.last
+    c_new_root.next = c_new_root.prev = NULL
+
+    # store original node
+    c_doc._private = c_node
+
+    # divert parent pointers of children
+    c_child = c_new_root.children
+    while c_child is not NULL:
+        c_child.parent = c_new_root
+        c_child = c_child.next
+
+    c_doc.children = c_new_root
+    return c_doc
+
+cdef void _destroyFakeDoc(xmlDoc* c_base_doc, xmlDoc* c_doc) noexcept:
+    # delete a temporary document
+    cdef xmlNode* c_child
+    cdef xmlNode* c_parent
+    cdef xmlNode* c_root
+    if c_doc is c_base_doc:
+        return
+    c_root = tree.xmlDocGetRootElement(c_doc)
+
+    # restore parent pointers of children
+    c_parent = <xmlNode*>c_doc._private
+    c_child = c_root.children
+    while c_child is not NULL:
+        c_child.parent = c_parent
+        c_child = c_child.next
+
+    # prevent recursive removal of children
+    c_root.children = c_root.last = NULL
+    tree.xmlFreeDoc(c_doc)
+
+cdef _Element _fakeDocElementFactory(_Document doc, xmlNode* c_element):
+    """Special element factory for cases where we need to create a fake
+    root document, but still need to instantiate arbitrary nodes from
+    it.  If we instantiate the fake root node, things will turn bad
+    when it's destroyed.
+
+    Instead, if we are asked to instantiate the fake root node, we
+    instantiate the original node instead.
+    """
+    if c_element.doc is not doc._c_doc:
+        if c_element.doc._private is not NULL:
+            if c_element is c_element.doc.children:
+                c_element = <xmlNode*>c_element.doc._private
+                #assert c_element.type == tree.XML_ELEMENT_NODE
+    return _elementFactory(doc, c_element)
+
+################################################################################
+# support for freeing tree elements when proxy objects are destroyed
+
+cdef int attemptDeallocation(xmlNode* c_node) noexcept:
+    """Attempt deallocation of c_node (or higher up in tree).
+    """
+    cdef xmlNode* c_top
+    # could be we actually aren't referring to the tree at all
+    if c_node is NULL:
+        #print "not freeing, node is NULL"
+        return 0
+    c_top = getDeallocationTop(c_node)
+    if c_top is not NULL:
+        #print "freeing:", c_top.name
+        _removeText(c_top.next) # tail
+        tree.xmlFreeNode(c_top)
+        return 1
+    return 0
+
+cdef xmlNode* getDeallocationTop(xmlNode* c_node) noexcept:
+    """Return the top of the tree that can be deallocated, or NULL.
+    """
+    cdef xmlNode* c_next
+    #print "trying to do deallocating:", c_node.type
+    if hasProxy(c_node):
+        #print "Not freeing: proxies still exist"
+        return NULL
+    while c_node.parent is not NULL:
+        c_node = c_node.parent
+        #print "checking:", c_current.type
+        if c_node.type == tree.XML_DOCUMENT_NODE or \
+               c_node.type == tree.XML_HTML_DOCUMENT_NODE:
+            #print "not freeing: still in doc"
+            return NULL
+        # if we're still attached to the document, don't deallocate
+        if hasProxy(c_node):
+            #print "Not freeing: proxies still exist"
+            return NULL
+    # see whether we have children to deallocate
+    if not canDeallocateChildNodes(c_node):
+        return NULL
+    # see whether we have siblings to deallocate
+    c_next = c_node.prev
+    while c_next:
+        if _isElement(c_next):
+            if hasProxy(c_next) or not canDeallocateChildNodes(c_next):
+                return NULL
+        c_next = c_next.prev
+    c_next = c_node.next
+    while c_next:
+        if _isElement(c_next):
+            if hasProxy(c_next) or not canDeallocateChildNodes(c_next):
+                return NULL
+        c_next = c_next.next
+    return c_node
+
+cdef int canDeallocateChildNodes(xmlNode* c_parent) noexcept:
+    cdef xmlNode* c_node
+    c_node = c_parent.children
+    tree.BEGIN_FOR_EACH_ELEMENT_FROM(c_parent, c_node, 1)
+    if hasProxy(c_node):
+        return 0
+    tree.END_FOR_EACH_ELEMENT_FROM(c_node)
+    return 1
+
+################################################################################
+# fix _Document references and namespaces when a node changes documents
+
+cdef void _copyParentNamespaces(xmlNode* c_from_node, xmlNode* c_to_node) noexcept nogil:
+    """Copy the namespaces of all ancestors of c_from_node to c_to_node.
+    """
+    cdef xmlNode* c_parent
+    cdef xmlNs* c_ns
+    cdef xmlNs* c_new_ns
+    cdef int prefix_known
+    c_parent = c_from_node.parent
+    while c_parent and (tree._isElementOrXInclude(c_parent) or
+                        c_parent.type == tree.XML_DOCUMENT_NODE):
+        c_new_ns = c_parent.nsDef
+        while c_new_ns:
+            # libxml2 will check if the prefix is already defined
+            tree.xmlNewNs(c_to_node, c_new_ns.href, c_new_ns.prefix)
+            c_new_ns = c_new_ns.next
+        c_parent = c_parent.parent
+
+
+ctypedef struct _ns_update_map:
+    xmlNs* old
+    xmlNs* new
+
+
+ctypedef struct _nscache:
+    _ns_update_map* ns_map
+    size_t size
+    size_t last
+
+
+cdef int _growNsCache(_nscache* c_ns_cache) except -1:
+    cdef _ns_update_map* ns_map_ptr
+    if c_ns_cache.size == 0:
+        c_ns_cache.size = 20
+    else:
+        c_ns_cache.size *= 2
+    ns_map_ptr = <_ns_update_map*> python.lxml_realloc(
+        c_ns_cache.ns_map, c_ns_cache.size, sizeof(_ns_update_map))
+    if not ns_map_ptr:
+        python.lxml_free(c_ns_cache.ns_map)
+        c_ns_cache.ns_map = NULL
+        raise MemoryError()
+    c_ns_cache.ns_map = ns_map_ptr
+    return 0
+
+
+cdef inline int _appendToNsCache(_nscache* c_ns_cache,
+                                 xmlNs* c_old_ns, xmlNs* c_new_ns) except -1:
+    if c_ns_cache.last >= c_ns_cache.size:
+        _growNsCache(c_ns_cache)
+    c_ns_cache.ns_map[c_ns_cache.last] = _ns_update_map(old=c_old_ns, new=c_new_ns)
+    c_ns_cache.last += 1
+
+
+cdef int _stripRedundantNamespaceDeclarations(xmlNode* c_element, _nscache* c_ns_cache,
+                                              xmlNs** c_del_ns_list) except -1:
+    """Removes namespace declarations from an element that are already
+    defined in its parents.  Does not free the xmlNs's, just prepends
+    them to the c_del_ns_list.
+    """
+    cdef xmlNs* c_ns
+    cdef xmlNs* c_ns_next
+    cdef xmlNs** c_nsdef
+    # use a xmlNs** to handle assignments to "c_element.nsDef" correctly
+    c_nsdef = &c_element.nsDef
+    while c_nsdef[0] is not NULL:
+        c_ns = tree.xmlSearchNsByHref(
+            c_element.doc, c_element.parent, c_nsdef[0].href)
+        if c_ns is NULL:
+            # new namespace href => keep and cache the ns declaration
+            _appendToNsCache(c_ns_cache, c_nsdef[0], c_nsdef[0])
+            c_nsdef = &c_nsdef[0].next
+        else:
+            # known namespace href => cache mapping and strip old ns
+            _appendToNsCache(c_ns_cache, c_nsdef[0], c_ns)
+            # cut out c_nsdef.next and prepend it to garbage chain
+            c_ns_next = c_nsdef[0].next
+            c_nsdef[0].next = c_del_ns_list[0]
+            c_del_ns_list[0] = c_nsdef[0]
+            c_nsdef[0] = c_ns_next
+    return 0
+
+
+cdef void _cleanUpFromNamespaceAdaptation(xmlNode* c_start_node,
+                                          _nscache* c_ns_cache, xmlNs* c_del_ns_list) noexcept:
+    # Try to recover from exceptions with really bad timing.  We were in the middle
+    # of ripping out xmlNS-es and likely ran out of memory.  Try to fix up the tree
+    # by re-adding the original xmlNs declarations (which might still be used in some
+    # places).
+    if c_ns_cache.ns_map:
+        python.lxml_free(c_ns_cache.ns_map)
+    if c_del_ns_list:
+        if not c_start_node.nsDef:
+            c_start_node.nsDef = c_del_ns_list
+        else:
+            c_ns = c_start_node.nsDef
+            while c_ns.next:
+                c_ns = c_ns.next
+            c_ns.next = c_del_ns_list
+
+
+cdef int moveNodeToDocument(_Document doc, xmlDoc* c_source_doc,
+                            xmlNode* c_element) except -1:
+    """Fix the xmlNs pointers of a node and its subtree that were moved.
+
+    Originally copied from libxml2's xmlReconciliateNs().  Expects
+    libxml2 doc pointers of node to be correct already, but fixes
+    _Document references.
+
+    For each node in the subtree, we do this:
+
+    1) Remove redundant declarations of namespace that are already
+       defined in its parents.
+
+    2) Replace namespaces that are *not* defined on the node or its
+       parents by the equivalent namespace declarations that *are*
+       defined on the node or its parents (possibly using a different
+       prefix).  If a namespace is unknown, declare a new one on the
+       node.
+
+    3) Reassign the names of tags and attribute from the dict of the
+       target document *iff* it is different from the dict used in the
+       source subtree.
+
+    4) Set the Document reference to the new Document (if different).
+       This is done on backtracking to keep the original Document
+       alive as long as possible, until all its elements are updated.
+
+    Note that the namespace declarations are removed from the tree in
+    step 1), but freed only after the complete subtree was traversed
+    and all occurrences were replaced by tree-internal pointers.
+    """
+    cdef xmlNode* c_start_node
+    cdef xmlNode* c_node
+    cdef xmlDoc* c_doc = doc._c_doc
+    cdef tree.xmlAttr* c_attr
+    cdef char* c_name
+    cdef _nscache c_ns_cache = [NULL, 0, 0]
+    cdef xmlNs* c_del_ns_list = NULL
+    cdef proxy_count = 0
+
+    if not tree._isElementOrXInclude(c_element):
+        return 0
+
+    c_start_node = c_element
+
+    tree.BEGIN_FOR_EACH_FROM(c_element, c_element, 1)
+    if tree._isElementOrXInclude(c_element):
+        if hasProxy(c_element):
+            proxy_count += 1
+
+        # 1) cut out namespaces defined here that are already known by
+        #    the ancestors
+        if c_element.nsDef is not NULL:
+            try:
+                _stripRedundantNamespaceDeclarations(c_element, &c_ns_cache, &c_del_ns_list)
+            except:
+                _cleanUpFromNamespaceAdaptation(c_start_node, &c_ns_cache, c_del_ns_list)
+                raise
+
+        # 2) make sure the namespaces of an element and its attributes
+        #    are declared in this document (i.e. on the node or its parents)
+        if c_element.ns is not NULL:
+            _fixCNs(doc, c_start_node, c_element, &c_ns_cache, c_del_ns_list)
+
+        c_node = <xmlNode*>c_element.properties
+        while c_node is not NULL:
+            if c_node.ns is not NULL:
+                _fixCNs(doc, c_start_node, c_node, &c_ns_cache, c_del_ns_list)
+            c_node = c_node.next
+
+    tree.END_FOR_EACH_FROM(c_element)
+
+    # free now unused namespace declarations
+    if c_del_ns_list is not NULL:
+        tree.xmlFreeNsList(c_del_ns_list)
+
+    # cleanup
+    if c_ns_cache.ns_map is not NULL:
+        python.lxml_free(c_ns_cache.ns_map)
+
+    # 3) fix the names in the tree if we moved it from a different thread
+    if doc._c_doc.dict is not c_source_doc.dict:
+        fixThreadDictNames(c_start_node, c_source_doc.dict, doc._c_doc.dict)
+
+    # 4) fix _Document references
+    #    (and potentially deallocate the source document)
+    if proxy_count > 0:
+        if proxy_count == 1 and c_start_node._private is not NULL:
+            proxy = getProxy(c_start_node)
+            if proxy is not None:
+                if proxy._doc is not doc:
+                    proxy._doc = doc
+            else:
+                fixElementDocument(c_start_node, doc, proxy_count)
+        else:
+            fixElementDocument(c_start_node, doc, proxy_count)
+
+    return 0
+
+
+cdef void _setTreeDoc(xmlNode* c_node, xmlDoc* c_doc) noexcept:
+    """Adaptation of 'xmlSetTreeDoc()' that deep-fixes the document links iteratively.
+    It avoids https://gitlab.gnome.org/GNOME/libxml2/issues/42
+    """
+    tree.BEGIN_FOR_EACH_FROM(c_node, c_node, 1)
+    if c_node.type == tree.XML_ELEMENT_NODE:
+        c_attr = <tree.xmlAttr*>c_node.properties
+        while c_attr:
+            if c_attr.atype == tree.XML_ATTRIBUTE_ID:
+                tree.xmlRemoveID(c_node.doc, c_attr)
+            c_attr.doc = c_doc
+            _fixDocChildren(c_attr.children, c_doc)
+            c_attr = c_attr.next
+    # Set doc link for all nodes, not only elements.
+    c_node.doc = c_doc
+    tree.END_FOR_EACH_FROM(c_node)
+
+
+cdef inline void _fixDocChildren(xmlNode* c_child, xmlDoc* c_doc) noexcept:
+    while c_child:
+        c_child.doc = c_doc
+        if c_child.children:
+            _fixDocChildren(c_child.children, c_doc)
+        c_child = c_child.next
+
+
+cdef int _fixCNs(_Document doc, xmlNode* c_start_node, xmlNode* c_node,
+                 _nscache* c_ns_cache, xmlNs* c_del_ns_list) except -1:
+    cdef xmlNs* c_ns = NULL
+    cdef bint is_prefixed_attr = (c_node.type == tree.XML_ATTRIBUTE_NODE and c_node.ns.prefix)
+
+    for ns_map in c_ns_cache.ns_map[:c_ns_cache.last]:
+        if c_node.ns is ns_map.old:
+            if is_prefixed_attr and not ns_map.new.prefix:
+                # avoid dropping prefix from attributes
+                continue
+            c_ns = ns_map.new
+            break
+
+    if c_ns:
+        c_node.ns = c_ns
+    else:
+        # not in cache or not acceptable
+        # => find a replacement from this document
+        try:
+            c_ns = doc._findOrBuildNodeNs(
+                c_start_node, c_node.ns.href, c_node.ns.prefix,
+                c_node.type == tree.XML_ATTRIBUTE_NODE)
+            c_node.ns = c_ns
+            _appendToNsCache(c_ns_cache, c_node.ns, c_ns)
+        except:
+            _cleanUpFromNamespaceAdaptation(c_start_node, c_ns_cache, c_del_ns_list)
+            raise
+    return 0
+
+
+cdef int fixElementDocument(xmlNode* c_element, _Document doc,
+                             size_t proxy_count) except -1:
+    cdef xmlNode* c_node = c_element
+    cdef _Element proxy = None # init-to-None required due to fake-loop below
+    tree.BEGIN_FOR_EACH_FROM(c_element, c_node, 1)
+    if c_node._private is not NULL:
+        proxy = getProxy(c_node)
+        if proxy is not None:
+            if proxy._doc is not doc:
+                proxy._doc = doc
+            proxy_count -= 1
+            if proxy_count == 0:
+                return 0
+    tree.END_FOR_EACH_FROM(c_node)
+
+
+cdef void fixThreadDictNames(xmlNode* c_element,
+                             tree.xmlDict* c_src_dict,
+                             tree.xmlDict* c_dict) noexcept nogil:
+    # re-assign the names of tags and attributes
+    #
+    # this should only be called when the element is based on a
+    # different libxml2 tag name dictionary
+    if c_element.type == tree.XML_DOCUMENT_NODE or \
+            c_element.type == tree.XML_HTML_DOCUMENT_NODE:
+        # may define "xml" namespace
+        fixThreadDictNsForNode(c_element, c_src_dict, c_dict)
+        if c_element.doc.extSubset:
+            fixThreadDictNamesForDtd(c_element.doc.extSubset, c_src_dict, c_dict)
+        if c_element.doc.intSubset:
+            fixThreadDictNamesForDtd(c_element.doc.intSubset, c_src_dict, c_dict)
+        c_element = c_element.children
+        while c_element is not NULL:
+            fixThreadDictNamesForNode(c_element, c_src_dict, c_dict)
+            c_element = c_element.next
+    elif tree._isElementOrXInclude(c_element):
+        fixThreadDictNamesForNode(c_element, c_src_dict, c_dict)
+
+
+cdef inline void _fixThreadDictPtr(const_xmlChar** c_ptr,
+                                   tree.xmlDict* c_src_dict,
+                                   tree.xmlDict* c_dict) noexcept nogil:
+    c_str = c_ptr[0]
+    if c_str and c_src_dict and tree.xmlDictOwns(c_src_dict, c_str):
+        # return value can be NULL on memory error, but we don't handle that here
+        c_str = tree.xmlDictLookup(c_dict, c_str, -1)
+        if c_str:
+            c_ptr[0] = c_str
+
+
+cdef void fixThreadDictNamesForNode(xmlNode* c_element,
+                                    tree.xmlDict* c_src_dict,
+                                    tree.xmlDict* c_dict) noexcept nogil:
+    cdef xmlNode* c_node = c_element
+    tree.BEGIN_FOR_EACH_FROM(c_element, c_node, 1)
+    if c_node.type in (tree.XML_ELEMENT_NODE, tree.XML_XINCLUDE_START):
+        fixThreadDictNamesForAttributes(
+            c_node.properties, c_src_dict, c_dict)
+        fixThreadDictNsForNode(c_node, c_src_dict, c_dict)
+        _fixThreadDictPtr(&c_node.name, c_src_dict, c_dict)
+    elif c_node.type == tree.XML_TEXT_NODE:
+        # libxml2's SAX2 parser interns some indentation space
+        fixThreadDictContentForNode(c_node, c_src_dict, c_dict)
+    elif c_node.type == tree.XML_COMMENT_NODE:
+        pass  # don't touch c_node.name
+    else:
+        _fixThreadDictPtr(&c_node.name, c_src_dict, c_dict)
+    tree.END_FOR_EACH_FROM(c_node)
+
+
+cdef inline void fixThreadDictNamesForAttributes(tree.xmlAttr* c_attr,
+                                                 tree.xmlDict* c_src_dict,
+                                                 tree.xmlDict* c_dict) noexcept nogil:
+    cdef xmlNode* c_child
+    cdef xmlNode* c_node = <xmlNode*>c_attr
+    while c_node is not NULL:
+        if c_node.type not in (tree.XML_TEXT_NODE, tree.XML_COMMENT_NODE):
+            _fixThreadDictPtr(&c_node.name, c_src_dict, c_dict)
+        # libxml2 keeps some (!) attribute values in the dict
+        c_child = c_node.children
+        while c_child is not NULL:
+            fixThreadDictContentForNode(c_child, c_src_dict, c_dict)
+            c_child = c_child.next
+        c_node = c_node.next
+
+
+cdef inline void fixThreadDictContentForNode(xmlNode* c_node,
+                                             tree.xmlDict* c_src_dict,
+                                             tree.xmlDict* c_dict) noexcept nogil:
+    if c_node.content is not NULL and \
+           c_node.content is not <xmlChar*>&c_node.properties:
+        if tree.xmlDictOwns(c_src_dict, c_node.content):
+            # result can be NULL on memory error, but we don't handle that here
+            c_node.content = <xmlChar*>tree.xmlDictLookup(c_dict, c_node.content, -1)
+
+
+cdef inline void fixThreadDictNsForNode(xmlNode* c_node,
+                                        tree.xmlDict* c_src_dict,
+                                        tree.xmlDict* c_dict) noexcept nogil:
+    cdef xmlNs* c_ns = c_node.nsDef
+    while c_ns is not NULL:
+        _fixThreadDictPtr(&c_ns.href, c_src_dict, c_dict)
+        _fixThreadDictPtr(&c_ns.prefix, c_src_dict, c_dict)
+        c_ns = c_ns.next
+
+
+cdef void fixThreadDictNamesForDtd(tree.xmlDtd* c_dtd,
+                                   tree.xmlDict* c_src_dict,
+                                   tree.xmlDict* c_dict) noexcept nogil:
+    cdef xmlNode* c_node
+    cdef tree.xmlElement* c_element
+    cdef tree.xmlAttribute* c_attribute
+    cdef tree.xmlEntity* c_entity
+
+    c_node = c_dtd.children
+    while c_node:
+        if c_node.type == tree.XML_ELEMENT_DECL:
+            c_element = <tree.xmlElement*>c_node
+            if c_element.content:
+                _fixThreadDictPtr(&c_element.content.name, c_src_dict, c_dict)
+                _fixThreadDictPtr(&c_element.content.prefix, c_src_dict, c_dict)
+            c_attribute = c_element.attributes
+            while c_attribute:
+                _fixThreadDictPtr(&c_attribute.defaultValue, c_src_dict, c_dict)
+                _fixThreadDictPtr(&c_attribute.name, c_src_dict, c_dict)
+                _fixThreadDictPtr(&c_attribute.prefix, c_src_dict, c_dict)
+                _fixThreadDictPtr(&c_attribute.elem, c_src_dict, c_dict)
+                c_attribute = c_attribute.nexth
+        elif c_node.type == tree.XML_ENTITY_DECL:
+            c_entity = <tree.xmlEntity*>c_node
+            _fixThreadDictPtr(&c_entity.name, c_src_dict, c_dict)
+            _fixThreadDictPtr(&c_entity.ExternalID, c_src_dict, c_dict)
+            _fixThreadDictPtr(&c_entity.SystemID, c_src_dict, c_dict)
+            _fixThreadDictPtr(<const_xmlChar**>&c_entity.content, c_src_dict, c_dict)
+        c_node = c_node.next
+
+
+################################################################################
+# adopt an xmlDoc from an external libxml2 document source
+
+cdef _Document _adoptForeignDoc(xmlDoc* c_doc, _BaseParser parser=None, bint is_owned=True):
+    """Convert and wrap an externally produced xmlDoc for use in lxml.
+    Assures that all '_private' pointers are NULL to prevent accidental
+    dereference into lxml proxy objects.
+    """
+    if c_doc is NULL:
+        raise ValueError("Illegal document provided: NULL")
+    if c_doc.type not in (tree.XML_DOCUMENT_NODE, tree.XML_HTML_DOCUMENT_NODE):
+        doc_type = c_doc.type
+        if is_owned:
+            tree.xmlFreeDoc(c_doc)
+        raise ValueError(f"Illegal document provided: expected XML or HTML, found {doc_type}")
+
+    cdef xmlNode* c_node = <xmlNode*>c_doc
+
+    if is_owned:
+        tree.BEGIN_FOR_EACH_FROM(<xmlNode*>c_doc, c_node, 1)
+        c_node._private = NULL
+        tree.END_FOR_EACH_FROM(c_node)
+    else:
+        # create a fresh copy that lxml owns
+        c_doc = tree.xmlCopyDoc(c_doc, 1)
+        if c_doc is NULL:
+            raise MemoryError()
+
+    return _documentFactory(c_doc, parser)
diff --git a/.venv/lib/python3.12/site-packages/lxml/public-api.pxi b/.venv/lib/python3.12/site-packages/lxml/public-api.pxi
new file mode 100644
index 00000000..fb8b2a2c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/public-api.pxi
@@ -0,0 +1,178 @@
+# Public C API for lxml.etree
+
+cdef public api _Element deepcopyNodeToDocument(_Document doc, xmlNode* c_root):
+    "Recursively copy the element into the document. doc is not modified."
+    cdef xmlNode* c_node
+    c_node = _copyNodeToDoc(c_root, doc._c_doc)
+    return _elementFactory(doc, c_node)
+
+cdef public api _ElementTree elementTreeFactory(_Element context_node):
+    _assertValidNode(context_node)
+    return newElementTree(context_node, _ElementTree)
+
+cdef public api _ElementTree newElementTree(_Element context_node,
+                                            object subclass):
+    if <void*>context_node is NULL or context_node is None:
+        raise TypeError
+    _assertValidNode(context_node)
+    return _newElementTree(context_node._doc, context_node, subclass)
+
+cdef public api _ElementTree adoptExternalDocument(xmlDoc* c_doc, parser, bint is_owned):
+    if c_doc is NULL:
+        raise TypeError
+    doc = _adoptForeignDoc(c_doc, parser, is_owned)
+    return _elementTreeFactory(doc, None)
+
+cdef public api _Element elementFactory(_Document doc, xmlNode* c_node):
+    if c_node is NULL or doc is None:
+        raise TypeError
+    return _elementFactory(doc, c_node)
+
+cdef public api _Element makeElement(tag, _Document doc, parser,
+                                     text, tail, attrib, nsmap):
+    return _makeElement(tag, NULL, doc, parser, text, tail, attrib, nsmap, None)
+
+cdef public api _Element makeSubElement(_Element parent, tag, text, tail,
+                                        attrib, nsmap):
+    _assertValidNode(parent)
+    return _makeSubElement(parent, tag, text, tail, attrib, nsmap, None)
+
+cdef public api void setElementClassLookupFunction(
+    _element_class_lookup_function function, state):
+    _setElementClassLookupFunction(function, state)
+
+cdef public api object lookupDefaultElementClass(state, doc, xmlNode* c_node):
+    return _lookupDefaultElementClass(state, doc, c_node)
+
+cdef public api object lookupNamespaceElementClass(state, doc, xmlNode* c_node):
+    return _find_nselement_class(state, doc, c_node)
+
+cdef public api object callLookupFallback(FallbackElementClassLookup lookup,
+                                          _Document doc, xmlNode* c_node):
+    return _callLookupFallback(lookup, doc, c_node)
+
+cdef public api int tagMatches(xmlNode* c_node, const_xmlChar* c_href, const_xmlChar* c_name):
+    if c_node is NULL:
+        return -1
+    return _tagMatches(c_node, c_href, c_name)
+
+cdef public api _Document documentOrRaise(object input):
+    return _documentOrRaise(input)
+
+cdef public api _Element rootNodeOrRaise(object input):
+    return _rootNodeOrRaise(input)
+
+cdef public api bint hasText(xmlNode* c_node):
+    return _hasText(c_node)
+
+cdef public api bint hasTail(xmlNode* c_node):
+    return _hasTail(c_node)
+
+cdef public api unicode textOf(xmlNode* c_node):
+    if c_node is NULL:
+        return None
+    return _collectText(c_node.children)
+
+cdef public api unicode tailOf(xmlNode* c_node):
+    if c_node is NULL:
+        return None
+    return _collectText(c_node.next)
+
+cdef public api int setNodeText(xmlNode* c_node, text) except -1:
+    if c_node is NULL:
+        raise ValueError
+    return _setNodeText(c_node, text)
+
+cdef public api int setTailText(xmlNode* c_node, text) except -1:
+    if c_node is NULL:
+        raise ValueError
+    return _setTailText(c_node, text)
+
+cdef public api unicode attributeValue(xmlNode* c_element, xmlAttr* c_attrib_node):
+    return _attributeValue(c_element, c_attrib_node)
+
+cdef public api unicode attributeValueFromNsName(xmlNode* c_element,
+                                                const_xmlChar* ns, const_xmlChar* name):
+    return _attributeValueFromNsName(c_element, ns, name)
+
+cdef public api object getAttributeValue(_Element element, key, default):
+    _assertValidNode(element)
+    return _getAttributeValue(element, key, default)
+
+cdef public api object iterattributes(_Element element, int keysvalues):
+    _assertValidNode(element)
+    return _attributeIteratorFactory(element, keysvalues)
+
+cdef public api list collectAttributes(xmlNode* c_element, int keysvalues):
+    return _collectAttributes(c_element, keysvalues)
+
+cdef public api int setAttributeValue(_Element element, key, value) except -1:
+    _assertValidNode(element)
+    return _setAttributeValue(element, key, value)
+
+cdef public api int delAttribute(_Element element, key) except -1:
+    _assertValidNode(element)
+    return _delAttribute(element, key)
+
+cdef public api int delAttributeFromNsName(tree.xmlNode* c_element,
+                                           const_xmlChar* c_href, const_xmlChar* c_name):
+    return _delAttributeFromNsName(c_element, c_href, c_name)
+
+cdef public api bint hasChild(xmlNode* c_node):
+    return _hasChild(c_node)
+
+cdef public api xmlNode* findChild(xmlNode* c_node, Py_ssize_t index):
+    return _findChild(c_node, index)
+
+cdef public api xmlNode* findChildForwards(xmlNode* c_node, Py_ssize_t index):
+    return _findChildForwards(c_node, index)
+
+cdef public api xmlNode* findChildBackwards(xmlNode* c_node, Py_ssize_t index):
+    return _findChildBackwards(c_node, index)
+
+cdef public api xmlNode* nextElement(xmlNode* c_node):
+    return _nextElement(c_node)
+
+cdef public api xmlNode* previousElement(xmlNode* c_node):
+    return _previousElement(c_node)
+
+cdef public api void appendChild(_Element parent, _Element child):
+    # deprecated, use appendChildToElement() instead!
+    _appendChild(parent, child)
+
+cdef public api int appendChildToElement(_Element parent, _Element child) except -1:
+    return _appendChild(parent, child)
+
+cdef public api unicode pyunicode(const_xmlChar* s):
+    if s is NULL:
+        raise TypeError
+    return funicode(s)
+
+cdef public api bytes utf8(object s):
+    return _utf8(s)
+
+cdef public api tuple getNsTag(object tag):
+    return _getNsTag(tag)
+
+cdef public api tuple getNsTagWithEmptyNs(object tag):
+    return _getNsTagWithEmptyNs(tag)
+
+cdef public api unicode namespacedName(xmlNode* c_node):
+    return _namespacedName(c_node)
+
+cdef public api unicode namespacedNameFromNsName(const_xmlChar* href, const_xmlChar* name):
+    return _namespacedNameFromNsName(href, name)
+
+cdef public api void iteratorStoreNext(_ElementIterator iterator, _Element node):
+    # deprecated!
+    iterator._storeNext(node)
+
+cdef public api void initTagMatch(_ElementTagMatcher matcher, tag):
+    # deprecated!
+    matcher._initTagMatch(tag)
+
+cdef public api tree.xmlNs* findOrBuildNodeNsPrefix(
+        _Document doc, xmlNode* c_node, const_xmlChar* href, const_xmlChar* prefix) except NULL:
+    if doc is None:
+        raise TypeError
+    return doc._findOrBuildNodeNs(c_node, href, prefix, 0)
diff --git a/.venv/lib/python3.12/site-packages/lxml/pyclasslookup.py b/.venv/lib/python3.12/site-packages/lxml/pyclasslookup.py
new file mode 100644
index 00000000..9e1496df
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/pyclasslookup.py
@@ -0,0 +1,3 @@
+# dummy module for backwards compatibility
+
+from lxml.etree import PythonElementClassLookup
diff --git a/.venv/lib/python3.12/site-packages/lxml/readonlytree.pxi b/.venv/lib/python3.12/site-packages/lxml/readonlytree.pxi
new file mode 100644
index 00000000..9bc9a660
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/readonlytree.pxi
@@ -0,0 +1,565 @@
+# read-only tree implementation
+
+@cython.internal
+cdef class _ReadOnlyProxy:
+    "A read-only proxy class suitable for PIs/Comments (for internal use only!)."
+    cdef bint _free_after_use
+    cdef xmlNode* _c_node
+    cdef _ReadOnlyProxy _source_proxy
+    cdef list _dependent_proxies
+    def __cinit__(self):
+        self._c_node = NULL
+        self._free_after_use = 0
+
+    cdef int _assertNode(self) except -1:
+        """This is our way of saying: this proxy is invalid!
+        """
+        if not self._c_node:
+            raise ReferenceError("Proxy invalidated!")
+        return 0
+
+    cdef int _raise_unsupported_type(self) except -1:
+        raise TypeError(f"Unsupported node type: {self._c_node.type}")
+
+    cdef void free_after_use(self) noexcept:
+        """Should the xmlNode* be freed when releasing the proxy?
+        """
+        self._free_after_use = 1
+
+    @property
+    def tag(self):
+        """Element tag
+        """
+        self._assertNode()
+        if self._c_node.type == tree.XML_ELEMENT_NODE:
+            return _namespacedName(self._c_node)
+        elif self._c_node.type == tree.XML_PI_NODE:
+            return ProcessingInstruction
+        elif self._c_node.type == tree.XML_COMMENT_NODE:
+            return Comment
+        elif self._c_node.type == tree.XML_ENTITY_REF_NODE:
+            return Entity
+        else:
+            self._raise_unsupported_type()
+
+    @property
+    def text(self):
+        """Text before the first subelement. This is either a string or
+        the value None, if there was no text.
+        """
+        self._assertNode()
+        if self._c_node.type == tree.XML_ELEMENT_NODE:
+            return _collectText(self._c_node.children)
+        elif self._c_node.type in (tree.XML_PI_NODE,
+                                   tree.XML_COMMENT_NODE):
+            if self._c_node.content is NULL:
+                return ''
+            else:
+                return funicode(self._c_node.content)
+        elif self._c_node.type == tree.XML_ENTITY_REF_NODE:
+            return f'&{funicode(self._c_node.name)};'
+        else:
+            self._raise_unsupported_type()
+        
+    @property
+    def tail(self):
+        """Text after this element's end tag, but before the next sibling
+        element's start tag. This is either a string or the value None, if
+        there was no text.
+        """
+        self._assertNode()
+        return _collectText(self._c_node.next)
+
+    @property
+    def sourceline(self):
+        """Original line number as found by the parser or None if unknown.
+        """
+        cdef long line
+        self._assertNode()
+        line = tree.xmlGetLineNo(self._c_node)
+        if line > 0:
+            return line
+        else:
+            return None
+
+    def __repr__(self):
+        self._assertNode()
+        if self._c_node.type == tree.XML_ELEMENT_NODE:
+            return "<Element %s at 0x%x>" % (self.tag, id(self))
+        elif self._c_node.type == tree.XML_COMMENT_NODE:
+            return "<!--%s-->" % self.text
+        elif self._c_node.type == tree.XML_ENTITY_NODE:
+            return "&%s;" % funicode(self._c_node.name)
+        elif self._c_node.type == tree.XML_PI_NODE:
+            text = self.text
+            if text:
+                return "<?%s %s?>" % (self.target, text)
+            else:
+                return "<?%s?>" % self.target
+        else:
+            self._raise_unsupported_type()
+
+    def __getitem__(self, x):
+        """Returns the subelement at the given position or the requested
+        slice.
+        """
+        cdef xmlNode* c_node = NULL
+        cdef Py_ssize_t step = 0, slicelength = 0
+        cdef Py_ssize_t c, i
+        cdef _node_to_node_function next_element
+        cdef list result
+        self._assertNode()
+        if isinstance(x, slice):
+            # slicing
+            if _isFullSlice(<slice>x):
+                return _collectChildren(self)
+            _findChildSlice(<slice>x, self._c_node, &c_node, &step, &slicelength)
+            if c_node is NULL:
+                return []
+            if step > 0:
+                next_element = _nextElement
+            else:
+                step = -step
+                next_element = _previousElement
+            result = []
+            c = 0
+            while c_node is not NULL and c < slicelength:
+                result.append(_newReadOnlyProxy(self._source_proxy, c_node))
+                result.append(_elementFactory(self._doc, c_node))
+                c = c + 1
+                for i from 0 <= i < step:
+                    c_node = next_element(c_node)
+            return result
+        else:
+            # indexing
+            c_node = _findChild(self._c_node, x)
+            if c_node is NULL:
+                raise IndexError, "list index out of range"
+            return _newReadOnlyProxy(self._source_proxy, c_node)
+
+    def __len__(self):
+        """Returns the number of subelements.
+        """
+        cdef Py_ssize_t c
+        cdef xmlNode* c_node
+        self._assertNode()
+        c = 0
+        c_node = self._c_node.children
+        while c_node is not NULL:
+            if tree._isElement(c_node):
+                c = c + 1
+            c_node = c_node.next
+        return c
+
+    def __bool__(self):
+        cdef xmlNode* c_node
+        self._assertNode()
+        c_node = _findChildBackwards(self._c_node, 0)
+        return c_node != NULL
+
+    def __deepcopy__(self, memo):
+        "__deepcopy__(self, memo)"
+        return self.__copy__()
+        
+    cpdef __copy__(self):
+        "__copy__(self)"
+        cdef xmlDoc* c_doc
+        cdef xmlNode* c_node
+        cdef _Document new_doc
+        if self._c_node is NULL:
+            return self
+        c_doc = _copyDocRoot(self._c_node.doc, self._c_node) # recursive
+        new_doc = _documentFactory(c_doc, None)
+        root = new_doc.getroot()
+        if root is not None:
+            return root
+        # Comment/PI
+        c_node = c_doc.children
+        while c_node is not NULL and c_node.type != self._c_node.type:
+            c_node = c_node.next
+        if c_node is NULL:
+            return None
+        return _elementFactory(new_doc, c_node)
+
+    def __iter__(self):
+        return iter(self.getchildren())
+
+    def iterchildren(self, tag=None, *, reversed=False):
+        """iterchildren(self, tag=None, reversed=False)
+
+        Iterate over the children of this element.
+        """
+        children = self.getchildren()
+        if tag is not None and tag != '*':
+            children = [ el for el in children if el.tag == tag ]
+        if reversed:
+            children = children[::-1]
+        return iter(children)
+
+    cpdef getchildren(self):
+        """Returns all subelements. The elements are returned in document
+        order.
+        """
+        cdef xmlNode* c_node
+        cdef list result
+        self._assertNode()
+        result = []
+        c_node = self._c_node.children
+        while c_node is not NULL:
+            if tree._isElement(c_node):
+                result.append(_newReadOnlyProxy(self._source_proxy, c_node))
+            c_node = c_node.next
+        return result
+
+    def getparent(self):
+        """Returns the parent of this element or None for the root element.
+        """
+        cdef xmlNode* c_parent
+        self._assertNode()
+        c_parent = self._c_node.parent
+        if c_parent is NULL or not tree._isElement(c_parent):
+            return None
+        else:
+            return _newReadOnlyProxy(self._source_proxy, c_parent)
+
+    def getnext(self):
+        """Returns the following sibling of this element or None.
+        """
+        cdef xmlNode* c_node
+        self._assertNode()
+        c_node = _nextElement(self._c_node)
+        if c_node is not NULL:
+            return _newReadOnlyProxy(self._source_proxy, c_node)
+        return None
+
+    def getprevious(self):
+        """Returns the preceding sibling of this element or None.
+        """
+        cdef xmlNode* c_node
+        self._assertNode()
+        c_node = _previousElement(self._c_node)
+        if c_node is not NULL:
+            return _newReadOnlyProxy(self._source_proxy, c_node)
+        return None
+
+
+@cython.final
+@cython.internal
+cdef class _ReadOnlyPIProxy(_ReadOnlyProxy):
+    """A read-only proxy for processing instructions (for internal use only!)"""
+    @property
+    def target(self):
+        self._assertNode()
+        return funicode(self._c_node.name)
+
+@cython.final
+@cython.internal
+cdef class _ReadOnlyEntityProxy(_ReadOnlyProxy):
+    """A read-only proxy for entity references (for internal use only!)"""
+    property name:
+        def __get__(self):
+            return funicode(self._c_node.name)
+
+        def __set__(self, value):
+            value_utf = _utf8(value)
+            if '&' in value or ';' in value:
+                raise ValueError(f"Invalid entity name '{value}'")
+            tree.xmlNodeSetName(self._c_node, _xcstr(value_utf))
+
+    @property
+    def text(self):
+        return f'&{funicode(self._c_node.name)};'
+
+
+@cython.internal
+cdef class _ReadOnlyElementProxy(_ReadOnlyProxy):
+    """The main read-only Element proxy class (for internal use only!)."""
+
+    @property
+    def attrib(self):
+        self._assertNode()
+        return dict(_collectAttributes(self._c_node, 3))
+
+    @property
+    def prefix(self):
+        """Namespace prefix or None.
+        """
+        self._assertNode()
+        if self._c_node.ns is not NULL:
+            if self._c_node.ns.prefix is not NULL:
+                return funicode(self._c_node.ns.prefix)
+        return None
+
+    @property
+    def nsmap(self):
+        """Namespace prefix->URI mapping known in the context of this
+        Element.  This includes all namespace declarations of the
+        parents.
+
+        Note that changing the returned dict has no effect on the Element.
+        """
+        self._assertNode()
+        return _build_nsmap(self._c_node)
+
+    def get(self, key, default=None):
+        """Gets an element attribute.
+        """
+        self._assertNode()
+        return _getNodeAttributeValue(self._c_node, key, default)
+
+    def keys(self):
+        """Gets a list of attribute names. The names are returned in an
+        arbitrary order (just like for an ordinary Python dictionary).
+        """
+        self._assertNode()
+        return _collectAttributes(self._c_node, 1)
+
+    def values(self):
+        """Gets element attributes, as a sequence. The attributes are returned
+        in an arbitrary order.
+        """
+        self._assertNode()
+        return _collectAttributes(self._c_node, 2)
+
+    def items(self):
+        """Gets element attributes, as a sequence. The attributes are returned
+        in an arbitrary order.
+        """
+        self._assertNode()
+        return _collectAttributes(self._c_node, 3)
+
+cdef _ReadOnlyProxy _newReadOnlyProxy(
+    _ReadOnlyProxy source_proxy, xmlNode* c_node):
+    cdef _ReadOnlyProxy el
+    if c_node.type == tree.XML_ELEMENT_NODE:
+        el = _ReadOnlyElementProxy.__new__(_ReadOnlyElementProxy)
+    elif c_node.type == tree.XML_PI_NODE:
+        el = _ReadOnlyPIProxy.__new__(_ReadOnlyPIProxy)
+    elif c_node.type in (tree.XML_COMMENT_NODE,
+                         tree.XML_ENTITY_REF_NODE):
+        el = _ReadOnlyProxy.__new__(_ReadOnlyProxy)
+    else:
+        raise TypeError(f"Unsupported element type: {c_node.type}")
+    el._c_node = c_node
+    _initReadOnlyProxy(el, source_proxy)
+    return el
+
+cdef inline _initReadOnlyProxy(_ReadOnlyProxy el,
+                               _ReadOnlyProxy source_proxy):
+    if source_proxy is None:
+        el._source_proxy = el
+        el._dependent_proxies = [el]
+    else:
+        el._source_proxy = source_proxy
+        source_proxy._dependent_proxies.append(el)
+
+cdef _freeReadOnlyProxies(_ReadOnlyProxy sourceProxy):
+    cdef xmlNode* c_node
+    cdef _ReadOnlyProxy el
+    if sourceProxy is None:
+        return
+    if sourceProxy._dependent_proxies is None:
+        return
+    for el in sourceProxy._dependent_proxies:
+        c_node = el._c_node
+        el._c_node = NULL
+        if el._free_after_use:
+            tree.xmlFreeNode(c_node)
+    del sourceProxy._dependent_proxies[:]
+
+# opaque wrapper around non-element nodes, e.g. the document node
+#
+# This class does not imply any restrictions on modifiability or
+# read-only status of the node, so use with caution.
+
+@cython.internal
+cdef class _OpaqueNodeWrapper:
+    cdef tree.xmlNode* _c_node
+    def __init__(self):
+        raise TypeError, "This type cannot be instantiated from Python"
+
+@cython.final
+@cython.internal
+cdef class _OpaqueDocumentWrapper(_OpaqueNodeWrapper):
+    cdef int _assertNode(self) except -1:
+        """This is our way of saying: this proxy is invalid!
+        """
+        assert self._c_node is not NULL, "Proxy invalidated!"
+        return 0
+
+    cpdef append(self, other_element):
+        """Append a copy of an Element to the list of children.
+        """
+        cdef xmlNode* c_next
+        cdef xmlNode* c_node
+        self._assertNode()
+        c_node = _roNodeOf(other_element)
+        if c_node.type == tree.XML_ELEMENT_NODE:
+            if tree.xmlDocGetRootElement(<tree.xmlDoc*>self._c_node) is not NULL:
+                raise ValueError, "cannot append, document already has a root element"
+        elif c_node.type not in (tree.XML_PI_NODE, tree.XML_COMMENT_NODE):
+            raise TypeError, f"unsupported element type for top-level node: {c_node.type}"
+        c_node = _copyNodeToDoc(c_node, <tree.xmlDoc*>self._c_node)
+        c_next = c_node.next
+        tree.xmlAddChild(self._c_node, c_node)
+        _moveTail(c_next, c_node)
+
+    def extend(self, elements):
+        """Append a copy of all Elements from a sequence to the list of
+        children.
+        """
+        self._assertNode()
+        for element in elements:
+            self.append(element)
+
+cdef _OpaqueNodeWrapper _newOpaqueAppendOnlyNodeWrapper(xmlNode* c_node):
+    cdef _OpaqueNodeWrapper node
+    if c_node.type in (tree.XML_DOCUMENT_NODE, tree.XML_HTML_DOCUMENT_NODE):
+        node = _OpaqueDocumentWrapper.__new__(_OpaqueDocumentWrapper)
+    else:
+        node = _OpaqueNodeWrapper.__new__(_OpaqueNodeWrapper)
+    node._c_node = c_node
+    return node
+
+# element proxies that allow restricted modification
+
+@cython.internal
+cdef class _ModifyContentOnlyProxy(_ReadOnlyProxy):
+    """A read-only proxy that allows changing the text content.
+    """
+    property text:
+        def __get__(self):
+            self._assertNode()
+            if self._c_node.content is NULL:
+                return ''
+            else:
+                return funicode(self._c_node.content)
+
+        def __set__(self, value):
+            cdef tree.xmlDict* c_dict
+            self._assertNode()
+            if value is None:
+                c_text = <const_xmlChar*>NULL
+            else:
+                value = _utf8(value)
+                c_text = _xcstr(value)
+            tree.xmlNodeSetContent(self._c_node, c_text)
+
+@cython.final
+@cython.internal
+cdef class _ModifyContentOnlyPIProxy(_ModifyContentOnlyProxy):
+    """A read-only proxy that allows changing the text/target content of a
+    processing instruction.
+    """
+    property target:
+        def __get__(self):
+            self._assertNode()
+            return funicode(self._c_node.name)
+
+        def __set__(self, value):
+            self._assertNode()
+            value = _utf8(value)
+            c_text = _xcstr(value)
+            tree.xmlNodeSetName(self._c_node, c_text)
+
+@cython.final
+@cython.internal
+cdef class _ModifyContentOnlyEntityProxy(_ModifyContentOnlyProxy):
+    "A read-only proxy for entity references (for internal use only!)"
+    property name:
+        def __get__(self):
+            return funicode(self._c_node.name)
+
+        def __set__(self, value):
+            value = _utf8(value)
+            assert '&' not in value and ';' not in value, \
+                f"Invalid entity name '{value}'"
+            c_text = _xcstr(value)
+            tree.xmlNodeSetName(self._c_node, c_text)
+
+
+@cython.final
+@cython.internal
+cdef class _AppendOnlyElementProxy(_ReadOnlyElementProxy):
+    """A read-only element that allows adding children and changing the
+    text content (i.e. everything that adds to the subtree).
+    """
+    cpdef append(self, other_element):
+        """Append a copy of an Element to the list of children.
+        """
+        cdef xmlNode* c_next
+        cdef xmlNode* c_node
+        self._assertNode()
+        c_node = _roNodeOf(other_element)
+        c_node = _copyNodeToDoc(c_node, self._c_node.doc)
+        c_next = c_node.next
+        tree.xmlAddChild(self._c_node, c_node)
+        _moveTail(c_next, c_node)
+            
+    def extend(self, elements):
+        """Append a copy of all Elements from a sequence to the list of
+        children.
+        """
+        self._assertNode()
+        for element in elements:
+            self.append(element)
+
+    property text:
+        """Text before the first subelement. This is either a string or the
+        value None, if there was no text.
+        """
+        def __get__(self):
+            self._assertNode()
+            return _collectText(self._c_node.children)
+
+        def __set__(self, value):
+            self._assertNode()
+            if isinstance(value, QName):
+                value = _resolveQNameText(self, value).decode('utf8')
+            _setNodeText(self._c_node, value)
+
+
+cdef _ReadOnlyProxy _newAppendOnlyProxy(
+    _ReadOnlyProxy source_proxy, xmlNode* c_node):
+    cdef _ReadOnlyProxy el
+    if c_node.type == tree.XML_ELEMENT_NODE:
+        el = _AppendOnlyElementProxy.__new__(_AppendOnlyElementProxy)
+    elif c_node.type == tree.XML_PI_NODE:
+        el = _ModifyContentOnlyPIProxy.__new__(_ModifyContentOnlyPIProxy)
+    elif c_node.type == tree.XML_COMMENT_NODE:
+        el = _ModifyContentOnlyProxy.__new__(_ModifyContentOnlyProxy)
+    else:
+        raise TypeError(f"Unsupported element type: {c_node.type}")
+    el._c_node = c_node
+    _initReadOnlyProxy(el, source_proxy)
+    return el
+
+cdef xmlNode* _roNodeOf(element) except NULL:
+    cdef xmlNode* c_node
+    if isinstance(element, _Element):
+        c_node = (<_Element>element)._c_node
+    elif isinstance(element, _ReadOnlyProxy):
+        c_node = (<_ReadOnlyProxy>element)._c_node
+    elif isinstance(element, _OpaqueNodeWrapper):
+        c_node = (<_OpaqueNodeWrapper>element)._c_node
+    else:
+        raise TypeError, f"invalid argument type {type(element)}"
+
+    if c_node is NULL:
+        raise TypeError, "invalid element"
+    return c_node
+
+cdef xmlNode* _nonRoNodeOf(element) except NULL:
+    cdef xmlNode* c_node
+    if isinstance(element, _Element):
+        c_node = (<_Element>element)._c_node
+    elif isinstance(element, _AppendOnlyElementProxy):
+        c_node = (<_AppendOnlyElementProxy>element)._c_node
+    elif isinstance(element, _OpaqueNodeWrapper):
+        c_node = (<_OpaqueNodeWrapper>element)._c_node
+    else:
+        raise TypeError, f"invalid argument type {type(element)}"
+
+    if c_node is NULL:
+        raise TypeError, "invalid element"
+    return c_node
diff --git a/.venv/lib/python3.12/site-packages/lxml/relaxng.pxi b/.venv/lib/python3.12/site-packages/lxml/relaxng.pxi
new file mode 100644
index 00000000..35f87589
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/relaxng.pxi
@@ -0,0 +1,165 @@
+# support for RelaxNG validation
+from lxml.includes cimport relaxng
+
+cdef object _rnc2rng
+try:
+    import rnc2rng as _rnc2rng
+except ImportError:
+    _rnc2rng = None
+
+
+cdef int _require_rnc2rng() except -1:
+    if _rnc2rng is None:
+        raise RelaxNGParseError(
+            'compact syntax not supported (please install rnc2rng)')
+    return 0
+
+
+cdef class RelaxNGError(LxmlError):
+    """Base class for RelaxNG errors.
+    """
+
+cdef class RelaxNGParseError(RelaxNGError):
+    """Error while parsing an XML document as RelaxNG.
+    """
+
+cdef class RelaxNGValidateError(RelaxNGError):
+    """Error while validating an XML document with a RelaxNG schema.
+    """
+
+
+################################################################################
+# RelaxNG
+
+cdef class RelaxNG(_Validator):
+    """RelaxNG(self, etree=None, file=None)
+    Turn a document into a Relax NG validator.
+
+    Either pass a schema as Element or ElementTree, or pass a file or
+    filename through the ``file`` keyword argument.
+    """
+    cdef relaxng.xmlRelaxNG* _c_schema
+    def __cinit__(self):
+        self._c_schema = NULL
+
+    def __init__(self, etree=None, *, file=None):
+        cdef _Document doc
+        cdef _Element root_node
+        cdef xmlDoc* fake_c_doc = NULL
+        cdef relaxng.xmlRelaxNGParserCtxt* parser_ctxt = NULL
+        _Validator.__init__(self)
+        if etree is not None:
+            doc = _documentOrRaise(etree)
+            root_node = _rootNodeOrRaise(etree)
+            fake_c_doc = _fakeRootDoc(doc._c_doc, root_node._c_node)
+            parser_ctxt = relaxng.xmlRelaxNGNewDocParserCtxt(fake_c_doc)
+        elif file is not None:
+            if _isString(file):
+                if file[-4:].lower() == '.rnc':
+                    _require_rnc2rng()
+                    rng_data_utf8 = _utf8(_rnc2rng.dumps(_rnc2rng.load(file)))
+                    doc = _parseMemoryDocument(rng_data_utf8, parser=None, url=file)
+                    parser_ctxt = relaxng.xmlRelaxNGNewDocParserCtxt(doc._c_doc)
+                else:
+                    doc = None
+                    filename = _encodeFilename(file)
+                    with self._error_log:
+                        orig_loader = _register_document_loader()
+                        parser_ctxt = relaxng.xmlRelaxNGNewParserCtxt(_cstr(filename))
+                        _reset_document_loader(orig_loader)
+            elif (_getFilenameForFile(file) or '')[-4:].lower() == '.rnc':
+                _require_rnc2rng()
+                rng_data_utf8 = _utf8(_rnc2rng.dumps(_rnc2rng.load(file)))
+                doc = _parseMemoryDocument(
+                    rng_data_utf8, parser=None, url=_getFilenameForFile(file))
+                parser_ctxt = relaxng.xmlRelaxNGNewDocParserCtxt(doc._c_doc)
+            else:
+                doc = _parseDocument(file, parser=None, base_url=None)
+                parser_ctxt = relaxng.xmlRelaxNGNewDocParserCtxt(doc._c_doc)
+        else:
+            raise RelaxNGParseError, "No tree or file given"
+
+        if parser_ctxt is NULL:
+            if fake_c_doc is not NULL:
+                _destroyFakeDoc(doc._c_doc, fake_c_doc)
+            raise RelaxNGParseError(
+                self._error_log._buildExceptionMessage(
+                    "Document is not parsable as Relax NG"),
+                self._error_log)
+
+        # Need a cast here because older libxml2 releases do not use 'const' in the functype.
+        relaxng.xmlRelaxNGSetParserStructuredErrors(
+            parser_ctxt, <xmlerror.xmlStructuredErrorFunc> _receiveError, <void*>self._error_log)
+        _connectGenericErrorLog(self._error_log, xmlerror.XML_FROM_RELAXNGP)
+        self._c_schema = relaxng.xmlRelaxNGParse(parser_ctxt)
+        _connectGenericErrorLog(None)
+
+        relaxng.xmlRelaxNGFreeParserCtxt(parser_ctxt)
+        if self._c_schema is NULL:
+            if fake_c_doc is not NULL:
+                _destroyFakeDoc(doc._c_doc, fake_c_doc)
+            raise RelaxNGParseError(
+                self._error_log._buildExceptionMessage(
+                    "Document is not valid Relax NG"),
+                self._error_log)
+        if fake_c_doc is not NULL:
+            _destroyFakeDoc(doc._c_doc, fake_c_doc)
+
+    def __dealloc__(self):
+        relaxng.xmlRelaxNGFree(self._c_schema)
+
+    def __call__(self, etree):
+        """__call__(self, etree)
+
+        Validate doc using Relax NG.
+
+        Returns true if document is valid, false if not."""
+        cdef _Document doc
+        cdef _Element root_node
+        cdef xmlDoc* c_doc
+        cdef relaxng.xmlRelaxNGValidCtxt* valid_ctxt
+        cdef int ret
+
+        assert self._c_schema is not NULL, "RelaxNG instance not initialised"
+        doc = _documentOrRaise(etree)
+        root_node = _rootNodeOrRaise(etree)
+
+        valid_ctxt = relaxng.xmlRelaxNGNewValidCtxt(self._c_schema)
+        if valid_ctxt is NULL:
+            raise MemoryError()
+
+        try:
+            self._error_log.clear()
+            # Need a cast here because older libxml2 releases do not use 'const' in the functype.
+            relaxng.xmlRelaxNGSetValidStructuredErrors(
+                valid_ctxt, <xmlerror.xmlStructuredErrorFunc> _receiveError, <void*>self._error_log)
+            _connectGenericErrorLog(self._error_log, xmlerror.XML_FROM_RELAXNGV)
+            c_doc = _fakeRootDoc(doc._c_doc, root_node._c_node)
+            with nogil:
+                ret = relaxng.xmlRelaxNGValidateDoc(valid_ctxt, c_doc)
+            _destroyFakeDoc(doc._c_doc, c_doc)
+        finally:
+            _connectGenericErrorLog(None)
+            relaxng.xmlRelaxNGFreeValidCtxt(valid_ctxt)
+
+        if ret == -1:
+            raise RelaxNGValidateError(
+                "Internal error in Relax NG validation",
+                self._error_log)
+        if ret == 0:
+            return True
+        else:
+            return False
+
+    @classmethod
+    def from_rnc_string(cls, src, base_url=None):
+        """Parse a RelaxNG schema in compact syntax from a text string
+
+        Requires the rnc2rng package to be installed.
+
+        Passing the source URL or file path of the source as 'base_url'
+        will enable resolving resource references relative to the source.
+        """
+        _require_rnc2rng()
+        rng_str = utf8(_rnc2rng.dumps(_rnc2rng.loads(src)))
+        return cls(_parseMemoryDocument(rng_str, parser=None, url=base_url))
diff --git a/.venv/lib/python3.12/site-packages/lxml/sax.cpython-312-x86_64-linux-gnu.so b/.venv/lib/python3.12/site-packages/lxml/sax.cpython-312-x86_64-linux-gnu.so
new file mode 100755
index 00000000..417417e6
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/sax.cpython-312-x86_64-linux-gnu.so
Binary files differdiff --git a/.venv/lib/python3.12/site-packages/lxml/sax.py b/.venv/lib/python3.12/site-packages/lxml/sax.py
new file mode 100644
index 00000000..eee44226
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/sax.py
@@ -0,0 +1,275 @@
+# cython: language_level=2
+
+"""
+SAX-based adapter to copy trees from/to the Python standard library.
+
+Use the `ElementTreeContentHandler` class to build an ElementTree from
+SAX events.
+
+Use the `ElementTreeProducer` class or the `saxify()` function to fire
+the SAX events of an ElementTree against a SAX ContentHandler.
+
+See https://lxml.de/sax.html
+"""
+
+
+from xml.sax.handler import ContentHandler
+from lxml import etree
+from lxml.etree import ElementTree, SubElement
+from lxml.etree import Comment, ProcessingInstruction
+
+
+class SaxError(etree.LxmlError):
+    """General SAX error.
+    """
+
+
+def _getNsTag(tag):
+    if tag[0] == '{':
+        return tuple(tag[1:].split('}', 1))
+    else:
+        return None, tag
+
+
+class ElementTreeContentHandler(ContentHandler):
+    """Build an lxml ElementTree from SAX events.
+    """
+    def __init__(self, makeelement=None):
+        ContentHandler.__init__(self)
+        self._root = None
+        self._root_siblings = []
+        self._element_stack = []
+        self._default_ns = None
+        self._ns_mapping = { None : [None] }
+        self._new_mappings = {}
+        if makeelement is None:
+            makeelement = etree.Element
+        self._makeelement = makeelement
+
+    def _get_etree(self):
+        "Contains the generated ElementTree after parsing is finished."
+        return ElementTree(self._root)
+
+    etree = property(_get_etree, doc=_get_etree.__doc__)
+
+    def setDocumentLocator(self, locator):
+        pass
+
+    def startDocument(self):
+        pass
+
+    def endDocument(self):
+        pass
+
+    def startPrefixMapping(self, prefix, uri):
+        self._new_mappings[prefix] = uri
+        try:
+            self._ns_mapping[prefix].append(uri)
+        except KeyError:
+            self._ns_mapping[prefix] = [uri]
+        if prefix is None:
+            self._default_ns = uri
+
+    def endPrefixMapping(self, prefix):
+        ns_uri_list = self._ns_mapping[prefix]
+        ns_uri_list.pop()
+        if prefix is None:
+            self._default_ns = ns_uri_list[-1]
+
+    def _buildTag(self, ns_name_tuple):
+        ns_uri, local_name = ns_name_tuple
+        if ns_uri:
+            el_tag = "{%s}%s" % ns_name_tuple
+        elif self._default_ns:
+            el_tag = "{%s}%s" % (self._default_ns, local_name)
+        else:
+            el_tag = local_name
+        return el_tag
+
+    def startElementNS(self, ns_name, qname, attributes=None):
+        el_name = self._buildTag(ns_name)
+        if attributes:
+            attrs = {}
+            try:
+                iter_attributes = attributes.iteritems()
+            except AttributeError:
+                iter_attributes = attributes.items()
+
+            for name_tuple, value in iter_attributes:
+                if name_tuple[0]:
+                    attr_name = "{%s}%s" % name_tuple
+                else:
+                    attr_name = name_tuple[1]
+                attrs[attr_name] = value
+        else:
+            attrs = None
+
+        element_stack = self._element_stack
+        if self._root is None:
+            element = self._root = \
+                      self._makeelement(el_name, attrs, self._new_mappings)
+            if self._root_siblings and hasattr(element, 'addprevious'):
+                for sibling in self._root_siblings:
+                    element.addprevious(sibling)
+            del self._root_siblings[:]
+        else:
+            element = SubElement(element_stack[-1], el_name,
+                                 attrs, self._new_mappings)
+        element_stack.append(element)
+
+        self._new_mappings.clear()
+
+    def processingInstruction(self, target, data):
+        pi = ProcessingInstruction(target, data)
+        if self._root is None:
+            self._root_siblings.append(pi)
+        else:
+            self._element_stack[-1].append(pi)
+
+    def endElementNS(self, ns_name, qname):
+        element = self._element_stack.pop()
+        el_tag = self._buildTag(ns_name)
+        if el_tag != element.tag:
+            raise SaxError("Unexpected element closed: " + el_tag)
+
+    def startElement(self, name, attributes=None):
+        if attributes:
+            attributes = {(None, k): v for k, v in attributes.items()}
+        self.startElementNS((None, name), name, attributes)
+
+    def endElement(self, name):
+        self.endElementNS((None, name), name)
+
+    def characters(self, data):
+        last_element = self._element_stack[-1]
+        try:
+            # if there already is a child element, we must append to its tail
+            last_element = last_element[-1]
+            last_element.tail = (last_element.tail or '') + data
+        except IndexError:
+            # otherwise: append to the text
+            last_element.text = (last_element.text or '') + data
+
+    ignorableWhitespace = characters
+
+
+class ElementTreeProducer:
+    """Produces SAX events for an element and children.
+    """
+    def __init__(self, element_or_tree, content_handler):
+        try:
+            element = element_or_tree.getroot()
+        except AttributeError:
+            element = element_or_tree
+        self._element = element
+        self._content_handler = content_handler
+        from xml.sax.xmlreader import AttributesNSImpl as attr_class
+        self._attr_class = attr_class
+        self._empty_attributes = attr_class({}, {})
+
+    def saxify(self):
+        self._content_handler.startDocument()
+
+        element = self._element
+        if hasattr(element, 'getprevious'):
+            siblings = []
+            sibling = element.getprevious()
+            while getattr(sibling, 'tag', None) is ProcessingInstruction:
+                siblings.append(sibling)
+                sibling = sibling.getprevious()
+            for sibling in siblings[::-1]:
+                self._recursive_saxify(sibling, {})
+
+        self._recursive_saxify(element, {})
+
+        if hasattr(element, 'getnext'):
+            sibling = element.getnext()
+            while getattr(sibling, 'tag', None) is ProcessingInstruction:
+                self._recursive_saxify(sibling, {})
+                sibling = sibling.getnext()
+
+        self._content_handler.endDocument()
+
+    def _recursive_saxify(self, element, parent_nsmap):
+        content_handler = self._content_handler
+        tag = element.tag
+        if tag is Comment or tag is ProcessingInstruction:
+            if tag is ProcessingInstruction:
+                content_handler.processingInstruction(
+                    element.target, element.text)
+            tail = element.tail
+            if tail:
+                content_handler.characters(tail)
+            return
+
+        element_nsmap = element.nsmap
+        new_prefixes = []
+        if element_nsmap != parent_nsmap:
+            # There have been updates to the namespace
+            for prefix, ns_uri in element_nsmap.items():
+                if parent_nsmap.get(prefix) != ns_uri:
+                    new_prefixes.append( (prefix, ns_uri) )
+
+        attribs = element.items()
+        if attribs:
+            attr_values = {}
+            attr_qnames = {}
+            for attr_ns_name, value in attribs:
+                attr_ns_tuple = _getNsTag(attr_ns_name)
+                attr_values[attr_ns_tuple] = value
+                attr_qnames[attr_ns_tuple] = self._build_qname(
+                    attr_ns_tuple[0], attr_ns_tuple[1], element_nsmap,
+                    preferred_prefix=None, is_attribute=True)
+            sax_attributes = self._attr_class(attr_values, attr_qnames)
+        else:
+            sax_attributes = self._empty_attributes
+
+        ns_uri, local_name = _getNsTag(tag)
+        qname = self._build_qname(
+            ns_uri, local_name, element_nsmap, element.prefix, is_attribute=False)
+
+        for prefix, uri in new_prefixes:
+            content_handler.startPrefixMapping(prefix, uri)
+        content_handler.startElementNS(
+            (ns_uri, local_name), qname, sax_attributes)
+        text = element.text
+        if text:
+            content_handler.characters(text)
+        for child in element:
+            self._recursive_saxify(child, element_nsmap)
+        content_handler.endElementNS((ns_uri, local_name), qname)
+        for prefix, uri in new_prefixes:
+            content_handler.endPrefixMapping(prefix)
+        tail = element.tail
+        if tail:
+            content_handler.characters(tail)
+
+    def _build_qname(self, ns_uri, local_name, nsmap, preferred_prefix, is_attribute):
+        if ns_uri is None:
+            return local_name
+
+        if not is_attribute and nsmap.get(preferred_prefix) == ns_uri:
+            prefix = preferred_prefix
+        else:
+            # Pick the first matching prefix, in alphabetical order.
+            candidates = [
+                pfx for (pfx, uri) in nsmap.items()
+                if pfx is not None and uri == ns_uri
+            ]
+            prefix = (
+                candidates[0] if len(candidates) == 1
+                else min(candidates) if candidates
+                else None
+            )
+
+        if prefix is None:
+            # Default namespace
+            return local_name
+        return prefix + ':' + local_name
+
+
+def saxify(element_or_tree, content_handler):
+    """One-shot helper to generate SAX events from an XML tree and fire
+    them against a SAX ContentHandler.
+    """
+    return ElementTreeProducer(element_or_tree, content_handler).saxify()
diff --git a/.venv/lib/python3.12/site-packages/lxml/saxparser.pxi b/.venv/lib/python3.12/site-packages/lxml/saxparser.pxi
new file mode 100644
index 00000000..dc03df9a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/saxparser.pxi
@@ -0,0 +1,875 @@
+# SAX-like interfaces
+
+class XMLSyntaxAssertionError(XMLSyntaxError, AssertionError):
+    """
+    An XMLSyntaxError that additionally inherits from AssertionError for
+    ElementTree / backwards compatibility reasons.
+
+    This class may get replaced by a plain XMLSyntaxError in a future version.
+    """
+    def __init__(self, message):
+        XMLSyntaxError.__init__(self, message, None, 0, 1)
+
+
+ctypedef enum _SaxParserEvents:
+    SAX_EVENT_START    = 1 << 0
+    SAX_EVENT_END      = 1 << 1
+    SAX_EVENT_DATA     = 1 << 2
+    SAX_EVENT_DOCTYPE  = 1 << 3
+    SAX_EVENT_PI       = 1 << 4
+    SAX_EVENT_COMMENT  = 1 << 5
+    SAX_EVENT_START_NS = 1 << 6
+    SAX_EVENT_END_NS   = 1 << 7
+
+ctypedef enum _ParseEventFilter:
+    PARSE_EVENT_FILTER_START     = 1 << 0
+    PARSE_EVENT_FILTER_END       = 1 << 1
+    PARSE_EVENT_FILTER_START_NS  = 1 << 2
+    PARSE_EVENT_FILTER_END_NS    = 1 << 3
+    PARSE_EVENT_FILTER_COMMENT   = 1 << 4
+    PARSE_EVENT_FILTER_PI        = 1 << 5
+
+
+cdef int _buildParseEventFilter(events) except -1:
+    cdef int event_filter = 0
+    for event in events:
+        if event == 'start':
+            event_filter |= PARSE_EVENT_FILTER_START
+        elif event == 'end':
+            event_filter |= PARSE_EVENT_FILTER_END
+        elif event == 'start-ns':
+            event_filter |= PARSE_EVENT_FILTER_START_NS
+        elif event == 'end-ns':
+            event_filter |= PARSE_EVENT_FILTER_END_NS
+        elif event == 'comment':
+            event_filter |= PARSE_EVENT_FILTER_COMMENT
+        elif event == 'pi':
+            event_filter |= PARSE_EVENT_FILTER_PI
+        else:
+            raise ValueError, f"invalid event name '{event}'"
+    return event_filter
+
+
+cdef class _SaxParserTarget:
+    cdef int _sax_event_filter
+
+    cdef _handleSaxStart(self, tag, attrib, nsmap):
+        return None
+    cdef _handleSaxEnd(self, tag):
+        return None
+    cdef int _handleSaxData(self, data) except -1:
+        return 0
+    cdef int _handleSaxDoctype(self, root_tag, public_id, system_id) except -1:
+        return 0
+    cdef _handleSaxPi(self, target, data):
+        return None
+    cdef _handleSaxComment(self, comment):
+        return None
+    cdef _handleSaxStartNs(self, prefix, uri):
+        return None
+    cdef _handleSaxEndNs(self, prefix):
+        return None
+
+
+#@cython.final
+@cython.internal
+@cython.no_gc_clear  # Required because parent class uses it - Cython bug.
+cdef class _SaxParserContext(_ParserContext):
+    """This class maps SAX2 events to parser target events.
+    """
+    cdef _SaxParserTarget _target
+    cdef _BaseParser _parser
+    cdef xmlparser.startElementNsSAX2Func _origSaxStart
+    cdef xmlparser.endElementNsSAX2Func   _origSaxEnd
+    cdef xmlparser.startElementSAXFunc    _origSaxStartNoNs
+    cdef xmlparser.endElementSAXFunc      _origSaxEndNoNs
+    cdef xmlparser.charactersSAXFunc      _origSaxData
+    cdef xmlparser.cdataBlockSAXFunc      _origSaxCData
+    cdef xmlparser.internalSubsetSAXFunc  _origSaxDoctype
+    cdef xmlparser.commentSAXFunc         _origSaxComment
+    cdef xmlparser.processingInstructionSAXFunc _origSaxPI
+    cdef xmlparser.startDocumentSAXFunc   _origSaxStartDocument
+
+    # for event collecting
+    cdef int _event_filter
+    cdef list _ns_stack
+    cdef list _node_stack
+    cdef _ParseEventsIterator events_iterator
+
+    # for iterparse
+    cdef _Element  _root
+    cdef _MultiTagMatcher _matcher
+
+    def __cinit__(self, _BaseParser parser):
+        self._ns_stack = []
+        self._node_stack = []
+        self._parser = parser
+        self.events_iterator = _ParseEventsIterator()
+
+    cdef void _setSaxParserTarget(self, _SaxParserTarget target) noexcept:
+        self._target = target
+
+    cdef void _initParserContext(self, xmlparser.xmlParserCtxt* c_ctxt) noexcept:
+        _ParserContext._initParserContext(self, c_ctxt)
+        if self._target is not None:
+            self._connectTarget(c_ctxt)
+        elif self._event_filter:
+            self._connectEvents(c_ctxt)
+
+    cdef void _connectTarget(self, xmlparser.xmlParserCtxt* c_ctxt) noexcept:
+        """Wrap original SAX2 callbacks to call into parser target.
+        """
+        sax = c_ctxt.sax
+        self._origSaxStart = sax.startElementNs = NULL
+        self._origSaxStartNoNs = sax.startElement = NULL
+        if self._target._sax_event_filter & (SAX_EVENT_START |
+                                             SAX_EVENT_START_NS |
+                                             SAX_EVENT_END_NS):
+            # intercept => overwrite orig callback
+            # FIXME: also intercept on when collecting END events
+            if sax.initialized == xmlparser.XML_SAX2_MAGIC:
+                sax.startElementNs = _handleSaxTargetStart
+            if self._target._sax_event_filter & SAX_EVENT_START:
+                sax.startElement = _handleSaxTargetStartNoNs
+
+        self._origSaxEnd = sax.endElementNs = NULL
+        self._origSaxEndNoNs = sax.endElement = NULL
+        if self._target._sax_event_filter & (SAX_EVENT_END |
+                                             SAX_EVENT_END_NS):
+            if sax.initialized == xmlparser.XML_SAX2_MAGIC:
+                sax.endElementNs = _handleSaxEnd
+            if self._target._sax_event_filter & SAX_EVENT_END:
+                sax.endElement = _handleSaxEndNoNs
+
+        self._origSaxData = sax.characters = sax.cdataBlock = NULL
+        if self._target._sax_event_filter & SAX_EVENT_DATA:
+            sax.characters = sax.cdataBlock = _handleSaxData
+
+        # doctype propagation is always required for entity replacement
+        self._origSaxDoctype = sax.internalSubset
+        if self._target._sax_event_filter & SAX_EVENT_DOCTYPE:
+            sax.internalSubset = _handleSaxTargetDoctype
+
+        self._origSaxPI = sax.processingInstruction = NULL
+        if self._target._sax_event_filter & SAX_EVENT_PI:
+            sax.processingInstruction = _handleSaxTargetPI
+
+        self._origSaxComment = sax.comment = NULL
+        if self._target._sax_event_filter & SAX_EVENT_COMMENT:
+            sax.comment = _handleSaxTargetComment
+
+        # enforce entity replacement
+        sax.reference = NULL
+        c_ctxt.replaceEntities = 1
+
+    cdef void _connectEvents(self, xmlparser.xmlParserCtxt* c_ctxt) noexcept:
+        """Wrap original SAX2 callbacks to collect parse events without parser target.
+        """
+        sax = c_ctxt.sax
+        self._origSaxStartDocument = sax.startDocument
+        sax.startDocument = _handleSaxStartDocument
+
+        # only override "start" event handler if needed
+        self._origSaxStart = sax.startElementNs
+        if self._event_filter == 0 or c_ctxt.html or \
+               self._event_filter & (PARSE_EVENT_FILTER_START |
+                                     PARSE_EVENT_FILTER_END |
+                                     PARSE_EVENT_FILTER_START_NS |
+                                     PARSE_EVENT_FILTER_END_NS):
+            sax.startElementNs = <xmlparser.startElementNsSAX2Func>_handleSaxStart
+
+        self._origSaxStartNoNs = sax.startElement
+        if self._event_filter == 0 or c_ctxt.html or \
+               self._event_filter & (PARSE_EVENT_FILTER_START |
+                                     PARSE_EVENT_FILTER_END):
+            sax.startElement = <xmlparser.startElementSAXFunc>_handleSaxStartNoNs
+
+        # only override "end" event handler if needed
+        self._origSaxEnd = sax.endElementNs
+        if self._event_filter == 0 or \
+               self._event_filter & (PARSE_EVENT_FILTER_END |
+                                     PARSE_EVENT_FILTER_END_NS):
+            sax.endElementNs = <xmlparser.endElementNsSAX2Func>_handleSaxEnd
+
+        self._origSaxEndNoNs = sax.endElement
+        if self._event_filter == 0 or \
+               self._event_filter & PARSE_EVENT_FILTER_END:
+            sax.endElement = <xmlparser.endElementSAXFunc>_handleSaxEndNoNs
+
+        self._origSaxComment = sax.comment
+        if self._event_filter & PARSE_EVENT_FILTER_COMMENT:
+            sax.comment = <xmlparser.commentSAXFunc>_handleSaxComment
+
+        self._origSaxPI = sax.processingInstruction
+        if self._event_filter & PARSE_EVENT_FILTER_PI:
+            sax.processingInstruction = <xmlparser.processingInstructionSAXFunc>_handleSaxPIEvent
+
+    cdef _setEventFilter(self, events, tag):
+        self._event_filter = _buildParseEventFilter(events)
+        if not self._event_filter or tag is None or tag == '*':
+            self._matcher = None
+        else:
+            self._matcher = _MultiTagMatcher.__new__(_MultiTagMatcher, tag)
+
+    cdef int startDocument(self, xmlDoc* c_doc) except -1:
+        try:
+            self._doc = _documentFactory(c_doc, self._parser)
+        finally:
+            self._parser = None  # clear circular reference ASAP
+        if self._matcher is not None:
+            self._matcher.cacheTags(self._doc, True) # force entry in libxml2 dict
+        return 0
+
+    cdef int pushEvent(self, event, xmlNode* c_node) except -1:
+        cdef _Element root
+        if self._root is None:
+            root = self._doc.getroot()
+            if root is not None and root._c_node.type == tree.XML_ELEMENT_NODE:
+                self._root = root
+        node = _elementFactory(self._doc, c_node)
+        self.events_iterator._events.append( (event, node) )
+        return 0
+
+    cdef int flushEvents(self) except -1:
+        events = self.events_iterator._events
+        while self._node_stack:
+            events.append( ('end', self._node_stack.pop()) )
+            _pushSaxNsEndEvents(self)
+        while self._ns_stack:
+            _pushSaxNsEndEvents(self)
+
+    cdef void _handleSaxException(self, xmlparser.xmlParserCtxt* c_ctxt) noexcept:
+        if c_ctxt.errNo == xmlerror.XML_ERR_OK:
+            c_ctxt.errNo = xmlerror.XML_ERR_INTERNAL_ERROR
+        # stop parsing immediately
+        c_ctxt.wellFormed = 0
+        c_ctxt.disableSAX = 1
+        c_ctxt.instate = xmlparser.XML_PARSER_EOF
+        self._store_raised()
+
+
+@cython.final
+@cython.internal
+cdef class _ParseEventsIterator:
+    """A reusable parse events iterator"""
+    cdef list _events
+    cdef int _event_index
+
+    def __cinit__(self):
+        self._events = []
+        self._event_index = 0
+
+    def __iter__(self):
+        return self
+
+    def __next__(self):
+        cdef int event_index = self._event_index
+        events = self._events
+        if event_index >= 2**10 or event_index * 2 >= len(events):
+            if event_index:
+                # clean up from time to time
+                del events[:event_index]
+                self._event_index = event_index = 0
+            if event_index >= len(events):
+                raise StopIteration
+        item = events[event_index]
+        self._event_index = event_index + 1
+        return item
+
+
+cdef list _build_prefix_uri_list(_SaxParserContext context, int c_nb_namespaces,
+                                 const_xmlChar** c_namespaces):
+    "Build [(prefix, uri)] list of declared namespaces."
+    cdef int i
+    namespaces = []
+    for i in xrange(c_nb_namespaces):
+        namespaces.append((funicodeOrEmpty(c_namespaces[0]), funicode(c_namespaces[1])))
+        c_namespaces += 2
+    return namespaces
+
+
+cdef void _handleSaxStart(
+        void* ctxt, const_xmlChar* c_localname, const_xmlChar* c_prefix,
+        const_xmlChar* c_namespace, int c_nb_namespaces,
+        const_xmlChar** c_namespaces,
+        int c_nb_attributes, int c_nb_defaulted,
+        const_xmlChar** c_attributes) noexcept with gil:
+    cdef int i
+    cdef size_t c_len
+    c_ctxt = <xmlparser.xmlParserCtxt*>ctxt
+    if c_ctxt._private is NULL or c_ctxt.disableSAX:
+        return
+    context = <_SaxParserContext>c_ctxt._private
+    cdef int event_filter = context._event_filter
+    try:
+        if (c_nb_namespaces and
+                event_filter & (PARSE_EVENT_FILTER_START_NS |
+                                PARSE_EVENT_FILTER_END_NS)):
+            declared_namespaces = _build_prefix_uri_list(
+                context, c_nb_namespaces, c_namespaces)
+            if event_filter & PARSE_EVENT_FILTER_START_NS:
+                for prefix_uri_tuple in declared_namespaces:
+                    context.events_iterator._events.append(("start-ns", prefix_uri_tuple))
+        else:
+            declared_namespaces = None
+
+        context._origSaxStart(c_ctxt, c_localname, c_prefix, c_namespace,
+                              c_nb_namespaces, c_namespaces, c_nb_attributes,
+                              c_nb_defaulted, c_attributes)
+        if c_ctxt.html:
+            _fixHtmlDictNodeNames(c_ctxt.dict, c_ctxt.node)
+            # The HTML parser in libxml2 reports the missing opening tags when it finds
+            # misplaced ones, but with tag names from C string constants that ignore the
+            # parser dict.  Thus, we need to intern the name ourselves.
+            c_localname = tree.xmlDictLookup(c_ctxt.dict, c_localname, -1)
+            if c_localname is NULL:
+                raise MemoryError()
+
+        if event_filter & PARSE_EVENT_FILTER_END_NS:
+            context._ns_stack.append(declared_namespaces)
+        if event_filter & (PARSE_EVENT_FILTER_END |
+                           PARSE_EVENT_FILTER_START):
+            _pushSaxStartEvent(context, c_ctxt, c_namespace, c_localname, None)
+    except:
+        context._handleSaxException(c_ctxt)
+    finally:
+        return  # swallow any further exceptions
+
+
+cdef void _handleSaxTargetStart(
+        void* ctxt, const_xmlChar* c_localname, const_xmlChar* c_prefix,
+        const_xmlChar* c_namespace, int c_nb_namespaces,
+        const_xmlChar** c_namespaces,
+        int c_nb_attributes, int c_nb_defaulted,
+        const_xmlChar** c_attributes) noexcept with gil:
+    cdef int i
+    cdef size_t c_len
+    c_ctxt = <xmlparser.xmlParserCtxt*>ctxt
+    if c_ctxt._private is NULL or c_ctxt.disableSAX:
+        return
+    context = <_SaxParserContext>c_ctxt._private
+
+    cdef int event_filter = context._event_filter
+    cdef int sax_event_filter = context._target._sax_event_filter
+    try:
+        if c_nb_namespaces:
+            declared_namespaces = _build_prefix_uri_list(
+                context, c_nb_namespaces, c_namespaces)
+
+            if event_filter & PARSE_EVENT_FILTER_START_NS:
+                for prefix_uri_tuple in declared_namespaces:
+                    context.events_iterator._events.append(("start-ns", prefix_uri_tuple))
+
+            if sax_event_filter & SAX_EVENT_START_NS:
+                for prefix, uri in declared_namespaces:
+                    context._target._handleSaxStartNs(prefix, uri)
+        else:
+            declared_namespaces = None
+
+        if sax_event_filter & SAX_EVENT_START:
+            if c_nb_defaulted > 0:
+                # only add default attributes if we asked for them
+                if c_ctxt.loadsubset & xmlparser.XML_COMPLETE_ATTRS == 0:
+                    c_nb_attributes -= c_nb_defaulted
+            if c_nb_attributes == 0:
+                attrib = IMMUTABLE_EMPTY_MAPPING
+            else:
+                attrib = {}
+                for i in xrange(c_nb_attributes):
+                    name = _namespacedNameFromNsName(
+                        c_attributes[2], c_attributes[0])
+                    if c_attributes[3] is NULL:
+                        value = ''
+                    else:
+                        c_len = c_attributes[4] - c_attributes[3]
+                        value = c_attributes[3][:c_len].decode('utf8')
+                    attrib[name] = value
+                    c_attributes += 5
+
+            nsmap = dict(declared_namespaces) if c_nb_namespaces else IMMUTABLE_EMPTY_MAPPING
+
+            element = _callTargetSaxStart(
+                context, c_ctxt,
+                _namespacedNameFromNsName(c_namespace, c_localname),
+                attrib, nsmap)
+        else:
+            element = None
+
+        if (event_filter & PARSE_EVENT_FILTER_END_NS or
+                sax_event_filter & SAX_EVENT_END_NS):
+            context._ns_stack.append(declared_namespaces)
+        if event_filter & (PARSE_EVENT_FILTER_END |
+                           PARSE_EVENT_FILTER_START):
+            _pushSaxStartEvent(context, c_ctxt, c_namespace,
+                               c_localname, element)
+    except:
+        context._handleSaxException(c_ctxt)
+    finally:
+        return  # swallow any further exceptions
+
+
+cdef void _handleSaxStartNoNs(void* ctxt, const_xmlChar* c_name,
+                              const_xmlChar** c_attributes) noexcept with gil:
+    c_ctxt = <xmlparser.xmlParserCtxt*>ctxt
+    if c_ctxt._private is NULL or c_ctxt.disableSAX:
+        return
+    context = <_SaxParserContext>c_ctxt._private
+    try:
+        context._origSaxStartNoNs(c_ctxt, c_name, c_attributes)
+        if c_ctxt.html:
+            _fixHtmlDictNodeNames(c_ctxt.dict, c_ctxt.node)
+            # The HTML parser in libxml2 reports the missing opening tags when it finds
+            # misplaced ones, but with tag names from C string constants that ignore the
+            # parser dict.  Thus, we need to intern the name ourselves.
+            c_name = tree.xmlDictLookup(c_ctxt.dict, c_name, -1)
+            if c_name is NULL:
+                raise MemoryError()
+        if context._event_filter & (PARSE_EVENT_FILTER_END |
+                                    PARSE_EVENT_FILTER_START):
+            _pushSaxStartEvent(context, c_ctxt, NULL, c_name, None)
+    except:
+        context._handleSaxException(c_ctxt)
+    finally:
+        return  # swallow any further exceptions
+
+
+cdef void _handleSaxTargetStartNoNs(void* ctxt, const_xmlChar* c_name,
+                                    const_xmlChar** c_attributes) noexcept with gil:
+    c_ctxt = <xmlparser.xmlParserCtxt*>ctxt
+    if c_ctxt._private is NULL or c_ctxt.disableSAX:
+        return
+    context = <_SaxParserContext>c_ctxt._private
+    try:
+        if c_attributes is NULL:
+            attrib = IMMUTABLE_EMPTY_MAPPING
+        else:
+            attrib = {}
+            while c_attributes[0] is not NULL:
+                name = funicode(c_attributes[0])
+                attrib[name] = funicodeOrEmpty(c_attributes[1])
+                c_attributes += 2
+        element = _callTargetSaxStart(
+            context, c_ctxt, funicode(c_name),
+            attrib, IMMUTABLE_EMPTY_MAPPING)
+        if context._event_filter & (PARSE_EVENT_FILTER_END |
+                                    PARSE_EVENT_FILTER_START):
+            _pushSaxStartEvent(context, c_ctxt, NULL, c_name, element)
+    except:
+        context._handleSaxException(c_ctxt)
+    finally:
+        return  # swallow any further exceptions
+
+
+cdef _callTargetSaxStart(_SaxParserContext context,
+                         xmlparser.xmlParserCtxt* c_ctxt,
+                         tag, attrib, nsmap):
+    element = context._target._handleSaxStart(tag, attrib, nsmap)
+    if element is not None and c_ctxt.input is not NULL:
+        if isinstance(element, _Element):
+            (<_Element>element)._c_node.line = (
+                <unsigned short>c_ctxt.input.line
+                if c_ctxt.input.line < 65535 else 65535)
+    return element
+
+
+cdef int _pushSaxStartEvent(_SaxParserContext context,
+                            xmlparser.xmlParserCtxt* c_ctxt,
+                            const_xmlChar* c_href,
+                            const_xmlChar* c_name, node) except -1:
+    if (context._matcher is None or
+            context._matcher.matchesNsTag(c_href, c_name)):
+        if node is None and context._target is None:
+            assert context._doc is not None
+            node = _elementFactory(context._doc, c_ctxt.node)
+        if context._event_filter & PARSE_EVENT_FILTER_START:
+            context.events_iterator._events.append(('start', node))
+        if (context._target is None and
+                context._event_filter & PARSE_EVENT_FILTER_END):
+            context._node_stack.append(node)
+    return 0
+
+
+cdef void _handleSaxEnd(void* ctxt, const_xmlChar* c_localname,
+                        const_xmlChar* c_prefix,
+                        const_xmlChar* c_namespace) noexcept with gil:
+    c_ctxt = <xmlparser.xmlParserCtxt*>ctxt
+    if c_ctxt._private is NULL or c_ctxt.disableSAX:
+        return
+    context = <_SaxParserContext>c_ctxt._private
+    try:
+        if context._target is not None:
+            if context._target._sax_event_filter & SAX_EVENT_END:
+                node = context._target._handleSaxEnd(
+                    _namespacedNameFromNsName(c_namespace, c_localname))
+            else:
+                node = None
+        else:
+            context._origSaxEnd(c_ctxt, c_localname, c_prefix, c_namespace)
+            node = None
+        _pushSaxEndEvent(context, c_namespace, c_localname, node)
+        _pushSaxNsEndEvents(context)
+    except:
+        context._handleSaxException(c_ctxt)
+    finally:
+        return  # swallow any further exceptions
+
+
+cdef void _handleSaxEndNoNs(void* ctxt, const_xmlChar* c_name) noexcept with gil:
+    c_ctxt = <xmlparser.xmlParserCtxt*>ctxt
+    if c_ctxt._private is NULL or c_ctxt.disableSAX:
+        return
+    context = <_SaxParserContext>c_ctxt._private
+    try:
+        if context._target is not None:
+            node = context._target._handleSaxEnd(funicode(c_name))
+        else:
+            context._origSaxEndNoNs(c_ctxt, c_name)
+            node = None
+        _pushSaxEndEvent(context, NULL, c_name, node)
+    except:
+        context._handleSaxException(c_ctxt)
+    finally:
+        return  # swallow any further exceptions
+
+
+cdef int _pushSaxNsEndEvents(_SaxParserContext context) except -1:
+    cdef bint build_events = context._event_filter & PARSE_EVENT_FILTER_END_NS
+    cdef bint call_target = (
+        context._target is not None
+        and context._target._sax_event_filter & SAX_EVENT_END_NS)
+    if not build_events and not call_target:
+        return 0
+
+    cdef list declared_namespaces = context._ns_stack.pop()
+    if declared_namespaces is None:
+        return 0
+
+    cdef tuple prefix_uri
+    for prefix_uri in reversed(declared_namespaces):
+        if call_target:
+            context._target._handleSaxEndNs(prefix_uri[0])
+        if build_events:
+            context.events_iterator._events.append(('end-ns', None))
+
+    return 0
+
+
+cdef int _pushSaxEndEvent(_SaxParserContext context,
+                          const_xmlChar* c_href,
+                          const_xmlChar* c_name, node) except -1:
+    if context._event_filter & PARSE_EVENT_FILTER_END:
+        if (context._matcher is None or
+                context._matcher.matchesNsTag(c_href, c_name)):
+            if context._target is None:
+                node = context._node_stack.pop()
+            context.events_iterator._events.append(('end', node))
+    return 0
+
+
+cdef void _handleSaxData(void* ctxt, const_xmlChar* c_data, int data_len) noexcept with gil:
+    # can only be called if parsing with a target
+    c_ctxt = <xmlparser.xmlParserCtxt*>ctxt
+    if c_ctxt._private is NULL or c_ctxt.disableSAX:
+        return
+    context = <_SaxParserContext>c_ctxt._private
+    try:
+        context._target._handleSaxData(
+            c_data[:data_len].decode('utf8'))
+    except:
+        context._handleSaxException(c_ctxt)
+    finally:
+        return  # swallow any further exceptions
+
+
+cdef void _handleSaxTargetDoctype(void* ctxt, const_xmlChar* c_name,
+                                  const_xmlChar* c_public,
+                                  const_xmlChar* c_system) noexcept with gil:
+    # can only be called if parsing with a target
+    c_ctxt = <xmlparser.xmlParserCtxt*>ctxt
+    if c_ctxt._private is NULL or c_ctxt.disableSAX:
+        return
+    context = <_SaxParserContext>c_ctxt._private
+    try:
+        context._target._handleSaxDoctype(
+            funicodeOrNone(c_name),
+            funicodeOrNone(c_public),
+            funicodeOrNone(c_system))
+    except:
+        context._handleSaxException(c_ctxt)
+    finally:
+        return  # swallow any further exceptions
+
+
+cdef void _handleSaxStartDocument(void* ctxt) noexcept with gil:
+    c_ctxt = <xmlparser.xmlParserCtxt*>ctxt
+    if c_ctxt._private is NULL or c_ctxt.disableSAX:
+        return
+    context = <_SaxParserContext>c_ctxt._private
+    context._origSaxStartDocument(ctxt)
+    c_doc = c_ctxt.myDoc
+    try:
+        context.startDocument(c_doc)
+    except:
+        context._handleSaxException(c_ctxt)
+    finally:
+        return  # swallow any further exceptions
+
+
+cdef void _handleSaxTargetPI(void* ctxt, const_xmlChar* c_target,
+                             const_xmlChar* c_data) noexcept with gil:
+    # can only be called if parsing with a target
+    c_ctxt = <xmlparser.xmlParserCtxt*>ctxt
+    if c_ctxt._private is NULL or c_ctxt.disableSAX:
+        return
+    context = <_SaxParserContext>c_ctxt._private
+    try:
+        pi = context._target._handleSaxPi(
+            funicodeOrNone(c_target),
+            funicodeOrEmpty(c_data))
+        if context._event_filter & PARSE_EVENT_FILTER_PI:
+            context.events_iterator._events.append(('pi', pi))
+    except:
+        context._handleSaxException(c_ctxt)
+    finally:
+        return  # swallow any further exceptions
+
+
+cdef void _handleSaxPIEvent(void* ctxt, const_xmlChar* target,
+                            const_xmlChar* data) noexcept with gil:
+    # can only be called when collecting pi events
+    c_ctxt = <xmlparser.xmlParserCtxt*>ctxt
+    if c_ctxt._private is NULL or c_ctxt.disableSAX:
+        return
+    context = <_SaxParserContext>c_ctxt._private
+    context._origSaxPI(ctxt, target, data)
+    c_node = _findLastEventNode(c_ctxt)
+    if c_node is NULL:
+        return
+    try:
+        context.pushEvent('pi', c_node)
+    except:
+        context._handleSaxException(c_ctxt)
+    finally:
+        return  # swallow any further exceptions
+
+
+cdef void _handleSaxTargetComment(void* ctxt, const_xmlChar* c_data) noexcept with gil:
+    # can only be called if parsing with a target
+    c_ctxt = <xmlparser.xmlParserCtxt*>ctxt
+    if c_ctxt._private is NULL or c_ctxt.disableSAX:
+        return
+    context = <_SaxParserContext>c_ctxt._private
+    try:
+        comment = context._target._handleSaxComment(funicodeOrEmpty(c_data))
+        if context._event_filter & PARSE_EVENT_FILTER_COMMENT:
+            context.events_iterator._events.append(('comment', comment))
+    except:
+        context._handleSaxException(c_ctxt)
+    finally:
+        return  # swallow any further exceptions
+
+
+cdef void _handleSaxComment(void* ctxt, const_xmlChar* text) noexcept with gil:
+    # can only be called when collecting comment events
+    c_ctxt = <xmlparser.xmlParserCtxt*>ctxt
+    if c_ctxt._private is NULL or c_ctxt.disableSAX:
+        return
+    context = <_SaxParserContext>c_ctxt._private
+    context._origSaxComment(ctxt, text)
+    c_node = _findLastEventNode(c_ctxt)
+    if c_node is NULL:
+        return
+    try:
+        context.pushEvent('comment', c_node)
+    except:
+        context._handleSaxException(c_ctxt)
+    finally:
+        return  # swallow any further exceptions
+
+
+cdef inline xmlNode* _findLastEventNode(xmlparser.xmlParserCtxt* c_ctxt):
+    # this mimics what libxml2 creates for comments/PIs
+    if c_ctxt.inSubset == 1:
+        return c_ctxt.myDoc.intSubset.last
+    elif c_ctxt.inSubset == 2:
+        return c_ctxt.myDoc.extSubset.last
+    elif c_ctxt.node is NULL:
+        return c_ctxt.myDoc.last
+    elif c_ctxt.node.type == tree.XML_ELEMENT_NODE:
+        return c_ctxt.node.last
+    else:
+        return c_ctxt.node.next
+
+
+############################################################
+## ET compatible XML tree builder
+############################################################
+
+cdef class TreeBuilder(_SaxParserTarget):
+    """TreeBuilder(self, element_factory=None, parser=None,
+                    comment_factory=None, pi_factory=None,
+                    insert_comments=True, insert_pis=True)
+
+    Parser target that builds a tree from parse event callbacks.
+
+    The factory arguments can be used to influence the creation of
+    elements, comments and processing instructions.
+
+    By default, comments and processing instructions are inserted into
+    the tree, but they can be ignored by passing the respective flags.
+
+    The final tree is returned by the ``close()`` method.
+    """
+    cdef _BaseParser _parser
+    cdef object _factory
+    cdef object _comment_factory
+    cdef object _pi_factory
+    cdef list _data
+    cdef list _element_stack
+    cdef object _element_stack_pop
+    cdef _Element _last # may be None
+    cdef bint _in_tail
+    cdef bint _insert_comments
+    cdef bint _insert_pis
+
+    def __init__(self, *, element_factory=None, parser=None,
+                 comment_factory=None, pi_factory=None,
+                 bint insert_comments=True, bint insert_pis=True):
+        self._sax_event_filter = \
+            SAX_EVENT_START | SAX_EVENT_END | SAX_EVENT_DATA | \
+            SAX_EVENT_PI | SAX_EVENT_COMMENT
+        self._data = [] # data collector
+        self._element_stack = [] # element stack
+        self._element_stack_pop = self._element_stack.pop
+        self._last = None # last element
+        self._in_tail = 0 # true if we're after an end tag
+        self._factory = element_factory
+        self._comment_factory = comment_factory if comment_factory is not None else Comment
+        self._pi_factory = pi_factory if pi_factory is not None else ProcessingInstruction
+        self._insert_comments = insert_comments
+        self._insert_pis = insert_pis
+        self._parser = parser
+
+    @cython.final
+    cdef int _flush(self) except -1:
+        if self._data:
+            if self._last is not None:
+                text = "".join(self._data)
+                if self._in_tail:
+                    assert self._last.tail is None, "internal error (tail)"
+                    self._last.tail = text
+                else:
+                    assert self._last.text is None, "internal error (text)"
+                    self._last.text = text
+            del self._data[:]
+        return 0
+
+    # internal SAX event handlers
+
+    @cython.final
+    cdef _handleSaxStart(self, tag, attrib, nsmap):
+        self._flush()
+        if self._factory is not None:
+            self._last = self._factory(tag, attrib)
+            if self._element_stack:
+                _appendChild(self._element_stack[-1], self._last)
+        elif self._element_stack:
+            self._last = _makeSubElement(
+                self._element_stack[-1], tag, None, None, attrib, nsmap, None)
+        else:
+            self._last = _makeElement(
+                tag, NULL, None, self._parser, None, None, attrib, nsmap, None)
+        self._element_stack.append(self._last)
+        self._in_tail = 0
+        return self._last
+
+    @cython.final
+    cdef _handleSaxEnd(self, tag):
+        self._flush()
+        self._last = self._element_stack_pop()
+        self._in_tail = 1
+        return self._last
+
+    @cython.final
+    cdef int _handleSaxData(self, data) except -1:
+        self._data.append(data)
+
+    @cython.final
+    cdef _handleSaxPi(self, target, data):
+        elem = self._pi_factory(target, data)
+        if self._insert_pis:
+            self._flush()
+            self._last = elem
+            if self._element_stack:
+                _appendChild(self._element_stack[-1], self._last)
+            self._in_tail = 1
+        return self._last
+
+    @cython.final
+    cdef _handleSaxComment(self, comment):
+        elem = self._comment_factory(comment)
+        if self._insert_comments:
+            self._flush()
+            self._last = elem
+            if self._element_stack:
+                _appendChild(self._element_stack[-1], self._last)
+            self._in_tail = 1
+        return elem
+
+    # Python level event handlers
+
+    def close(self):
+        """close(self)
+
+        Flushes the builder buffers, and returns the toplevel document
+        element.  Raises XMLSyntaxError on inconsistencies.
+        """
+        if self._element_stack:
+            raise XMLSyntaxAssertionError("missing end tags")
+        # TODO: this does not necessarily seem like an error case.  Why not just return None?
+        if self._last is None:
+            raise XMLSyntaxAssertionError("missing toplevel element")
+        return self._last
+
+    def data(self, data):
+        """data(self, data)
+
+        Adds text to the current element.  The value should be either an
+        8-bit string containing ASCII text, or a Unicode string.
+        """
+        self._handleSaxData(data)
+
+    def start(self, tag, attrs, nsmap=None):
+        """start(self, tag, attrs, nsmap=None)
+
+        Opens a new element.
+        """
+        if nsmap is None:
+            nsmap = IMMUTABLE_EMPTY_MAPPING
+        return self._handleSaxStart(tag, attrs, nsmap)
+
+    def end(self, tag):
+        """end(self, tag)
+
+        Closes the current element.
+        """
+        element = self._handleSaxEnd(tag)
+        assert self._last.tag == tag,\
+            f"end tag mismatch (expected {self._last.tag}, got {tag})"
+        return element
+
+    def pi(self, target, data=None):
+        """pi(self, target, data=None)
+
+        Creates a processing instruction using the factory, appends it
+        (unless disabled) and returns it.
+        """
+        return self._handleSaxPi(target, data)
+
+    def comment(self, comment):
+        """comment(self, comment)
+
+        Creates a comment using the factory, appends it (unless disabled)
+        and returns it.
+        """
+        return self._handleSaxComment(comment)
diff --git a/.venv/lib/python3.12/site-packages/lxml/schematron.pxi b/.venv/lib/python3.12/site-packages/lxml/schematron.pxi
new file mode 100644
index 00000000..ea0881fd
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/schematron.pxi
@@ -0,0 +1,168 @@
+# support for Schematron validation
+from lxml.includes cimport schematron
+
+
+cdef class SchematronError(LxmlError):
+    """Base class of all Schematron errors.
+    """
+
+cdef class SchematronParseError(SchematronError):
+    """Error while parsing an XML document as Schematron schema.
+    """
+
+cdef class SchematronValidateError(SchematronError):
+    """Error while validating an XML document with a Schematron schema.
+    """
+
+
+################################################################################
+# Schematron
+
+cdef class Schematron(_Validator):
+    """Schematron(self, etree=None, file=None)
+    A Schematron validator.
+
+    Pass a root Element or an ElementTree to turn it into a validator.
+    Alternatively, pass a filename as keyword argument 'file' to parse from
+    the file system.
+
+    Schematron is a less well known, but very powerful schema language.  The main
+    idea is to use the capabilities of XPath to put restrictions on the structure
+    and the content of XML documents.  Here is a simple example::
+
+      >>> schematron = Schematron(XML('''
+      ... <schema xmlns="http://www.ascc.net/xml/schematron" >
+      ...   <pattern name="id is the only permitted attribute name">
+      ...     <rule context="*">
+      ...       <report test="@*[not(name()='id')]">Attribute
+      ...         <name path="@*[not(name()='id')]"/> is forbidden<name/>
+      ...       </report>
+      ...     </rule>
+      ...   </pattern>
+      ... </schema>
+      ... '''))
+
+      >>> xml = XML('''
+      ... <AAA name="aaa">
+      ...   <BBB id="bbb"/>
+      ...   <CCC color="ccc"/>
+      ... </AAA>
+      ... ''')
+
+      >>> schematron.validate(xml)
+      0
+
+      >>> xml = XML('''
+      ... <AAA id="aaa">
+      ...   <BBB id="bbb"/>
+      ...   <CCC/>
+      ... </AAA>
+      ... ''')
+
+      >>> schematron.validate(xml)
+      1
+
+    Schematron was added to libxml2 in version 2.6.21.  Before version 2.6.32,
+    however, Schematron lacked support for error reporting other than to stderr.
+    This version is therefore required to retrieve validation warnings and
+    errors in lxml.
+    """
+    cdef schematron.xmlSchematron* _c_schema
+    cdef xmlDoc* _c_schema_doc
+    def __cinit__(self):
+        self._c_schema = NULL
+        self._c_schema_doc = NULL
+
+    def __init__(self, etree=None, *, file=None):
+        cdef _Document doc
+        cdef _Element root_node
+        cdef xmlNode* c_node
+        cdef char* c_href
+        cdef schematron.xmlSchematronParserCtxt* parser_ctxt = NULL
+        _Validator.__init__(self)
+        if not config.ENABLE_SCHEMATRON:
+            raise SchematronError, \
+                "lxml.etree was compiled without Schematron support."
+        if etree is not None:
+            doc = _documentOrRaise(etree)
+            root_node = _rootNodeOrRaise(etree)
+            self._c_schema_doc = _copyDocRoot(doc._c_doc, root_node._c_node)
+            parser_ctxt = schematron.xmlSchematronNewDocParserCtxt(self._c_schema_doc)
+        elif file is not None:
+            filename = _getFilenameForFile(file)
+            if filename is None:
+                # XXX assume a string object
+                filename = file
+            filename = _encodeFilename(filename)
+            with self._error_log:
+                orig_loader = _register_document_loader()
+                parser_ctxt = schematron.xmlSchematronNewParserCtxt(_cstr(filename))
+                _reset_document_loader(orig_loader)
+        else:
+            raise SchematronParseError, "No tree or file given"
+
+        if parser_ctxt is NULL:
+            if self._c_schema_doc is not NULL:
+                tree.xmlFreeDoc(self._c_schema_doc)
+                self._c_schema_doc = NULL
+            raise MemoryError()
+
+        try:
+            with self._error_log:
+                orig_loader = _register_document_loader()
+                self._c_schema = schematron.xmlSchematronParse(parser_ctxt)
+                _reset_document_loader(orig_loader)
+        finally:
+            schematron.xmlSchematronFreeParserCtxt(parser_ctxt)
+
+        if self._c_schema is NULL:
+            raise SchematronParseError(
+                "Document is not a valid Schematron schema",
+                self._error_log)
+
+    def __dealloc__(self):
+        schematron.xmlSchematronFree(self._c_schema)
+        if self._c_schema_doc is not NULL:
+            tree.xmlFreeDoc(self._c_schema_doc)
+
+    def __call__(self, etree):
+        """__call__(self, etree)
+
+        Validate doc using Schematron.
+
+        Returns true if document is valid, false if not."""
+        cdef _Document doc
+        cdef _Element root_node
+        cdef xmlDoc* c_doc
+        cdef schematron.xmlSchematronValidCtxt* valid_ctxt
+        cdef int ret
+
+        assert self._c_schema is not NULL, "Schematron instance not initialised"
+        doc = _documentOrRaise(etree)
+        root_node = _rootNodeOrRaise(etree)
+
+        valid_ctxt = schematron.xmlSchematronNewValidCtxt(
+            self._c_schema, schematron.XML_SCHEMATRON_OUT_ERROR)
+        if valid_ctxt is NULL:
+            raise MemoryError()
+
+        try:
+            self._error_log.clear()
+            # Need a cast here because older libxml2 releases do not use 'const' in the functype.
+            schematron.xmlSchematronSetValidStructuredErrors(
+                valid_ctxt, <xmlerror.xmlStructuredErrorFunc> _receiveError, <void*>self._error_log)
+            c_doc = _fakeRootDoc(doc._c_doc, root_node._c_node)
+            with nogil:
+                ret = schematron.xmlSchematronValidateDoc(valid_ctxt, c_doc)
+            _destroyFakeDoc(doc._c_doc, c_doc)
+        finally:
+            schematron.xmlSchematronFreeValidCtxt(valid_ctxt)
+
+        if ret == -1:
+            raise SchematronValidateError(
+                "Internal error in Schematron validation",
+                self._error_log)
+        if ret == 0:
+            return True
+        else:
+            return False
diff --git a/.venv/lib/python3.12/site-packages/lxml/serializer.pxi b/.venv/lib/python3.12/site-packages/lxml/serializer.pxi
new file mode 100644
index 00000000..f0de0f9f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/serializer.pxi
@@ -0,0 +1,1781 @@
+# XML serialization and output functions
+
+cdef object GzipFile
+from gzip import GzipFile
+
+
+cdef class SerialisationError(LxmlError):
+    """A libxml2 error that occurred during serialisation.
+    """
+
+
+cdef enum _OutputMethods:
+    OUTPUT_METHOD_XML
+    OUTPUT_METHOD_HTML
+    OUTPUT_METHOD_TEXT
+
+
+cdef int _findOutputMethod(method) except -1:
+    if method is None:
+        return OUTPUT_METHOD_XML
+    method = method.lower()
+    if method == "xml":
+        return OUTPUT_METHOD_XML
+    if method == "html":
+        return OUTPUT_METHOD_HTML
+    if method == "text":
+        return OUTPUT_METHOD_TEXT
+    raise ValueError(f"unknown output method {method!r}")
+
+
+cdef _textToString(xmlNode* c_node, encoding, bint with_tail):
+    cdef bint needs_conversion
+    cdef const_xmlChar* c_text
+    cdef xmlNode* c_text_node
+    cdef tree.xmlBuffer* c_buffer
+    cdef int error_result
+
+    c_buffer = tree.xmlBufferCreate()
+    if c_buffer is NULL:
+        raise MemoryError()
+
+    with nogil:
+        error_result = tree.xmlNodeBufGetContent(c_buffer, c_node)
+        if with_tail:
+            c_text_node = _textNodeOrSkip(c_node.next)
+            while c_text_node is not NULL:
+                tree.xmlBufferWriteChar(c_buffer, <const_char*>c_text_node.content)
+                c_text_node = _textNodeOrSkip(c_text_node.next)
+        c_text = tree.xmlBufferContent(c_buffer)
+
+    if error_result < 0 or c_text is NULL:
+        tree.xmlBufferFree(c_buffer)
+        raise SerialisationError, "Error during serialisation (out of memory?)"
+
+    try:
+        needs_conversion = 0
+        if encoding is unicode:
+            needs_conversion = 1
+        elif encoding is not None:
+            # Python prefers lower case encoding names
+            encoding = encoding.lower()
+            if encoding not in ('utf8', 'utf-8'):
+                if encoding == 'ascii':
+                    if isutf8l(c_text, tree.xmlBufferLength(c_buffer)):
+                        # will raise a decode error below
+                        needs_conversion = 1
+                else:
+                    needs_conversion = 1
+
+        if needs_conversion:
+            text = (<const_char*>c_text)[:tree.xmlBufferLength(c_buffer)].decode('utf8')
+            if encoding is not unicode:
+                encoding = _utf8(encoding)
+                text = python.PyUnicode_AsEncodedString(
+                    text, encoding, 'strict')
+        else:
+            text = (<unsigned char*>c_text)[:tree.xmlBufferLength(c_buffer)]
+    finally:
+        tree.xmlBufferFree(c_buffer)
+    return text
+
+
+cdef _tostring(_Element element, encoding, doctype, method,
+               bint write_xml_declaration, bint write_complete_document,
+               bint pretty_print, bint with_tail, int standalone):
+    """Serialize an element to an encoded string representation of its XML
+    tree.
+    """
+    cdef tree.xmlOutputBuffer* c_buffer
+    cdef tree.xmlBuf* c_result_buffer
+    cdef tree.xmlCharEncodingHandler* enchandler
+    cdef const_char* c_enc
+    cdef const_xmlChar* c_version
+    cdef const_xmlChar* c_doctype
+    cdef int c_method
+    cdef int error_result
+    if element is None:
+        return None
+    _assertValidNode(element)
+    c_method = _findOutputMethod(method)
+    if c_method == OUTPUT_METHOD_TEXT:
+        return _textToString(element._c_node, encoding, with_tail)
+    if encoding is None or encoding is unicode:
+        c_enc = NULL
+    else:
+        encoding = _utf8(encoding)
+        c_enc = _cstr(encoding)
+    if doctype is None:
+        c_doctype = NULL
+    else:
+        doctype = _utf8(doctype)
+        c_doctype = _xcstr(doctype)
+    # it is necessary to *and* find the encoding handler *and* use
+    # encoding during output
+    enchandler = tree.xmlFindCharEncodingHandler(c_enc)
+    if enchandler is NULL and c_enc is not NULL:
+        if encoding is not None:
+            encoding = encoding.decode('UTF-8')
+        raise LookupError, f"unknown encoding: '{encoding}'"
+    c_buffer = tree.xmlAllocOutputBuffer(enchandler)
+    if c_buffer is NULL:
+        tree.xmlCharEncCloseFunc(enchandler)
+        raise MemoryError()
+
+    with nogil:
+        _writeNodeToBuffer(c_buffer, element._c_node, c_enc, c_doctype, c_method,
+                           write_xml_declaration, write_complete_document,
+                           pretty_print, with_tail, standalone)
+        tree.xmlOutputBufferFlush(c_buffer)
+        if c_buffer.conv is not NULL:
+            c_result_buffer = c_buffer.conv
+        else:
+            c_result_buffer = c_buffer.buffer
+
+    error_result = c_buffer.error
+    if error_result != xmlerror.XML_ERR_OK:
+        tree.xmlOutputBufferClose(c_buffer)
+        _raiseSerialisationError(error_result)
+
+    try:
+        if encoding is unicode:
+            result = (<unsigned char*>tree.xmlBufContent(
+                c_result_buffer))[:tree.xmlBufUse(c_result_buffer)].decode('UTF-8')
+        else:
+            result = <bytes>(<unsigned char*>tree.xmlBufContent(
+                c_result_buffer))[:tree.xmlBufUse(c_result_buffer)]
+    finally:
+        error_result = tree.xmlOutputBufferClose(c_buffer)
+    if error_result == -1:
+        _raiseSerialisationError(error_result)
+    return result
+
+cdef bytes _tostringC14N(element_or_tree, bint exclusive, bint with_comments, inclusive_ns_prefixes):
+    cdef xmlDoc* c_doc
+    cdef xmlChar* c_buffer = NULL
+    cdef int byte_count = -1
+    cdef bytes result
+    cdef _Document doc
+    cdef _Element element
+    cdef xmlChar **c_inclusive_ns_prefixes
+
+    if isinstance(element_or_tree, _Element):
+        _assertValidNode(<_Element>element_or_tree)
+        doc = (<_Element>element_or_tree)._doc
+        c_doc = _plainFakeRootDoc(doc._c_doc, (<_Element>element_or_tree)._c_node, 0)
+    else:
+        doc = _documentOrRaise(element_or_tree)
+        _assertValidDoc(doc)
+        c_doc = doc._c_doc
+
+    c_inclusive_ns_prefixes = _convert_ns_prefixes(c_doc.dict, inclusive_ns_prefixes) if inclusive_ns_prefixes else NULL
+    try:
+         with nogil:
+             byte_count = c14n.xmlC14NDocDumpMemory(
+                 c_doc, NULL, exclusive, c_inclusive_ns_prefixes, with_comments, &c_buffer)
+
+    finally:
+         _destroyFakeDoc(doc._c_doc, c_doc)
+         if c_inclusive_ns_prefixes is not NULL:
+            python.lxml_free(c_inclusive_ns_prefixes)
+
+    if byte_count < 0 or c_buffer is NULL:
+        if c_buffer is not NULL:
+            tree.xmlFree(c_buffer)
+        raise C14NError, "C14N failed"
+    try:
+        result = c_buffer[:byte_count]
+    finally:
+        tree.xmlFree(c_buffer)
+    return result
+
+cdef _raiseSerialisationError(int error_result):
+    if error_result == xmlerror.XML_ERR_NO_MEMORY:
+        raise MemoryError()
+    message = ErrorTypes._getName(error_result)
+    if message is None:
+        message = f"unknown error {error_result}"
+    raise SerialisationError, message
+
+############################################################
+# low-level serialisation functions
+
+cdef void _writeDoctype(tree.xmlOutputBuffer* c_buffer,
+                        const_xmlChar* c_doctype) noexcept nogil:
+    tree.xmlOutputBufferWrite(c_buffer, tree.xmlStrlen(c_doctype),
+                              <const_char*>c_doctype)
+    tree.xmlOutputBufferWriteString(c_buffer, "\n")
+
+cdef void _writeNodeToBuffer(tree.xmlOutputBuffer* c_buffer,
+                             xmlNode* c_node, const_char* encoding, const_xmlChar* c_doctype,
+                             int c_method, bint write_xml_declaration,
+                             bint write_complete_document,
+                             bint pretty_print, bint with_tail,
+                             int standalone) noexcept nogil:
+    cdef xmlNode* c_nsdecl_node
+    cdef xmlDoc* c_doc = c_node.doc
+    if write_xml_declaration and c_method == OUTPUT_METHOD_XML:
+        _writeDeclarationToBuffer(c_buffer, c_doc.version, encoding, standalone)
+
+    # comments/processing instructions before doctype declaration
+    if write_complete_document and not c_buffer.error and c_doc.intSubset:
+        _writePrevSiblings(c_buffer, <xmlNode*>c_doc.intSubset, encoding, pretty_print)
+
+    if c_doctype:
+        _writeDoctype(c_buffer, c_doctype)
+    # write internal DTD subset, preceding PIs/comments, etc.
+    if write_complete_document and not c_buffer.error:
+        if c_doctype is NULL:
+            _writeDtdToBuffer(c_buffer, c_doc, c_node.name, c_method, encoding)
+        _writePrevSiblings(c_buffer, c_node, encoding, pretty_print)
+
+    c_nsdecl_node = c_node
+    if not c_node.parent or c_node.parent.type != tree.XML_DOCUMENT_NODE:
+        # copy the node and add namespaces from parents
+        # this is required to make libxml write them
+        c_nsdecl_node = tree.xmlCopyNode(c_node, 2)
+        if not c_nsdecl_node:
+            c_buffer.error = xmlerror.XML_ERR_NO_MEMORY
+            return
+        _copyParentNamespaces(c_node, c_nsdecl_node)
+
+        c_nsdecl_node.parent = c_node.parent
+        c_nsdecl_node.children = c_node.children
+        c_nsdecl_node.last = c_node.last
+
+    # write node
+    if c_method == OUTPUT_METHOD_HTML:
+        tree.htmlNodeDumpFormatOutput(
+            c_buffer, c_doc, c_nsdecl_node, encoding, pretty_print)
+    else:
+        tree.xmlNodeDumpOutput(
+            c_buffer, c_doc, c_nsdecl_node, 0, pretty_print, encoding)
+
+    if c_nsdecl_node is not c_node:
+        # clean up
+        c_nsdecl_node.children = c_nsdecl_node.last = NULL
+        tree.xmlFreeNode(c_nsdecl_node)
+
+    if c_buffer.error:
+        return
+
+    # write tail, trailing comments, etc.
+    if with_tail:
+        _writeTail(c_buffer, c_node, encoding, c_method, pretty_print)
+    if write_complete_document:
+        _writeNextSiblings(c_buffer, c_node, encoding, pretty_print)
+    if pretty_print:
+        tree.xmlOutputBufferWrite(c_buffer, 1, "\n")
+
+cdef void _writeDeclarationToBuffer(tree.xmlOutputBuffer* c_buffer,
+                                    const_xmlChar* version, const_char* encoding,
+                                    int standalone) noexcept nogil:
+    if version is NULL:
+        version = <unsigned char*>"1.0"
+    tree.xmlOutputBufferWrite(c_buffer, 15, "<?xml version='")
+    tree.xmlOutputBufferWriteString(c_buffer, <const_char*>version)
+    tree.xmlOutputBufferWrite(c_buffer, 12, "' encoding='")
+    tree.xmlOutputBufferWriteString(c_buffer, encoding)
+    if standalone == 0:
+        tree.xmlOutputBufferWrite(c_buffer, 20, "' standalone='no'?>\n")
+    elif standalone == 1:
+        tree.xmlOutputBufferWrite(c_buffer, 21, "' standalone='yes'?>\n")
+    else:
+        tree.xmlOutputBufferWrite(c_buffer, 4, "'?>\n")
+
+cdef void _writeDtdToBuffer(tree.xmlOutputBuffer* c_buffer,
+                            xmlDoc* c_doc, const_xmlChar* c_root_name,
+                            int c_method, const_char* encoding) noexcept nogil:
+    cdef tree.xmlDtd* c_dtd
+    cdef xmlNode* c_node
+    cdef char* quotechar
+    c_dtd = c_doc.intSubset
+    if not c_dtd or not c_dtd.name:
+        return
+
+    # Name in document type declaration must match the root element tag.
+    # For XML, case sensitive match, for HTML insensitive.
+    if c_method == OUTPUT_METHOD_HTML:
+        if tree.xmlStrcasecmp(c_root_name, c_dtd.name) != 0:
+            return
+    else:
+        if tree.xmlStrcmp(c_root_name, c_dtd.name) != 0:
+            return
+
+    tree.xmlOutputBufferWrite(c_buffer, 10, "<!DOCTYPE ")
+    tree.xmlOutputBufferWriteString(c_buffer, <const_char*>c_dtd.name)
+
+    cdef const_xmlChar* public_id = c_dtd.ExternalID
+    cdef const_xmlChar* sys_url = c_dtd.SystemID
+    if public_id and public_id[0] == b'\0':
+        public_id = NULL
+    if sys_url and sys_url[0] == b'\0':
+        sys_url = NULL
+
+    if public_id:
+        tree.xmlOutputBufferWrite(c_buffer, 9, ' PUBLIC "')
+        tree.xmlOutputBufferWriteString(c_buffer, <const_char*>public_id)
+        if sys_url:
+            tree.xmlOutputBufferWrite(c_buffer, 2, '" ')
+        else:
+            tree.xmlOutputBufferWrite(c_buffer, 1, '"')
+    elif sys_url:
+        tree.xmlOutputBufferWrite(c_buffer, 8, ' SYSTEM ')
+
+    if sys_url:
+        if tree.xmlStrchr(sys_url, b'"'):
+            quotechar = '\''
+        else:
+            quotechar = '"'
+        tree.xmlOutputBufferWrite(c_buffer, 1, quotechar)
+        tree.xmlOutputBufferWriteString(c_buffer, <const_char*>sys_url)
+        tree.xmlOutputBufferWrite(c_buffer, 1, quotechar)
+
+    if (not c_dtd.entities and not c_dtd.elements and
+           not c_dtd.attributes and not c_dtd.notations and
+           not c_dtd.pentities):
+        tree.xmlOutputBufferWrite(c_buffer, 2, '>\n')
+        return
+
+    tree.xmlOutputBufferWrite(c_buffer, 3, ' [\n')
+    if c_dtd.notations and not c_buffer.error:
+        c_buf = tree.xmlBufferCreate()
+        if not c_buf:
+            c_buffer.error = xmlerror.XML_ERR_NO_MEMORY
+            return
+        tree.xmlDumpNotationTable(c_buf, <tree.xmlNotationTable*>c_dtd.notations)
+        tree.xmlOutputBufferWrite(
+            c_buffer, tree.xmlBufferLength(c_buf),
+            <const_char*>tree.xmlBufferContent(c_buf))
+        tree.xmlBufferFree(c_buf)
+    c_node = c_dtd.children
+    while c_node and not c_buffer.error:
+        tree.xmlNodeDumpOutput(c_buffer, c_node.doc, c_node, 0, 0, encoding)
+        c_node = c_node.next
+    tree.xmlOutputBufferWrite(c_buffer, 3, "]>\n")
+
+cdef void _writeTail(tree.xmlOutputBuffer* c_buffer, xmlNode* c_node,
+                     const_char* encoding, int c_method, bint pretty_print) noexcept nogil:
+    "Write the element tail."
+    c_node = c_node.next
+    while c_node and not c_buffer.error and c_node.type in (
+            tree.XML_TEXT_NODE, tree.XML_CDATA_SECTION_NODE):
+        if c_method == OUTPUT_METHOD_HTML:
+            tree.htmlNodeDumpFormatOutput(
+                c_buffer, c_node.doc, c_node, encoding, pretty_print)
+        else:
+            tree.xmlNodeDumpOutput(
+                c_buffer, c_node.doc, c_node, 0, pretty_print, encoding)
+        c_node = c_node.next
+
+cdef void _writePrevSiblings(tree.xmlOutputBuffer* c_buffer, xmlNode* c_node,
+                             const_char* encoding, bint pretty_print) noexcept nogil:
+    cdef xmlNode* c_sibling
+    if c_node.parent and _isElement(c_node.parent):
+        return
+    # we are at a root node, so add PI and comment siblings
+    c_sibling = c_node
+    while c_sibling.prev and \
+            (c_sibling.prev.type == tree.XML_PI_NODE or
+             c_sibling.prev.type == tree.XML_COMMENT_NODE):
+        c_sibling = c_sibling.prev
+    while c_sibling is not c_node and not c_buffer.error:
+        tree.xmlNodeDumpOutput(c_buffer, c_node.doc, c_sibling, 0,
+                               pretty_print, encoding)
+        if pretty_print:
+            tree.xmlOutputBufferWriteString(c_buffer, "\n")
+        c_sibling = c_sibling.next
+
+cdef void _writeNextSiblings(tree.xmlOutputBuffer* c_buffer, xmlNode* c_node,
+                             const_char* encoding, bint pretty_print) noexcept nogil:
+    cdef xmlNode* c_sibling
+    if c_node.parent and _isElement(c_node.parent):
+        return
+    # we are at a root node, so add PI and comment siblings
+    c_sibling = c_node.next
+    while not c_buffer.error and c_sibling and \
+            (c_sibling.type == tree.XML_PI_NODE or
+             c_sibling.type == tree.XML_COMMENT_NODE):
+        if pretty_print:
+            tree.xmlOutputBufferWriteString(c_buffer, "\n")
+        tree.xmlNodeDumpOutput(c_buffer, c_node.doc, c_sibling, 0,
+                               pretty_print, encoding)
+        c_sibling = c_sibling.next
+
+
+# copied and adapted from libxml2 (xmlBufAttrSerializeTxtContent())
+cdef _write_attr_string(tree.xmlOutputBuffer* buf, const char *string):
+    cdef const char *base
+    cdef const char *cur
+
+    if string == NULL:
+        return
+
+    base = cur = <const char*>string
+    while cur[0] != 0:
+        if cur[0] == b'\n':
+            if base != cur:
+                tree.xmlOutputBufferWrite(buf, cur - base, base)
+
+            tree.xmlOutputBufferWrite(buf, 5, "&#10;")
+            cur += 1
+            base = cur
+
+        elif cur[0] == b'\r':
+            if base != cur:
+                tree.xmlOutputBufferWrite(buf, cur - base, base)
+
+            tree.xmlOutputBufferWrite(buf, 5, "&#13;")
+            cur += 1
+            base = cur
+
+        elif cur[0] == b'\t':
+            if base != cur:
+                tree.xmlOutputBufferWrite(buf, cur - base, base)
+
+            tree.xmlOutputBufferWrite(buf, 4, "&#9;")
+            cur += 1
+            base = cur
+
+        elif cur[0] == b'"':
+            if base != cur:
+                tree.xmlOutputBufferWrite(buf, cur - base, base)
+
+            tree.xmlOutputBufferWrite(buf, 6, "&quot;")
+            cur += 1
+            base = cur
+
+        elif cur[0] == b'<':
+            if base != cur:
+                tree.xmlOutputBufferWrite(buf, cur - base, base)
+
+            tree.xmlOutputBufferWrite(buf, 4, "&lt;")
+            cur += 1
+            base = cur
+
+        elif cur[0] == b'>':
+            if base != cur:
+                tree.xmlOutputBufferWrite(buf, cur - base, base)
+
+            tree.xmlOutputBufferWrite(buf, 4, "&gt;")
+            cur += 1
+            base = cur
+        elif cur[0] == b'&':
+            if base != cur:
+                tree.xmlOutputBufferWrite(buf, cur - base, base)
+
+            tree.xmlOutputBufferWrite(buf, 5, "&amp;")
+            cur += 1
+            base = cur
+
+        else:
+            # Leave further encoding and escaping to the buffer encoder.
+            cur += 1
+
+    if base != cur:
+        tree.xmlOutputBufferWrite(buf, cur - base, base)
+
+
+############################################################
+# output to file-like objects
+
+cdef object io_open
+from io import open as io_open
+
+cdef object gzip
+import gzip
+
+cdef object getwriter
+from codecs import getwriter
+cdef object utf8_writer = getwriter('utf8')
+
+cdef object contextmanager
+from contextlib import contextmanager
+
+cdef object _open_utf8_file
+
+@contextmanager
+def _open_utf8_file(file, compression=0):
+    file = _getFSPathOrObject(file)
+    if _isString(file):
+        if compression:
+            with gzip.GzipFile(file, mode='wb', compresslevel=compression) as zf:
+                yield utf8_writer(zf)
+        else:
+            with io_open(file, 'w', encoding='utf8') as f:
+                yield f
+    else:
+        if compression:
+            with gzip.GzipFile(fileobj=file, mode='wb', compresslevel=compression) as zf:
+                yield utf8_writer(zf)
+        else:
+            yield utf8_writer(file)
+
+
+@cython.final
+@cython.internal
+cdef class _FilelikeWriter:
+    cdef object _filelike
+    cdef object _close_filelike
+    cdef _ExceptionContext _exc_context
+    cdef _ErrorLog error_log
+    def __cinit__(self, filelike, exc_context=None, compression=None, close=False):
+        if compression is not None and compression > 0:
+            filelike = GzipFile(
+                fileobj=filelike, mode='wb', compresslevel=compression)
+            self._close_filelike = filelike.close
+        elif close:
+            self._close_filelike = filelike.close
+        self._filelike = filelike
+        if exc_context is None:
+            self._exc_context = _ExceptionContext()
+        else:
+            self._exc_context = exc_context
+        self.error_log = _ErrorLog()
+
+    cdef tree.xmlOutputBuffer* _createOutputBuffer(
+        self, tree.xmlCharEncodingHandler* enchandler) except NULL:
+        cdef tree.xmlOutputBuffer* c_buffer
+        c_buffer = tree.xmlOutputBufferCreateIO(
+            <tree.xmlOutputWriteCallback>_writeFilelikeWriter, _closeFilelikeWriter,
+            <python.PyObject*>self, enchandler)
+        if c_buffer is NULL:
+            raise IOError, "Could not create I/O writer context."
+        return c_buffer
+
+    cdef int write(self, char* c_buffer, int size) noexcept:
+        try:
+            if self._filelike is None:
+                raise IOError, "File is already closed"
+            py_buffer = <bytes>c_buffer[:size]
+            self._filelike.write(py_buffer)
+        except:
+            size = -1
+            self._exc_context._store_raised()
+        finally:
+            return size  # and swallow any further exceptions
+
+    cdef int close(self) noexcept:
+        retval = 0
+        try:
+            if self._close_filelike is not None:
+                self._close_filelike()
+            # we should not close the file here as we didn't open it
+            self._filelike = None
+        except:
+            retval = -1
+            self._exc_context._store_raised()
+        finally:
+            return retval  # and swallow any further exceptions
+
+cdef int _writeFilelikeWriter(void* ctxt, char* c_buffer, int length) noexcept:
+    return (<_FilelikeWriter>ctxt).write(c_buffer, length)
+
+cdef int _closeFilelikeWriter(void* ctxt) noexcept:
+    return (<_FilelikeWriter>ctxt).close()
+
+cdef _tofilelike(f, _Element element, encoding, doctype, method,
+                 bint write_xml_declaration, bint write_doctype,
+                 bint pretty_print, bint with_tail, int standalone,
+                 int compression):
+    cdef _FilelikeWriter writer = None
+    cdef tree.xmlOutputBuffer* c_buffer
+    cdef tree.xmlCharEncodingHandler* enchandler
+    cdef const_char* c_enc
+    cdef const_xmlChar* c_doctype
+    cdef int error_result
+
+    c_method = _findOutputMethod(method)
+    if c_method == OUTPUT_METHOD_TEXT:
+        data = _textToString(element._c_node, encoding, with_tail)
+        if compression:
+            bytes_out = BytesIO()
+            with GzipFile(fileobj=bytes_out, mode='wb', compresslevel=compression) as gzip_file:
+                gzip_file.write(data)
+            data = bytes_out.getvalue()
+        f = _getFSPathOrObject(f)
+        if _isString(f):
+            filename8 = _encodeFilename(f)
+            with open(filename8, 'wb') as f:
+                f.write(data)
+        else:
+            f.write(data)
+        return
+
+    if encoding is None:
+        c_enc = NULL
+    else:
+        encoding = _utf8(encoding)
+        c_enc = _cstr(encoding)
+    if doctype is None:
+        c_doctype = NULL
+    else:
+        doctype = _utf8(doctype)
+        c_doctype = _xcstr(doctype)
+
+    writer = _create_output_buffer(f, c_enc, compression, &c_buffer, close=False)
+    if writer is None:
+        with nogil:
+            error_result = _serialise_node(
+                c_buffer, c_doctype, c_enc, element._c_node, c_method,
+                write_xml_declaration, write_doctype, pretty_print, with_tail, standalone)
+    else:
+        error_result = _serialise_node(
+            c_buffer, c_doctype, c_enc, element._c_node, c_method,
+            write_xml_declaration, write_doctype, pretty_print, with_tail, standalone)
+
+    if writer is not None:
+        writer._exc_context._raise_if_stored()
+    if error_result != xmlerror.XML_ERR_OK:
+        _raiseSerialisationError(error_result)
+
+
+cdef int _serialise_node(tree.xmlOutputBuffer* c_buffer, const_xmlChar* c_doctype,
+                         const_char* c_enc, xmlNode* c_node, int c_method,
+                         bint write_xml_declaration, bint write_doctype, bint pretty_print,
+                         bint with_tail, int standalone) noexcept nogil:
+    _writeNodeToBuffer(
+        c_buffer, c_node, c_enc, c_doctype, c_method,
+        write_xml_declaration, write_doctype, pretty_print, with_tail, standalone)
+    error_result = c_buffer.error
+    if error_result == xmlerror.XML_ERR_OK:
+        error_result = tree.xmlOutputBufferClose(c_buffer)
+        if error_result != -1:
+            error_result = xmlerror.XML_ERR_OK
+    else:
+        tree.xmlOutputBufferClose(c_buffer)
+    return error_result
+
+
+cdef _FilelikeWriter _create_output_buffer(
+        f, const_char* c_enc, int c_compression,
+        tree.xmlOutputBuffer** c_buffer_ret, bint close):
+    cdef tree.xmlOutputBuffer* c_buffer
+    cdef _FilelikeWriter writer
+    cdef bytes filename8
+    enchandler = tree.xmlFindCharEncodingHandler(c_enc)
+    if enchandler is NULL:
+        raise LookupError(
+            f"unknown encoding: '{c_enc.decode('UTF-8') if c_enc is not NULL else u''}'")
+    try:
+        f = _getFSPathOrObject(f)
+        if _isString(f):
+            filename8 = _encodeFilename(f)
+            if b'%' in filename8 and (
+                    # Exclude absolute Windows paths and file:// URLs.
+                    _isFilePath(<const xmlChar*>filename8) not in (NO_FILE_PATH, ABS_WIN_FILE_PATH)
+                    or filename8[:7].lower() == b'file://'):
+                # A file path (not a URL) containing the '%' URL escape character.
+                # libxml2 uses URL-unescaping on these, so escape the path before passing it in.
+                filename8 = filename8.replace(b'%', b'%25')
+            c_buffer = tree.xmlOutputBufferCreateFilename(
+                _cstr(filename8), enchandler, c_compression)
+            if c_buffer is NULL:
+                python.PyErr_SetFromErrno(IOError)  # raises IOError
+            writer = None
+        elif hasattr(f, 'write'):
+            writer = _FilelikeWriter(f, compression=c_compression, close=close)
+            c_buffer = writer._createOutputBuffer(enchandler)
+        else:
+            raise TypeError(
+                f"File or filename expected, got '{python._fqtypename(f).decode('UTF-8')}'")
+    except:
+        tree.xmlCharEncCloseFunc(enchandler)
+        raise
+    c_buffer_ret[0] = c_buffer
+    return writer
+
+cdef xmlChar **_convert_ns_prefixes(tree.xmlDict* c_dict, ns_prefixes) except NULL:
+    cdef size_t i, num_ns_prefixes = len(ns_prefixes)
+    # Need to allocate one extra memory block to handle last NULL entry
+    c_ns_prefixes = <xmlChar **>python.lxml_malloc(num_ns_prefixes + 1, sizeof(xmlChar*))
+    if not c_ns_prefixes:
+        raise MemoryError()
+    i = 0
+    try:
+        for prefix in ns_prefixes:
+             prefix_utf = _utf8(prefix)
+             c_prefix = tree.xmlDictExists(c_dict, _xcstr(prefix_utf), len(prefix_utf))
+             if c_prefix:
+                 # unknown prefixes do not need to get serialised
+                 c_ns_prefixes[i] = <xmlChar*>c_prefix
+                 i += 1
+    except:
+        python.lxml_free(c_ns_prefixes)
+        raise
+
+    c_ns_prefixes[i] = NULL  # append end marker
+    return c_ns_prefixes
+
+cdef _tofilelikeC14N(f, _Element element, bint exclusive, bint with_comments,
+                     int compression, inclusive_ns_prefixes):
+    cdef _FilelikeWriter writer = None
+    cdef tree.xmlOutputBuffer* c_buffer
+    cdef xmlChar **c_inclusive_ns_prefixes = NULL
+    cdef char* c_filename
+    cdef xmlDoc* c_base_doc
+    cdef xmlDoc* c_doc
+    cdef int bytes_count, error = 0
+
+    c_base_doc = element._c_node.doc
+    c_doc = _fakeRootDoc(c_base_doc, element._c_node)
+    try:
+        c_inclusive_ns_prefixes = (
+            _convert_ns_prefixes(c_doc.dict, inclusive_ns_prefixes)
+            if inclusive_ns_prefixes else NULL)
+
+        f = _getFSPathOrObject(f)
+        if _isString(f):
+            filename8 = _encodeFilename(f)
+            c_filename = _cstr(filename8)
+            with nogil:
+                error = c14n.xmlC14NDocSave(
+                    c_doc, NULL, exclusive, c_inclusive_ns_prefixes,
+                    with_comments, c_filename, compression)
+        elif hasattr(f, 'write'):
+            writer   = _FilelikeWriter(f, compression=compression)
+            c_buffer = writer._createOutputBuffer(NULL)
+            try:
+                with writer.error_log:
+                    bytes_count = c14n.xmlC14NDocSaveTo(
+                        c_doc, NULL, exclusive, c_inclusive_ns_prefixes,
+                        with_comments, c_buffer)
+            finally:
+                error = tree.xmlOutputBufferClose(c_buffer)
+            if bytes_count < 0:
+                error = bytes_count
+            elif error != -1:
+                error = xmlerror.XML_ERR_OK
+        else:
+            raise TypeError(f"File or filename expected, got '{python._fqtypename(f).decode('UTF-8')}'")
+    finally:
+        _destroyFakeDoc(c_base_doc, c_doc)
+        if c_inclusive_ns_prefixes is not NULL:
+            python.lxml_free(c_inclusive_ns_prefixes)
+
+    if writer is not None:
+        writer._exc_context._raise_if_stored()
+
+    if error < 0:
+        message = "C14N failed"
+        if writer is not None:
+            errors = writer.error_log
+            if len(errors):
+                message = errors[0].message
+        raise C14NError(message)
+
+
+# C14N 2.0
+
+def canonicalize(xml_data=None, *, out=None, from_file=None, **options):
+    """Convert XML to its C14N 2.0 serialised form.
+
+    If *out* is provided, it must be a file or file-like object that receives
+    the serialised canonical XML output (text, not bytes) through its ``.write()``
+    method.  To write to a file, open it in text mode with encoding "utf-8".
+    If *out* is not provided, this function returns the output as text string.
+
+    Either *xml_data* (an XML string, tree or Element) or *file*
+    (a file path or file-like object) must be provided as input.
+
+    The configuration options are the same as for the ``C14NWriterTarget``.
+    """
+    if xml_data is None and from_file is None:
+        raise ValueError("Either 'xml_data' or 'from_file' must be provided as input")
+
+    sio = None
+    if out is None:
+        sio = out = StringIO()
+
+    target = C14NWriterTarget(out.write, **options)
+
+    if xml_data is not None and not isinstance(xml_data, basestring):
+        _tree_to_target(xml_data, target)
+        return sio.getvalue() if sio is not None else None
+
+    cdef _FeedParser parser = XMLParser(
+        target=target,
+        attribute_defaults=True,
+        collect_ids=False,
+    )
+
+    if xml_data is not None:
+        parser.feed(xml_data)
+        parser.close()
+    elif from_file is not None:
+        try:
+            _parseDocument(from_file, parser, base_url=None)
+        except _TargetParserResult:
+            pass
+
+    return sio.getvalue() if sio is not None else None
+
+
+cdef _tree_to_target(element, target):
+    for event, elem in iterwalk(element, events=('start', 'end', 'start-ns', 'comment', 'pi')):
+        text = None
+        if event == 'start':
+            target.start(elem.tag, elem.attrib)
+            text = elem.text
+        elif event == 'end':
+            target.end(elem.tag)
+            text = elem.tail
+        elif event == 'start-ns':
+            target.start_ns(*elem)
+            continue
+        elif event == 'comment':
+            target.comment(elem.text)
+            text = elem.tail
+        elif event == 'pi':
+            target.pi(elem.target, elem.text)
+            text = elem.tail
+        if text:
+            target.data(text)
+    return target.close()
+
+
+cdef object _looks_like_prefix_name = re.compile(r'^\w+:\w+$', re.UNICODE).match
+
+
+cdef class C14NWriterTarget:
+    """
+    Canonicalization writer target for the XMLParser.
+
+    Serialises parse events to XML C14N 2.0.
+
+    Configuration options:
+
+    - *with_comments*: set to true to include comments
+    - *strip_text*: set to true to strip whitespace before and after text content
+    - *rewrite_prefixes*: set to true to replace namespace prefixes by "n{number}"
+    - *qname_aware_tags*: a set of qname aware tag names in which prefixes
+                          should be replaced in text content
+    - *qname_aware_attrs*: a set of qname aware attribute names in which prefixes
+                           should be replaced in text content
+    - *exclude_attrs*: a set of attribute names that should not be serialised
+    - *exclude_tags*: a set of tag names that should not be serialised
+    """
+    cdef object _write
+    cdef list _data
+    cdef set _qname_aware_tags
+    cdef object _find_qname_aware_attrs
+    cdef list _declared_ns_stack
+    cdef list _ns_stack
+    cdef dict _prefix_map
+    cdef list _preserve_space
+    cdef tuple _pending_start
+    cdef set _exclude_tags
+    cdef set _exclude_attrs
+    cdef Py_ssize_t _ignored_depth
+    cdef bint _with_comments
+    cdef bint _strip_text
+    cdef bint _rewrite_prefixes
+    cdef bint _root_seen
+    cdef bint _root_done
+
+    def __init__(self, write, *,
+                 with_comments=False, strip_text=False, rewrite_prefixes=False,
+                 qname_aware_tags=None, qname_aware_attrs=None,
+                 exclude_attrs=None, exclude_tags=None):
+        self._write = write
+        self._data = []
+        self._with_comments = with_comments
+        self._strip_text = strip_text
+        self._exclude_attrs = set(exclude_attrs) if exclude_attrs else None
+        self._exclude_tags = set(exclude_tags) if exclude_tags else None
+
+        self._rewrite_prefixes = rewrite_prefixes
+        if qname_aware_tags:
+            self._qname_aware_tags = set(qname_aware_tags)
+        else:
+            self._qname_aware_tags = None
+        if qname_aware_attrs:
+            self._find_qname_aware_attrs = set(qname_aware_attrs).intersection
+        else:
+            self._find_qname_aware_attrs = None
+
+        # Stack with globally and newly declared namespaces as (uri, prefix) pairs.
+        self._declared_ns_stack = [[
+            ("http://www.w3.org/XML/1998/namespace", "xml"),
+        ]]
+        # Stack with user declared namespace prefixes as (uri, prefix) pairs.
+        self._ns_stack = []
+        if not rewrite_prefixes:
+            self._ns_stack.append(_DEFAULT_NAMESPACE_PREFIXES_ITEMS)
+        self._ns_stack.append([])
+        self._prefix_map = {}
+        self._preserve_space = [False]
+        self._pending_start = None
+        self._ignored_depth = 0
+        self._root_seen = False
+        self._root_done = False
+
+    def _iter_namespaces(self, ns_stack):
+        for namespaces in reversed(ns_stack):
+            if namespaces:  # almost no element declares new namespaces
+                yield from namespaces
+
+    cdef _resolve_prefix_name(self, prefixed_name):
+        prefix, name = prefixed_name.split(':', 1)
+        for uri, p in self._iter_namespaces(self._ns_stack):
+            if p == prefix:
+                return f'{{{uri}}}{name}'
+        raise ValueError(f'Prefix {prefix} of QName "{prefixed_name}" is not declared in scope')
+
+    cdef _qname(self, qname, uri=None):
+        if uri is None:
+            uri, tag = qname[1:].rsplit('}', 1) if qname[:1] == '{' else ('', qname)
+        else:
+            tag = qname
+
+        prefixes_seen = set()
+        for u, prefix in self._iter_namespaces(self._declared_ns_stack):
+            if u == uri and prefix not in prefixes_seen:
+                return f'{prefix}:{tag}' if prefix else tag, tag, uri
+            prefixes_seen.add(prefix)
+
+        # Not declared yet => add new declaration.
+        if self._rewrite_prefixes:
+            if uri in self._prefix_map:
+                prefix = self._prefix_map[uri]
+            else:
+                prefix = self._prefix_map[uri] = f'n{len(self._prefix_map)}'
+            self._declared_ns_stack[-1].append((uri, prefix))
+            return f'{prefix}:{tag}', tag, uri
+
+        if not uri and '' not in prefixes_seen:
+            # No default namespace declared => no prefix needed.
+            return tag, tag, uri
+
+        for u, prefix in self._iter_namespaces(self._ns_stack):
+            if u == uri:
+                self._declared_ns_stack[-1].append((uri, prefix))
+                return f'{prefix}:{tag}' if prefix else tag, tag, uri
+
+        if not uri:
+            # As soon as a default namespace is defined,
+            # anything that has no namespace (and thus, no prefix) goes there.
+            return tag, tag, uri
+
+        raise ValueError(f'Namespace "{uri}" of name "{tag}" is not declared in scope')
+
+    def data(self, data):
+        if not self._ignored_depth:
+            self._data.append(data)
+
+    cdef _flush(self):
+        cdef unicode data = ''.join(self._data)
+        del self._data[:]
+        if self._strip_text and not self._preserve_space[-1]:
+            data = data.strip()
+        if self._pending_start is not None:
+            (tag, attrs, new_namespaces), self._pending_start = self._pending_start, None
+            qname_text = data if ':' in data and _looks_like_prefix_name(data) else None
+            self._start(tag, attrs, new_namespaces, qname_text)
+            if qname_text is not None:
+                return
+        if data and self._root_seen:
+            self._write(_escape_cdata_c14n(data))
+
+    def start_ns(self, prefix, uri):
+        if self._ignored_depth:
+            return
+        # we may have to resolve qnames in text content
+        if self._data:
+            self._flush()
+        self._ns_stack[-1].append((uri, prefix))
+
+    def start(self, tag, attrs):
+        if self._exclude_tags is not None and (
+                self._ignored_depth or tag in self._exclude_tags):
+            self._ignored_depth += 1
+            return
+        if self._data:
+            self._flush()
+
+        new_namespaces = []
+        self._declared_ns_stack.append(new_namespaces)
+
+        if self._qname_aware_tags is not None and tag in self._qname_aware_tags:
+            # Need to parse text first to see if it requires a prefix declaration.
+            self._pending_start = (tag, attrs, new_namespaces)
+            return
+        self._start(tag, attrs, new_namespaces)
+
+    cdef _start(self, tag, attrs, new_namespaces, qname_text=None):
+        if self._exclude_attrs is not None and attrs:
+            attrs = {k: v for k, v in attrs.items() if k not in self._exclude_attrs}
+
+        qnames = {tag, *attrs}
+        resolved_names = {}
+
+        # Resolve prefixes in attribute and tag text.
+        if qname_text is not None:
+            qname = resolved_names[qname_text] = self._resolve_prefix_name(qname_text)
+            qnames.add(qname)
+        if self._find_qname_aware_attrs is not None and attrs:
+            qattrs = self._find_qname_aware_attrs(attrs)
+            if qattrs:
+                for attr_name in qattrs:
+                    value = attrs[attr_name]
+                    if _looks_like_prefix_name(value):
+                        qname = resolved_names[value] = self._resolve_prefix_name(value)
+                        qnames.add(qname)
+            else:
+                qattrs = None
+        else:
+            qattrs = None
+
+        # Assign prefixes in lexicographical order of used URIs.
+        parsed_qnames = {n: self._qname(n) for n in sorted(
+            qnames, key=lambda n: n.split('}', 1))}
+
+        # Write namespace declarations in prefix order ...
+        if new_namespaces:
+            attr_list = [
+                ('xmlns:' + prefix if prefix else 'xmlns', uri)
+                for uri, prefix in new_namespaces
+            ]
+            attr_list.sort()
+        else:
+            # almost always empty
+            attr_list = []
+
+        # ... followed by attributes in URI+name order
+        if attrs:
+            for k, v in sorted(attrs.items()):
+                if qattrs is not None and k in qattrs and v in resolved_names:
+                    v = parsed_qnames[resolved_names[v]][0]
+                attr_qname, attr_name, uri = parsed_qnames[k]
+                # No prefix for attributes in default ('') namespace.
+                attr_list.append((attr_qname if uri else attr_name, v))
+
+        # Honour xml:space attributes.
+        space_behaviour = attrs.get('{http://www.w3.org/XML/1998/namespace}space')
+        self._preserve_space.append(
+            space_behaviour == 'preserve' if space_behaviour
+            else self._preserve_space[-1])
+
+        # Write the tag.
+        write = self._write
+        write('<' + parsed_qnames[tag][0])
+        if attr_list:
+            write(''.join([f' {k}="{_escape_attrib_c14n(v)}"' for k, v in attr_list]))
+        write('>')
+
+        # Write the resolved qname text content.
+        if qname_text is not None:
+            write(_escape_cdata_c14n(parsed_qnames[resolved_names[qname_text]][0]))
+
+        self._root_seen = True
+        self._ns_stack.append([])
+
+    def end(self, tag):
+        if self._ignored_depth:
+            self._ignored_depth -= 1
+            return
+        if self._data:
+            self._flush()
+        self._write(f'</{self._qname(tag)[0]}>')
+        self._preserve_space.pop()
+        self._root_done = len(self._preserve_space) == 1
+        self._declared_ns_stack.pop()
+        self._ns_stack.pop()
+
+    def comment(self, text):
+        if not self._with_comments:
+            return
+        if self._ignored_depth:
+            return
+        if self._root_done:
+            self._write('\n')
+        elif self._root_seen and self._data:
+            self._flush()
+        self._write(f'<!--{_escape_cdata_c14n(text)}-->')
+        if not self._root_seen:
+            self._write('\n')
+
+    def pi(self, target, data):
+        if self._ignored_depth:
+            return
+        if self._root_done:
+            self._write('\n')
+        elif self._root_seen and self._data:
+            self._flush()
+        self._write(
+            f'<?{target} {_escape_cdata_c14n(data)}?>' if data else f'<?{target}?>')
+        if not self._root_seen:
+            self._write('\n')
+
+    def close(self):
+        return None
+
+
+cdef _raise_serialization_error(text):
+    raise TypeError("cannot serialize %r (type %s)" % (text, type(text).__name__))
+
+
+cdef unicode _escape_cdata_c14n(stext):
+    # escape character data
+    cdef unicode text
+    cdef Py_UCS4 ch
+    cdef Py_ssize_t start = 0, pos = 0
+    cdef list substrings = None
+    try:
+        text = unicode(stext)
+    except (TypeError, AttributeError):
+        return _raise_serialization_error(stext)
+
+    for pos, ch in enumerate(text):
+        if ch == '&':
+            escape = '&amp;'
+        elif ch == '<':
+            escape = '&lt;'
+        elif ch == '>':
+            escape = '&gt;'
+        elif ch == '\r':
+            escape = '&#xD;'
+        else:
+            continue
+
+        if substrings is None:
+            substrings = []
+        if pos > start:
+            substrings.append(text[start:pos])
+        substrings.append(escape)
+        start = pos + 1
+
+    if substrings is None:
+        return text
+    if pos >= start:
+        substrings.append(text[start:pos+1])
+    return ''.join(substrings)
+
+
+cdef unicode _escape_attrib_c14n(stext):
+    # escape attribute value
+    cdef unicode text
+    cdef Py_UCS4 ch
+    cdef Py_ssize_t start = 0, pos = 0
+    cdef list substrings = None
+    try:
+        text = unicode(stext)
+    except (TypeError, AttributeError):
+        return _raise_serialization_error(stext)
+
+    for pos, ch in enumerate(text):
+        if ch == '&':
+            escape = '&amp;'
+        elif ch == '<':
+            escape = '&lt;'
+        elif ch == '"':
+            escape = '&quot;'
+        elif ch == '\t':
+            escape = '&#x9;'
+        elif ch == '\n':
+            escape = '&#xA;'
+        elif ch == '\r':
+            escape = '&#xD;'
+        else:
+            continue
+
+        if substrings is None:
+            substrings = []
+        if pos > start:
+            substrings.append(text[start:pos])
+        substrings.append(escape)
+        start = pos + 1
+
+    if substrings is None:
+        return text
+    if pos >= start:
+        substrings.append(text[start:pos+1])
+    return ''.join(substrings)
+
+
+# incremental serialisation
+
+cdef class xmlfile:
+    """xmlfile(self, output_file, encoding=None, compression=None, close=False, buffered=True)
+
+    A simple mechanism for incremental XML serialisation.
+
+    Usage example::
+
+         with xmlfile("somefile.xml", encoding='utf-8') as xf:
+             xf.write_declaration(standalone=True)
+             xf.write_doctype('<!DOCTYPE root SYSTEM "some.dtd">')
+
+             # generate an element (the root element)
+             with xf.element('root'):
+                  # write a complete Element into the open root element
+                  xf.write(etree.Element('test'))
+
+                  # generate and write more Elements, e.g. through iterparse
+                  for element in generate_some_elements():
+                      # serialise generated elements into the XML file
+                      xf.write(element)
+
+                  # or write multiple Elements or strings at once
+                  xf.write(etree.Element('start'), "text", etree.Element('end'))
+
+    If 'output_file' is a file(-like) object, passing ``close=True`` will
+    close it when exiting the context manager.  By default, it is left
+    to the owner to do that.  When a file path is used, lxml will take care
+    of opening and closing the file itself.  Also, when a compression level
+    is set, lxml will deliberately close the file to make sure all data gets
+    compressed and written.
+
+    Setting ``buffered=False`` will flush the output after each operation,
+    such as opening or closing an ``xf.element()`` block or calling
+    ``xf.write()``.  Alternatively, calling ``xf.flush()`` can be used to
+    explicitly flush any pending output when buffering is enabled.
+    """
+    cdef object output_file
+    cdef bytes encoding
+    cdef _IncrementalFileWriter writer
+    cdef _AsyncIncrementalFileWriter async_writer
+    cdef int compresslevel
+    cdef bint close
+    cdef bint buffered
+    cdef int method
+
+    def __init__(self, output_file not None, encoding=None, compression=None,
+                 close=False, buffered=True):
+        self.output_file = output_file
+        self.encoding = _utf8orNone(encoding)
+        self.compresslevel = compression or 0
+        self.close = close
+        self.buffered = buffered
+        self.method = OUTPUT_METHOD_XML
+
+    def __enter__(self):
+        assert self.output_file is not None
+        self.writer = _IncrementalFileWriter(
+            self.output_file, self.encoding, self.compresslevel,
+            self.close, self.buffered, self.method)
+        return self.writer
+
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        if self.writer is not None:
+            old_writer, self.writer = self.writer, None
+            raise_on_error = exc_type is None
+            old_writer._close(raise_on_error)
+            if self.close:
+                self.output_file = None
+
+    async def __aenter__(self):
+        assert self.output_file is not None
+        if isinstance(self.output_file, basestring):
+            raise TypeError("Cannot asynchronously write to a plain file")
+        if not hasattr(self.output_file, 'write'):
+            raise TypeError("Output file needs an async .write() method")
+        self.async_writer = _AsyncIncrementalFileWriter(
+            self.output_file, self.encoding, self.compresslevel,
+            self.close, self.buffered, self.method)
+        return self.async_writer
+
+    async def __aexit__(self, exc_type, exc_val, exc_tb):
+        if self.async_writer is not None:
+            old_writer, self.async_writer = self.async_writer, None
+            raise_on_error = exc_type is None
+            await old_writer._close(raise_on_error)
+            if self.close:
+                self.output_file = None
+
+
+cdef class htmlfile(xmlfile):
+    """htmlfile(self, output_file, encoding=None, compression=None, close=False, buffered=True)
+
+    A simple mechanism for incremental HTML serialisation.  Works the same as
+    xmlfile.
+    """
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.method = OUTPUT_METHOD_HTML
+
+
+cdef enum _IncrementalFileWriterStatus:
+    WRITER_STARTING = 0
+    WRITER_DECL_WRITTEN = 1
+    WRITER_DTD_WRITTEN = 2
+    WRITER_IN_ELEMENT = 3
+    WRITER_FINISHED = 4
+
+
+@cython.final
+@cython.internal
+cdef class _IncrementalFileWriter:
+    cdef tree.xmlOutputBuffer* _c_out
+    cdef bytes _encoding
+    cdef const_char* _c_encoding
+    cdef _FilelikeWriter _target
+    cdef list _element_stack
+    cdef int _status
+    cdef int _method
+    cdef bint _buffered
+
+    def __cinit__(self, outfile, bytes encoding, int compresslevel, bint close,
+                  bint buffered, int method):
+        self._status = WRITER_STARTING
+        self._element_stack = []
+        if encoding is None:
+            # We always need a document encoding to make the attribute serialisation
+            # of libxml2 identical to ours.
+            encoding = b'ASCII'
+        self._encoding = encoding
+        self._c_encoding = _cstr(encoding)
+        self._buffered = buffered
+        self._target = _create_output_buffer(
+            outfile, self._c_encoding, compresslevel, &self._c_out, close)
+        self._method = method
+
+    def __dealloc__(self):
+        if self._c_out is not NULL:
+            tree.xmlOutputBufferClose(self._c_out)
+
+    def write_declaration(self, version=None, standalone=None, doctype=None):
+        """write_declaration(self, version=None, standalone=None, doctype=None)
+
+        Write an XML declaration and (optionally) a doctype into the file.
+        """
+        assert self._c_out is not NULL
+        cdef const_xmlChar* c_version
+        cdef int c_standalone
+        if self._method != OUTPUT_METHOD_XML:
+            raise LxmlSyntaxError("only XML documents have declarations")
+        if self._status >= WRITER_DECL_WRITTEN:
+            raise LxmlSyntaxError("XML declaration already written")
+        version = _utf8orNone(version)
+        c_version = _xcstr(version) if version is not None else NULL
+        doctype = _utf8orNone(doctype)
+        if standalone is None:
+            c_standalone = -1
+        else:
+            c_standalone = 1 if standalone else 0
+        _writeDeclarationToBuffer(self._c_out, c_version, self._c_encoding, c_standalone)
+        if doctype is not None:
+            _writeDoctype(self._c_out, _xcstr(doctype))
+            self._status = WRITER_DTD_WRITTEN
+        else:
+            self._status = WRITER_DECL_WRITTEN
+        if not self._buffered:
+            tree.xmlOutputBufferFlush(self._c_out)
+        self._handle_error(self._c_out.error)
+
+    def write_doctype(self, doctype):
+        """write_doctype(self, doctype)
+
+        Writes the given doctype declaration verbatimly into the file.
+        """
+        assert self._c_out is not NULL
+        if doctype is None:
+            return
+        if self._status >= WRITER_DTD_WRITTEN:
+            raise LxmlSyntaxError("DOCTYPE already written or cannot write it here")
+        doctype = _utf8(doctype)
+        _writeDoctype(self._c_out, _xcstr(doctype))
+        self._status = WRITER_DTD_WRITTEN
+        if not self._buffered:
+            tree.xmlOutputBufferFlush(self._c_out)
+        self._handle_error(self._c_out.error)
+
+    def method(self, method):
+        """method(self, method)
+
+        Returns a context manager that overrides and restores the output method.
+        method is one of (None, 'xml', 'html') where None means 'xml'.
+        """
+        assert self._c_out is not NULL
+        c_method = self._method if method is None else _findOutputMethod(method)
+        return _MethodChanger(self, c_method)
+
+    def element(self, tag, attrib=None, nsmap=None, method=None, **_extra):
+        """element(self, tag, attrib=None, nsmap=None, method, **_extra)
+
+        Returns a context manager that writes an opening and closing tag.
+        method is one of (None, 'xml', 'html') where None means 'xml'.
+        """
+        assert self._c_out is not NULL
+        attributes = []
+        if attrib is not None:
+            for name, value in _iter_attrib(attrib):
+                if name not in _extra:
+                    ns, name = _getNsTag(name)
+                    attributes.append((ns, name, _utf8(value)))
+        if _extra:
+            for name, value in _extra.iteritems():
+                ns, name = _getNsTag(name)
+                attributes.append((ns, name, _utf8(value)))
+        reversed_nsmap = {}
+        if nsmap:
+            for prefix, ns in nsmap.items():
+                if prefix is not None:
+                    prefix = _utf8(prefix)
+                    _prefixValidOrRaise(prefix)
+                reversed_nsmap[_utf8(ns)] = prefix
+        ns, name = _getNsTag(tag)
+
+        c_method = self._method if method is None else _findOutputMethod(method)
+
+        return _FileWriterElement(self, (ns, name, attributes, reversed_nsmap), c_method)
+
+    cdef _write_qname(self, bytes name, bytes prefix):
+        if prefix:  # empty bytes for no prefix (not None to allow sorting)
+            tree.xmlOutputBufferWrite(self._c_out, len(prefix), _cstr(prefix))
+            tree.xmlOutputBufferWrite(self._c_out, 1, ':')
+        tree.xmlOutputBufferWrite(self._c_out, len(name), _cstr(name))
+
+    cdef _write_start_element(self, element_config):
+        if self._status > WRITER_IN_ELEMENT:
+            raise LxmlSyntaxError("cannot append trailing element to complete XML document")
+        ns, name, attributes, nsmap = element_config
+        flat_namespace_map, new_namespaces = self._collect_namespaces(nsmap)
+        prefix = self._find_prefix(ns, flat_namespace_map, new_namespaces)
+        tree.xmlOutputBufferWrite(self._c_out, 1, '<')
+        self._write_qname(name, prefix)
+
+        self._write_attributes_and_namespaces(
+            attributes, flat_namespace_map, new_namespaces)
+
+        tree.xmlOutputBufferWrite(self._c_out, 1, '>')
+        if not self._buffered:
+            tree.xmlOutputBufferFlush(self._c_out)
+        self._handle_error(self._c_out.error)
+
+        self._element_stack.append((ns, name, prefix, flat_namespace_map))
+        self._status = WRITER_IN_ELEMENT
+
+    cdef _write_attributes_and_namespaces(self, list attributes,
+                                          dict flat_namespace_map,
+                                          list new_namespaces):
+        if attributes:
+            # _find_prefix() may append to new_namespaces => build them first
+            attributes = [
+                (self._find_prefix(ns, flat_namespace_map, new_namespaces), name, value)
+                for ns, name, value in attributes ]
+        if new_namespaces:
+            new_namespaces.sort()
+            self._write_attributes_list(new_namespaces)
+        if attributes:
+            self._write_attributes_list(attributes)
+
+    cdef _write_attributes_list(self, list attributes):
+        for prefix, name, value in attributes:
+            tree.xmlOutputBufferWrite(self._c_out, 1, ' ')
+            self._write_qname(name, prefix)
+            tree.xmlOutputBufferWrite(self._c_out, 2, '="')
+            _write_attr_string(self._c_out, _cstr(value))
+
+            tree.xmlOutputBufferWrite(self._c_out, 1, '"')
+
+    cdef _write_end_element(self, element_config):
+        if self._status != WRITER_IN_ELEMENT:
+            raise LxmlSyntaxError("not in an element")
+        if not self._element_stack or self._element_stack[-1][:2] != element_config[:2]:
+            raise LxmlSyntaxError("inconsistent exit action in context manager")
+
+        # If previous write operations failed, the context manager exit might still call us.
+        # That is ok, but we stop writing closing tags and handling errors in that case.
+        # For all non-I/O errors, we continue writing closing tags if we can.
+        ok_to_write = self._c_out.error == xmlerror.XML_ERR_OK
+
+        name, prefix = self._element_stack.pop()[1:3]
+        if ok_to_write:
+            tree.xmlOutputBufferWrite(self._c_out, 2, '</')
+            self._write_qname(name, prefix)
+            tree.xmlOutputBufferWrite(self._c_out, 1, '>')
+
+        if not self._element_stack:
+            self._status = WRITER_FINISHED
+        if ok_to_write:
+            if not self._buffered:
+                tree.xmlOutputBufferFlush(self._c_out)
+            self._handle_error(self._c_out.error)
+
+    cdef _find_prefix(self, bytes href, dict flat_namespaces_map, list new_namespaces):
+        if href is None:
+            return None
+        if href in flat_namespaces_map:
+            return flat_namespaces_map[href]
+        # need to create a new prefix
+        prefixes = flat_namespaces_map.values()
+        i = 0
+        while True:
+            prefix = _utf8('ns%d' % i)
+            if prefix not in prefixes:
+                new_namespaces.append((b'xmlns', prefix, href))
+                flat_namespaces_map[href] = prefix
+                return prefix
+            i += 1
+
+    cdef _collect_namespaces(self, dict nsmap):
+        new_namespaces = []
+        flat_namespaces_map = {}
+        for ns, prefix in nsmap.iteritems():
+            flat_namespaces_map[ns] = prefix
+            if prefix is None:
+                # use empty bytes rather than None to allow sorting
+                new_namespaces.append((b'', b'xmlns', ns))
+            else:
+                new_namespaces.append((b'xmlns', prefix, ns))
+        # merge in flat namespace map of parent
+        if self._element_stack:
+            for ns, prefix in (<dict>self._element_stack[-1][-1]).iteritems():
+                if flat_namespaces_map.get(ns) is None:
+                    # unknown or empty prefix => prefer a 'real' prefix
+                    flat_namespaces_map[ns] = prefix
+        return flat_namespaces_map, new_namespaces
+
+    def write(self, *args, bint with_tail=True, bint pretty_print=False, method=None):
+        """write(self, *args, with_tail=True, pretty_print=False, method=None)
+
+        Write subtrees or strings into the file.
+
+        If method is not None, it should be one of ('html', 'xml', 'text')
+        to temporarily override the output method.
+        """
+        assert self._c_out is not NULL
+        c_method = self._method if method is None else _findOutputMethod(method)
+
+        for content in args:
+            if _isString(content):
+                if self._status != WRITER_IN_ELEMENT:
+                    if self._status > WRITER_IN_ELEMENT or content.strip():
+                        raise LxmlSyntaxError("not in an element")
+                bstring = _utf8(content)
+                if not bstring:
+                    continue
+
+                ns, name, _, _ = self._element_stack[-1]
+                if (c_method == OUTPUT_METHOD_HTML and
+                        ns in (None, b'http://www.w3.org/1999/xhtml') and
+                        name in (b'script', b'style')):
+                    tree.xmlOutputBufferWrite(self._c_out, len(bstring), _cstr(bstring))
+
+                else:
+                    tree.xmlOutputBufferWriteEscape(self._c_out, _xcstr(bstring), NULL)
+
+            elif iselement(content):
+                if self._status > WRITER_IN_ELEMENT:
+                    raise LxmlSyntaxError("cannot append trailing element to complete XML document")
+                _writeNodeToBuffer(self._c_out, (<_Element>content)._c_node,
+                                   self._c_encoding, NULL, c_method,
+                                   False, False, pretty_print, with_tail, False)
+                if (<_Element>content)._c_node.type == tree.XML_ELEMENT_NODE:
+                    if not self._element_stack:
+                        self._status = WRITER_FINISHED
+
+            elif content is not None:
+                raise TypeError(
+                    f"got invalid input value of type {type(content)}, expected string or Element")
+            self._handle_error(self._c_out.error)
+        if not self._buffered:
+            tree.xmlOutputBufferFlush(self._c_out)
+            self._handle_error(self._c_out.error)
+
+    def flush(self):
+        """flush(self)
+
+        Write any pending content of the current output buffer to the stream.
+        """
+        assert self._c_out is not NULL
+        tree.xmlOutputBufferFlush(self._c_out)
+        self._handle_error(self._c_out.error)
+
+    cdef _close(self, bint raise_on_error):
+        if raise_on_error:
+            if self._status < WRITER_IN_ELEMENT:
+                raise LxmlSyntaxError("no content written")
+            if self._element_stack:
+                raise LxmlSyntaxError("pending open tags on close")
+        error_result = self._c_out.error
+        if error_result == xmlerror.XML_ERR_OK:
+            error_result = tree.xmlOutputBufferClose(self._c_out)
+            if error_result != -1:
+                error_result = xmlerror.XML_ERR_OK
+        else:
+            tree.xmlOutputBufferClose(self._c_out)
+        self._status = WRITER_FINISHED
+        self._c_out = NULL
+        del self._element_stack[:]
+        if raise_on_error:
+            self._handle_error(error_result)
+
+    cdef _handle_error(self, int error_result):
+        if error_result != xmlerror.XML_ERR_OK:
+            if self._target is not None:
+                self._target._exc_context._raise_if_stored()
+            _raiseSerialisationError(error_result)
+
+
+@cython.final
+@cython.internal
+cdef class _AsyncDataWriter:
+    cdef list _data
+    def __cinit__(self):
+        self._data = []
+
+    cdef bytes collect(self):
+        data = b''.join(self._data)
+        del self._data[:]
+        return data
+
+    def write(self, data):
+        self._data.append(data)
+
+    def close(self):
+        pass
+
+
+@cython.final
+@cython.internal
+cdef class _AsyncIncrementalFileWriter:
+    cdef _IncrementalFileWriter _writer
+    cdef _AsyncDataWriter _buffer
+    cdef object _async_outfile
+    cdef int _flush_after_writes
+    cdef bint _should_close
+    cdef bint _buffered
+
+    def __cinit__(self, async_outfile, bytes encoding, int compresslevel, bint close,
+                  bint buffered, int method):
+        self._flush_after_writes = 20
+        self._async_outfile = async_outfile
+        self._should_close = close
+        self._buffered = buffered
+        self._buffer = _AsyncDataWriter()
+        self._writer = _IncrementalFileWriter(
+            self._buffer, encoding, compresslevel, close=True, buffered=False, method=method)
+
+    cdef bytes _flush(self):
+        if not self._buffered or len(self._buffer._data) > self._flush_after_writes:
+            return self._buffer.collect()
+        return None
+
+    async def flush(self):
+        self._writer.flush()
+        data = self._buffer.collect()
+        if data:
+            await self._async_outfile.write(data)
+
+    async def write_declaration(self, version=None, standalone=None, doctype=None):
+        self._writer.write_declaration(version, standalone, doctype)
+        data = self._flush()
+        if data:
+            await self._async_outfile.write(data)
+
+    async def write_doctype(self, doctype):
+        self._writer.write_doctype(doctype)
+        data = self._flush()
+        if data:
+            await self._async_outfile.write(data)
+
+    async def write(self, *args, with_tail=True, pretty_print=False, method=None):
+        self._writer.write(*args, with_tail=with_tail, pretty_print=pretty_print, method=method)
+        data = self._flush()
+        if data:
+            await self._async_outfile.write(data)
+
+    def method(self, method):
+        return self._writer.method(method)
+
+    def element(self, tag, attrib=None, nsmap=None, method=None, **_extra):
+        element_writer = self._writer.element(tag, attrib, nsmap, method, **_extra)
+        return _AsyncFileWriterElement(element_writer, self)
+
+    async def _close(self, bint raise_on_error):
+        self._writer._close(raise_on_error)
+        data = self._buffer.collect()
+        if data:
+            await self._async_outfile.write(data)
+        if self._should_close:
+            await self._async_outfile.close()
+
+
+@cython.final
+@cython.internal
+cdef class _AsyncFileWriterElement:
+    cdef _FileWriterElement _element_writer
+    cdef _AsyncIncrementalFileWriter _writer
+
+    def __cinit__(self, _FileWriterElement element_writer not None,
+                  _AsyncIncrementalFileWriter writer not None):
+        self._element_writer = element_writer
+        self._writer = writer
+
+    async def __aenter__(self):
+        self._element_writer.__enter__()
+        data = self._writer._flush()
+        if data:
+            await self._writer._async_outfile.write(data)
+
+    async def __aexit__(self, *args):
+        self._element_writer.__exit__(*args)
+        data = self._writer._flush()
+        if data:
+            await self._writer._async_outfile.write(data)
+
+
+@cython.final
+@cython.internal
+@cython.freelist(8)
+cdef class _FileWriterElement:
+    cdef _IncrementalFileWriter _writer
+    cdef object _element
+    cdef int _new_method
+    cdef int _old_method
+
+    def __cinit__(self, _IncrementalFileWriter writer not None, element_config, int method):
+        self._writer = writer
+        self._element = element_config
+        self._new_method = method
+        self._old_method = writer._method
+
+    def __enter__(self):
+        self._writer._method = self._new_method
+        self._writer._write_start_element(self._element)
+
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        self._writer._write_end_element(self._element)
+        self._writer._method = self._old_method
+
+
+@cython.final
+@cython.internal
+@cython.freelist(8)
+cdef class _MethodChanger:
+    cdef _IncrementalFileWriter _writer
+    cdef int _new_method
+    cdef int _old_method
+    cdef bint _entered
+    cdef bint _exited
+
+    def __cinit__(self, _IncrementalFileWriter writer not None, int method):
+        self._writer = writer
+        self._new_method = method
+        self._old_method = writer._method
+        self._entered = False
+        self._exited = False
+
+    def __enter__(self):
+        if self._entered:
+            raise LxmlSyntaxError("Inconsistent enter action in context manager")
+        self._writer._method = self._new_method
+        self._entered = True
+
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        if self._exited:
+            raise LxmlSyntaxError("Inconsistent exit action in context manager")
+        if self._writer._method != self._new_method:
+            raise LxmlSyntaxError("Method changed outside of context manager")
+        self._writer._method = self._old_method
+        self._exited = True
+
+    async def __aenter__(self):
+        # for your async convenience
+        return self.__enter__()
+
+    async def __aexit__(self, *args):
+        # for your async convenience
+        return self.__exit__(*args)
diff --git a/.venv/lib/python3.12/site-packages/lxml/usedoctest.py b/.venv/lib/python3.12/site-packages/lxml/usedoctest.py
new file mode 100644
index 00000000..f1da8cad
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/usedoctest.py
@@ -0,0 +1,13 @@
+"""Doctest module for XML comparison.
+
+Usage::
+
+   >>> import lxml.usedoctest
+   >>> # now do your XML doctests ...
+
+See `lxml.doctestcompare`
+"""
+
+from lxml import doctestcompare
+
+doctestcompare.temp_install(del_module=__name__)
diff --git a/.venv/lib/python3.12/site-packages/lxml/xinclude.pxi b/.venv/lib/python3.12/site-packages/lxml/xinclude.pxi
new file mode 100644
index 00000000..5c9ac450
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/xinclude.pxi
@@ -0,0 +1,67 @@
+# XInclude processing
+
+from lxml.includes cimport xinclude
+
+
+cdef class XIncludeError(LxmlError):
+    """Error during XInclude processing.
+    """
+
+
+cdef class XInclude:
+    """XInclude(self)
+    XInclude processor.
+
+    Create an instance and call it on an Element to run XInclude
+    processing.
+    """
+    cdef _ErrorLog _error_log
+    def __init__(self):
+        self._error_log = _ErrorLog()
+
+    @property
+    def error_log(self):
+        assert self._error_log is not None, "XInclude instance not initialised"
+        return self._error_log.copy()
+
+    def __call__(self, _Element node not None):
+        "__call__(self, node)"
+        # We cannot pass the XML_PARSE_NOXINCNODE option as this would free
+        # the XInclude nodes - there may still be Python references to them!
+        # Therefore, we allow XInclude nodes to be converted to
+        # XML_XINCLUDE_START nodes.  XML_XINCLUDE_END nodes are added as
+        # siblings.  Tree traversal will simply ignore them as they are not
+        # typed as elements.  The included fragment is added between the two,
+        # i.e. as a sibling, which does not conflict with traversal.
+        cdef int result
+        _assertValidNode(node)
+        assert self._error_log is not None, "XInclude processor not initialised"
+        if node._doc._parser is not None:
+            parse_options = node._doc._parser._parse_options
+            context = node._doc._parser._getParserContext()
+            c_context = <void*>context
+        else:
+            parse_options = 0
+            context = None
+            c_context = NULL
+
+        self._error_log.connect()
+        if tree.LIBXML_VERSION < 20704 or not c_context:
+            __GLOBAL_PARSER_CONTEXT.pushImpliedContext(context)
+        with nogil:
+            orig_loader = _register_document_loader()
+            if c_context:
+                result = xinclude.xmlXIncludeProcessTreeFlagsData(
+                    node._c_node, parse_options, c_context)
+            else:
+                result = xinclude.xmlXIncludeProcessTree(node._c_node)
+            _reset_document_loader(orig_loader)
+        if tree.LIBXML_VERSION < 20704 or not c_context:
+            __GLOBAL_PARSER_CONTEXT.popImpliedContext()
+        self._error_log.disconnect()
+
+        if result == -1:
+            raise XIncludeError(
+                self._error_log._buildExceptionMessage(
+                    "XInclude processing failed"),
+                self._error_log)
diff --git a/.venv/lib/python3.12/site-packages/lxml/xmlerror.pxi b/.venv/lib/python3.12/site-packages/lxml/xmlerror.pxi
new file mode 100644
index 00000000..79442a8b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/xmlerror.pxi
@@ -0,0 +1,1654 @@
+# DEBUG and error logging
+
+from lxml.includes cimport xmlerror
+from lxml cimport cvarargs
+
+DEF GLOBAL_ERROR_LOG = "_GlobalErrorLog"
+DEF XSLT_ERROR_LOG = "_XSLTErrorLog"
+
+# module level API functions
+
+def clear_error_log():
+    """clear_error_log()
+
+    Clear the global error log.  Note that this log is already bound to a
+    fixed size.
+
+    Note: since lxml 2.2, the global error log is local to a thread
+    and this function will only clear the global error log of the
+    current thread.
+    """
+    _getThreadErrorLog(GLOBAL_ERROR_LOG).clear()
+
+
+# setup for global log:
+
+cdef void _initThreadLogging() noexcept:
+    # Disable generic error lines from libxml2.
+    _connectGenericErrorLog(None)
+
+    # Divert XSLT error messages to the global XSLT error log instead of stderr.
+    xslt.xsltSetGenericErrorFunc(NULL, <xmlerror.xmlGenericErrorFunc>_receiveXSLTError)
+
+
+# Logging classes
+
+@cython.final
+@cython.freelist(16)
+cdef class _LogEntry:
+    """A log message entry from an error log.
+
+    Attributes:
+
+    - message: the message text
+    - domain: the domain ID (see lxml.etree.ErrorDomains)
+    - type: the message type ID (see lxml.etree.ErrorTypes)
+    - level: the log level ID (see lxml.etree.ErrorLevels)
+    - line: the line at which the message originated (if applicable)
+    - column: the character column at which the message originated (if applicable)
+    - filename: the name of the file in which the message originated (if applicable)
+    - path: the location in which the error was found (if available)
+    """
+    cdef readonly int domain
+    cdef readonly int type
+    cdef readonly int level
+    cdef readonly long line
+    cdef readonly int column
+    cdef basestring _message
+    cdef basestring _filename
+    cdef char* _c_message
+    cdef xmlChar* _c_filename
+    cdef xmlChar* _c_path
+
+    def __dealloc__(self):
+        tree.xmlFree(self._c_message)
+        tree.xmlFree(self._c_filename)
+        tree.xmlFree(self._c_path)
+
+    @cython.final
+    cdef int _setError(self, const xmlerror.xmlError* error) except -1:
+        self.domain   = error.domain
+        self.type     = error.code
+        self.level    = <int>error.level
+        self.line     = <long>error.line
+        self.column   = error.int2
+        self._c_message = NULL
+        self._c_filename = NULL
+        self._c_path = NULL
+        if (error.message is NULL or
+                error.message[0] == b'\0' or
+                error.message[0] == b'\n' and error.message[1] == b'\0'):
+            self._message = "unknown error"
+        else:
+            self._message = None
+            self._c_message = <char*> tree.xmlStrdup(
+                <const_xmlChar*> error.message)
+            if not self._c_message:
+                raise MemoryError()
+        if error.file is NULL:
+            self._filename = '<string>'
+        else:
+            self._filename = None
+            self._c_filename = tree.xmlStrdup(<const_xmlChar*> error.file)
+            if not self._c_filename:
+                raise MemoryError()
+        if error.node is not NULL:
+            self._c_path = tree.xmlGetNodePath(<xmlNode*> error.node)
+            c_line = tree.xmlGetLineNo(<xmlNode*> error.node)
+            if c_line > limits.INT_MAX:
+                self.line = c_line
+
+    @cython.final
+    cdef _setGeneric(self, int domain, int type, int level, long line,
+                     message, filename):
+        self.domain  = domain
+        self.type    = type
+        self.level   = level
+        self.line    = line
+        self.column  = 0
+        self._message = message
+        self._filename = filename
+        self._c_path = NULL
+
+    def __repr__(self):
+        return "%s:%d:%d:%s:%s:%s: %s" % (
+            self.filename, self.line, self.column, self.level_name,
+            self.domain_name, self.type_name, self.message)
+
+    @property
+    def domain_name(self):
+        """The name of the error domain.  See lxml.etree.ErrorDomains
+        """
+        return ErrorDomains._getName(self.domain, "unknown")
+
+    @property
+    def type_name(self):
+        """The name of the error type.  See lxml.etree.ErrorTypes
+        """
+        if self.domain == ErrorDomains.RELAXNGV:
+            getName = RelaxNGErrorTypes._getName
+        else:
+            getName = ErrorTypes._getName
+        return getName(self.type, "unknown")
+
+    @property
+    def level_name(self):
+        """The name of the error level.  See lxml.etree.ErrorLevels
+        """
+        return ErrorLevels._getName(self.level, "unknown")
+
+    @property
+    def message(self):
+        """The log message string.
+        """
+        cdef size_t size
+        if self._message is not None:
+            return self._message
+        if self._c_message is NULL:
+            return None
+        size = cstring_h.strlen(self._c_message)
+        if size > 0 and self._c_message[size-1] == b'\n':
+            size -= 1  # strip EOL
+        # cannot use funicode() here because the message may contain
+        # byte encoded file paths etc.
+        try:
+            self._message = self._c_message[:size].decode('utf8')
+        except UnicodeDecodeError:
+            try:
+                self._message = self._c_message[:size].decode(
+                    'ascii', 'backslashreplace')
+            except UnicodeDecodeError:
+                self._message = '<undecodable error message>'
+        if self._c_message:
+            # clean up early
+            tree.xmlFree(self._c_message)
+            self._c_message = NULL
+        return self._message
+
+    @property
+    def filename(self):
+        """The file path where the report originated, if any.
+        """
+        if self._filename is None:
+            if self._c_filename is not NULL:
+                self._filename = _decodeFilename(self._c_filename)
+                # clean up early
+                tree.xmlFree(self._c_filename)
+                self._c_filename = NULL
+        return self._filename
+
+    @property
+    def path(self):
+        """The XPath for the node where the error was detected.
+        """
+        return funicode(self._c_path) if self._c_path is not NULL else None
+
+
+cdef class _BaseErrorLog:
+    cdef _LogEntry _first_error
+    cdef readonly object last_error
+    def __init__(self, first_error, last_error):
+        self._first_error = first_error
+        self.last_error = last_error
+
+    cpdef copy(self):
+        return _BaseErrorLog(self._first_error, self.last_error)
+
+    def __repr__(self):
+        return ''
+
+    cpdef receive(self, _LogEntry entry):
+        pass
+
+    @cython.final
+    cdef int _receive(self, const xmlerror.xmlError* error) except -1:
+        cdef bint is_error
+        cdef _LogEntry entry
+        cdef _BaseErrorLog global_log
+        entry = _LogEntry.__new__(_LogEntry)
+        entry._setError(error)
+        is_error = error.level == xmlerror.XML_ERR_ERROR or \
+                   error.level == xmlerror.XML_ERR_FATAL
+        global_log = _getThreadErrorLog(GLOBAL_ERROR_LOG)
+        if global_log is not self:
+            global_log.receive(entry)
+            if is_error:
+                global_log.last_error = entry
+        self.receive(entry)
+        if is_error:
+            self.last_error = entry
+
+    @cython.final
+    cdef int _receiveGeneric(self, int domain, int type, int level, long line,
+                             message, filename) except -1:
+        cdef bint is_error
+        cdef _LogEntry entry
+        cdef _BaseErrorLog global_log
+        entry = _LogEntry.__new__(_LogEntry)
+        entry._setGeneric(domain, type, level, line, message, filename)
+        is_error = level == xmlerror.XML_ERR_ERROR or \
+                   level == xmlerror.XML_ERR_FATAL
+        global_log = _getThreadErrorLog(GLOBAL_ERROR_LOG)
+        if global_log is not self:
+            global_log.receive(entry)
+            if is_error:
+                global_log.last_error = entry
+        self.receive(entry)
+        if is_error:
+            self.last_error = entry
+
+    @cython.final
+    cdef _buildParseException(self, exctype, default_message):
+        code = xmlerror.XML_ERR_INTERNAL_ERROR
+        if self._first_error is None:
+            return exctype(default_message, code, 0, 0)
+        message = self._first_error.message
+        if message:
+            code = self._first_error.type
+        else:
+            message = default_message
+        line = self._first_error.line
+        column = self._first_error.column
+        filename = self._first_error.filename
+        if line > 0:
+            if column > 0:
+                message = f"{message}, line {line}, column {column}"
+            else:
+                message = f"{message}, line {line}"
+        return exctype(message, code, line, column, filename)
+
+    @cython.final
+    cdef _buildExceptionMessage(self, default_message):
+        if self._first_error is None:
+            return default_message
+        if self._first_error.message:
+            message = self._first_error.message
+        elif default_message is None:
+            return None
+        else:
+            message = default_message
+        if self._first_error.line > 0:
+            if self._first_error.column > 0:
+                message = f"{message}, line {self._first_error.line}, column {self._first_error.column}"
+            else:
+                message = f"{message}, line {self._first_error.line}"
+        return message
+
+cdef class _ListErrorLog(_BaseErrorLog):
+    "Immutable base version of a list based error log."
+    cdef list _entries
+    cdef int _offset
+    def __init__(self, entries, first_error, last_error):
+        if entries:
+            if first_error is None:
+                first_error = entries[0]
+            if last_error is None:
+                last_error = entries[-1]
+        _BaseErrorLog.__init__(self, first_error, last_error)
+        self._entries = entries
+
+    cpdef copy(self):
+        """Creates a shallow copy of this error log.  Reuses the list of
+        entries.
+        """
+        cdef _ListErrorLog log = _ListErrorLog(
+            self._entries, self._first_error, self.last_error)
+        log._offset = self._offset
+        return log
+
+    def __iter__(self):
+        entries = self._entries
+        if self._offset:
+            entries = islice(entries, self._offset)
+        return iter(entries)
+
+    def __repr__(self):
+        return '\n'.join([repr(entry) for entry in self])
+
+    def __getitem__(self, index):
+        if self._offset:
+            index += self._offset
+        return self._entries[index]
+
+    def __len__(self):
+        return len(self._entries) - self._offset
+
+    def __contains__(self, error_type):
+        cdef Py_ssize_t i
+        for i, entry in enumerate(self._entries):
+            if i < self._offset:
+                continue
+            if entry.type == error_type:
+                return True
+        return False
+
+    def __bool__(self):
+        return len(self._entries) > self._offset
+
+    def filter_domains(self, domains):
+        """Filter the errors by the given domains and return a new error log
+        containing the matches.
+        """
+        cdef _LogEntry entry
+        if isinstance(domains, int):
+            domains = (domains,)
+        filtered = [entry for entry in self if entry.domain in domains]
+        return _ListErrorLog(filtered, None, None)
+
+    def filter_types(self, types):
+        """filter_types(self, types)
+
+        Filter the errors by the given types and return a new error
+        log containing the matches.
+        """
+        cdef _LogEntry entry
+        if isinstance(types, int):
+            types = (types,)
+        filtered = [entry for entry in self if entry.type in types]
+        return _ListErrorLog(filtered, None, None)
+
+    def filter_levels(self, levels):
+        """filter_levels(self, levels)
+
+        Filter the errors by the given error levels and return a new
+        error log containing the matches.
+        """
+        cdef _LogEntry entry
+        if isinstance(levels, int):
+            levels = (levels,)
+        filtered = [entry for entry in self if entry.level in levels]
+        return _ListErrorLog(filtered, None, None)
+
+    def filter_from_level(self, level):
+        """filter_from_level(self, level)
+
+        Return a log with all messages of the requested level of worse.
+        """
+        cdef _LogEntry entry
+        filtered = [entry for entry in self if entry.level >= level]
+        return _ListErrorLog(filtered, None, None)
+
+    def filter_from_fatals(self):
+        """filter_from_fatals(self)
+
+        Convenience method to get all fatal error messages.
+        """
+        return self.filter_from_level(ErrorLevels.FATAL)
+    
+    def filter_from_errors(self):
+        """filter_from_errors(self)
+
+        Convenience method to get all error messages or worse.
+        """
+        return self.filter_from_level(ErrorLevels.ERROR)
+    
+    def filter_from_warnings(self):
+        """filter_from_warnings(self)
+
+        Convenience method to get all warnings or worse.
+        """
+        return self.filter_from_level(ErrorLevels.WARNING)
+
+
+@cython.final
+@cython.internal
+cdef class _ErrorLogContext:
+    """
+    Error log context for the 'with' statement.
+    Stores a reference to the current callbacks to allow for
+    recursively stacked log contexts.
+    """
+    cdef xmlerror.xmlStructuredErrorFunc old_error_func
+    cdef void* old_error_context
+    cdef xmlerror.xmlGenericErrorFunc old_xslt_error_func
+    cdef void* old_xslt_error_context
+    cdef _BaseErrorLog old_xslt_error_log
+
+    cdef int push_error_log(self, _BaseErrorLog log) except -1:
+        self.old_error_func = xmlerror.xmlStructuredError
+        self.old_error_context = xmlerror.xmlStructuredErrorContext
+        xmlerror.xmlSetStructuredErrorFunc(
+            <void*>log, <xmlerror.xmlStructuredErrorFunc>_receiveError)
+
+        # xslt.xsltSetGenericErrorFunc() is not thread-local => keep error log in TLS
+        self.old_xslt_error_func = xslt.xsltGenericError
+        self.old_xslt_error_context = xslt.xsltGenericErrorContext
+        self.old_xslt_error_log = _getThreadErrorLog(XSLT_ERROR_LOG)
+        _setThreadErrorLog(XSLT_ERROR_LOG, log)
+        xslt.xsltSetGenericErrorFunc(
+            NULL, <xmlerror.xmlGenericErrorFunc>_receiveXSLTError)
+        return 0
+
+    cdef int pop_error_log(self) except -1:
+        xmlerror.xmlSetStructuredErrorFunc(
+            self.old_error_context, self.old_error_func)
+        xslt.xsltSetGenericErrorFunc(
+            self.old_xslt_error_context, self.old_xslt_error_func)
+        _setThreadErrorLog(XSLT_ERROR_LOG, self.old_xslt_error_log)
+        self.old_xslt_error_log= None
+        return 0
+
+
+cdef class _ErrorLog(_ListErrorLog):
+    cdef list _logContexts
+    def __cinit__(self):
+        self._logContexts = []
+
+    def __init__(self):
+        _ListErrorLog.__init__(self, [], None, None)
+
+    @cython.final
+    cdef int __enter__(self) except -1:
+        return self.connect()
+
+    def __exit__(self, *args):
+        #  TODO: make this a cdef function when Cython supports it
+        self.disconnect()
+
+    @cython.final
+    cdef int connect(self) except -1:
+        self._first_error = None
+        del self._entries[:]
+
+        cdef _ErrorLogContext context = _ErrorLogContext.__new__(_ErrorLogContext)
+        context.push_error_log(self)
+        self._logContexts.append(context)
+        return 0
+
+    @cython.final
+    cdef int disconnect(self) except -1:
+        cdef _ErrorLogContext context = self._logContexts.pop()
+        context.pop_error_log()
+        return 0
+
+    cpdef clear(self):
+        self._first_error = None
+        self.last_error = None
+        self._offset = 0
+        del self._entries[:]
+
+    cpdef copy(self):
+        """Creates a shallow copy of this error log and the list of entries.
+        """
+        return _ListErrorLog(
+            self._entries[self._offset:],
+            self._first_error, self.last_error)
+
+    def __iter__(self):
+        return iter(self._entries[self._offset:])
+
+    cpdef receive(self, _LogEntry entry):
+        if self._first_error is None and entry.level >= xmlerror.XML_ERR_ERROR:
+            self._first_error = entry
+        self._entries.append(entry)
+
+cdef class _DomainErrorLog(_ErrorLog):
+    def __init__(self, domains):
+        _ErrorLog.__init__(self)
+        self._accepted_domains = tuple(domains)
+
+    cpdef receive(self, _LogEntry entry):
+        if entry.domain in self._accepted_domains:
+            _ErrorLog.receive(self, entry)
+
+cdef class _RotatingErrorLog(_ErrorLog):
+    cdef int _max_len
+    def __init__(self, max_len):
+        _ErrorLog.__init__(self)
+        self._max_len = max_len
+
+    cpdef receive(self, _LogEntry entry):
+        if self._first_error is None and entry.level >= xmlerror.XML_ERR_ERROR:
+            self._first_error = entry
+        self._entries.append(entry)
+
+        if len(self._entries) > self._max_len:
+            self._offset += 1
+            if self._offset > self._max_len // 3:
+                offset = self._offset
+                self._offset = 0
+                del self._entries[:offset]
+
+cdef class PyErrorLog(_BaseErrorLog):
+    """PyErrorLog(self, logger_name=None, logger=None)
+    A global error log that connects to the Python stdlib logging package.
+
+    The constructor accepts an optional logger name or a readily
+    instantiated logger instance.
+
+    If you want to change the mapping between libxml2's ErrorLevels and Python
+    logging levels, you can modify the level_map dictionary from a subclass.
+
+    The default mapping is::
+
+            ErrorLevels.WARNING = logging.WARNING
+            ErrorLevels.ERROR   = logging.ERROR
+            ErrorLevels.FATAL   = logging.CRITICAL
+
+    You can also override the method ``receive()`` that takes a LogEntry
+    object and calls ``self.log(log_entry, format_string, arg1, arg2, ...)``
+    with appropriate data.
+    """
+    cdef readonly dict level_map
+    cdef object _map_level
+    cdef object _log
+    def __init__(self, logger_name=None, logger=None):
+        _BaseErrorLog.__init__(self, None, None)
+        import logging
+        self.level_map = {
+            ErrorLevels.WARNING : logging.WARNING,
+            ErrorLevels.ERROR   : logging.ERROR,
+            ErrorLevels.FATAL   : logging.CRITICAL
+            }
+        self._map_level = self.level_map.get
+        if logger is None:
+            if logger_name:
+                logger = logging.getLogger(logger_name)
+            else:
+                logger = logging.getLogger()
+        self._log = logger.log
+
+    cpdef copy(self):
+        """Dummy method that returns an empty error log.
+        """
+        return _ListErrorLog([], None, None)
+
+    def log(self, log_entry, message, *args):
+        """log(self, log_entry, message, *args)
+
+        Called by the .receive() method to log a _LogEntry instance to
+        the Python logging system.  This handles the error level
+        mapping.
+
+        In the default implementation, the ``message`` argument
+        receives a complete log line, and there are no further
+        ``args``.  To change the message format, it is best to
+        override the .receive() method instead of this one.
+        """
+        self._log(
+            self._map_level(log_entry.level, 0),
+            message, *args
+            )
+
+    cpdef receive(self, _LogEntry log_entry):
+        """receive(self, log_entry)
+
+        Receive a _LogEntry instance from the logging system.  Calls
+        the .log() method with appropriate parameters::
+
+            self.log(log_entry, repr(log_entry))
+
+        You can override this method to provide your own log output
+        format.
+        """
+        self.log(log_entry, repr(log_entry))
+
+# thread-local, global list log to collect error output messages from
+# libxml2/libxslt
+
+cdef _BaseErrorLog __GLOBAL_ERROR_LOG = _RotatingErrorLog(__MAX_LOG_SIZE)
+
+
+cdef _BaseErrorLog _getThreadErrorLog(name):
+    """Retrieve the current error log with name 'name' of this thread."""
+    cdef python.PyObject* thread_dict
+    thread_dict = python.PyThreadState_GetDict()
+    if thread_dict is NULL:
+        return __GLOBAL_ERROR_LOG
+    try:
+        return (<object>thread_dict)[name]
+    except KeyError:
+        log = (<object>thread_dict)[name] = \
+              _RotatingErrorLog(__MAX_LOG_SIZE)
+        return log
+
+
+cdef _setThreadErrorLog(name, _BaseErrorLog log):
+    """Set the global error log of this thread."""
+    cdef python.PyObject* thread_dict
+    thread_dict = python.PyThreadState_GetDict()
+    if thread_dict is NULL:
+        if name == GLOBAL_ERROR_LOG:
+            global __GLOBAL_ERROR_LOG
+            __GLOBAL_ERROR_LOG = log
+    else:
+        (<object>thread_dict)[name] = log
+
+
+cdef __copyGlobalErrorLog():
+    "Helper function for properties in exceptions."
+    return _getThreadErrorLog(GLOBAL_ERROR_LOG).copy()
+
+
+def use_global_python_log(PyErrorLog log not None):
+    """use_global_python_log(log)
+
+    Replace the global error log by an etree.PyErrorLog that uses the
+    standard Python logging package.
+
+    Note that this disables access to the global error log from exceptions.
+    Parsers, XSLT etc. will continue to provide their normal local error log.
+
+    Note: prior to lxml 2.2, this changed the error log globally.
+    Since lxml 2.2, the global error log is local to a thread and this
+    function will only set the global error log of the current thread.
+    """
+    _setThreadErrorLog(GLOBAL_ERROR_LOG, log)
+
+
+# local log functions: forward error to logger object
+cdef void _forwardError(void* c_log_handler, const xmlerror.xmlError* error) noexcept with gil:
+    cdef _BaseErrorLog log_handler
+    if c_log_handler is not NULL:
+        log_handler = <_BaseErrorLog>c_log_handler
+    elif error.domain == xmlerror.XML_FROM_XSLT:
+        log_handler = _getThreadErrorLog(XSLT_ERROR_LOG)
+    else:
+        log_handler = _getThreadErrorLog(GLOBAL_ERROR_LOG)
+    log_handler._receive(error)
+
+
+cdef void _receiveError(void* c_log_handler, const xmlerror.xmlError* error) noexcept nogil:
+    # no Python objects here, may be called without thread context !
+    if __DEBUG:
+        _forwardError(c_log_handler, error)
+
+
+cdef void _receiveXSLTError(void* c_log_handler, char* msg, ...) noexcept nogil:
+    # no Python objects here, may be called without thread context !
+    cdef cvarargs.va_list args
+    cvarargs.va_start(args, msg)
+    _receiveGenericError(c_log_handler, xmlerror.XML_FROM_XSLT, msg, args)
+    cvarargs.va_end(args)
+
+cdef void _receiveRelaxNGParseError(void* c_log_handler, char* msg, ...) noexcept nogil:
+    # no Python objects here, may be called without thread context !
+    cdef cvarargs.va_list args
+    cvarargs.va_start(args, msg)
+    _receiveGenericError(c_log_handler, xmlerror.XML_FROM_RELAXNGP, msg, args)
+    cvarargs.va_end(args)
+
+cdef void _receiveRelaxNGValidationError(void* c_log_handler, char* msg, ...) noexcept nogil:
+    # no Python objects here, may be called without thread context !
+    cdef cvarargs.va_list args
+    cvarargs.va_start(args, msg)
+    _receiveGenericError(c_log_handler, xmlerror.XML_FROM_RELAXNGV, msg, args)
+    cvarargs.va_end(args)
+
+# dummy function: no log output at all
+cdef void _nullGenericErrorFunc(void* ctxt, char* msg, ...) noexcept nogil:
+    pass
+
+
+cdef void _connectGenericErrorLog(log, int c_domain=-1) noexcept:
+    cdef xmlerror.xmlGenericErrorFunc error_func = NULL
+    c_log = <void*>log
+    if c_domain == xmlerror.XML_FROM_XSLT:
+        error_func = <xmlerror.xmlGenericErrorFunc>_receiveXSLTError
+    elif c_domain == xmlerror.XML_FROM_RELAXNGP:
+        error_func = <xmlerror.xmlGenericErrorFunc>_receiveRelaxNGParseError
+    elif c_domain == xmlerror.XML_FROM_RELAXNGV:
+        error_func = <xmlerror.xmlGenericErrorFunc>_receiveRelaxNGValidationError
+
+    if log is None or error_func is NULL:
+        c_log = NULL
+        error_func = <xmlerror.xmlGenericErrorFunc>_nullGenericErrorFunc
+    xmlerror.xmlSetGenericErrorFunc(c_log, error_func)
+
+
+cdef void _receiveGenericError(void* c_log_handler, int c_domain,
+                               char* msg, cvarargs.va_list args) noexcept nogil:
+    # no Python objects here, may be called without thread context !
+    cdef xmlerror.xmlError c_error
+    cdef char* c_text
+    cdef char* c_message
+    cdef char* c_element
+    cdef char* c_pos
+    cdef char* c_name_pos
+    cdef char* c_str
+    cdef int text_size, element_size, format_count, c_int
+    if not __DEBUG or msg is NULL:
+        return
+    if msg[0] in b'\n\0':
+        return
+
+    c_text = c_element = c_error.file = c_error.node = NULL
+    c_error.line = 0
+
+    # parse "NAME %s" chunks from the format string
+    c_name_pos = c_pos = msg
+    format_count = 0
+    while c_pos[0]:
+        if c_pos[0] == b'%':
+            c_pos += 1
+            if c_pos[0] == b's':  # "%s"
+                format_count += 1
+                c_str = cvarargs.va_charptr(args)
+                if c_pos == msg + 1:
+                    c_text = c_str  # msg == "%s..."
+                elif c_name_pos[0] == b'e':
+                    if cstring_h.strncmp(c_name_pos, 'element %s', 10) == 0:
+                        c_element = c_str
+                elif c_name_pos[0] == b'f':
+                    if cstring_h.strncmp(c_name_pos, 'file %s', 7) == 0:
+                        if cstring_h.strncmp('string://__STRING__XSLT',
+                                             c_str, 23) == 0:
+                            c_str = '<xslt>'
+                        c_error.file = c_str
+            elif c_pos[0] == b'd':  # "%d"
+                format_count += 1
+                c_int = cvarargs.va_int(args)
+                if cstring_h.strncmp(c_name_pos, 'line %d', 7) == 0:
+                    c_error.line = c_int
+            elif c_pos[0] != b'%':  # "%%" == "%"
+                format_count += 1
+                break  # unexpected format or end of string => abort
+        elif c_pos[0] == b' ':
+            if c_pos[1] != b'%':
+                c_name_pos = c_pos + 1
+        c_pos += 1
+
+    c_message = NULL
+    if c_text is NULL:
+        if c_element is not NULL and format_count == 1:
+            # special case: a single occurrence of 'element %s'
+            text_size    = cstring_h.strlen(msg)
+            element_size = cstring_h.strlen(c_element)
+            c_message = <char*>stdlib.malloc(
+                (text_size + element_size + 1) * sizeof(char))
+            stdio.sprintf(c_message, msg, c_element)
+            c_error.message = c_message
+        else:
+            c_error.message = ''
+    elif c_element is NULL:
+        c_error.message = c_text
+    else:
+        text_size    = cstring_h.strlen(c_text)
+        element_size = cstring_h.strlen(c_element)
+        c_message = <char*>stdlib.malloc(
+            (text_size + 12 + element_size + 1) * sizeof(char))
+        if c_message is NULL:
+            c_error.message = c_text
+        else:
+            stdio.sprintf(c_message, "%s, element '%s'", c_text, c_element)
+            c_error.message = c_message
+
+    c_error.domain = c_domain
+    c_error.code   = xmlerror.XML_ERR_OK    # what else?
+    c_error.level  = xmlerror.XML_ERR_ERROR # what else?
+    c_error.int2   = 0
+
+    _forwardError(c_log_handler, &c_error)
+
+    if c_message is not NULL:
+        stdlib.free(c_message)
+
+################################################################################
+## CONSTANTS FROM "xmlerror.h" (or rather libxml-xmlerror.html)
+################################################################################
+
+cdef __initErrorConstants():
+    "Called at setup time to parse the constants and build the classes below."
+    global __ERROR_LEVELS, __ERROR_DOMAINS, __PARSER_ERROR_TYPES, __RELAXNG_ERROR_TYPES
+    const_defs = ((ErrorLevels,          __ERROR_LEVELS),
+                  (ErrorDomains,         __ERROR_DOMAINS),
+                  (ErrorTypes,           __PARSER_ERROR_TYPES),
+                  (RelaxNGErrorTypes,    __RELAXNG_ERROR_TYPES))
+
+    for cls, constants in const_defs:
+        reverse_dict = {}
+        cls._names   = reverse_dict
+        cls._getName = reverse_dict.get
+        for line in constants.splitlines():
+            if not line:
+                continue
+            name, value = line.split('=')
+            value = int(value)
+            setattr(cls, name, value)
+            reverse_dict[value] = name
+
+    # discard the global string references after use
+    __ERROR_LEVELS = __ERROR_DOMAINS = __PARSER_ERROR_TYPES = __RELAXNG_ERROR_TYPES = None
+
+
+class ErrorLevels(object):
+    """Libxml2 error levels"""
+
+class ErrorDomains(object):
+    """Libxml2 error domains"""
+
+class ErrorTypes(object):
+    """Libxml2 error types"""
+
+class RelaxNGErrorTypes(object):
+    """Libxml2 RelaxNG error types"""
+
+
+# --- BEGIN: GENERATED CONSTANTS ---
+
+# This section is generated by the script 'update-error-constants.py'.
+
+cdef object __ERROR_LEVELS = """\
+NONE=0
+WARNING=1
+ERROR=2
+FATAL=3
+"""
+
+cdef object __ERROR_DOMAINS = """\
+NONE=0
+PARSER=1
+TREE=2
+NAMESPACE=3
+DTD=4
+HTML=5
+MEMORY=6
+OUTPUT=7
+IO=8
+FTP=9
+HTTP=10
+XINCLUDE=11
+XPATH=12
+XPOINTER=13
+REGEXP=14
+DATATYPE=15
+SCHEMASP=16
+SCHEMASV=17
+RELAXNGP=18
+RELAXNGV=19
+CATALOG=20
+C14N=21
+XSLT=22
+VALID=23
+CHECK=24
+WRITER=25
+MODULE=26
+I18N=27
+SCHEMATRONV=28
+BUFFER=29
+URI=30
+"""
+
+cdef object __PARSER_ERROR_TYPES = """\
+ERR_OK=0
+ERR_INTERNAL_ERROR=1
+ERR_NO_MEMORY=2
+ERR_DOCUMENT_START=3
+ERR_DOCUMENT_EMPTY=4
+ERR_DOCUMENT_END=5
+ERR_INVALID_HEX_CHARREF=6
+ERR_INVALID_DEC_CHARREF=7
+ERR_INVALID_CHARREF=8
+ERR_INVALID_CHAR=9
+ERR_CHARREF_AT_EOF=10
+ERR_CHARREF_IN_PROLOG=11
+ERR_CHARREF_IN_EPILOG=12
+ERR_CHARREF_IN_DTD=13
+ERR_ENTITYREF_AT_EOF=14
+ERR_ENTITYREF_IN_PROLOG=15
+ERR_ENTITYREF_IN_EPILOG=16
+ERR_ENTITYREF_IN_DTD=17
+ERR_PEREF_AT_EOF=18
+ERR_PEREF_IN_PROLOG=19
+ERR_PEREF_IN_EPILOG=20
+ERR_PEREF_IN_INT_SUBSET=21
+ERR_ENTITYREF_NO_NAME=22
+ERR_ENTITYREF_SEMICOL_MISSING=23
+ERR_PEREF_NO_NAME=24
+ERR_PEREF_SEMICOL_MISSING=25
+ERR_UNDECLARED_ENTITY=26
+WAR_UNDECLARED_ENTITY=27
+ERR_UNPARSED_ENTITY=28
+ERR_ENTITY_IS_EXTERNAL=29
+ERR_ENTITY_IS_PARAMETER=30
+ERR_UNKNOWN_ENCODING=31
+ERR_UNSUPPORTED_ENCODING=32
+ERR_STRING_NOT_STARTED=33
+ERR_STRING_NOT_CLOSED=34
+ERR_NS_DECL_ERROR=35
+ERR_ENTITY_NOT_STARTED=36
+ERR_ENTITY_NOT_FINISHED=37
+ERR_LT_IN_ATTRIBUTE=38
+ERR_ATTRIBUTE_NOT_STARTED=39
+ERR_ATTRIBUTE_NOT_FINISHED=40
+ERR_ATTRIBUTE_WITHOUT_VALUE=41
+ERR_ATTRIBUTE_REDEFINED=42
+ERR_LITERAL_NOT_STARTED=43
+ERR_LITERAL_NOT_FINISHED=44
+ERR_COMMENT_NOT_FINISHED=45
+ERR_PI_NOT_STARTED=46
+ERR_PI_NOT_FINISHED=47
+ERR_NOTATION_NOT_STARTED=48
+ERR_NOTATION_NOT_FINISHED=49
+ERR_ATTLIST_NOT_STARTED=50
+ERR_ATTLIST_NOT_FINISHED=51
+ERR_MIXED_NOT_STARTED=52
+ERR_MIXED_NOT_FINISHED=53
+ERR_ELEMCONTENT_NOT_STARTED=54
+ERR_ELEMCONTENT_NOT_FINISHED=55
+ERR_XMLDECL_NOT_STARTED=56
+ERR_XMLDECL_NOT_FINISHED=57
+ERR_CONDSEC_NOT_STARTED=58
+ERR_CONDSEC_NOT_FINISHED=59
+ERR_EXT_SUBSET_NOT_FINISHED=60
+ERR_DOCTYPE_NOT_FINISHED=61
+ERR_MISPLACED_CDATA_END=62
+ERR_CDATA_NOT_FINISHED=63
+ERR_RESERVED_XML_NAME=64
+ERR_SPACE_REQUIRED=65
+ERR_SEPARATOR_REQUIRED=66
+ERR_NMTOKEN_REQUIRED=67
+ERR_NAME_REQUIRED=68
+ERR_PCDATA_REQUIRED=69
+ERR_URI_REQUIRED=70
+ERR_PUBID_REQUIRED=71
+ERR_LT_REQUIRED=72
+ERR_GT_REQUIRED=73
+ERR_LTSLASH_REQUIRED=74
+ERR_EQUAL_REQUIRED=75
+ERR_TAG_NAME_MISMATCH=76
+ERR_TAG_NOT_FINISHED=77
+ERR_STANDALONE_VALUE=78
+ERR_ENCODING_NAME=79
+ERR_HYPHEN_IN_COMMENT=80
+ERR_INVALID_ENCODING=81
+ERR_EXT_ENTITY_STANDALONE=82
+ERR_CONDSEC_INVALID=83
+ERR_VALUE_REQUIRED=84
+ERR_NOT_WELL_BALANCED=85
+ERR_EXTRA_CONTENT=86
+ERR_ENTITY_CHAR_ERROR=87
+ERR_ENTITY_PE_INTERNAL=88
+ERR_ENTITY_LOOP=89
+ERR_ENTITY_BOUNDARY=90
+ERR_INVALID_URI=91
+ERR_URI_FRAGMENT=92
+WAR_CATALOG_PI=93
+ERR_NO_DTD=94
+ERR_CONDSEC_INVALID_KEYWORD=95
+ERR_VERSION_MISSING=96
+WAR_UNKNOWN_VERSION=97
+WAR_LANG_VALUE=98
+WAR_NS_URI=99
+WAR_NS_URI_RELATIVE=100
+ERR_MISSING_ENCODING=101
+WAR_SPACE_VALUE=102
+ERR_NOT_STANDALONE=103
+ERR_ENTITY_PROCESSING=104
+ERR_NOTATION_PROCESSING=105
+WAR_NS_COLUMN=106
+WAR_ENTITY_REDEFINED=107
+ERR_UNKNOWN_VERSION=108
+ERR_VERSION_MISMATCH=109
+ERR_NAME_TOO_LONG=110
+ERR_USER_STOP=111
+ERR_COMMENT_ABRUPTLY_ENDED=112
+NS_ERR_XML_NAMESPACE=200
+NS_ERR_UNDEFINED_NAMESPACE=201
+NS_ERR_QNAME=202
+NS_ERR_ATTRIBUTE_REDEFINED=203
+NS_ERR_EMPTY=204
+NS_ERR_COLON=205
+DTD_ATTRIBUTE_DEFAULT=500
+DTD_ATTRIBUTE_REDEFINED=501
+DTD_ATTRIBUTE_VALUE=502
+DTD_CONTENT_ERROR=503
+DTD_CONTENT_MODEL=504
+DTD_CONTENT_NOT_DETERMINIST=505
+DTD_DIFFERENT_PREFIX=506
+DTD_ELEM_DEFAULT_NAMESPACE=507
+DTD_ELEM_NAMESPACE=508
+DTD_ELEM_REDEFINED=509
+DTD_EMPTY_NOTATION=510
+DTD_ENTITY_TYPE=511
+DTD_ID_FIXED=512
+DTD_ID_REDEFINED=513
+DTD_ID_SUBSET=514
+DTD_INVALID_CHILD=515
+DTD_INVALID_DEFAULT=516
+DTD_LOAD_ERROR=517
+DTD_MISSING_ATTRIBUTE=518
+DTD_MIXED_CORRUPT=519
+DTD_MULTIPLE_ID=520
+DTD_NO_DOC=521
+DTD_NO_DTD=522
+DTD_NO_ELEM_NAME=523
+DTD_NO_PREFIX=524
+DTD_NO_ROOT=525
+DTD_NOTATION_REDEFINED=526
+DTD_NOTATION_VALUE=527
+DTD_NOT_EMPTY=528
+DTD_NOT_PCDATA=529
+DTD_NOT_STANDALONE=530
+DTD_ROOT_NAME=531
+DTD_STANDALONE_WHITE_SPACE=532
+DTD_UNKNOWN_ATTRIBUTE=533
+DTD_UNKNOWN_ELEM=534
+DTD_UNKNOWN_ENTITY=535
+DTD_UNKNOWN_ID=536
+DTD_UNKNOWN_NOTATION=537
+DTD_STANDALONE_DEFAULTED=538
+DTD_XMLID_VALUE=539
+DTD_XMLID_TYPE=540
+DTD_DUP_TOKEN=541
+HTML_STRUCURE_ERROR=800
+HTML_UNKNOWN_TAG=801
+RNGP_ANYNAME_ATTR_ANCESTOR=1000
+RNGP_ATTR_CONFLICT=1001
+RNGP_ATTRIBUTE_CHILDREN=1002
+RNGP_ATTRIBUTE_CONTENT=1003
+RNGP_ATTRIBUTE_EMPTY=1004
+RNGP_ATTRIBUTE_NOOP=1005
+RNGP_CHOICE_CONTENT=1006
+RNGP_CHOICE_EMPTY=1007
+RNGP_CREATE_FAILURE=1008
+RNGP_DATA_CONTENT=1009
+RNGP_DEF_CHOICE_AND_INTERLEAVE=1010
+RNGP_DEFINE_CREATE_FAILED=1011
+RNGP_DEFINE_EMPTY=1012
+RNGP_DEFINE_MISSING=1013
+RNGP_DEFINE_NAME_MISSING=1014
+RNGP_ELEM_CONTENT_EMPTY=1015
+RNGP_ELEM_CONTENT_ERROR=1016
+RNGP_ELEMENT_EMPTY=1017
+RNGP_ELEMENT_CONTENT=1018
+RNGP_ELEMENT_NAME=1019
+RNGP_ELEMENT_NO_CONTENT=1020
+RNGP_ELEM_TEXT_CONFLICT=1021
+RNGP_EMPTY=1022
+RNGP_EMPTY_CONSTRUCT=1023
+RNGP_EMPTY_CONTENT=1024
+RNGP_EMPTY_NOT_EMPTY=1025
+RNGP_ERROR_TYPE_LIB=1026
+RNGP_EXCEPT_EMPTY=1027
+RNGP_EXCEPT_MISSING=1028
+RNGP_EXCEPT_MULTIPLE=1029
+RNGP_EXCEPT_NO_CONTENT=1030
+RNGP_EXTERNALREF_EMTPY=1031
+RNGP_EXTERNAL_REF_FAILURE=1032
+RNGP_EXTERNALREF_RECURSE=1033
+RNGP_FORBIDDEN_ATTRIBUTE=1034
+RNGP_FOREIGN_ELEMENT=1035
+RNGP_GRAMMAR_CONTENT=1036
+RNGP_GRAMMAR_EMPTY=1037
+RNGP_GRAMMAR_MISSING=1038
+RNGP_GRAMMAR_NO_START=1039
+RNGP_GROUP_ATTR_CONFLICT=1040
+RNGP_HREF_ERROR=1041
+RNGP_INCLUDE_EMPTY=1042
+RNGP_INCLUDE_FAILURE=1043
+RNGP_INCLUDE_RECURSE=1044
+RNGP_INTERLEAVE_ADD=1045
+RNGP_INTERLEAVE_CREATE_FAILED=1046
+RNGP_INTERLEAVE_EMPTY=1047
+RNGP_INTERLEAVE_NO_CONTENT=1048
+RNGP_INVALID_DEFINE_NAME=1049
+RNGP_INVALID_URI=1050
+RNGP_INVALID_VALUE=1051
+RNGP_MISSING_HREF=1052
+RNGP_NAME_MISSING=1053
+RNGP_NEED_COMBINE=1054
+RNGP_NOTALLOWED_NOT_EMPTY=1055
+RNGP_NSNAME_ATTR_ANCESTOR=1056
+RNGP_NSNAME_NO_NS=1057
+RNGP_PARAM_FORBIDDEN=1058
+RNGP_PARAM_NAME_MISSING=1059
+RNGP_PARENTREF_CREATE_FAILED=1060
+RNGP_PARENTREF_NAME_INVALID=1061
+RNGP_PARENTREF_NO_NAME=1062
+RNGP_PARENTREF_NO_PARENT=1063
+RNGP_PARENTREF_NOT_EMPTY=1064
+RNGP_PARSE_ERROR=1065
+RNGP_PAT_ANYNAME_EXCEPT_ANYNAME=1066
+RNGP_PAT_ATTR_ATTR=1067
+RNGP_PAT_ATTR_ELEM=1068
+RNGP_PAT_DATA_EXCEPT_ATTR=1069
+RNGP_PAT_DATA_EXCEPT_ELEM=1070
+RNGP_PAT_DATA_EXCEPT_EMPTY=1071
+RNGP_PAT_DATA_EXCEPT_GROUP=1072
+RNGP_PAT_DATA_EXCEPT_INTERLEAVE=1073
+RNGP_PAT_DATA_EXCEPT_LIST=1074
+RNGP_PAT_DATA_EXCEPT_ONEMORE=1075
+RNGP_PAT_DATA_EXCEPT_REF=1076
+RNGP_PAT_DATA_EXCEPT_TEXT=1077
+RNGP_PAT_LIST_ATTR=1078
+RNGP_PAT_LIST_ELEM=1079
+RNGP_PAT_LIST_INTERLEAVE=1080
+RNGP_PAT_LIST_LIST=1081
+RNGP_PAT_LIST_REF=1082
+RNGP_PAT_LIST_TEXT=1083
+RNGP_PAT_NSNAME_EXCEPT_ANYNAME=1084
+RNGP_PAT_NSNAME_EXCEPT_NSNAME=1085
+RNGP_PAT_ONEMORE_GROUP_ATTR=1086
+RNGP_PAT_ONEMORE_INTERLEAVE_ATTR=1087
+RNGP_PAT_START_ATTR=1088
+RNGP_PAT_START_DATA=1089
+RNGP_PAT_START_EMPTY=1090
+RNGP_PAT_START_GROUP=1091
+RNGP_PAT_START_INTERLEAVE=1092
+RNGP_PAT_START_LIST=1093
+RNGP_PAT_START_ONEMORE=1094
+RNGP_PAT_START_TEXT=1095
+RNGP_PAT_START_VALUE=1096
+RNGP_PREFIX_UNDEFINED=1097
+RNGP_REF_CREATE_FAILED=1098
+RNGP_REF_CYCLE=1099
+RNGP_REF_NAME_INVALID=1100
+RNGP_REF_NO_DEF=1101
+RNGP_REF_NO_NAME=1102
+RNGP_REF_NOT_EMPTY=1103
+RNGP_START_CHOICE_AND_INTERLEAVE=1104
+RNGP_START_CONTENT=1105
+RNGP_START_EMPTY=1106
+RNGP_START_MISSING=1107
+RNGP_TEXT_EXPECTED=1108
+RNGP_TEXT_HAS_CHILD=1109
+RNGP_TYPE_MISSING=1110
+RNGP_TYPE_NOT_FOUND=1111
+RNGP_TYPE_VALUE=1112
+RNGP_UNKNOWN_ATTRIBUTE=1113
+RNGP_UNKNOWN_COMBINE=1114
+RNGP_UNKNOWN_CONSTRUCT=1115
+RNGP_UNKNOWN_TYPE_LIB=1116
+RNGP_URI_FRAGMENT=1117
+RNGP_URI_NOT_ABSOLUTE=1118
+RNGP_VALUE_EMPTY=1119
+RNGP_VALUE_NO_CONTENT=1120
+RNGP_XMLNS_NAME=1121
+RNGP_XML_NS=1122
+XPATH_EXPRESSION_OK=1200
+XPATH_NUMBER_ERROR=1201
+XPATH_UNFINISHED_LITERAL_ERROR=1202
+XPATH_START_LITERAL_ERROR=1203
+XPATH_VARIABLE_REF_ERROR=1204
+XPATH_UNDEF_VARIABLE_ERROR=1205
+XPATH_INVALID_PREDICATE_ERROR=1206
+XPATH_EXPR_ERROR=1207
+XPATH_UNCLOSED_ERROR=1208
+XPATH_UNKNOWN_FUNC_ERROR=1209
+XPATH_INVALID_OPERAND=1210
+XPATH_INVALID_TYPE=1211
+XPATH_INVALID_ARITY=1212
+XPATH_INVALID_CTXT_SIZE=1213
+XPATH_INVALID_CTXT_POSITION=1214
+XPATH_MEMORY_ERROR=1215
+XPTR_SYNTAX_ERROR=1216
+XPTR_RESOURCE_ERROR=1217
+XPTR_SUB_RESOURCE_ERROR=1218
+XPATH_UNDEF_PREFIX_ERROR=1219
+XPATH_ENCODING_ERROR=1220
+XPATH_INVALID_CHAR_ERROR=1221
+TREE_INVALID_HEX=1300
+TREE_INVALID_DEC=1301
+TREE_UNTERMINATED_ENTITY=1302
+TREE_NOT_UTF8=1303
+SAVE_NOT_UTF8=1400
+SAVE_CHAR_INVALID=1401
+SAVE_NO_DOCTYPE=1402
+SAVE_UNKNOWN_ENCODING=1403
+REGEXP_COMPILE_ERROR=1450
+IO_UNKNOWN=1500
+IO_EACCES=1501
+IO_EAGAIN=1502
+IO_EBADF=1503
+IO_EBADMSG=1504
+IO_EBUSY=1505
+IO_ECANCELED=1506
+IO_ECHILD=1507
+IO_EDEADLK=1508
+IO_EDOM=1509
+IO_EEXIST=1510
+IO_EFAULT=1511
+IO_EFBIG=1512
+IO_EINPROGRESS=1513
+IO_EINTR=1514
+IO_EINVAL=1515
+IO_EIO=1516
+IO_EISDIR=1517
+IO_EMFILE=1518
+IO_EMLINK=1519
+IO_EMSGSIZE=1520
+IO_ENAMETOOLONG=1521
+IO_ENFILE=1522
+IO_ENODEV=1523
+IO_ENOENT=1524
+IO_ENOEXEC=1525
+IO_ENOLCK=1526
+IO_ENOMEM=1527
+IO_ENOSPC=1528
+IO_ENOSYS=1529
+IO_ENOTDIR=1530
+IO_ENOTEMPTY=1531
+IO_ENOTSUP=1532
+IO_ENOTTY=1533
+IO_ENXIO=1534
+IO_EPERM=1535
+IO_EPIPE=1536
+IO_ERANGE=1537
+IO_EROFS=1538
+IO_ESPIPE=1539
+IO_ESRCH=1540
+IO_ETIMEDOUT=1541
+IO_EXDEV=1542
+IO_NETWORK_ATTEMPT=1543
+IO_ENCODER=1544
+IO_FLUSH=1545
+IO_WRITE=1546
+IO_NO_INPUT=1547
+IO_BUFFER_FULL=1548
+IO_LOAD_ERROR=1549
+IO_ENOTSOCK=1550
+IO_EISCONN=1551
+IO_ECONNREFUSED=1552
+IO_ENETUNREACH=1553
+IO_EADDRINUSE=1554
+IO_EALREADY=1555
+IO_EAFNOSUPPORT=1556
+XINCLUDE_RECURSION=1600
+XINCLUDE_PARSE_VALUE=1601
+XINCLUDE_ENTITY_DEF_MISMATCH=1602
+XINCLUDE_NO_HREF=1603
+XINCLUDE_NO_FALLBACK=1604
+XINCLUDE_HREF_URI=1605
+XINCLUDE_TEXT_FRAGMENT=1606
+XINCLUDE_TEXT_DOCUMENT=1607
+XINCLUDE_INVALID_CHAR=1608
+XINCLUDE_BUILD_FAILED=1609
+XINCLUDE_UNKNOWN_ENCODING=1610
+XINCLUDE_MULTIPLE_ROOT=1611
+XINCLUDE_XPTR_FAILED=1612
+XINCLUDE_XPTR_RESULT=1613
+XINCLUDE_INCLUDE_IN_INCLUDE=1614
+XINCLUDE_FALLBACKS_IN_INCLUDE=1615
+XINCLUDE_FALLBACK_NOT_IN_INCLUDE=1616
+XINCLUDE_DEPRECATED_NS=1617
+XINCLUDE_FRAGMENT_ID=1618
+CATALOG_MISSING_ATTR=1650
+CATALOG_ENTRY_BROKEN=1651
+CATALOG_PREFER_VALUE=1652
+CATALOG_NOT_CATALOG=1653
+CATALOG_RECURSION=1654
+SCHEMAP_PREFIX_UNDEFINED=1700
+SCHEMAP_ATTRFORMDEFAULT_VALUE=1701
+SCHEMAP_ATTRGRP_NONAME_NOREF=1702
+SCHEMAP_ATTR_NONAME_NOREF=1703
+SCHEMAP_COMPLEXTYPE_NONAME_NOREF=1704
+SCHEMAP_ELEMFORMDEFAULT_VALUE=1705
+SCHEMAP_ELEM_NONAME_NOREF=1706
+SCHEMAP_EXTENSION_NO_BASE=1707
+SCHEMAP_FACET_NO_VALUE=1708
+SCHEMAP_FAILED_BUILD_IMPORT=1709
+SCHEMAP_GROUP_NONAME_NOREF=1710
+SCHEMAP_IMPORT_NAMESPACE_NOT_URI=1711
+SCHEMAP_IMPORT_REDEFINE_NSNAME=1712
+SCHEMAP_IMPORT_SCHEMA_NOT_URI=1713
+SCHEMAP_INVALID_BOOLEAN=1714
+SCHEMAP_INVALID_ENUM=1715
+SCHEMAP_INVALID_FACET=1716
+SCHEMAP_INVALID_FACET_VALUE=1717
+SCHEMAP_INVALID_MAXOCCURS=1718
+SCHEMAP_INVALID_MINOCCURS=1719
+SCHEMAP_INVALID_REF_AND_SUBTYPE=1720
+SCHEMAP_INVALID_WHITE_SPACE=1721
+SCHEMAP_NOATTR_NOREF=1722
+SCHEMAP_NOTATION_NO_NAME=1723
+SCHEMAP_NOTYPE_NOREF=1724
+SCHEMAP_REF_AND_SUBTYPE=1725
+SCHEMAP_RESTRICTION_NONAME_NOREF=1726
+SCHEMAP_SIMPLETYPE_NONAME=1727
+SCHEMAP_TYPE_AND_SUBTYPE=1728
+SCHEMAP_UNKNOWN_ALL_CHILD=1729
+SCHEMAP_UNKNOWN_ANYATTRIBUTE_CHILD=1730
+SCHEMAP_UNKNOWN_ATTR_CHILD=1731
+SCHEMAP_UNKNOWN_ATTRGRP_CHILD=1732
+SCHEMAP_UNKNOWN_ATTRIBUTE_GROUP=1733
+SCHEMAP_UNKNOWN_BASE_TYPE=1734
+SCHEMAP_UNKNOWN_CHOICE_CHILD=1735
+SCHEMAP_UNKNOWN_COMPLEXCONTENT_CHILD=1736
+SCHEMAP_UNKNOWN_COMPLEXTYPE_CHILD=1737
+SCHEMAP_UNKNOWN_ELEM_CHILD=1738
+SCHEMAP_UNKNOWN_EXTENSION_CHILD=1739
+SCHEMAP_UNKNOWN_FACET_CHILD=1740
+SCHEMAP_UNKNOWN_FACET_TYPE=1741
+SCHEMAP_UNKNOWN_GROUP_CHILD=1742
+SCHEMAP_UNKNOWN_IMPORT_CHILD=1743
+SCHEMAP_UNKNOWN_LIST_CHILD=1744
+SCHEMAP_UNKNOWN_NOTATION_CHILD=1745
+SCHEMAP_UNKNOWN_PROCESSCONTENT_CHILD=1746
+SCHEMAP_UNKNOWN_REF=1747
+SCHEMAP_UNKNOWN_RESTRICTION_CHILD=1748
+SCHEMAP_UNKNOWN_SCHEMAS_CHILD=1749
+SCHEMAP_UNKNOWN_SEQUENCE_CHILD=1750
+SCHEMAP_UNKNOWN_SIMPLECONTENT_CHILD=1751
+SCHEMAP_UNKNOWN_SIMPLETYPE_CHILD=1752
+SCHEMAP_UNKNOWN_TYPE=1753
+SCHEMAP_UNKNOWN_UNION_CHILD=1754
+SCHEMAP_ELEM_DEFAULT_FIXED=1755
+SCHEMAP_REGEXP_INVALID=1756
+SCHEMAP_FAILED_LOAD=1757
+SCHEMAP_NOTHING_TO_PARSE=1758
+SCHEMAP_NOROOT=1759
+SCHEMAP_REDEFINED_GROUP=1760
+SCHEMAP_REDEFINED_TYPE=1761
+SCHEMAP_REDEFINED_ELEMENT=1762
+SCHEMAP_REDEFINED_ATTRGROUP=1763
+SCHEMAP_REDEFINED_ATTR=1764
+SCHEMAP_REDEFINED_NOTATION=1765
+SCHEMAP_FAILED_PARSE=1766
+SCHEMAP_UNKNOWN_PREFIX=1767
+SCHEMAP_DEF_AND_PREFIX=1768
+SCHEMAP_UNKNOWN_INCLUDE_CHILD=1769
+SCHEMAP_INCLUDE_SCHEMA_NOT_URI=1770
+SCHEMAP_INCLUDE_SCHEMA_NO_URI=1771
+SCHEMAP_NOT_SCHEMA=1772
+SCHEMAP_UNKNOWN_MEMBER_TYPE=1773
+SCHEMAP_INVALID_ATTR_USE=1774
+SCHEMAP_RECURSIVE=1775
+SCHEMAP_SUPERNUMEROUS_LIST_ITEM_TYPE=1776
+SCHEMAP_INVALID_ATTR_COMBINATION=1777
+SCHEMAP_INVALID_ATTR_INLINE_COMBINATION=1778
+SCHEMAP_MISSING_SIMPLETYPE_CHILD=1779
+SCHEMAP_INVALID_ATTR_NAME=1780
+SCHEMAP_REF_AND_CONTENT=1781
+SCHEMAP_CT_PROPS_CORRECT_1=1782
+SCHEMAP_CT_PROPS_CORRECT_2=1783
+SCHEMAP_CT_PROPS_CORRECT_3=1784
+SCHEMAP_CT_PROPS_CORRECT_4=1785
+SCHEMAP_CT_PROPS_CORRECT_5=1786
+SCHEMAP_DERIVATION_OK_RESTRICTION_1=1787
+SCHEMAP_DERIVATION_OK_RESTRICTION_2_1_1=1788
+SCHEMAP_DERIVATION_OK_RESTRICTION_2_1_2=1789
+SCHEMAP_DERIVATION_OK_RESTRICTION_2_2=1790
+SCHEMAP_DERIVATION_OK_RESTRICTION_3=1791
+SCHEMAP_WILDCARD_INVALID_NS_MEMBER=1792
+SCHEMAP_INTERSECTION_NOT_EXPRESSIBLE=1793
+SCHEMAP_UNION_NOT_EXPRESSIBLE=1794
+SCHEMAP_SRC_IMPORT_3_1=1795
+SCHEMAP_SRC_IMPORT_3_2=1796
+SCHEMAP_DERIVATION_OK_RESTRICTION_4_1=1797
+SCHEMAP_DERIVATION_OK_RESTRICTION_4_2=1798
+SCHEMAP_DERIVATION_OK_RESTRICTION_4_3=1799
+SCHEMAP_COS_CT_EXTENDS_1_3=1800
+SCHEMAV_NOROOT=1801
+SCHEMAV_UNDECLAREDELEM=1802
+SCHEMAV_NOTTOPLEVEL=1803
+SCHEMAV_MISSING=1804
+SCHEMAV_WRONGELEM=1805
+SCHEMAV_NOTYPE=1806
+SCHEMAV_NOROLLBACK=1807
+SCHEMAV_ISABSTRACT=1808
+SCHEMAV_NOTEMPTY=1809
+SCHEMAV_ELEMCONT=1810
+SCHEMAV_HAVEDEFAULT=1811
+SCHEMAV_NOTNILLABLE=1812
+SCHEMAV_EXTRACONTENT=1813
+SCHEMAV_INVALIDATTR=1814
+SCHEMAV_INVALIDELEM=1815
+SCHEMAV_NOTDETERMINIST=1816
+SCHEMAV_CONSTRUCT=1817
+SCHEMAV_INTERNAL=1818
+SCHEMAV_NOTSIMPLE=1819
+SCHEMAV_ATTRUNKNOWN=1820
+SCHEMAV_ATTRINVALID=1821
+SCHEMAV_VALUE=1822
+SCHEMAV_FACET=1823
+SCHEMAV_CVC_DATATYPE_VALID_1_2_1=1824
+SCHEMAV_CVC_DATATYPE_VALID_1_2_2=1825
+SCHEMAV_CVC_DATATYPE_VALID_1_2_3=1826
+SCHEMAV_CVC_TYPE_3_1_1=1827
+SCHEMAV_CVC_TYPE_3_1_2=1828
+SCHEMAV_CVC_FACET_VALID=1829
+SCHEMAV_CVC_LENGTH_VALID=1830
+SCHEMAV_CVC_MINLENGTH_VALID=1831
+SCHEMAV_CVC_MAXLENGTH_VALID=1832
+SCHEMAV_CVC_MININCLUSIVE_VALID=1833
+SCHEMAV_CVC_MAXINCLUSIVE_VALID=1834
+SCHEMAV_CVC_MINEXCLUSIVE_VALID=1835
+SCHEMAV_CVC_MAXEXCLUSIVE_VALID=1836
+SCHEMAV_CVC_TOTALDIGITS_VALID=1837
+SCHEMAV_CVC_FRACTIONDIGITS_VALID=1838
+SCHEMAV_CVC_PATTERN_VALID=1839
+SCHEMAV_CVC_ENUMERATION_VALID=1840
+SCHEMAV_CVC_COMPLEX_TYPE_2_1=1841
+SCHEMAV_CVC_COMPLEX_TYPE_2_2=1842
+SCHEMAV_CVC_COMPLEX_TYPE_2_3=1843
+SCHEMAV_CVC_COMPLEX_TYPE_2_4=1844
+SCHEMAV_CVC_ELT_1=1845
+SCHEMAV_CVC_ELT_2=1846
+SCHEMAV_CVC_ELT_3_1=1847
+SCHEMAV_CVC_ELT_3_2_1=1848
+SCHEMAV_CVC_ELT_3_2_2=1849
+SCHEMAV_CVC_ELT_4_1=1850
+SCHEMAV_CVC_ELT_4_2=1851
+SCHEMAV_CVC_ELT_4_3=1852
+SCHEMAV_CVC_ELT_5_1_1=1853
+SCHEMAV_CVC_ELT_5_1_2=1854
+SCHEMAV_CVC_ELT_5_2_1=1855
+SCHEMAV_CVC_ELT_5_2_2_1=1856
+SCHEMAV_CVC_ELT_5_2_2_2_1=1857
+SCHEMAV_CVC_ELT_5_2_2_2_2=1858
+SCHEMAV_CVC_ELT_6=1859
+SCHEMAV_CVC_ELT_7=1860
+SCHEMAV_CVC_ATTRIBUTE_1=1861
+SCHEMAV_CVC_ATTRIBUTE_2=1862
+SCHEMAV_CVC_ATTRIBUTE_3=1863
+SCHEMAV_CVC_ATTRIBUTE_4=1864
+SCHEMAV_CVC_COMPLEX_TYPE_3_1=1865
+SCHEMAV_CVC_COMPLEX_TYPE_3_2_1=1866
+SCHEMAV_CVC_COMPLEX_TYPE_3_2_2=1867
+SCHEMAV_CVC_COMPLEX_TYPE_4=1868
+SCHEMAV_CVC_COMPLEX_TYPE_5_1=1869
+SCHEMAV_CVC_COMPLEX_TYPE_5_2=1870
+SCHEMAV_ELEMENT_CONTENT=1871
+SCHEMAV_DOCUMENT_ELEMENT_MISSING=1872
+SCHEMAV_CVC_COMPLEX_TYPE_1=1873
+SCHEMAV_CVC_AU=1874
+SCHEMAV_CVC_TYPE_1=1875
+SCHEMAV_CVC_TYPE_2=1876
+SCHEMAV_CVC_IDC=1877
+SCHEMAV_CVC_WILDCARD=1878
+SCHEMAV_MISC=1879
+XPTR_UNKNOWN_SCHEME=1900
+XPTR_CHILDSEQ_START=1901
+XPTR_EVAL_FAILED=1902
+XPTR_EXTRA_OBJECTS=1903
+C14N_CREATE_CTXT=1950
+C14N_REQUIRES_UTF8=1951
+C14N_CREATE_STACK=1952
+C14N_INVALID_NODE=1953
+C14N_UNKNOW_NODE=1954
+C14N_RELATIVE_NAMESPACE=1955
+FTP_PASV_ANSWER=2000
+FTP_EPSV_ANSWER=2001
+FTP_ACCNT=2002
+FTP_URL_SYNTAX=2003
+HTTP_URL_SYNTAX=2020
+HTTP_USE_IP=2021
+HTTP_UNKNOWN_HOST=2022
+SCHEMAP_SRC_SIMPLE_TYPE_1=3000
+SCHEMAP_SRC_SIMPLE_TYPE_2=3001
+SCHEMAP_SRC_SIMPLE_TYPE_3=3002
+SCHEMAP_SRC_SIMPLE_TYPE_4=3003
+SCHEMAP_SRC_RESOLVE=3004
+SCHEMAP_SRC_RESTRICTION_BASE_OR_SIMPLETYPE=3005
+SCHEMAP_SRC_LIST_ITEMTYPE_OR_SIMPLETYPE=3006
+SCHEMAP_SRC_UNION_MEMBERTYPES_OR_SIMPLETYPES=3007
+SCHEMAP_ST_PROPS_CORRECT_1=3008
+SCHEMAP_ST_PROPS_CORRECT_2=3009
+SCHEMAP_ST_PROPS_CORRECT_3=3010
+SCHEMAP_COS_ST_RESTRICTS_1_1=3011
+SCHEMAP_COS_ST_RESTRICTS_1_2=3012
+SCHEMAP_COS_ST_RESTRICTS_1_3_1=3013
+SCHEMAP_COS_ST_RESTRICTS_1_3_2=3014
+SCHEMAP_COS_ST_RESTRICTS_2_1=3015
+SCHEMAP_COS_ST_RESTRICTS_2_3_1_1=3016
+SCHEMAP_COS_ST_RESTRICTS_2_3_1_2=3017
+SCHEMAP_COS_ST_RESTRICTS_2_3_2_1=3018
+SCHEMAP_COS_ST_RESTRICTS_2_3_2_2=3019
+SCHEMAP_COS_ST_RESTRICTS_2_3_2_3=3020
+SCHEMAP_COS_ST_RESTRICTS_2_3_2_4=3021
+SCHEMAP_COS_ST_RESTRICTS_2_3_2_5=3022
+SCHEMAP_COS_ST_RESTRICTS_3_1=3023
+SCHEMAP_COS_ST_RESTRICTS_3_3_1=3024
+SCHEMAP_COS_ST_RESTRICTS_3_3_1_2=3025
+SCHEMAP_COS_ST_RESTRICTS_3_3_2_2=3026
+SCHEMAP_COS_ST_RESTRICTS_3_3_2_1=3027
+SCHEMAP_COS_ST_RESTRICTS_3_3_2_3=3028
+SCHEMAP_COS_ST_RESTRICTS_3_3_2_4=3029
+SCHEMAP_COS_ST_RESTRICTS_3_3_2_5=3030
+SCHEMAP_COS_ST_DERIVED_OK_2_1=3031
+SCHEMAP_COS_ST_DERIVED_OK_2_2=3032
+SCHEMAP_S4S_ELEM_NOT_ALLOWED=3033
+SCHEMAP_S4S_ELEM_MISSING=3034
+SCHEMAP_S4S_ATTR_NOT_ALLOWED=3035
+SCHEMAP_S4S_ATTR_MISSING=3036
+SCHEMAP_S4S_ATTR_INVALID_VALUE=3037
+SCHEMAP_SRC_ELEMENT_1=3038
+SCHEMAP_SRC_ELEMENT_2_1=3039
+SCHEMAP_SRC_ELEMENT_2_2=3040
+SCHEMAP_SRC_ELEMENT_3=3041
+SCHEMAP_P_PROPS_CORRECT_1=3042
+SCHEMAP_P_PROPS_CORRECT_2_1=3043
+SCHEMAP_P_PROPS_CORRECT_2_2=3044
+SCHEMAP_E_PROPS_CORRECT_2=3045
+SCHEMAP_E_PROPS_CORRECT_3=3046
+SCHEMAP_E_PROPS_CORRECT_4=3047
+SCHEMAP_E_PROPS_CORRECT_5=3048
+SCHEMAP_E_PROPS_CORRECT_6=3049
+SCHEMAP_SRC_INCLUDE=3050
+SCHEMAP_SRC_ATTRIBUTE_1=3051
+SCHEMAP_SRC_ATTRIBUTE_2=3052
+SCHEMAP_SRC_ATTRIBUTE_3_1=3053
+SCHEMAP_SRC_ATTRIBUTE_3_2=3054
+SCHEMAP_SRC_ATTRIBUTE_4=3055
+SCHEMAP_NO_XMLNS=3056
+SCHEMAP_NO_XSI=3057
+SCHEMAP_COS_VALID_DEFAULT_1=3058
+SCHEMAP_COS_VALID_DEFAULT_2_1=3059
+SCHEMAP_COS_VALID_DEFAULT_2_2_1=3060
+SCHEMAP_COS_VALID_DEFAULT_2_2_2=3061
+SCHEMAP_CVC_SIMPLE_TYPE=3062
+SCHEMAP_COS_CT_EXTENDS_1_1=3063
+SCHEMAP_SRC_IMPORT_1_1=3064
+SCHEMAP_SRC_IMPORT_1_2=3065
+SCHEMAP_SRC_IMPORT_2=3066
+SCHEMAP_SRC_IMPORT_2_1=3067
+SCHEMAP_SRC_IMPORT_2_2=3068
+SCHEMAP_INTERNAL=3069
+SCHEMAP_NOT_DETERMINISTIC=3070
+SCHEMAP_SRC_ATTRIBUTE_GROUP_1=3071
+SCHEMAP_SRC_ATTRIBUTE_GROUP_2=3072
+SCHEMAP_SRC_ATTRIBUTE_GROUP_3=3073
+SCHEMAP_MG_PROPS_CORRECT_1=3074
+SCHEMAP_MG_PROPS_CORRECT_2=3075
+SCHEMAP_SRC_CT_1=3076
+SCHEMAP_DERIVATION_OK_RESTRICTION_2_1_3=3077
+SCHEMAP_AU_PROPS_CORRECT_2=3078
+SCHEMAP_A_PROPS_CORRECT_2=3079
+SCHEMAP_C_PROPS_CORRECT=3080
+SCHEMAP_SRC_REDEFINE=3081
+SCHEMAP_SRC_IMPORT=3082
+SCHEMAP_WARN_SKIP_SCHEMA=3083
+SCHEMAP_WARN_UNLOCATED_SCHEMA=3084
+SCHEMAP_WARN_ATTR_REDECL_PROH=3085
+SCHEMAP_WARN_ATTR_POINTLESS_PROH=3086
+SCHEMAP_AG_PROPS_CORRECT=3087
+SCHEMAP_COS_CT_EXTENDS_1_2=3088
+SCHEMAP_AU_PROPS_CORRECT=3089
+SCHEMAP_A_PROPS_CORRECT_3=3090
+SCHEMAP_COS_ALL_LIMITED=3091
+SCHEMATRONV_ASSERT=4000
+SCHEMATRONV_REPORT=4001
+MODULE_OPEN=4900
+MODULE_CLOSE=4901
+CHECK_FOUND_ELEMENT=5000
+CHECK_FOUND_ATTRIBUTE=5001
+CHECK_FOUND_TEXT=5002
+CHECK_FOUND_CDATA=5003
+CHECK_FOUND_ENTITYREF=5004
+CHECK_FOUND_ENTITY=5005
+CHECK_FOUND_PI=5006
+CHECK_FOUND_COMMENT=5007
+CHECK_FOUND_DOCTYPE=5008
+CHECK_FOUND_FRAGMENT=5009
+CHECK_FOUND_NOTATION=5010
+CHECK_UNKNOWN_NODE=5011
+CHECK_ENTITY_TYPE=5012
+CHECK_NO_PARENT=5013
+CHECK_NO_DOC=5014
+CHECK_NO_NAME=5015
+CHECK_NO_ELEM=5016
+CHECK_WRONG_DOC=5017
+CHECK_NO_PREV=5018
+CHECK_WRONG_PREV=5019
+CHECK_NO_NEXT=5020
+CHECK_WRONG_NEXT=5021
+CHECK_NOT_DTD=5022
+CHECK_NOT_ATTR=5023
+CHECK_NOT_ATTR_DECL=5024
+CHECK_NOT_ELEM_DECL=5025
+CHECK_NOT_ENTITY_DECL=5026
+CHECK_NOT_NS_DECL=5027
+CHECK_NO_HREF=5028
+CHECK_WRONG_PARENT=5029
+CHECK_NS_SCOPE=5030
+CHECK_NS_ANCESTOR=5031
+CHECK_NOT_UTF8=5032
+CHECK_NO_DICT=5033
+CHECK_NOT_NCNAME=5034
+CHECK_OUTSIDE_DICT=5035
+CHECK_WRONG_NAME=5036
+CHECK_NAME_NOT_NULL=5037
+I18N_NO_NAME=6000
+I18N_NO_HANDLER=6001
+I18N_EXCESS_HANDLER=6002
+I18N_CONV_FAILED=6003
+I18N_NO_OUTPUT=6004
+BUF_OVERFLOW=7000
+"""
+
+cdef object __RELAXNG_ERROR_TYPES = """\
+RELAXNG_OK=0
+RELAXNG_ERR_MEMORY=1
+RELAXNG_ERR_TYPE=2
+RELAXNG_ERR_TYPEVAL=3
+RELAXNG_ERR_DUPID=4
+RELAXNG_ERR_TYPECMP=5
+RELAXNG_ERR_NOSTATE=6
+RELAXNG_ERR_NODEFINE=7
+RELAXNG_ERR_LISTEXTRA=8
+RELAXNG_ERR_LISTEMPTY=9
+RELAXNG_ERR_INTERNODATA=10
+RELAXNG_ERR_INTERSEQ=11
+RELAXNG_ERR_INTEREXTRA=12
+RELAXNG_ERR_ELEMNAME=13
+RELAXNG_ERR_ATTRNAME=14
+RELAXNG_ERR_ELEMNONS=15
+RELAXNG_ERR_ATTRNONS=16
+RELAXNG_ERR_ELEMWRONGNS=17
+RELAXNG_ERR_ATTRWRONGNS=18
+RELAXNG_ERR_ELEMEXTRANS=19
+RELAXNG_ERR_ATTREXTRANS=20
+RELAXNG_ERR_ELEMNOTEMPTY=21
+RELAXNG_ERR_NOELEM=22
+RELAXNG_ERR_NOTELEM=23
+RELAXNG_ERR_ATTRVALID=24
+RELAXNG_ERR_CONTENTVALID=25
+RELAXNG_ERR_EXTRACONTENT=26
+RELAXNG_ERR_INVALIDATTR=27
+RELAXNG_ERR_DATAELEM=28
+RELAXNG_ERR_VALELEM=29
+RELAXNG_ERR_LISTELEM=30
+RELAXNG_ERR_DATATYPE=31
+RELAXNG_ERR_VALUE=32
+RELAXNG_ERR_LIST=33
+RELAXNG_ERR_NOGRAMMAR=34
+RELAXNG_ERR_EXTRADATA=35
+RELAXNG_ERR_LACKDATA=36
+RELAXNG_ERR_INTERNAL=37
+RELAXNG_ERR_ELEMWRONG=38
+RELAXNG_ERR_TEXTWRONG=39
+"""
+# --- END: GENERATED CONSTANTS ---
+
+__initErrorConstants()
diff --git a/.venv/lib/python3.12/site-packages/lxml/xmlid.pxi b/.venv/lib/python3.12/site-packages/lxml/xmlid.pxi
new file mode 100644
index 00000000..1531f6d9
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/xmlid.pxi
@@ -0,0 +1,179 @@
+cdef object _find_id_attributes
+
+def XMLID(text, parser=None, *, base_url=None):
+    """XMLID(text, parser=None, base_url=None)
+
+    Parse the text and return a tuple (root node, ID dictionary).  The root
+    node is the same as returned by the XML() function.  The dictionary
+    contains string-element pairs.  The dictionary keys are the values of 'id'
+    attributes.  The elements referenced by the ID are stored as dictionary
+    values.
+    """
+    cdef dict dic
+    global _find_id_attributes
+    if _find_id_attributes is None:
+        _find_id_attributes = XPath('//*[string(@id)]')
+
+    # ElementTree compatible implementation: parse and look for 'id' attributes
+    root = XML(text, parser, base_url=base_url)
+    dic = {}
+    for elem in _find_id_attributes(root):
+        dic[elem.get('id')] = elem
+    return root, dic
+
+def XMLDTDID(text, parser=None, *, base_url=None):
+    """XMLDTDID(text, parser=None, base_url=None)
+
+    Parse the text and return a tuple (root node, ID dictionary).  The root
+    node is the same as returned by the XML() function.  The dictionary
+    contains string-element pairs.  The dictionary keys are the values of ID
+    attributes as defined by the DTD.  The elements referenced by the ID are
+    stored as dictionary values.
+
+    Note that you must not modify the XML tree if you use the ID dictionary.
+    The results are undefined.
+    """
+    cdef _Element root
+    root = XML(text, parser, base_url=base_url)
+    # xml:id spec compatible implementation: use DTD ID attributes from libxml2
+    if root._doc._c_doc.ids is NULL:
+        return root, {}
+    else:
+        return root, _IDDict(root)
+
+def parseid(source, parser=None, *, base_url=None):
+    """parseid(source, parser=None)
+
+    Parses the source into a tuple containing an ElementTree object and an
+    ID dictionary.  If no parser is provided as second argument, the default
+    parser is used.
+
+    Note that you must not modify the XML tree if you use the ID dictionary.
+    The results are undefined.
+    """
+    cdef _Document doc
+    doc = _parseDocument(source, parser, base_url)
+    return _elementTreeFactory(doc, None), _IDDict(doc)
+
+cdef class _IDDict:
+    """IDDict(self, etree)
+    A dictionary-like proxy class that mapps ID attributes to elements.
+
+    The dictionary must be instantiated with the root element of a parsed XML
+    document, otherwise the behaviour is undefined.  Elements and XML trees
+    that were created or modified 'by hand' are not supported.
+    """
+    cdef _Document _doc
+    cdef object _keys
+    cdef object _items
+    def __cinit__(self, etree):
+        cdef _Document doc
+        doc = _documentOrRaise(etree)
+        if doc._c_doc.ids is NULL:
+            raise ValueError, "No ID dictionary available."
+        self._doc = doc
+        self._keys  = None
+        self._items = None
+
+    def copy(self):
+        return _IDDict(self._doc)
+
+    def __getitem__(self, id_name):
+        cdef tree.xmlHashTable* c_ids
+        cdef tree.xmlID* c_id
+        cdef xmlAttr* c_attr
+        c_ids = self._doc._c_doc.ids
+        id_utf = _utf8(id_name)
+        c_id = <tree.xmlID*>tree.xmlHashLookup(c_ids, _xcstr(id_utf))
+        if c_id is NULL:
+            raise KeyError, "key not found."
+        c_attr = c_id.attr
+        if c_attr is NULL or c_attr.parent is NULL:
+            raise KeyError, "ID attribute not found."
+        return _elementFactory(self._doc, c_attr.parent)
+
+    def get(self, id_name):
+        return self[id_name]
+
+    def __contains__(self, id_name):
+        cdef tree.xmlID* c_id
+        id_utf = _utf8(id_name)
+        c_id = <tree.xmlID*>tree.xmlHashLookup(
+            self._doc._c_doc.ids, _xcstr(id_utf))
+        return c_id is not NULL
+
+    def has_key(self, id_name):
+        return id_name in self
+
+    def __repr__(self):
+        return repr(dict(self))
+
+    def keys(self):
+        if self._keys is None:
+            self._keys = self._build_keys()
+        return self._keys[:]
+
+    def __iter__(self):
+        if self._keys is None:
+            self._keys = self._build_keys()
+        return iter(self._keys)
+
+    def iterkeys(self):
+        return self
+
+    def __len__(self):
+        if self._keys is None:
+            self._keys = self._build_keys()
+        return len(self._keys)
+
+    def items(self):
+        if self._items is None:
+            self._items = self._build_items()
+        return self._items[:]
+
+    def iteritems(self):
+        if self._items is None:
+            self._items = self._build_items()
+        return iter(self._items)
+
+    def values(self):
+        cdef list values = []
+        if self._items is None:
+            self._items = self._build_items()
+        for item in self._items:
+            value = python.PyTuple_GET_ITEM(item, 1)
+            python.Py_INCREF(value)
+            values.append(value)
+        return values
+
+    def itervalues(self):
+        return iter(self.values())
+
+    cdef object _build_keys(self):
+        keys = []
+        tree.xmlHashScan(<tree.xmlHashTable*>self._doc._c_doc.ids,
+                         <tree.xmlHashScanner>_collectIdHashKeys, <python.PyObject*>keys)
+        return keys
+
+    cdef object _build_items(self):
+        items = []
+        context = (items, self._doc)
+        tree.xmlHashScan(<tree.xmlHashTable*>self._doc._c_doc.ids,
+                         <tree.xmlHashScanner>_collectIdHashItemList, <python.PyObject*>context)
+        return items
+
+cdef void _collectIdHashItemList(void* payload, void* context, xmlChar* name) noexcept:
+    # collect elements from ID attribute hash table
+    cdef list lst
+    c_id = <tree.xmlID*>payload
+    if c_id is NULL or c_id.attr is NULL or c_id.attr.parent is NULL:
+        return
+    lst, doc = <tuple>context
+    element = _elementFactory(doc, c_id.attr.parent)
+    lst.append( (funicode(name), element) )
+
+cdef void _collectIdHashKeys(void* payload, void* collect_list, xmlChar* name) noexcept:
+    c_id = <tree.xmlID*>payload
+    if c_id is NULL or c_id.attr is NULL or c_id.attr.parent is NULL:
+        return
+    (<list>collect_list).append(funicode(name))
diff --git a/.venv/lib/python3.12/site-packages/lxml/xmlschema.pxi b/.venv/lib/python3.12/site-packages/lxml/xmlschema.pxi
new file mode 100644
index 00000000..ac5f9587
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/xmlschema.pxi
@@ -0,0 +1,215 @@
+#  support for XMLSchema validation
+from lxml.includes cimport xmlschema
+
+
+cdef class XMLSchemaError(LxmlError):
+    """Base class of all XML Schema errors
+    """
+
+cdef class XMLSchemaParseError(XMLSchemaError):
+    """Error while parsing an XML document as XML Schema.
+    """
+
+cdef class XMLSchemaValidateError(XMLSchemaError):
+    """Error while validating an XML document with an XML Schema.
+    """
+
+
+################################################################################
+# XMLSchema
+
+cdef XPath _check_for_default_attributes = XPath(
+    "boolean(//xs:attribute[@default or @fixed][1])",
+    namespaces={'xs': 'http://www.w3.org/2001/XMLSchema'})
+
+
+cdef class XMLSchema(_Validator):
+    """XMLSchema(self, etree=None, file=None)
+    Turn a document into an XML Schema validator.
+
+    Either pass a schema as Element or ElementTree, or pass a file or
+    filename through the ``file`` keyword argument.
+
+    Passing the ``attribute_defaults`` boolean option will make the
+    schema insert default/fixed attributes into validated documents.
+    """
+    cdef xmlschema.xmlSchema* _c_schema
+    cdef _Document _doc
+    cdef bint _has_default_attributes
+    cdef bint _add_attribute_defaults
+
+    def __cinit__(self):
+        self._has_default_attributes = True # play it safe
+        self._add_attribute_defaults = False
+
+    def __init__(self, etree=None, *, file=None, bint attribute_defaults=False):
+        cdef xmlschema.xmlSchemaParserCtxt* parser_ctxt
+        cdef xmlDoc* c_doc
+
+        self._add_attribute_defaults = attribute_defaults
+        _Validator.__init__(self)
+        c_doc = NULL
+        if etree is not None:
+            doc = _documentOrRaise(etree)
+            root_node = _rootNodeOrRaise(etree)
+            c_doc = _copyDocRoot(doc._c_doc, root_node._c_node)
+            self._doc = _documentFactory(c_doc, doc._parser)
+            parser_ctxt = xmlschema.xmlSchemaNewDocParserCtxt(c_doc)
+        elif file is not None:
+            file = _getFSPathOrObject(file)
+            if _isString(file):
+                filename = _encodeFilename(file)
+                parser_ctxt = xmlschema.xmlSchemaNewParserCtxt(_cstr(filename))
+            else:
+                self._doc = _parseDocument(file, None, None)
+                parser_ctxt = xmlschema.xmlSchemaNewDocParserCtxt(self._doc._c_doc)
+        else:
+            raise XMLSchemaParseError, "No tree or file given"
+
+        if parser_ctxt is NULL:
+            raise MemoryError()
+
+        # Need a cast here because older libxml2 releases do not use 'const' in the functype.
+        xmlschema.xmlSchemaSetParserStructuredErrors(
+            parser_ctxt, <xmlerror.xmlStructuredErrorFunc> _receiveError, <void*>self._error_log)
+        if self._doc is not None:
+            # calling xmlSchemaParse on a schema with imports or
+            # includes will cause libxml2 to create an internal
+            # context for parsing, so push an implied context to route
+            # resolve requests to the document's parser
+            __GLOBAL_PARSER_CONTEXT.pushImpliedContextFromParser(self._doc._parser)
+        with nogil:
+            orig_loader = _register_document_loader()
+            self._c_schema = xmlschema.xmlSchemaParse(parser_ctxt)
+            _reset_document_loader(orig_loader)
+        if self._doc is not None:
+            __GLOBAL_PARSER_CONTEXT.popImpliedContext()
+        xmlschema.xmlSchemaFreeParserCtxt(parser_ctxt)
+
+        if self._c_schema is NULL:
+            raise XMLSchemaParseError(
+                self._error_log._buildExceptionMessage(
+                    "Document is not valid XML Schema"),
+                self._error_log)
+
+        if self._doc is not None:
+            self._has_default_attributes = _check_for_default_attributes(self._doc)
+        self._add_attribute_defaults = attribute_defaults and self._has_default_attributes
+
+    def __dealloc__(self):
+        xmlschema.xmlSchemaFree(self._c_schema)
+
+    def __call__(self, etree):
+        """__call__(self, etree)
+
+        Validate doc using XML Schema.
+
+        Returns true if document is valid, false if not.
+        """
+        cdef xmlschema.xmlSchemaValidCtxt* valid_ctxt
+        cdef _Document doc
+        cdef _Element root_node
+        cdef xmlDoc* c_doc
+        cdef int ret
+
+        assert self._c_schema is not NULL, "Schema instance not initialised"
+        doc = _documentOrRaise(etree)
+        root_node = _rootNodeOrRaise(etree)
+
+        valid_ctxt = xmlschema.xmlSchemaNewValidCtxt(self._c_schema)
+        if valid_ctxt is NULL:
+            raise MemoryError()
+
+        try:
+            if self._add_attribute_defaults:
+                xmlschema.xmlSchemaSetValidOptions(
+                    valid_ctxt, xmlschema.XML_SCHEMA_VAL_VC_I_CREATE)
+
+            self._error_log.clear()
+            # Need a cast here because older libxml2 releases do not use 'const' in the functype.
+            xmlschema.xmlSchemaSetValidStructuredErrors(
+                valid_ctxt, <xmlerror.xmlStructuredErrorFunc> _receiveError, <void*>self._error_log)
+
+            c_doc = _fakeRootDoc(doc._c_doc, root_node._c_node)
+            with nogil:
+                ret = xmlschema.xmlSchemaValidateDoc(valid_ctxt, c_doc)
+            _destroyFakeDoc(doc._c_doc, c_doc)
+        finally:
+            xmlschema.xmlSchemaFreeValidCtxt(valid_ctxt)
+
+        if ret == -1:
+            raise XMLSchemaValidateError(
+                "Internal error in XML Schema validation.",
+                self._error_log)
+        if ret == 0:
+            return True
+        else:
+            return False
+
+    cdef _ParserSchemaValidationContext _newSaxValidator(
+            self, bint add_default_attributes):
+        cdef _ParserSchemaValidationContext context
+        context = _ParserSchemaValidationContext.__new__(_ParserSchemaValidationContext)
+        context._schema = self
+        context._add_default_attributes = (self._has_default_attributes and (
+            add_default_attributes or self._add_attribute_defaults))
+        return context
+
+@cython.final
+@cython.internal
+cdef class _ParserSchemaValidationContext:
+    cdef XMLSchema _schema
+    cdef xmlschema.xmlSchemaValidCtxt* _valid_ctxt
+    cdef xmlschema.xmlSchemaSAXPlugStruct* _sax_plug
+    cdef bint _add_default_attributes
+    def __cinit__(self):
+        self._valid_ctxt = NULL
+        self._sax_plug = NULL
+        self._add_default_attributes = False
+
+    def __dealloc__(self):
+        self.disconnect()
+        if self._valid_ctxt:
+            xmlschema.xmlSchemaFreeValidCtxt(self._valid_ctxt)
+
+    cdef _ParserSchemaValidationContext copy(self):
+        assert self._schema is not None, "_ParserSchemaValidationContext not initialised"
+        return self._schema._newSaxValidator(
+            self._add_default_attributes)
+
+    cdef void inject_default_attributes(self, xmlDoc* c_doc) noexcept:
+        # we currently need to insert default attributes manually
+        # after parsing, as libxml2 does not support this at parse
+        # time
+        if self._add_default_attributes:
+            with nogil:
+                xmlschema.xmlSchemaValidateDoc(self._valid_ctxt, c_doc)
+
+    cdef int connect(self, xmlparser.xmlParserCtxt* c_ctxt, _BaseErrorLog error_log) except -1:
+        if self._valid_ctxt is NULL:
+            self._valid_ctxt = xmlschema.xmlSchemaNewValidCtxt(
+                self._schema._c_schema)
+            if self._valid_ctxt is NULL:
+                raise MemoryError()
+            if self._add_default_attributes:
+                xmlschema.xmlSchemaSetValidOptions(
+                    self._valid_ctxt, xmlschema.XML_SCHEMA_VAL_VC_I_CREATE)
+        if error_log is not None:
+            # Need a cast here because older libxml2 releases do not use 'const' in the functype.
+            xmlschema.xmlSchemaSetValidStructuredErrors(
+                self._valid_ctxt, <xmlerror.xmlStructuredErrorFunc> _receiveError, <void*>error_log)
+        self._sax_plug = xmlschema.xmlSchemaSAXPlug(
+            self._valid_ctxt, &c_ctxt.sax, &c_ctxt.userData)
+
+    cdef void disconnect(self) noexcept:
+        if self._sax_plug is not NULL:
+            xmlschema.xmlSchemaSAXUnplug(self._sax_plug)
+            self._sax_plug = NULL
+        if self._valid_ctxt is not NULL:
+            xmlschema.xmlSchemaSetValidStructuredErrors(
+                self._valid_ctxt, NULL, NULL)
+
+    cdef bint isvalid(self) noexcept:
+        if self._valid_ctxt is NULL:
+            return 1 # valid
+        return xmlschema.xmlSchemaIsValid(self._valid_ctxt)
diff --git a/.venv/lib/python3.12/site-packages/lxml/xpath.pxi b/.venv/lib/python3.12/site-packages/lxml/xpath.pxi
new file mode 100644
index 00000000..352f6313
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/xpath.pxi
@@ -0,0 +1,487 @@
+# XPath evaluation
+
+class XPathSyntaxError(LxmlSyntaxError, XPathError):
+    pass
+
+################################################################################
+# XPath
+
+cdef object _XPATH_SYNTAX_ERRORS = (
+    xmlerror.XML_XPATH_NUMBER_ERROR,
+    xmlerror.XML_XPATH_UNFINISHED_LITERAL_ERROR,
+    xmlerror.XML_XPATH_VARIABLE_REF_ERROR,
+    xmlerror.XML_XPATH_INVALID_PREDICATE_ERROR,
+    xmlerror.XML_XPATH_UNCLOSED_ERROR,
+    xmlerror.XML_XPATH_INVALID_CHAR_ERROR
+)
+
+cdef object _XPATH_EVAL_ERRORS = (
+    xmlerror.XML_XPATH_UNDEF_VARIABLE_ERROR,
+    xmlerror.XML_XPATH_UNDEF_PREFIX_ERROR,
+    xmlerror.XML_XPATH_UNKNOWN_FUNC_ERROR,
+    xmlerror.XML_XPATH_INVALID_OPERAND,
+    xmlerror.XML_XPATH_INVALID_TYPE,
+    xmlerror.XML_XPATH_INVALID_ARITY,
+    xmlerror.XML_XPATH_INVALID_CTXT_SIZE,
+    xmlerror.XML_XPATH_INVALID_CTXT_POSITION
+)
+
+cdef int _register_xpath_function(void* ctxt, name_utf, ns_utf) noexcept:
+    if ns_utf is None:
+        return xpath.xmlXPathRegisterFunc(
+            <xpath.xmlXPathContext*>ctxt, _xcstr(name_utf),
+            _xpath_function_call)
+    else:
+        return xpath.xmlXPathRegisterFuncNS(
+            <xpath.xmlXPathContext*>ctxt, _xcstr(name_utf), _xcstr(ns_utf),
+            _xpath_function_call)
+
+cdef int _unregister_xpath_function(void* ctxt, name_utf, ns_utf) noexcept:
+    if ns_utf is None:
+        return xpath.xmlXPathRegisterFunc(
+            <xpath.xmlXPathContext*>ctxt, _xcstr(name_utf), NULL)
+    else:
+        return xpath.xmlXPathRegisterFuncNS(
+            <xpath.xmlXPathContext*>ctxt, _xcstr(name_utf), _xcstr(ns_utf), NULL)
+
+
+@cython.final
+@cython.internal
+cdef class _XPathContext(_BaseContext):
+    cdef object _variables
+    def __init__(self, namespaces, extensions, error_log, enable_regexp, variables,
+                 build_smart_strings):
+        self._variables = variables
+        _BaseContext.__init__(self, namespaces, extensions, error_log, enable_regexp,
+                              build_smart_strings)
+
+    cdef set_context(self, xpath.xmlXPathContext* xpathCtxt):
+        self._set_xpath_context(xpathCtxt)
+        # This would be a good place to set up the XPath parser dict, but
+        # we cannot use the current thread dict as we do not know which
+        # thread will execute the XPath evaluator - so, no dict for now.
+        self.registerLocalNamespaces()
+        self.registerLocalFunctions(xpathCtxt, _register_xpath_function)
+
+    cdef register_context(self, _Document doc):
+        self._register_context(doc)
+        self.registerGlobalNamespaces()
+        self.registerGlobalFunctions(self._xpathCtxt, _register_xpath_function)
+        self.registerExsltFunctions()
+        if self._variables is not None:
+            self.registerVariables(self._variables)
+
+    cdef unregister_context(self):
+        self.unregisterGlobalFunctions(
+            self._xpathCtxt, _unregister_xpath_function)
+        self.unregisterGlobalNamespaces()
+        xpath.xmlXPathRegisteredVariablesCleanup(self._xpathCtxt)
+        self._cleanup_context()
+
+    cdef void registerExsltFunctions(self) noexcept:
+        if xslt.LIBXSLT_VERSION < 10125:
+            # we'd only execute dummy functions anyway
+            return
+        tree.xmlHashScan(
+            self._xpathCtxt.nsHash, _registerExsltFunctionsForNamespaces,
+            self._xpathCtxt)
+
+    cdef registerVariables(self, variable_dict):
+        for name, value in variable_dict.items():
+            name_utf = self._to_utf(name)
+            xpath.xmlXPathRegisterVariable(
+                self._xpathCtxt, _xcstr(name_utf), _wrapXPathObject(value, None, None))
+
+    cdef registerVariable(self, name, value):
+        name_utf = self._to_utf(name)
+        xpath.xmlXPathRegisterVariable(
+            self._xpathCtxt, _xcstr(name_utf), _wrapXPathObject(value, None, None))
+
+
+cdef void _registerExsltFunctionsForNamespaces(
+        void* _c_href, void* _ctxt, const_xmlChar* c_prefix) noexcept:
+    c_href = <const_xmlChar*> _c_href
+    ctxt = <xpath.xmlXPathContext*> _ctxt
+
+    if tree.xmlStrcmp(c_href, xslt.EXSLT_DATE_NAMESPACE) == 0:
+        xslt.exsltDateXpathCtxtRegister(ctxt, c_prefix)
+    elif tree.xmlStrcmp(c_href, xslt.EXSLT_SETS_NAMESPACE) == 0:
+        xslt.exsltSetsXpathCtxtRegister(ctxt, c_prefix)
+    elif tree.xmlStrcmp(c_href, xslt.EXSLT_MATH_NAMESPACE) == 0:
+        xslt.exsltMathXpathCtxtRegister(ctxt, c_prefix)
+    elif tree.xmlStrcmp(c_href, xslt.EXSLT_STRINGS_NAMESPACE) == 0:
+        xslt.exsltStrXpathCtxtRegister(ctxt, c_prefix)
+
+
+cdef class _XPathEvaluatorBase:
+    cdef xpath.xmlXPathContext* _xpathCtxt
+    cdef _XPathContext _context
+    cdef python.PyThread_type_lock _eval_lock
+    cdef _ErrorLog _error_log
+    def __cinit__(self):
+        self._xpathCtxt = NULL
+        if config.ENABLE_THREADING:
+            self._eval_lock = python.PyThread_allocate_lock()
+            if self._eval_lock is NULL:
+                raise MemoryError()
+        self._error_log = _ErrorLog()
+
+    def __init__(self, namespaces, extensions, enable_regexp,
+                 smart_strings):
+        self._context = _XPathContext(namespaces, extensions, self._error_log,
+                                      enable_regexp, None, smart_strings)
+
+    @property
+    def error_log(self):
+        assert self._error_log is not None, "XPath evaluator not initialised"
+        return self._error_log.copy()
+
+    def __dealloc__(self):
+        if self._xpathCtxt is not NULL:
+            xpath.xmlXPathFreeContext(self._xpathCtxt)
+        if config.ENABLE_THREADING:
+            if self._eval_lock is not NULL:
+                python.PyThread_free_lock(self._eval_lock)
+
+    cdef set_context(self, xpath.xmlXPathContext* xpathCtxt):
+        self._xpathCtxt = xpathCtxt
+        self._context.set_context(xpathCtxt)
+
+    cdef bint _checkAbsolutePath(self, char* path) noexcept:
+        cdef char c
+        if path is NULL:
+            return 0
+        c = path[0]
+        while c == c' ' or c == c'\t':
+            path = path + 1
+            c = path[0]
+        return c == c'/'
+
+    @cython.final
+    cdef int _lock(self) except -1:
+        cdef int result
+        if config.ENABLE_THREADING and self._eval_lock != NULL:
+            with nogil:
+                result = python.PyThread_acquire_lock(
+                    self._eval_lock, python.WAIT_LOCK)
+            if result == 0:
+                raise XPathError, "XPath evaluator locking failed"
+        return 0
+
+    @cython.final
+    cdef void _unlock(self) noexcept:
+        if config.ENABLE_THREADING and self._eval_lock != NULL:
+            python.PyThread_release_lock(self._eval_lock)
+
+    cdef _build_parse_error(self):
+        cdef _BaseErrorLog entries
+        entries = self._error_log.filter_types(_XPATH_SYNTAX_ERRORS)
+        if entries:
+            message = entries._buildExceptionMessage(None)
+            if message is not None:
+                return XPathSyntaxError(message, self._error_log)
+        return XPathSyntaxError(
+            self._error_log._buildExceptionMessage("Error in xpath expression"),
+            self._error_log)
+
+    cdef _build_eval_error(self):
+        cdef _BaseErrorLog entries
+        entries = self._error_log.filter_types(_XPATH_EVAL_ERRORS)
+        if not entries:
+            entries = self._error_log.filter_types(_XPATH_SYNTAX_ERRORS)
+        if entries:
+            message = entries._buildExceptionMessage(None)
+            if message is not None:
+                return XPathEvalError(message, self._error_log)
+        return XPathEvalError(
+            self._error_log._buildExceptionMessage("Error in xpath expression"),
+            self._error_log)
+
+    cdef object _handle_result(self, xpath.xmlXPathObject* xpathObj, _Document doc):
+        if self._context._exc._has_raised():
+            if xpathObj is not NULL:
+                _freeXPathObject(xpathObj)
+                xpathObj = NULL
+            self._context._release_temp_refs()
+            self._context._exc._raise_if_stored()
+
+        if xpathObj is NULL:
+            self._context._release_temp_refs()
+            raise self._build_eval_error()
+
+        try:
+            result = _unwrapXPathObject(xpathObj, doc, self._context)
+        finally:
+            _freeXPathObject(xpathObj)
+            self._context._release_temp_refs()
+
+        return result
+
+
+cdef class XPathElementEvaluator(_XPathEvaluatorBase):
+    """XPathElementEvaluator(self, element, namespaces=None, extensions=None, regexp=True, smart_strings=True)
+    Create an XPath evaluator for an element.
+
+    Absolute XPath expressions (starting with '/') will be evaluated against
+    the ElementTree as returned by getroottree().
+
+    Additional namespace declarations can be passed with the
+    'namespace' keyword argument.  EXSLT regular expression support
+    can be disabled with the 'regexp' boolean keyword (defaults to
+    True).  Smart strings will be returned for string results unless
+    you pass ``smart_strings=False``.
+    """
+    cdef _Element _element
+    def __init__(self, _Element element not None, *, namespaces=None,
+                 extensions=None, regexp=True, smart_strings=True):
+        cdef xpath.xmlXPathContext* xpathCtxt
+        cdef int ns_register_status
+        cdef _Document doc
+        _assertValidNode(element)
+        _assertValidDoc(element._doc)
+        self._element = element
+        doc = element._doc
+        _XPathEvaluatorBase.__init__(self, namespaces, extensions,
+                                     regexp, smart_strings)
+        xpathCtxt = xpath.xmlXPathNewContext(doc._c_doc)
+        if xpathCtxt is NULL:
+            raise MemoryError()
+        self.set_context(xpathCtxt)
+
+    def register_namespace(self, prefix, uri):
+        """Register a namespace with the XPath context.
+        """
+        assert self._xpathCtxt is not NULL, "XPath context not initialised"
+        self._context.addNamespace(prefix, uri)
+
+    def register_namespaces(self, namespaces):
+        """Register a prefix -> uri dict.
+        """
+        assert self._xpathCtxt is not NULL, "XPath context not initialised"
+        for prefix, uri in namespaces.items():
+            self._context.addNamespace(prefix, uri)
+
+    def __call__(self, _path, **_variables):
+        """__call__(self, _path, **_variables)
+
+        Evaluate an XPath expression on the document.
+
+        Variables may be provided as keyword arguments.  Note that namespaces
+        are currently not supported for variables.
+
+        Absolute XPath expressions (starting with '/') will be evaluated
+        against the ElementTree as returned by getroottree().
+        """
+        cdef xpath.xmlXPathObject*  xpathObj
+        cdef _Document doc
+        assert self._xpathCtxt is not NULL, "XPath context not initialised"
+        path = _utf8(_path)
+        doc = self._element._doc
+
+        self._lock()
+        self._xpathCtxt.node = self._element._c_node
+        try:
+            self._context.register_context(doc)
+            self._context.registerVariables(_variables)
+            c_path = _xcstr(path)
+            with nogil:
+                xpathObj = xpath.xmlXPathEvalExpression(
+                    c_path, self._xpathCtxt)
+            result = self._handle_result(xpathObj, doc)
+        finally:
+            self._context.unregister_context()
+            self._unlock()
+
+        return result
+
+
+cdef class XPathDocumentEvaluator(XPathElementEvaluator):
+    """XPathDocumentEvaluator(self, etree, namespaces=None, extensions=None, regexp=True, smart_strings=True)
+    Create an XPath evaluator for an ElementTree.
+
+    Additional namespace declarations can be passed with the
+    'namespace' keyword argument.  EXSLT regular expression support
+    can be disabled with the 'regexp' boolean keyword (defaults to
+    True).  Smart strings will be returned for string results unless
+    you pass ``smart_strings=False``.
+    """
+    def __init__(self, _ElementTree etree not None, *, namespaces=None,
+                 extensions=None, regexp=True, smart_strings=True):
+        XPathElementEvaluator.__init__(
+            self, etree._context_node, namespaces=namespaces, 
+            extensions=extensions, regexp=regexp,
+            smart_strings=smart_strings)
+
+    def __call__(self, _path, **_variables):
+        """__call__(self, _path, **_variables)
+
+        Evaluate an XPath expression on the document.
+
+        Variables may be provided as keyword arguments.  Note that namespaces
+        are currently not supported for variables.
+        """
+        cdef xpath.xmlXPathObject*  xpathObj
+        cdef xmlDoc* c_doc
+        cdef _Document doc
+        assert self._xpathCtxt is not NULL, "XPath context not initialised"
+        path = _utf8(_path)
+        doc = self._element._doc
+
+        self._lock()
+        try:
+            self._context.register_context(doc)
+            c_doc = _fakeRootDoc(doc._c_doc, self._element._c_node)
+            try:
+                self._context.registerVariables(_variables)
+                c_path = _xcstr(path)
+                with nogil:
+                    self._xpathCtxt.doc  = c_doc
+                    self._xpathCtxt.node = tree.xmlDocGetRootElement(c_doc)
+                    xpathObj = xpath.xmlXPathEvalExpression(
+                        c_path, self._xpathCtxt)
+                result = self._handle_result(xpathObj, doc)
+            finally:
+                _destroyFakeDoc(doc._c_doc, c_doc)
+                self._context.unregister_context()
+        finally:
+            self._unlock()
+
+        return result
+
+
+def XPathEvaluator(etree_or_element, *, namespaces=None, extensions=None,
+                   regexp=True, smart_strings=True):
+    """XPathEvaluator(etree_or_element, namespaces=None, extensions=None, regexp=True, smart_strings=True)
+
+    Creates an XPath evaluator for an ElementTree or an Element.
+
+    The resulting object can be called with an XPath expression as argument
+    and XPath variables provided as keyword arguments.
+
+    Additional namespace declarations can be passed with the
+    'namespace' keyword argument.  EXSLT regular expression support
+    can be disabled with the 'regexp' boolean keyword (defaults to
+    True).  Smart strings will be returned for string results unless
+    you pass ``smart_strings=False``.
+    """
+    if isinstance(etree_or_element, _ElementTree):
+        return XPathDocumentEvaluator(
+            etree_or_element, namespaces=namespaces,
+            extensions=extensions, regexp=regexp, smart_strings=smart_strings)
+    else:
+        return XPathElementEvaluator(
+            etree_or_element, namespaces=namespaces,
+            extensions=extensions, regexp=regexp, smart_strings=smart_strings)
+
+
+cdef class XPath(_XPathEvaluatorBase):
+    """XPath(self, path, namespaces=None, extensions=None, regexp=True, smart_strings=True)
+    A compiled XPath expression that can be called on Elements and ElementTrees.
+
+    Besides the XPath expression, you can pass prefix-namespace
+    mappings and extension functions to the constructor through the
+    keyword arguments ``namespaces`` and ``extensions``.  EXSLT
+    regular expression support can be disabled with the 'regexp'
+    boolean keyword (defaults to True).  Smart strings will be
+    returned for string results unless you pass
+    ``smart_strings=False``.
+    """
+    cdef xpath.xmlXPathCompExpr* _xpath
+    cdef bytes _path
+    def __cinit__(self):
+        self._xpath = NULL
+
+    def __init__(self, path, *, namespaces=None, extensions=None,
+                 regexp=True, smart_strings=True):
+        cdef xpath.xmlXPathContext* xpathCtxt
+        _XPathEvaluatorBase.__init__(self, namespaces, extensions,
+                                     regexp, smart_strings)
+        self._path = _utf8(path)
+        xpathCtxt = xpath.xmlXPathNewContext(NULL)
+        if xpathCtxt is NULL:
+            raise MemoryError()
+        self.set_context(xpathCtxt)
+        self._xpath = xpath.xmlXPathCtxtCompile(xpathCtxt, _xcstr(self._path))
+        if self._xpath is NULL:
+            raise self._build_parse_error()
+
+    def __call__(self, _etree_or_element, **_variables):
+        "__call__(self, _etree_or_element, **_variables)"
+        cdef xpath.xmlXPathObject*  xpathObj
+        cdef _Document document
+        cdef _Element element
+
+        assert self._xpathCtxt is not NULL, "XPath context not initialised"
+        document = _documentOrRaise(_etree_or_element)
+        element  = _rootNodeOrRaise(_etree_or_element)
+
+        self._lock()
+        self._xpathCtxt.doc  = document._c_doc
+        self._xpathCtxt.node = element._c_node
+
+        try:
+            self._context.register_context(document)
+            self._context.registerVariables(_variables)
+            with nogil:
+                xpathObj = xpath.xmlXPathCompiledEval(
+                    self._xpath, self._xpathCtxt)
+            result = self._handle_result(xpathObj, document)
+        finally:
+            self._context.unregister_context()
+            self._unlock()
+        return result
+
+    @property
+    def path(self):
+        """The literal XPath expression.
+        """
+        return self._path.decode('UTF-8')
+
+    def __dealloc__(self):
+        if self._xpath is not NULL:
+            xpath.xmlXPathFreeCompExpr(self._xpath)
+
+    def __repr__(self):
+        return self.path
+
+
+cdef object _replace_strings = re.compile(b'("[^"]*")|(\'[^\']*\')').sub
+cdef object _find_namespaces = re.compile(b'({[^}]+})').findall
+
+cdef class ETXPath(XPath):
+    """ETXPath(self, path, extensions=None, regexp=True, smart_strings=True)
+    Special XPath class that supports the ElementTree {uri} notation for namespaces.
+
+    Note that this class does not accept the ``namespace`` keyword
+    argument. All namespaces must be passed as part of the path
+    string.  Smart strings will be returned for string results unless
+    you pass ``smart_strings=False``.
+    """
+    def __init__(self, path, *, extensions=None, regexp=True,
+                 smart_strings=True):
+        path, namespaces = self._nsextract_path(path)
+        XPath.__init__(self, path, namespaces=namespaces,
+                       extensions=extensions, regexp=regexp,
+                       smart_strings=smart_strings)
+
+    cdef _nsextract_path(self, path):
+        # replace {namespaces} by new prefixes
+        cdef dict namespaces = {}
+        cdef list namespace_defs = []
+        cdef int i
+        path_utf = _utf8(path)
+        stripped_path = _replace_strings(b'', path_utf) # remove string literals
+        i = 1
+        for namespace_def in _find_namespaces(stripped_path):
+            if namespace_def not in namespace_defs:
+                prefix = python.PyBytes_FromFormat("__xpp%02d", i)
+                i += 1
+                namespace_defs.append(namespace_def)
+                namespace = namespace_def[1:-1] # remove '{}'
+                namespace = (<bytes>namespace).decode('utf8')
+                namespaces[prefix.decode('utf8')] = namespace
+                prefix_str = prefix + b':'
+                # FIXME: this also replaces {namespaces} within strings!
+                path_utf = path_utf.replace(namespace_def, prefix_str)
+        path = path_utf.decode('utf8')
+        return path, namespaces
diff --git a/.venv/lib/python3.12/site-packages/lxml/xslt.pxi b/.venv/lib/python3.12/site-packages/lxml/xslt.pxi
new file mode 100644
index 00000000..f7a7be29
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/xslt.pxi
@@ -0,0 +1,950 @@
+# XSLT
+from lxml.includes cimport xslt
+
+
+cdef class XSLTError(LxmlError):
+    """Base class of all XSLT errors.
+    """
+
+cdef class XSLTParseError(XSLTError):
+    """Error parsing a stylesheet document.
+    """
+
+cdef class XSLTApplyError(XSLTError):
+    """Error running an XSL transformation.
+    """
+
+class XSLTSaveError(XSLTError, SerialisationError):
+    """Error serialising an XSLT result.
+    """
+
+cdef class XSLTExtensionError(XSLTError):
+    """Error registering an XSLT extension.
+    """
+
+
+# version information
+LIBXSLT_COMPILED_VERSION = __unpackIntVersion(xslt.LIBXSLT_VERSION)
+LIBXSLT_VERSION = __unpackIntVersion(xslt.xsltLibxsltVersion)
+
+
+################################################################################
+# Where do we store what?
+#
+# xsltStylesheet->doc->_private
+#    == _XSLTResolverContext for XSL stylesheet
+#
+# xsltTransformContext->_private
+#    == _XSLTResolverContext for transformed document
+#
+################################################################################
+
+
+################################################################################
+# XSLT document loaders
+
+@cython.final
+@cython.internal
+cdef class _XSLTResolverContext(_ResolverContext):
+    cdef xmlDoc* _c_style_doc
+    cdef _BaseParser _parser
+
+    cdef _XSLTResolverContext _copy(self):
+        cdef _XSLTResolverContext context
+        context = _XSLTResolverContext()
+        _initXSLTResolverContext(context, self._parser)
+        context._c_style_doc = self._c_style_doc
+        return context
+
+cdef _initXSLTResolverContext(_XSLTResolverContext context,
+                              _BaseParser parser):
+    _initResolverContext(context, parser.resolvers)
+    context._parser = parser
+    context._c_style_doc = NULL
+
+cdef xmlDoc* _xslt_resolve_from_python(const_xmlChar* c_uri, void* c_context,
+                                       int parse_options, int* error) with gil:
+    # call the Python document loaders
+    cdef _XSLTResolverContext context
+    cdef _ResolverRegistry resolvers
+    cdef _InputDocument doc_ref
+    cdef xmlDoc* c_doc
+    cdef xmlDoc* c_return_doc = NULL
+
+    error[0] = 0
+    context = <_XSLTResolverContext>c_context
+
+    # shortcut if we resolve the stylesheet itself
+    c_doc = context._c_style_doc
+    try:
+        if c_doc is not NULL and c_doc.URL is not NULL:
+            if tree.xmlStrcmp(c_uri, c_doc.URL) == 0:
+                c_return_doc = _copyDoc(c_doc, 1)
+                return c_return_doc  # 'goto', see 'finally' below
+
+        # delegate to the Python resolvers
+        resolvers = context._resolvers
+        if tree.xmlStrncmp(<unsigned char*>'string://__STRING__XSLT__/', c_uri, 26) == 0:
+            c_uri += 26
+        uri = _decodeFilename(c_uri)
+        doc_ref = resolvers.resolve(uri, None, context)
+
+        if doc_ref is not None:
+            if doc_ref._type == PARSER_DATA_STRING:
+                c_return_doc = _parseDoc(
+                    doc_ref._data_bytes, doc_ref._filename, context._parser)
+            elif doc_ref._type == PARSER_DATA_FILENAME:
+                c_return_doc = _parseDocFromFile(
+                    doc_ref._filename, context._parser)
+            elif doc_ref._type == PARSER_DATA_FILE:
+                c_return_doc = _parseDocFromFilelike(
+                    doc_ref._file, doc_ref._filename, context._parser)
+            elif doc_ref._type == PARSER_DATA_EMPTY:
+                c_return_doc = _newXMLDoc()
+            if c_return_doc is not NULL and c_return_doc.URL is NULL:
+                c_return_doc.URL = tree.xmlStrdup(c_uri)
+    except:
+        error[0] = 1
+        context._store_raised()
+    finally:
+        return c_return_doc  # and swallow any further exceptions
+
+
+cdef void _xslt_store_resolver_exception(const_xmlChar* c_uri, void* context,
+                                         xslt.xsltLoadType c_type) noexcept with gil:
+    try:
+        message = f"Cannot resolve URI {_decodeFilename(c_uri)}"
+        if c_type == xslt.XSLT_LOAD_DOCUMENT:
+            exception = XSLTApplyError(message)
+        else:
+            exception = XSLTParseError(message)
+        (<_XSLTResolverContext>context)._store_exception(exception)
+    except BaseException as e:
+        (<_XSLTResolverContext>context)._store_exception(e)
+    finally:
+        return  # and swallow any further exceptions
+
+
+cdef xmlDoc* _xslt_doc_loader(const_xmlChar* c_uri, tree.xmlDict* c_dict,
+                              int parse_options, void* c_ctxt,
+                              xslt.xsltLoadType c_type) noexcept nogil:
+    # nogil => no Python objects here, may be called without thread context !
+    cdef xmlDoc* c_doc
+    cdef xmlDoc* result
+    cdef void* c_pcontext
+    cdef int error = 0
+    # find resolver contexts of stylesheet and transformed doc
+    if c_type == xslt.XSLT_LOAD_DOCUMENT:
+        # transformation time
+        c_pcontext = (<xslt.xsltTransformContext*>c_ctxt)._private
+    elif c_type == xslt.XSLT_LOAD_STYLESHEET:
+        # include/import resolution while parsing
+        c_pcontext = (<xslt.xsltStylesheet*>c_ctxt).doc._private
+    else:
+        c_pcontext = NULL
+
+    if c_pcontext is NULL:
+        # can't call Python without context, fall back to default loader
+        return XSLT_DOC_DEFAULT_LOADER(
+            c_uri, c_dict, parse_options, c_ctxt, c_type)
+
+    c_doc = _xslt_resolve_from_python(c_uri, c_pcontext, parse_options, &error)
+    if c_doc is NULL and not error:
+        c_doc = XSLT_DOC_DEFAULT_LOADER(
+            c_uri, c_dict, parse_options, c_ctxt, c_type)
+        if c_doc is NULL:
+            _xslt_store_resolver_exception(c_uri, c_pcontext, c_type)
+
+    if c_doc is not NULL and c_type == xslt.XSLT_LOAD_STYLESHEET:
+        c_doc._private = c_pcontext
+    return c_doc
+
+cdef xslt.xsltDocLoaderFunc XSLT_DOC_DEFAULT_LOADER = xslt.xsltDocDefaultLoader
+xslt.xsltSetLoaderFunc(<xslt.xsltDocLoaderFunc>_xslt_doc_loader)
+
+################################################################################
+# XSLT file/network access control
+
+cdef class XSLTAccessControl:
+    """XSLTAccessControl(self, read_file=True, write_file=True, create_dir=True, read_network=True, write_network=True)
+
+    Access control for XSLT: reading/writing files, directories and
+    network I/O.  Access to a type of resource is granted or denied by
+    passing any of the following boolean keyword arguments.  All of
+    them default to True to allow access.
+
+    - read_file
+    - write_file
+    - create_dir
+    - read_network
+    - write_network
+
+    For convenience, there is also a class member `DENY_ALL` that
+    provides an XSLTAccessControl instance that is readily configured
+    to deny everything, and a `DENY_WRITE` member that denies all
+    write access but allows read access.
+
+    See `XSLT`.
+    """
+    cdef xslt.xsltSecurityPrefs* _prefs
+    def __cinit__(self):
+        self._prefs = xslt.xsltNewSecurityPrefs()
+        if self._prefs is NULL:
+            raise MemoryError()
+
+    def __init__(self, *, bint read_file=True, bint write_file=True, bint create_dir=True,
+                 bint read_network=True, bint write_network=True):
+        self._setAccess(xslt.XSLT_SECPREF_READ_FILE, read_file)
+        self._setAccess(xslt.XSLT_SECPREF_WRITE_FILE, write_file)
+        self._setAccess(xslt.XSLT_SECPREF_CREATE_DIRECTORY, create_dir)
+        self._setAccess(xslt.XSLT_SECPREF_READ_NETWORK, read_network)
+        self._setAccess(xslt.XSLT_SECPREF_WRITE_NETWORK, write_network)
+
+    DENY_ALL = XSLTAccessControl(
+        read_file=False, write_file=False, create_dir=False,
+        read_network=False, write_network=False)
+
+    DENY_WRITE = XSLTAccessControl(
+        read_file=True, write_file=False, create_dir=False,
+        read_network=True, write_network=False)
+
+    def __dealloc__(self):
+        if self._prefs is not NULL:
+            xslt.xsltFreeSecurityPrefs(self._prefs)
+
+    @cython.final
+    cdef _setAccess(self, xslt.xsltSecurityOption option, bint allow):
+        cdef xslt.xsltSecurityCheck function
+        if allow:
+            function = xslt.xsltSecurityAllow
+        else:
+            function = xslt.xsltSecurityForbid
+        xslt.xsltSetSecurityPrefs(self._prefs, option, function)
+
+    @cython.final
+    cdef void _register_in_context(self, xslt.xsltTransformContext* ctxt) noexcept:
+        xslt.xsltSetCtxtSecurityPrefs(self._prefs, ctxt)
+
+    @property
+    def options(self):
+        """The access control configuration as a map of options."""
+        return {
+            'read_file': self._optval(xslt.XSLT_SECPREF_READ_FILE),
+            'write_file': self._optval(xslt.XSLT_SECPREF_WRITE_FILE),
+            'create_dir': self._optval(xslt.XSLT_SECPREF_CREATE_DIRECTORY),
+            'read_network': self._optval(xslt.XSLT_SECPREF_READ_NETWORK),
+            'write_network': self._optval(xslt.XSLT_SECPREF_WRITE_NETWORK),
+        }
+
+    @cython.final
+    cdef _optval(self, xslt.xsltSecurityOption option):
+        cdef xslt.xsltSecurityCheck function
+        function = xslt.xsltGetSecurityPrefs(self._prefs, option)
+        if function is <xslt.xsltSecurityCheck>xslt.xsltSecurityAllow:
+            return True
+        elif function is <xslt.xsltSecurityCheck>xslt.xsltSecurityForbid:
+            return False
+        else:
+            return None
+
+    def __repr__(self):
+        items = sorted(self.options.items())
+        return "%s(%s)" % (
+            python._fqtypename(self).decode('UTF-8').split('.')[-1],
+            ', '.join(["%s=%r" % item for item in items]))
+
+################################################################################
+# XSLT
+
+cdef int _register_xslt_function(void* ctxt, name_utf, ns_utf) noexcept:
+    if ns_utf is None:
+        return 0
+    # libxml2 internalises the strings if ctxt has a dict
+    return xslt.xsltRegisterExtFunction(
+        <xslt.xsltTransformContext*>ctxt, _xcstr(name_utf), _xcstr(ns_utf),
+        <xslt.xmlXPathFunction>_xpath_function_call)
+
+cdef dict EMPTY_DICT = {}
+
+@cython.final
+@cython.internal
+cdef class _XSLTContext(_BaseContext):
+    cdef xslt.xsltTransformContext* _xsltCtxt
+    cdef _ReadOnlyElementProxy _extension_element_proxy
+    cdef dict _extension_elements
+    def __cinit__(self):
+        self._xsltCtxt = NULL
+        self._extension_elements = EMPTY_DICT
+
+    def __init__(self, namespaces, extensions, error_log, enable_regexp,
+                 build_smart_strings):
+        if extensions is not None and extensions:
+            for ns_name_tuple, extension in extensions.items():
+                if ns_name_tuple[0] is None:
+                    raise XSLTExtensionError, \
+                        "extensions must not have empty namespaces"
+                if isinstance(extension, XSLTExtension):
+                    if self._extension_elements is EMPTY_DICT:
+                        self._extension_elements = {}
+                        extensions = extensions.copy()
+                    ns_utf   = _utf8(ns_name_tuple[0])
+                    name_utf = _utf8(ns_name_tuple[1])
+                    self._extension_elements[(ns_utf, name_utf)] = extension
+                    del extensions[ns_name_tuple]
+        _BaseContext.__init__(self, namespaces, extensions, error_log, enable_regexp,
+                              build_smart_strings)
+
+    cdef _BaseContext _copy(self):
+        cdef _XSLTContext context
+        context = <_XSLTContext>_BaseContext._copy(self)
+        context._extension_elements = self._extension_elements
+        return context
+
+    cdef register_context(self, xslt.xsltTransformContext* xsltCtxt,
+                               _Document doc):
+        self._xsltCtxt = xsltCtxt
+        self._set_xpath_context(xsltCtxt.xpathCtxt)
+        self._register_context(doc)
+        self.registerLocalFunctions(xsltCtxt, _register_xslt_function)
+        self.registerGlobalFunctions(xsltCtxt, _register_xslt_function)
+        _registerXSLTExtensions(xsltCtxt, self._extension_elements)
+
+    cdef free_context(self):
+        self._cleanup_context()
+        self._release_context()
+        if self._xsltCtxt is not NULL:
+            xslt.xsltFreeTransformContext(self._xsltCtxt)
+            self._xsltCtxt = NULL
+        self._release_temp_refs()
+
+
+@cython.final
+@cython.internal
+@cython.freelist(8)
+cdef class _XSLTQuotedStringParam:
+    """A wrapper class for literal XSLT string parameters that require
+    quote escaping.
+    """
+    cdef bytes strval
+    def __cinit__(self, strval):
+        self.strval = _utf8(strval)
+
+
+@cython.no_gc_clear
+cdef class XSLT:
+    """XSLT(self, xslt_input, extensions=None, regexp=True, access_control=None)
+
+    Turn an XSL document into an XSLT object.
+
+    Calling this object on a tree or Element will execute the XSLT::
+
+        transform = etree.XSLT(xsl_tree)
+        result = transform(xml_tree)
+
+    Keyword arguments of the constructor:
+
+    - extensions: a dict mapping ``(namespace, name)`` pairs to
+      extension functions or extension elements
+    - regexp: enable exslt regular expression support in XPath
+      (default: True)
+    - access_control: access restrictions for network or file
+      system (see `XSLTAccessControl`)
+
+    Keyword arguments of the XSLT call:
+
+    - profile_run: enable XSLT profiling and make the profile available
+      as XML document in ``result.xslt_profile`` (default: False)
+
+    Other keyword arguments of the call are passed to the stylesheet
+    as parameters.
+    """
+    cdef _XSLTContext _context
+    cdef xslt.xsltStylesheet* _c_style
+    cdef _XSLTResolverContext _xslt_resolver_context
+    cdef XSLTAccessControl _access_control
+    cdef _ErrorLog _error_log
+
+    def __cinit__(self):
+        self._c_style = NULL
+
+    def __init__(self, xslt_input, *, extensions=None, regexp=True,
+                 access_control=None):
+        cdef xslt.xsltStylesheet* c_style = NULL
+        cdef xmlDoc* c_doc
+        cdef _Document doc
+        cdef _Element root_node
+
+        doc = _documentOrRaise(xslt_input)
+        root_node = _rootNodeOrRaise(xslt_input)
+
+        # set access control or raise TypeError
+        self._access_control = access_control
+
+        # make a copy of the document as stylesheet parsing modifies it
+        c_doc = _copyDocRoot(doc._c_doc, root_node._c_node)
+
+        # make sure we always have a stylesheet URL
+        if c_doc.URL is NULL:
+            doc_url_utf = python.PyUnicode_AsASCIIString(
+                f"string://__STRING__XSLT__/{id(self)}.xslt")
+            c_doc.URL = tree.xmlStrdup(_xcstr(doc_url_utf))
+
+        self._error_log = _ErrorLog()
+        self._xslt_resolver_context = _XSLTResolverContext()
+        _initXSLTResolverContext(self._xslt_resolver_context, doc._parser)
+        # keep a copy in case we need to access the stylesheet via 'document()'
+        self._xslt_resolver_context._c_style_doc = _copyDoc(c_doc, 1)
+        c_doc._private = <python.PyObject*>self._xslt_resolver_context
+
+        with self._error_log:
+            orig_loader = _register_document_loader()
+            c_style = xslt.xsltParseStylesheetDoc(c_doc)
+            _reset_document_loader(orig_loader)
+
+        if c_style is NULL or c_style.errors:
+            tree.xmlFreeDoc(c_doc)
+            if c_style is not NULL:
+                xslt.xsltFreeStylesheet(c_style)
+            self._xslt_resolver_context._raise_if_stored()
+            # last error seems to be the most accurate here
+            if self._error_log.last_error is not None and \
+                    self._error_log.last_error.message:
+                raise XSLTParseError(self._error_log.last_error.message,
+                                     self._error_log)
+            else:
+                raise XSLTParseError(
+                    self._error_log._buildExceptionMessage(
+                        "Cannot parse stylesheet"),
+                    self._error_log)
+
+        c_doc._private = NULL # no longer used!
+        self._c_style = c_style
+        self._context = _XSLTContext(None, extensions, self._error_log, regexp, True)
+
+    def __dealloc__(self):
+        if self._xslt_resolver_context is not None and \
+               self._xslt_resolver_context._c_style_doc is not NULL:
+            tree.xmlFreeDoc(self._xslt_resolver_context._c_style_doc)
+        # this cleans up the doc copy as well
+        if self._c_style is not NULL:
+            xslt.xsltFreeStylesheet(self._c_style)
+
+    @property
+    def error_log(self):
+        """The log of errors and warnings of an XSLT execution."""
+        return self._error_log.copy()
+
+    @staticmethod
+    def strparam(strval):
+        """strparam(strval)
+
+        Mark an XSLT string parameter that requires quote escaping
+        before passing it into the transformation.  Use it like this::
+
+            result = transform(doc, some_strval = XSLT.strparam(
+                '''it's \"Monty Python's\" ...'''))
+
+        Escaped string parameters can be reused without restriction.
+        """
+        return _XSLTQuotedStringParam(strval)
+
+    @staticmethod
+    def set_global_max_depth(int max_depth):
+        """set_global_max_depth(max_depth)
+
+        The maximum traversal depth that the stylesheet engine will allow.
+        This does not only count the template recursion depth but also takes
+        the number of variables/parameters into account.  The required setting
+        for a run depends on both the stylesheet and the input data.
+
+        Example::
+
+            XSLT.set_global_max_depth(5000)
+
+        Note that this is currently a global, module-wide setting because
+        libxslt does not support it at a per-stylesheet level.
+        """
+        if max_depth < 0:
+            raise ValueError("cannot set a maximum stylesheet traversal depth < 0")
+        xslt.xsltMaxDepth = max_depth
+
+    def tostring(self, _ElementTree result_tree):
+        """tostring(self, result_tree)
+
+        Save result doc to string based on stylesheet output method.
+
+        :deprecated: use str(result_tree) instead.
+        """
+        return str(result_tree)
+
+    def __deepcopy__(self, memo):
+        return self.__copy__()
+
+    def __copy__(self):
+        return _copyXSLT(self)
+
+    def __call__(self, _input, *, profile_run=False, **kw):
+        """__call__(self, _input, profile_run=False, **kw)
+
+        Execute the XSL transformation on a tree or Element.
+
+        Pass the ``profile_run`` option to get profile information
+        about the XSLT.  The result of the XSLT will have a property
+        xslt_profile that holds an XML tree with profiling data.
+        """
+        cdef _XSLTContext context = None
+        cdef _XSLTResolverContext resolver_context
+        cdef _Document input_doc
+        cdef _Element root_node
+        cdef _Document result_doc
+        cdef _Document profile_doc = None
+        cdef xmlDoc* c_profile_doc
+        cdef xslt.xsltTransformContext* transform_ctxt
+        cdef xmlDoc* c_result = NULL
+        cdef xmlDoc* c_doc
+        cdef tree.xmlDict* c_dict
+        cdef const_char** params = NULL
+
+        assert self._c_style is not NULL, "XSLT stylesheet not initialised"
+        input_doc = _documentOrRaise(_input)
+        root_node = _rootNodeOrRaise(_input)
+
+        c_doc = _fakeRootDoc(input_doc._c_doc, root_node._c_node)
+
+        transform_ctxt = xslt.xsltNewTransformContext(self._c_style, c_doc)
+        if transform_ctxt is NULL:
+            _destroyFakeDoc(input_doc._c_doc, c_doc)
+            raise MemoryError()
+
+        # using the stylesheet dict is safer than using a possibly
+        # unrelated dict from the current thread.  Almost all
+        # non-input tag/attr names will come from the stylesheet
+        # anyway.
+        if transform_ctxt.dict is not NULL:
+            xmlparser.xmlDictFree(transform_ctxt.dict)
+        if kw:
+            # parameter values are stored in the dict
+            # => avoid unnecessarily cluttering the global dict
+            transform_ctxt.dict = xmlparser.xmlDictCreateSub(self._c_style.doc.dict)
+            if transform_ctxt.dict is NULL:
+                xslt.xsltFreeTransformContext(transform_ctxt)
+                raise MemoryError()
+        else:
+            transform_ctxt.dict = self._c_style.doc.dict
+            xmlparser.xmlDictReference(transform_ctxt.dict)
+
+        xslt.xsltSetCtxtParseOptions(
+            transform_ctxt, input_doc._parser._parse_options)
+
+        if profile_run:
+            transform_ctxt.profile = 1
+
+        try:
+            context = self._context._copy()
+            context.register_context(transform_ctxt, input_doc)
+
+            resolver_context = self._xslt_resolver_context._copy()
+            transform_ctxt._private = <python.PyObject*>resolver_context
+
+            _convert_xslt_parameters(transform_ctxt, kw, &params)
+            c_result = self._run_transform(
+                c_doc, params, context, transform_ctxt)
+            if params is not NULL:
+                # deallocate space for parameters
+                python.lxml_free(params)
+
+            if transform_ctxt.state != xslt.XSLT_STATE_OK:
+                if c_result is not NULL:
+                    tree.xmlFreeDoc(c_result)
+                    c_result = NULL
+
+            if transform_ctxt.profile:
+                c_profile_doc = xslt.xsltGetProfileInformation(transform_ctxt)
+                if c_profile_doc is not NULL:
+                    profile_doc = _documentFactory(
+                        c_profile_doc, input_doc._parser)
+        finally:
+            if context is not None:
+                context.free_context()
+            _destroyFakeDoc(input_doc._c_doc, c_doc)
+
+        try:
+            if resolver_context is not None and resolver_context._has_raised():
+                if c_result is not NULL:
+                    tree.xmlFreeDoc(c_result)
+                    c_result = NULL
+                resolver_context._raise_if_stored()
+
+            if context._exc._has_raised():
+                if c_result is not NULL:
+                    tree.xmlFreeDoc(c_result)
+                    c_result = NULL
+                context._exc._raise_if_stored()
+
+            if c_result is NULL:
+                # last error seems to be the most accurate here
+                error = self._error_log.last_error
+                if error is not None and error.message:
+                    if error.line > 0:
+                        message = f"{error.message}, line {error.line}"
+                    else:
+                        message = error.message
+                elif error is not None and error.line > 0:
+                    message = f"Error applying stylesheet, line {error.line}"
+                else:
+                    message = "Error applying stylesheet"
+                raise XSLTApplyError(message, self._error_log)
+        finally:
+            if resolver_context is not None:
+                resolver_context.clear()
+
+        result_doc = _documentFactory(c_result, input_doc._parser)
+
+        c_dict = c_result.dict
+        xmlparser.xmlDictReference(c_dict)
+        __GLOBAL_PARSER_CONTEXT.initThreadDictRef(&c_result.dict)
+        if c_dict is not c_result.dict or \
+                self._c_style.doc.dict is not c_result.dict or \
+                input_doc._c_doc.dict is not c_result.dict:
+            with nogil:
+                if c_dict is not c_result.dict:
+                    fixThreadDictNames(<xmlNode*>c_result,
+                                       c_dict, c_result.dict)
+                if self._c_style.doc.dict is not c_result.dict:
+                    fixThreadDictNames(<xmlNode*>c_result,
+                                       self._c_style.doc.dict, c_result.dict)
+                if input_doc._c_doc.dict is not c_result.dict:
+                    fixThreadDictNames(<xmlNode*>c_result,
+                                       input_doc._c_doc.dict, c_result.dict)
+        xmlparser.xmlDictFree(c_dict)
+
+        return _xsltResultTreeFactory(result_doc, self, profile_doc)
+
+    cdef xmlDoc* _run_transform(self, xmlDoc* c_input_doc,
+                                const_char** params, _XSLTContext context,
+                                xslt.xsltTransformContext* transform_ctxt):
+        cdef xmlDoc* c_result
+        xslt.xsltSetTransformErrorFunc(transform_ctxt, <void*>self._error_log,
+                                       <xmlerror.xmlGenericErrorFunc>_receiveXSLTError)
+        if self._access_control is not None:
+            self._access_control._register_in_context(transform_ctxt)
+        with self._error_log, nogil:
+            orig_loader = _register_document_loader()
+            c_result = xslt.xsltApplyStylesheetUser(
+                self._c_style, c_input_doc, params, NULL, NULL, transform_ctxt)
+            _reset_document_loader(orig_loader)
+        return c_result
+
+
+cdef _convert_xslt_parameters(xslt.xsltTransformContext* transform_ctxt,
+                              dict parameters, const_char*** params_ptr):
+    cdef Py_ssize_t i, parameter_count
+    cdef const_char** params
+    cdef tree.xmlDict* c_dict = transform_ctxt.dict
+    params_ptr[0] = NULL
+    parameter_count = len(parameters)
+    if parameter_count == 0:
+        return
+    # allocate space for parameters
+    # * 2 as we want an entry for both key and value,
+    # and + 1 as array is NULL terminated
+    params = <const_char**>python.lxml_malloc(parameter_count * 2 + 1, sizeof(const_char*))
+    if not params:
+        raise MemoryError()
+    try:
+        i = 0
+        for key, value in parameters.iteritems():
+            k = _utf8(key)
+            if isinstance(value, _XSLTQuotedStringParam):
+                v = (<_XSLTQuotedStringParam>value).strval
+                xslt.xsltQuoteOneUserParam(
+                    transform_ctxt, _xcstr(k), _xcstr(v))
+            else:
+                if isinstance(value, XPath):
+                    v = (<XPath>value)._path
+                else:
+                    v = _utf8(value)
+                params[i] = <const_char*>tree.xmlDictLookup(c_dict, _xcstr(k), len(k))
+                i += 1
+                params[i] = <const_char*>tree.xmlDictLookup(c_dict, _xcstr(v), len(v))
+                i += 1
+    except:
+        python.lxml_free(params)
+        raise
+    params[i] = NULL
+    params_ptr[0] = params
+
+cdef XSLT _copyXSLT(XSLT stylesheet):
+    cdef XSLT new_xslt
+    cdef xmlDoc* c_doc
+    assert stylesheet._c_style is not NULL, "XSLT stylesheet not initialised"
+    new_xslt = XSLT.__new__(XSLT)
+    new_xslt._access_control = stylesheet._access_control
+    new_xslt._error_log = _ErrorLog()
+    new_xslt._context = stylesheet._context._copy()
+
+    new_xslt._xslt_resolver_context = stylesheet._xslt_resolver_context._copy()
+    new_xslt._xslt_resolver_context._c_style_doc = _copyDoc(
+        stylesheet._xslt_resolver_context._c_style_doc, 1)
+
+    c_doc = _copyDoc(stylesheet._c_style.doc, 1)
+    new_xslt._c_style = xslt.xsltParseStylesheetDoc(c_doc)
+    if new_xslt._c_style is NULL:
+        tree.xmlFreeDoc(c_doc)
+        raise MemoryError()
+
+    return new_xslt
+
+@cython.final
+cdef class _XSLTResultTree(_ElementTree):
+    """The result of an XSLT evaluation.
+
+    Use ``str()`` or ``bytes()`` (or ``unicode()`` in Python 2.x) to serialise to a string,
+    and the ``.write_output()`` method to write serialise to a file.
+    """
+    cdef XSLT _xslt
+    cdef _Document _profile
+    cdef xmlChar* _buffer
+    cdef Py_ssize_t _buffer_len
+    cdef Py_ssize_t _buffer_refcnt
+
+    def write_output(self, file, *, compression=0):
+        """write_output(self, file, *, compression=0)
+
+        Serialise the XSLT output to a file or file-like object.
+
+        As opposed to the generic ``.write()`` method, ``.write_output()`` serialises
+        the result as defined by the ``<xsl:output>`` tag.
+        """
+        cdef _FilelikeWriter writer = None
+        cdef _Document doc
+        cdef int r, rclose, c_compression
+        cdef const_xmlChar* c_encoding = NULL
+        cdef tree.xmlOutputBuffer* c_buffer
+
+        if self._context_node is not None:
+            doc = self._context_node._doc
+        else:
+            doc = None
+        if doc is None:
+            doc = self._doc
+            if doc is None:
+                raise XSLTSaveError("No document to serialise")
+        c_compression = compression or 0
+        xslt.LXML_GET_XSLT_ENCODING(c_encoding, self._xslt._c_style)
+        writer = _create_output_buffer(file, <const_char*>c_encoding, compression, &c_buffer, close=False)
+        if writer is None:
+            with nogil:
+                r = xslt.xsltSaveResultTo(c_buffer, doc._c_doc, self._xslt._c_style)
+                rclose = tree.xmlOutputBufferClose(c_buffer)
+        else:
+            r = xslt.xsltSaveResultTo(c_buffer, doc._c_doc, self._xslt._c_style)
+            rclose = tree.xmlOutputBufferClose(c_buffer)
+        if writer is not None:
+            writer._exc_context._raise_if_stored()
+        if r < 0 or rclose == -1:
+            python.PyErr_SetFromErrno(IOError)  # raises IOError
+
+    cdef _saveToStringAndSize(self, xmlChar** s, int* l):
+        cdef _Document doc
+        cdef int r
+        if self._context_node is not None:
+            doc = self._context_node._doc
+        else:
+            doc = None
+        if doc is None:
+            doc = self._doc
+            if doc is None:
+                s[0] = NULL
+                return
+        with nogil:
+            r = xslt.xsltSaveResultToString(s, l, doc._c_doc,
+                                            self._xslt._c_style)
+        if r == -1:
+            raise MemoryError()
+
+    def __str__(self):
+        cdef xmlChar* encoding
+        cdef xmlChar* s = NULL
+        cdef int l = 0
+        self._saveToStringAndSize(&s, &l)
+        if s is NULL:
+            return ''
+        encoding = self._xslt._c_style.encoding
+        try:
+            if encoding is NULL:
+                result = s[:l].decode('UTF-8')
+            else:
+                result = s[:l].decode(encoding)
+        finally:
+            tree.xmlFree(s)
+        return _stripEncodingDeclaration(result)
+
+    def __getbuffer__(self, Py_buffer* buffer, int flags):
+        cdef int l = 0
+        if buffer is NULL:
+            return
+        if self._buffer is NULL or flags & python.PyBUF_WRITABLE:
+            self._saveToStringAndSize(<xmlChar**>&buffer.buf, &l)
+            buffer.len = l
+            if self._buffer is NULL and not flags & python.PyBUF_WRITABLE:
+                self._buffer = <xmlChar*>buffer.buf
+                self._buffer_len = l
+                self._buffer_refcnt = 1
+        else:
+            buffer.buf = self._buffer
+            buffer.len = self._buffer_len
+            self._buffer_refcnt += 1
+        if flags & python.PyBUF_WRITABLE:
+            buffer.readonly = 0
+        else:
+            buffer.readonly = 1
+        if flags & python.PyBUF_FORMAT:
+            buffer.format = "B"
+        else:
+            buffer.format = NULL
+        buffer.ndim = 0
+        buffer.shape = NULL
+        buffer.strides = NULL
+        buffer.suboffsets = NULL
+        buffer.itemsize = 1
+        buffer.internal = NULL
+        if buffer.obj is not self: # set by Cython?
+            buffer.obj = self
+
+    def __releasebuffer__(self, Py_buffer* buffer):
+        if buffer is NULL:
+            return
+        if <xmlChar*>buffer.buf is self._buffer:
+            self._buffer_refcnt -= 1
+            if self._buffer_refcnt == 0:
+                tree.xmlFree(<char*>self._buffer)
+                self._buffer = NULL
+        else:
+            tree.xmlFree(<char*>buffer.buf)
+        buffer.buf = NULL
+
+    property xslt_profile:
+        """Return an ElementTree with profiling data for the stylesheet run.
+        """
+        def __get__(self):
+            cdef object root
+            if self._profile is None:
+                return None
+            root = self._profile.getroot()
+            if root is None:
+                return None
+            return ElementTree(root)
+
+        def __del__(self):
+            self._profile = None
+
+cdef _xsltResultTreeFactory(_Document doc, XSLT xslt, _Document profile):
+    cdef _XSLTResultTree result
+    result = <_XSLTResultTree>_newElementTree(doc, None, _XSLTResultTree)
+    result._xslt = xslt
+    result._profile = profile
+    return result
+
+# functions like "output" and "write" are a potential security risk, but we
+# rely on the user to configure XSLTAccessControl as needed
+xslt.xsltRegisterAllExtras()
+
+# enable EXSLT support for XSLT
+xslt.exsltRegisterAll()
+
+
+################################################################################
+# XSLT PI support
+
+cdef object _RE_PI_HREF = re.compile(r'\s+href\s*=\s*(?:\'([^\']*)\'|"([^"]*)")')
+cdef object _FIND_PI_HREF = _RE_PI_HREF.findall
+cdef object _REPLACE_PI_HREF = _RE_PI_HREF.sub
+cdef XPath __findStylesheetByID = None
+
+cdef _findStylesheetByID(_Document doc, id):
+    global __findStylesheetByID
+    if __findStylesheetByID is None:
+        __findStylesheetByID = XPath(
+            "//xsl:stylesheet[@xml:id = $id]",
+            namespaces={"xsl" : "http://www.w3.org/1999/XSL/Transform"})
+    return __findStylesheetByID(doc, id=id)
+
+cdef class _XSLTProcessingInstruction(PIBase):
+    def parseXSL(self, parser=None):
+        """parseXSL(self, parser=None)
+
+        Try to parse the stylesheet referenced by this PI and return
+        an ElementTree for it.  If the stylesheet is embedded in the
+        same document (referenced via xml:id), find and return an
+        ElementTree for the stylesheet Element.
+
+        The optional ``parser`` keyword argument can be passed to specify the
+        parser used to read from external stylesheet URLs.
+        """
+        cdef _Document result_doc
+        cdef _Element  result_node
+        cdef bytes href_utf
+        cdef const_xmlChar* c_href
+        cdef xmlAttr* c_attr
+        _assertValidNode(self)
+        if self._c_node.content is NULL:
+            raise ValueError, "PI lacks content"
+        hrefs = _FIND_PI_HREF(' ' + (<unsigned char*>self._c_node.content).decode('UTF-8'))
+        if len(hrefs) != 1:
+            raise ValueError, "malformed PI attributes"
+        hrefs = hrefs[0]
+        href_utf = utf8(hrefs[0] or hrefs[1])
+        c_href = _xcstr(href_utf)
+
+        if c_href[0] != c'#':
+            # normal URL, try to parse from it
+            c_href = tree.xmlBuildURI(
+                c_href,
+                tree.xmlNodeGetBase(self._c_node.doc, self._c_node))
+            if c_href is not NULL:
+                try:
+                    href_utf = <unsigned char*>c_href
+                finally:
+                    tree.xmlFree(<char*>c_href)
+            result_doc = _parseDocumentFromURL(href_utf, parser)
+            return _elementTreeFactory(result_doc, None)
+
+        # ID reference to embedded stylesheet
+        # try XML:ID lookup
+        _assertValidDoc(self._doc)
+        c_href += 1 # skip leading '#'
+        c_attr = tree.xmlGetID(self._c_node.doc, c_href)
+        if c_attr is not NULL and c_attr.doc is self._c_node.doc:
+            result_node = _elementFactory(self._doc, c_attr.parent)
+            return _elementTreeFactory(result_node._doc, result_node)
+
+        # try XPath search
+        root = _findStylesheetByID(self._doc, funicode(c_href))
+        if not root:
+            raise ValueError, "reference to non-existing embedded stylesheet"
+        elif len(root) > 1:
+            raise ValueError, "ambiguous reference to embedded stylesheet"
+        result_node = root[0]
+        return _elementTreeFactory(result_node._doc, result_node)
+
+    def set(self, key, value):
+        """set(self, key, value)
+
+        Supports setting the 'href' pseudo-attribute in the text of
+        the processing instruction.
+        """
+        if key != "href":
+            raise AttributeError, \
+                "only setting the 'href' attribute is supported on XSLT-PIs"
+        if value is None:
+            attrib = ""
+        elif '"' in value or '>' in value:
+            raise ValueError, "Invalid URL, must not contain '\"' or '>'"
+        else:
+            attrib = f' href="{value}"'
+        text = ' ' + self.text
+        if _FIND_PI_HREF(text):
+            self.text = _REPLACE_PI_HREF(attrib, text)
+        else:
+            self.text = text + attrib
diff --git a/.venv/lib/python3.12/site-packages/lxml/xsltext.pxi b/.venv/lib/python3.12/site-packages/lxml/xsltext.pxi
new file mode 100644
index 00000000..21894b9e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/lxml/xsltext.pxi
@@ -0,0 +1,242 @@
+# XSLT extension elements
+
+cdef class XSLTExtension:
+    """Base class of an XSLT extension element.
+    """
+    def execute(self, context, self_node, input_node, output_parent):
+        """execute(self, context, self_node, input_node, output_parent)
+        Execute this extension element.
+
+        Subclasses must override this method.  They may append
+        elements to the `output_parent` element here, or set its text
+        content.  To this end, the `input_node` provides read-only
+        access to the current node in the input document, and the
+        `self_node` points to the extension element in the stylesheet.
+
+        Note that the `output_parent` parameter may be `None` if there
+        is no parent element in the current context (e.g. no content
+        was added to the output tree yet).
+        """
+        pass
+
+    def apply_templates(self, _XSLTContext context not None, node, output_parent=None,
+                        *, elements_only=False, remove_blank_text=False):
+        """apply_templates(self, context, node, output_parent=None, elements_only=False, remove_blank_text=False)
+
+        Call this method to retrieve the result of applying templates
+        to an element.
+
+        The return value is a list of elements or text strings that
+        were generated by the XSLT processor.  If you pass
+        ``elements_only=True``, strings will be discarded from the result
+        list.  The option ``remove_blank_text=True`` will only discard
+        strings that consist entirely of whitespace (e.g. formatting).
+        These options do not apply to Elements, only to bare string results.
+
+        If you pass an Element as `output_parent` parameter, the result
+        will instead be appended to the element (including attributes
+        etc.) and the return value will be `None`.  This is a safe way
+        to generate content into the output document directly, without
+        having to take care of special values like text or attributes.
+        Note that the string discarding options will be ignored in this
+        case.
+        """
+        cdef xmlNode* c_parent
+        cdef xmlNode* c_node
+        cdef xmlNode* c_context_node
+        assert context._xsltCtxt is not NULL, "XSLT context not initialised"
+        c_context_node = _roNodeOf(node)
+        #assert c_context_node.doc is context._xsltContext.node.doc, \
+        #    "switching input documents during transformation is not currently supported"
+
+        if output_parent is not None:
+            c_parent = _nonRoNodeOf(output_parent)
+        else:
+            c_parent = tree.xmlNewDocNode(
+                context._xsltCtxt.output, NULL, <unsigned char*>"fake-parent", NULL)
+
+        c_node = context._xsltCtxt.insert
+        context._xsltCtxt.insert = c_parent
+        xslt.xsltProcessOneNode(
+            context._xsltCtxt, c_context_node, NULL)
+        context._xsltCtxt.insert = c_node
+
+        if output_parent is not None:
+            return None
+
+        try:
+            return self._collectXSLTResultContent(
+                context, c_parent, elements_only, remove_blank_text)
+        finally:
+            # free all intermediate nodes that will not be freed by proxies
+            tree.xmlFreeNode(c_parent)
+
+    def process_children(self, _XSLTContext context not None, output_parent=None,
+                         *, elements_only=False, remove_blank_text=False):
+        """process_children(self, context, output_parent=None, elements_only=False, remove_blank_text=False)
+
+        Call this method to process the XSLT content of the extension
+        element itself.
+
+        The return value is a list of elements or text strings that
+        were generated by the XSLT processor.  If you pass
+        ``elements_only=True``, strings will be discarded from the result
+        list.  The option ``remove_blank_text=True`` will only discard
+        strings that consist entirely of whitespace (e.g. formatting).
+        These options do not apply to Elements, only to bare string results.
+
+        If you pass an Element as `output_parent` parameter, the result
+        will instead be appended to the element (including attributes
+        etc.) and the return value will be `None`.  This is a safe way
+        to generate content into the output document directly, without
+        having to take care of special values like text or attributes.
+        Note that the string discarding options will be ignored in this
+        case.
+        """
+        cdef xmlNode* c_parent
+        cdef xslt.xsltTransformContext* c_ctxt = context._xsltCtxt
+        cdef xmlNode* c_old_output_parent = c_ctxt.insert
+        assert context._xsltCtxt is not NULL, "XSLT context not initialised"
+
+        # output_parent node is used for adding results instead of
+        # elements list used in apply_templates, that's easier and allows to
+        # use attributes added to extension element with <xsl:attribute>.
+
+        if output_parent is not None:
+            c_parent = _nonRoNodeOf(output_parent)
+        else:
+            c_parent = tree.xmlNewDocNode(
+                context._xsltCtxt.output, NULL, <unsigned char*>"fake-parent", NULL)
+
+        c_ctxt.insert = c_parent
+        xslt.xsltApplyOneTemplate(c_ctxt,
+            c_ctxt.node, c_ctxt.inst.children, NULL, NULL)
+        c_ctxt.insert = c_old_output_parent
+
+        if output_parent is not None:
+            return None
+
+        try:
+            return self._collectXSLTResultContent(
+                context, c_parent, elements_only, remove_blank_text)
+        finally:
+            # free all intermediate nodes that will not be freed by proxies
+            tree.xmlFreeNode(c_parent)
+
+    cdef _collectXSLTResultContent(self, _XSLTContext context, xmlNode* c_parent,
+                                   bint elements_only, bint remove_blank_text):
+        cdef xmlNode* c_node
+        cdef xmlNode* c_next
+        cdef _ReadOnlyProxy proxy
+        cdef list results = [] # or maybe _collectAttributes(c_parent, 2) ?
+        c_node = c_parent.children
+        while c_node is not NULL:
+            c_next = c_node.next
+            if c_node.type == tree.XML_TEXT_NODE:
+                if not elements_only:
+                    s = funicode(c_node.content)
+                    if not remove_blank_text or s.strip():
+                        results.append(s)
+                    s = None
+            elif c_node.type == tree.XML_ELEMENT_NODE:
+                proxy = _newReadOnlyProxy(
+                    context._extension_element_proxy, c_node)
+                results.append(proxy)
+                # unlink node and make sure it will be freed later on
+                tree.xmlUnlinkNode(c_node)
+                proxy.free_after_use()
+            else:
+                raise TypeError, \
+                    f"unsupported XSLT result type: {c_node.type}"
+            c_node = c_next
+        return results
+
+
+cdef _registerXSLTExtensions(xslt.xsltTransformContext* c_ctxt,
+                             extension_dict):
+    for ns_utf, name_utf in extension_dict:
+        xslt.xsltRegisterExtElement(
+            c_ctxt, _xcstr(name_utf), _xcstr(ns_utf),
+            <xslt.xsltTransformFunction>_callExtensionElement)
+
+cdef void _callExtensionElement(xslt.xsltTransformContext* c_ctxt,
+                                xmlNode* c_context_node,
+                                xmlNode* c_inst_node,
+                                void* dummy) noexcept with gil:
+    cdef _XSLTContext context
+    cdef XSLTExtension extension
+    cdef python.PyObject* dict_result
+    cdef xmlNode* c_node
+    cdef _ReadOnlyProxy context_node = None, self_node = None
+    cdef object output_parent # not restricted to ro-nodes
+    c_uri = _getNs(c_inst_node)
+    if c_uri is NULL:
+        # not allowed, and should never happen
+        return
+    if c_ctxt.xpathCtxt.userData is NULL:
+        # just for safety, should never happen
+        return
+    context = <_XSLTContext>c_ctxt.xpathCtxt.userData
+    try:
+        try:
+            dict_result = python.PyDict_GetItem(
+                context._extension_elements, (c_uri, c_inst_node.name))
+            if dict_result is NULL:
+                raise KeyError, f"extension element {funicode(c_inst_node.name)} not found"
+            extension = <object>dict_result
+
+            try:
+                # build the context proxy nodes
+                self_node = _newReadOnlyProxy(None, c_inst_node)
+                if _isElement(c_ctxt.insert):
+                    output_parent = _newAppendOnlyProxy(self_node, c_ctxt.insert)
+                else:
+                    # may be the document node or other stuff
+                    output_parent = _newOpaqueAppendOnlyNodeWrapper(c_ctxt.insert)
+                if c_context_node.type in (tree.XML_DOCUMENT_NODE,
+                                           tree.XML_HTML_DOCUMENT_NODE):
+                    c_node = tree.xmlDocGetRootElement(<xmlDoc*>c_context_node)
+                    if c_node is not NULL:
+                        context_node = _newReadOnlyProxy(self_node, c_node)
+                    else:
+                        context_node = None
+                elif c_context_node.type in (tree.XML_ATTRIBUTE_NODE,
+                                             tree.XML_TEXT_NODE,
+                                             tree.XML_CDATA_SECTION_NODE):
+                    # this isn't easy to support using read-only
+                    # nodes, as the smart-string factory must
+                    # instantiate the parent proxy somehow...
+                    raise TypeError(f"Unsupported element type: {c_context_node.type}")
+                else:
+                    context_node  = _newReadOnlyProxy(self_node, c_context_node)
+
+                # run the XSLT extension
+                context._extension_element_proxy = self_node
+                extension.execute(context, self_node, context_node, output_parent)
+            finally:
+                context._extension_element_proxy = None
+                if self_node is not None:
+                    _freeReadOnlyProxies(self_node)
+        except Exception as e:
+            try:
+                e = unicode(e).encode("UTF-8")
+            except:
+                e = repr(e).encode("UTF-8")
+            message = python.PyBytes_FromFormat(
+                "Error executing extension element '%s': %s",
+                c_inst_node.name, _cstr(e))
+            xslt.xsltTransformError(c_ctxt, NULL, c_inst_node, "%s", message)
+            context._exc._store_raised()
+        except:
+            # just in case
+            message = python.PyBytes_FromFormat(
+                "Error executing extension element '%s'", c_inst_node.name)
+            xslt.xsltTransformError(c_ctxt, NULL, c_inst_node, "%s", message)
+            context._exc._store_raised()
+    except:
+        # no Python functions here - everything can fail...
+        xslt.xsltTransformError(c_ctxt, NULL, c_inst_node,
+                                "Error during XSLT extension element evaluation")
+        context._exc._store_raised()
+    finally:
+        return  # swallow any further exceptions