From 4da3b913e1351dc36ce4bfa130d8819406949d87 Mon Sep 17 00:00:00 2001 From: Felipe Correa Date: Wed, 4 Feb 2026 02:10:01 -0300 Subject: [PATCH 1/2] fix: Add lxml 6.x compatibility for XML signature namespace handling - Reparse XML after signing to ensure namespaces are correctly associated with elements (required for lxml 6.x when using default namespace) - Update dependency constraints to allow lxml >=5.4.0 and signxml >=4.1.0 - Fix certificate test to use platform-independent temp directory lxml 6.0 changed how elements with default namespaces (xmlns without prefix) are handled internally. Elements created with nsmap={None: "..."} no longer have the namespace in their tag property, causing XPath queries with namespace prefixes to fail. Co-Authored-By: Claude Opus 4.5 --- pynfe/processamento/assinatura.py | 8 +++++++- pyproject.toml | 4 ++-- requirements.txt | 8 ++++---- tests/test_certificadoA1.py | 6 ++++-- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/pynfe/processamento/assinatura.py b/pynfe/processamento/assinatura.py index 50c5e263..43b6e690 100644 --- a/pynfe/processamento/assinatura.py +++ b/pynfe/processamento/assinatura.py @@ -48,7 +48,13 @@ def assinar(self, xml: etree._Element, retorna_string=False) -> Union[str, etree ref_uri = ("#%s" % reference) if reference else None signed_root = signer.sign(xml, key=self.key, cert=self.cert, reference_uri=ref_uri) + + # Reparse to ensure namespaces are correctly associated with elements + # This is required for lxml 6.x compatibility when using default namespace (None prefix) + signed_xml_str = etree.tostring(signed_root, encoding="unicode", pretty_print=False) + signed_root = etree.fromstring(signed_xml_str) + if retorna_string: - return etree.tostring(signed_root, encoding="unicode", pretty_print=False) + return signed_xml_str else: return signed_root diff --git a/pyproject.toml b/pyproject.toml index 2f56a155..22243d68 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,9 +21,9 @@ line-length = 100 [tool.poetry.dependencies] requests = "^2.32.4" -signxml = "^4.1.0" +signxml = ">=4.1.0" cryptography = "43.0.3" -lxml = "5.4.0" +lxml = ">=5.4.0" pyopenssl = "^25.1.0" [tool.poetry.group.dev.dependencies] diff --git a/requirements.txt b/requirements.txt index a6557b46..f5f05db4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,8 @@ # Dependencias basicas -requests -lxml -signxml -cryptography +requests>=2.32.4 +lxml>=5.4.0 +signxml>=4.1.0 +cryptography>=43.0.3 # Opcional para NFS-e #-r requirements-nfse.txt # Opcional para Desenvolvimento (Pytest, Ruff, MyPy, etc) diff --git a/tests/test_certificadoA1.py b/tests/test_certificadoA1.py index f53d3a2a..dd13e32e 100644 --- a/tests/test_certificadoA1.py +++ b/tests/test_certificadoA1.py @@ -1,6 +1,7 @@ #!/usr/bin/env python # *-* encoding: utf8 *-* +import tempfile import unittest from pynfe.entidades.certificado import CertificadoA1 @@ -17,8 +18,9 @@ def test_assinatura_com_caminho(self): self.a1 = CertificadoA1(self.certificado_correto) cert = self.a1.separar_arquivo(senha=self.senha_correto, caminho=self.certificado_correto) - self.assertTrue(cert[0].startswith("/tmp/")) - self.assertTrue(cert[1].startswith("/tmp/")) + temp_dir = tempfile.gettempdir() + self.assertTrue(cert[0].startswith(temp_dir)) + self.assertTrue(cert[1].startswith(temp_dir)) self.a1.excluir() From 4fd6e627e59b1f575afcf81f39165b0af69777cf Mon Sep 17 00:00:00 2001 From: Felipe Correa Date: Wed, 4 Feb 2026 22:08:09 -0300 Subject: [PATCH 2/2] Update pyproject.toml Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 22243d68..63745d1e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,7 @@ line-length = 100 [tool.poetry.dependencies] requests = "^2.32.4" signxml = ">=4.1.0" -cryptography = "43.0.3" +cryptography = ">=43.0.3" lxml = ">=5.4.0" pyopenssl = "^25.1.0"