ps-python-docx 1.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. docx/__init__.py +68 -0
  2. docx/api.py +37 -0
  3. docx/blkcntnr.py +101 -0
  4. docx/comments.py +163 -0
  5. docx/dml/__init__.py +0 -0
  6. docx/dml/color.py +112 -0
  7. docx/document.py +275 -0
  8. docx/drawing/__init__.py +59 -0
  9. docx/enum/__init__.py +0 -0
  10. docx/enum/base.py +150 -0
  11. docx/enum/dml.py +103 -0
  12. docx/enum/section.py +86 -0
  13. docx/enum/shape.py +19 -0
  14. docx/enum/style.py +452 -0
  15. docx/enum/table.py +136 -0
  16. docx/enum/text.py +367 -0
  17. docx/exceptions.py +18 -0
  18. docx/image/__init__.py +23 -0
  19. docx/image/bmp.py +43 -0
  20. docx/image/constants.py +172 -0
  21. docx/image/exceptions.py +13 -0
  22. docx/image/gif.py +38 -0
  23. docx/image/helpers.py +86 -0
  24. docx/image/image.py +234 -0
  25. docx/image/jpeg.py +425 -0
  26. docx/image/png.py +253 -0
  27. docx/image/tiff.py +289 -0
  28. docx/opc/__init__.py +0 -0
  29. docx/opc/constants.py +306 -0
  30. docx/opc/coreprops.py +142 -0
  31. docx/opc/exceptions.py +12 -0
  32. docx/opc/oxml.py +247 -0
  33. docx/opc/package.py +219 -0
  34. docx/opc/packuri.py +109 -0
  35. docx/opc/part.py +247 -0
  36. docx/opc/parts/__init__.py +0 -0
  37. docx/opc/parts/coreprops.py +48 -0
  38. docx/opc/phys_pkg.py +119 -0
  39. docx/opc/pkgreader.py +254 -0
  40. docx/opc/pkgwriter.py +115 -0
  41. docx/opc/rel.py +153 -0
  42. docx/opc/shared.py +31 -0
  43. docx/opc/spec.py +24 -0
  44. docx/oxml/__init__.py +261 -0
  45. docx/oxml/comments.py +124 -0
  46. docx/oxml/coreprops.py +298 -0
  47. docx/oxml/document.py +88 -0
  48. docx/oxml/drawing.py +11 -0
  49. docx/oxml/exceptions.py +10 -0
  50. docx/oxml/ns.py +109 -0
  51. docx/oxml/numbering.py +109 -0
  52. docx/oxml/parser.py +62 -0
  53. docx/oxml/section.py +537 -0
  54. docx/oxml/settings.py +138 -0
  55. docx/oxml/shape.py +299 -0
  56. docx/oxml/shared.py +52 -0
  57. docx/oxml/simpletypes.py +434 -0
  58. docx/oxml/styles.py +341 -0
  59. docx/oxml/table.py +977 -0
  60. docx/oxml/text/__init__.py +0 -0
  61. docx/oxml/text/font.py +333 -0
  62. docx/oxml/text/hyperlink.py +45 -0
  63. docx/oxml/text/pagebreak.py +278 -0
  64. docx/oxml/text/paragraph.py +106 -0
  65. docx/oxml/text/parfmt.py +392 -0
  66. docx/oxml/text/run.py +307 -0
  67. docx/oxml/xmlchemy.py +696 -0
  68. docx/package.py +110 -0
  69. docx/parts/__init__.py +0 -0
  70. docx/parts/comments.py +51 -0
  71. docx/parts/document.py +182 -0
  72. docx/parts/hdrftr.py +53 -0
  73. docx/parts/image.py +80 -0
  74. docx/parts/numbering.py +32 -0
  75. docx/parts/settings.py +50 -0
  76. docx/parts/story.py +95 -0
  77. docx/parts/styles.py +42 -0
  78. docx/parts/theme.py +53 -0
  79. docx/py.typed +0 -0
  80. docx/section.py +479 -0
  81. docx/settings.py +35 -0
  82. docx/shape.py +103 -0
  83. docx/shared.py +382 -0
  84. docx/styles/__init__.py +40 -0
  85. docx/styles/latent.py +198 -0
  86. docx/styles/style.py +264 -0
  87. docx/styles/styles.py +147 -0
  88. docx/table.py +537 -0
  89. docx/templates/default-comments.xml +12 -0
  90. docx/templates/default-docx-template/[Content_Types].xml +17 -0
  91. docx/templates/default-docx-template/_rels/.rels +7 -0
  92. docx/templates/default-docx-template/customXml/_rels/item1.xml.rels +4 -0
  93. docx/templates/default-docx-template/customXml/item1.xml +2 -0
  94. docx/templates/default-docx-template/customXml/itemProps1.xml +6 -0
  95. docx/templates/default-docx-template/docProps/app.xml +36 -0
  96. docx/templates/default-docx-template/docProps/core.xml +13 -0
  97. docx/templates/default-docx-template/docProps/thumbnail.jpeg +0 -0
  98. docx/templates/default-docx-template/word/_rels/document.xml.rels +11 -0
  99. docx/templates/default-docx-template/word/document.xml +11 -0
  100. docx/templates/default-docx-template/word/fontTable.xml +61 -0
  101. docx/templates/default-docx-template/word/numbering.xml +201 -0
  102. docx/templates/default-docx-template/word/settings.xml +53 -0
  103. docx/templates/default-docx-template/word/styles.xml +11844 -0
  104. docx/templates/default-docx-template/word/stylesWithEffects.xml +11800 -0
  105. docx/templates/default-docx-template/word/theme/theme1.xml +318 -0
  106. docx/templates/default-docx-template/word/webSettings.xml +5 -0
  107. docx/templates/default-footer.xml +27 -0
  108. docx/templates/default-header.xml +27 -0
  109. docx/templates/default-settings.xml +26 -0
  110. docx/templates/default-styles.xml +190 -0
  111. docx/templates/default.docx +0 -0
  112. docx/text/__init__.py +0 -0
  113. docx/text/font.py +472 -0
  114. docx/text/hyperlink.py +121 -0
  115. docx/text/pagebreak.py +104 -0
  116. docx/text/paragraph.py +173 -0
  117. docx/text/parfmt.py +286 -0
  118. docx/text/run.py +257 -0
  119. docx/text/tabstops.py +123 -0
  120. docx/theme.py +67 -0
  121. docx/types.py +34 -0
  122. ps_python_docx-1.3.0.dist-info/METADATA +77 -0
  123. ps_python_docx-1.3.0.dist-info/RECORD +126 -0
  124. ps_python_docx-1.3.0.dist-info/WHEEL +5 -0
  125. ps_python_docx-1.3.0.dist-info/licenses/LICENSE +20 -0
  126. ps_python_docx-1.3.0.dist-info/top_level.txt +1 -0
docx/oxml/document.py ADDED
@@ -0,0 +1,88 @@
1
+ """Custom element classes that correspond to the document part, e.g. <w:document>."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING, Callable, List
6
+
7
+ from docx.oxml.section import CT_SectPr
8
+ from docx.oxml.xmlchemy import BaseOxmlElement, ZeroOrMore, ZeroOrOne
9
+
10
+ if TYPE_CHECKING:
11
+ from docx.oxml.table import CT_Tbl
12
+ from docx.oxml.text.paragraph import CT_P
13
+
14
+
15
+ class CT_Document(BaseOxmlElement):
16
+ """``<w:document>`` element, the root element of a document.xml file."""
17
+
18
+ body: CT_Body = ZeroOrOne("w:body") # pyright: ignore[reportAssignmentType]
19
+
20
+ @property
21
+ def sectPr_lst(self) -> List[CT_SectPr]:
22
+ """All `w:sectPr` elements directly accessible from document element.
23
+
24
+ Note this does not include a `sectPr` child in a paragraphs wrapped in
25
+ revision marks or other intervening layer, perhaps `w:sdt` or customXml
26
+ elements.
27
+
28
+ `w:sectPr` elements appear in document order. The last one is always
29
+ `w:body/w:sectPr`, all preceding are `w:p/w:pPr/w:sectPr`.
30
+ """
31
+ xpath = "./w:body/w:p/w:pPr/w:sectPr | ./w:body/w:sectPr"
32
+ return self.xpath(xpath)
33
+
34
+
35
+ class CT_Body(BaseOxmlElement):
36
+ """`w:body`, the container element for the main document story in `document.xml`."""
37
+
38
+ add_p: Callable[[], CT_P]
39
+ get_or_add_sectPr: Callable[[], CT_SectPr]
40
+ p_lst: List[CT_P]
41
+ tbl_lst: List[CT_Tbl]
42
+
43
+ _insert_tbl: Callable[[CT_Tbl], CT_Tbl]
44
+
45
+ p = ZeroOrMore("w:p", successors=("w:sectPr",))
46
+ tbl = ZeroOrMore("w:tbl", successors=("w:sectPr",))
47
+ sectPr: CT_SectPr | None = ZeroOrOne( # pyright: ignore[reportAssignmentType]
48
+ "w:sectPr", successors=()
49
+ )
50
+
51
+ def add_section_break(self) -> CT_SectPr:
52
+ """Return `w:sectPr` element for new section added at end of document.
53
+
54
+ The last `w:sectPr` becomes the second-to-last, with the new `w:sectPr` being an
55
+ exact clone of the previous one, except that all header and footer references
56
+ are removed (and are therefore now "inherited" from the prior section).
57
+
58
+ A copy of the previously-last `w:sectPr` will now appear in a new `w:p` at the
59
+ end of the document. The returned `w:sectPr` is the sentinel `w:sectPr` for the
60
+ document (and as implemented, `is` the prior sentinel `w:sectPr` with headers
61
+ and footers removed).
62
+ """
63
+ # ---get the sectPr at file-end, which controls last section (sections[-1])---
64
+ sentinel_sectPr = self.get_or_add_sectPr()
65
+ # ---add exact copy to new `w:p` element; that is now second-to last section---
66
+ self.add_p().set_sectPr(sentinel_sectPr.clone())
67
+ # ---remove any header or footer references from "new" last section---
68
+ for hdrftr_ref in sentinel_sectPr.xpath("w:headerReference|w:footerReference"):
69
+ sentinel_sectPr.remove(hdrftr_ref)
70
+ # ---the sentinel `w:sectPr` now controls the new last section---
71
+ return sentinel_sectPr
72
+
73
+ def clear_content(self):
74
+ """Remove all content child elements from this <w:body> element.
75
+
76
+ Leave the <w:sectPr> element if it is present.
77
+ """
78
+ for content_elm in self.xpath("./*[not(self::w:sectPr)]"):
79
+ self.remove(content_elm)
80
+
81
+ @property
82
+ def inner_content_elements(self) -> List[CT_P | CT_Tbl]:
83
+ """Generate all `w:p` and `w:tbl` elements in this document-body.
84
+
85
+ Elements appear in document order. Elements shaded by nesting in a `w:ins` or
86
+ other "wrapper" element will not be included.
87
+ """
88
+ return self.xpath("./w:p | ./w:tbl")
docx/oxml/drawing.py ADDED
@@ -0,0 +1,11 @@
1
+ """Custom element-classes for DrawingML-related elements like `<w:drawing>`.
2
+
3
+ For legacy reasons, many DrawingML-related elements are in `docx.oxml.shape`. Expect
4
+ those to move over here as we have reason to touch them.
5
+ """
6
+
7
+ from docx.oxml.xmlchemy import BaseOxmlElement
8
+
9
+
10
+ class CT_Drawing(BaseOxmlElement):
11
+ """`<w:drawing>` element, containing a DrawingML object like a picture or chart."""
@@ -0,0 +1,10 @@
1
+ """Exceptions for oxml sub-package."""
2
+
3
+
4
+ class XmlchemyError(Exception):
5
+ """Generic error class."""
6
+
7
+
8
+ class InvalidXmlError(XmlchemyError):
9
+ """Raised when invalid XML is encountered, such as on attempt to access a missing
10
+ required child element."""
docx/oxml/ns.py ADDED
@@ -0,0 +1,109 @@
1
+ """Namespace-related objects."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Dict
6
+
7
+ nsmap = {
8
+ "a": "http://schemas.openxmlformats.org/drawingml/2006/main",
9
+ "c": "http://schemas.openxmlformats.org/drawingml/2006/chart",
10
+ "cp": "http://schemas.openxmlformats.org/package/2006/metadata/core-properties",
11
+ "dc": "http://purl.org/dc/elements/1.1/",
12
+ "dcmitype": "http://purl.org/dc/dcmitype/",
13
+ "dcterms": "http://purl.org/dc/terms/",
14
+ "dgm": "http://schemas.openxmlformats.org/drawingml/2006/diagram",
15
+ "m": "http://schemas.openxmlformats.org/officeDocument/2006/math",
16
+ "pic": "http://schemas.openxmlformats.org/drawingml/2006/picture",
17
+ "r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
18
+ "sl": "http://schemas.openxmlformats.org/schemaLibrary/2006/main",
19
+ "w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
20
+ "w14": "http://schemas.microsoft.com/office/word/2010/wordml",
21
+ "wp": "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing",
22
+ "xml": "http://www.w3.org/XML/1998/namespace",
23
+ "xsi": "http://www.w3.org/2001/XMLSchema-instance",
24
+ }
25
+
26
+ pfxmap = {value: key for key, value in nsmap.items()}
27
+
28
+
29
+ class NamespacePrefixedTag(str):
30
+ """Value object that knows the semantics of an XML tag having a namespace prefix."""
31
+
32
+ def __new__(cls, nstag: str):
33
+ return super(NamespacePrefixedTag, cls).__new__(cls, nstag)
34
+
35
+ def __init__(self, nstag: str):
36
+ self._pfx, self._local_part = nstag.split(":")
37
+ self._ns_uri = nsmap[self._pfx]
38
+
39
+ @property
40
+ def clark_name(self) -> str:
41
+ return "{%s}%s" % (self._ns_uri, self._local_part)
42
+
43
+ @classmethod
44
+ def from_clark_name(cls, clark_name: str) -> NamespacePrefixedTag:
45
+ nsuri, local_name = clark_name[1:].split("}")
46
+ nstag = "%s:%s" % (pfxmap[nsuri], local_name)
47
+ return cls(nstag)
48
+
49
+ @property
50
+ def local_part(self) -> str:
51
+ """The local part of this tag.
52
+
53
+ E.g. "foobar" is returned for tag "f:foobar".
54
+ """
55
+ return self._local_part
56
+
57
+ @property
58
+ def nsmap(self) -> Dict[str, str]:
59
+ """Single-member dict mapping prefix of this tag to it's namespace name.
60
+
61
+ Example: `{"f": "http://foo/bar"}`. This is handy for passing to xpath calls
62
+ and other uses.
63
+ """
64
+ return {self._pfx: self._ns_uri}
65
+
66
+ @property
67
+ def nspfx(self) -> str:
68
+ """The namespace-prefix for this tag.
69
+
70
+ For example, "f" is returned for tag "f:foobar".
71
+ """
72
+ return self._pfx
73
+
74
+ @property
75
+ def nsuri(self) -> str:
76
+ """The namespace URI for this tag.
77
+
78
+ For example, "http://foo/bar" would be returned for tag "f:foobar" if the "f"
79
+ prefix maps to "http://foo/bar" in nsmap.
80
+ """
81
+ return self._ns_uri
82
+
83
+
84
+ def nsdecls(*prefixes: str) -> str:
85
+ """Namespace declaration including each namespace-prefix in `prefixes`.
86
+
87
+ Handy for adding required namespace declarations to a tree root element.
88
+ """
89
+ return " ".join(['xmlns:%s="%s"' % (pfx, nsmap[pfx]) for pfx in prefixes])
90
+
91
+
92
+ def nspfxmap(*nspfxs: str) -> Dict[str, str]:
93
+ """Subset namespace-prefix mappings specified by *nspfxs*.
94
+
95
+ Any number of namespace prefixes can be supplied, e.g. namespaces("a", "r", "p").
96
+ """
97
+ return {pfx: nsmap[pfx] for pfx in nspfxs}
98
+
99
+
100
+ def qn(tag: str) -> str:
101
+ """Stands for "qualified name".
102
+
103
+ This utility function converts a familiar namespace-prefixed tag name like "w:p"
104
+ into a Clark-notation qualified tag name for lxml. For example, `qn("w:p")` returns
105
+ "{http://schemas.openxmlformats.org/wordprocessingml/2006/main}p".
106
+ """
107
+ prefix, tagroot = tag.split(":")
108
+ uri = nsmap[prefix]
109
+ return "{%s}%s" % (uri, tagroot)
docx/oxml/numbering.py ADDED
@@ -0,0 +1,109 @@
1
+ """Custom element classes related to the numbering part."""
2
+
3
+ from docx.oxml.parser import OxmlElement
4
+ from docx.oxml.shared import CT_DecimalNumber
5
+ from docx.oxml.simpletypes import ST_DecimalNumber
6
+ from docx.oxml.xmlchemy import (
7
+ BaseOxmlElement,
8
+ OneAndOnlyOne,
9
+ RequiredAttribute,
10
+ ZeroOrMore,
11
+ ZeroOrOne,
12
+ )
13
+
14
+
15
+ class CT_Num(BaseOxmlElement):
16
+ """``<w:num>`` element, which represents a concrete list definition instance, having
17
+ a required child <w:abstractNumId> that references an abstract numbering definition
18
+ that defines most of the formatting details."""
19
+
20
+ abstractNumId = OneAndOnlyOne("w:abstractNumId")
21
+ lvlOverride = ZeroOrMore("w:lvlOverride")
22
+ numId = RequiredAttribute("w:numId", ST_DecimalNumber)
23
+
24
+ def add_lvlOverride(self, ilvl):
25
+ """Return a newly added CT_NumLvl (<w:lvlOverride>) element having its ``ilvl``
26
+ attribute set to `ilvl`."""
27
+ return self._add_lvlOverride(ilvl=ilvl)
28
+
29
+ @classmethod
30
+ def new(cls, num_id, abstractNum_id):
31
+ """Return a new ``<w:num>`` element having numId of `num_id` and having a
32
+ ``<w:abstractNumId>`` child with val attribute set to `abstractNum_id`."""
33
+ num = OxmlElement("w:num")
34
+ num.numId = num_id
35
+ abstractNumId = CT_DecimalNumber.new("w:abstractNumId", abstractNum_id)
36
+ num.append(abstractNumId)
37
+ return num
38
+
39
+
40
+ class CT_NumLvl(BaseOxmlElement):
41
+ """``<w:lvlOverride>`` element, which identifies a level in a list definition to
42
+ override with settings it contains."""
43
+
44
+ startOverride = ZeroOrOne("w:startOverride", successors=("w:lvl",))
45
+ ilvl = RequiredAttribute("w:ilvl", ST_DecimalNumber)
46
+
47
+ def add_startOverride(self, val):
48
+ """Return a newly added CT_DecimalNumber element having tagname
49
+ ``w:startOverride`` and ``val`` attribute set to `val`."""
50
+ return self._add_startOverride(val=val)
51
+
52
+
53
+ class CT_NumPr(BaseOxmlElement):
54
+ """A ``<w:numPr>`` element, a container for numbering properties applied to a
55
+ paragraph."""
56
+
57
+ ilvl = ZeroOrOne("w:ilvl", successors=("w:numId", "w:numberingChange", "w:ins"))
58
+ numId = ZeroOrOne("w:numId", successors=("w:numberingChange", "w:ins"))
59
+
60
+ # @ilvl.setter
61
+ # def _set_ilvl(self, val):
62
+ # """
63
+ # Get or add a <w:ilvl> child and set its ``w:val`` attribute to `val`.
64
+ # """
65
+ # ilvl = self.get_or_add_ilvl()
66
+ # ilvl.val = val
67
+
68
+ # @numId.setter
69
+ # def numId(self, val):
70
+ # """
71
+ # Get or add a <w:numId> child and set its ``w:val`` attribute to
72
+ # `val`.
73
+ # """
74
+ # numId = self.get_or_add_numId()
75
+ # numId.val = val
76
+
77
+
78
+ class CT_Numbering(BaseOxmlElement):
79
+ """``<w:numbering>`` element, the root element of a numbering part, i.e.
80
+ numbering.xml."""
81
+
82
+ num = ZeroOrMore("w:num", successors=("w:numIdMacAtCleanup",))
83
+
84
+ def add_num(self, abstractNum_id):
85
+ """Return a newly added CT_Num (<w:num>) element referencing the abstract
86
+ numbering definition identified by `abstractNum_id`."""
87
+ next_num_id = self._next_numId
88
+ num = CT_Num.new(next_num_id, abstractNum_id)
89
+ return self._insert_num(num)
90
+
91
+ def num_having_numId(self, numId):
92
+ """Return the ``<w:num>`` child element having ``numId`` attribute matching
93
+ `numId`."""
94
+ xpath = './w:num[@w:numId="%d"]' % numId
95
+ try:
96
+ return self.xpath(xpath)[0]
97
+ except IndexError:
98
+ raise KeyError("no <w:num> element with numId %d" % numId)
99
+
100
+ @property
101
+ def _next_numId(self):
102
+ """The first ``numId`` unused by a ``<w:num>`` element, starting at 1 and
103
+ filling any gaps in numbering between existing ``<w:num>`` elements."""
104
+ numId_strs = self.xpath("./w:num/@w:numId")
105
+ num_ids = [int(numId_str) for numId_str in numId_strs]
106
+ for num in range(1, len(num_ids) + 2):
107
+ if num not in num_ids:
108
+ break
109
+ return num
docx/oxml/parser.py ADDED
@@ -0,0 +1,62 @@
1
+ # pyright: reportImportCycles=false
2
+
3
+ """XML parser for python-docx."""
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import TYPE_CHECKING, Dict, Type, cast
8
+
9
+ from lxml import etree
10
+
11
+ from docx.oxml.ns import NamespacePrefixedTag, nsmap
12
+
13
+ if TYPE_CHECKING:
14
+ from docx.oxml.xmlchemy import BaseOxmlElement
15
+
16
+
17
+ # -- configure XML parser --
18
+ element_class_lookup = etree.ElementNamespaceClassLookup()
19
+ oxml_parser = etree.XMLParser(remove_blank_text=True, resolve_entities=False)
20
+ oxml_parser.set_element_class_lookup(element_class_lookup)
21
+
22
+
23
+ def parse_xml(xml: str | bytes) -> "BaseOxmlElement":
24
+ """Root lxml element obtained by parsing XML character string `xml`.
25
+
26
+ The custom parser is used, so custom element classes are produced for elements in
27
+ `xml` that have them.
28
+ """
29
+ return cast("BaseOxmlElement", etree.fromstring(xml, oxml_parser))
30
+
31
+
32
+ def register_element_cls(tag: str, cls: Type["BaseOxmlElement"]):
33
+ """Register an lxml custom element-class to use for `tag`.
34
+
35
+ A instance of `cls` to be constructed when the oxml parser encounters an element
36
+ with matching `tag`. `tag` is a string of the form `nspfx:tagroot`, e.g.
37
+ `'w:document'`.
38
+ """
39
+ nspfx, tagroot = tag.split(":")
40
+ namespace = element_class_lookup.get_namespace(nsmap[nspfx])
41
+ namespace[tagroot] = cls
42
+
43
+
44
+ def OxmlElement(
45
+ nsptag_str: str,
46
+ attrs: Dict[str, str] | None = None,
47
+ nsdecls: Dict[str, str] | None = None,
48
+ ) -> BaseOxmlElement | etree._Element: # pyright: ignore[reportPrivateUsage]
49
+ """Return a 'loose' lxml element having the tag specified by `nsptag_str`.
50
+
51
+ The tag in `nsptag_str` must contain the standard namespace prefix, e.g. `a:tbl`.
52
+ The resulting element is an instance of the custom element class for this tag name
53
+ if one is defined. A dictionary of attribute values may be provided as `attrs`; they
54
+ are set if present. All namespaces defined in the dict `nsdecls` are declared in the
55
+ element using the key as the prefix and the value as the namespace name. If
56
+ `nsdecls` is not provided, a single namespace declaration is added based on the
57
+ prefix on `nsptag_str`.
58
+ """
59
+ nsptag = NamespacePrefixedTag(nsptag_str)
60
+ if nsdecls is None:
61
+ nsdecls = nsptag.nsmap
62
+ return oxml_parser.makeelement(nsptag.clark_name, attrib=attrs, nsmap=nsdecls)