details:   https://code.tryton.org/tryton/commit/8f9a1a21539c
branch:    default
user:      Nicolas Évrard <[email protected]>
date:      Tue Sep 01 18:49:45 2026 +0200
description:
        Add function to import XML files for tests

        Close #14627
diffstat:

 trytond/CHANGELOG           |   1 +
 trytond/doc/ref/convert.rst |  20 +++++++++++++++++
 trytond/doc/ref/index.rst   |   1 +
 trytond/trytond/convert.py  |  51 +++++++++++++++++++++++++++++++++++++-------
 4 files changed, 65 insertions(+), 8 deletions(-)

diffs (139 lines):

diff -r 419eb1c2556c -r 8f9a1a21539c trytond/CHANGELOG
--- a/trytond/CHANGELOG Tue Sep 01 21:57:25 2026 +0200
+++ b/trytond/CHANGELOG Tue Sep 01 18:49:45 2026 +0200
@@ -1,3 +1,4 @@
+* Add function to import XML files
 * Add support for router in Pool
 * Add explicit request context in URLAccessor
 * Retry unfinished queued tasks
diff -r 419eb1c2556c -r 8f9a1a21539c trytond/doc/ref/convert.rst
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/trytond/doc/ref/convert.rst       Tue Sep 01 18:49:45 2026 +0200
@@ -0,0 +1,20 @@
+.. _ref-convert:
+.. module:: trytond.convert
+
+=======
+Convert
+=======
+
+The ``convert`` module provides a way to import records from XML files.
+
+
+import_xml
+----------
+
+.. function:: import_xml(xml, module)
+
+Imports the records from ``xml`` into the database as if they were defined in
+``module``.
+Those records are not store in the Model Data table.
+``xml`` can be a :py:class:`string <str>`, a :py:class:`bytes <bytes>` or a
+file object.
diff -r 419eb1c2556c -r 8f9a1a21539c trytond/doc/ref/index.rst
--- a/trytond/doc/ref/index.rst Tue Sep 01 21:57:25 2026 +0200
+++ b/trytond/doc/ref/index.rst Tue Sep 01 18:49:45 2026 +0200
@@ -28,4 +28,5 @@
    filestore
    cache
    bus
+   convert
    tests
diff -r 419eb1c2556c -r 8f9a1a21539c trytond/trytond/convert.py
--- a/trytond/trytond/convert.py        Tue Sep 01 21:57:25 2026 +0200
+++ b/trytond/trytond/convert.py        Tue Sep 01 18:49:45 2026 +0200
@@ -1,6 +1,7 @@
 # This file is part of Tryton.  The COPYRIGHT file at the top level of
 # this repository contains the full copyright notices and license terms.
 import datetime
+import io
 import logging
 import os.path
 import re
@@ -13,6 +14,8 @@
 from trytond.pyson import CONTEXT, PYSONEncoder
 from trytond.transaction import Transaction, inactive_records
 
+__all__ = ['import_xml']
+
 logger = logging.getLogger(__name__)
 
 CDATA_START = re.compile(r'^\s*\<\!\[cdata\[', re.IGNORECASE)
@@ -377,13 +380,16 @@
 
 class TrytondXmlHandler(sax.handler.ContentHandler):
 
-    def __init__(self, pool, module, module_state, modules, languages):
+    def __init__(
+            self, pool, module, module_state, modules, languages,
+            store_model_data=True):
         "Register known taghandlers, and managed tags."
-        sax.handler.ContentHandler.__init__(self)
+        super().__init__()
 
         self.pool = pool
         self.module = module
         self.ModelData = pool.get('ir.model.data')
+        self.store_model_data = store_model_data
         self.fs2db = FS2DBAccessor(self.ModelData)
         self.to_delete = self.populate_to_delete()
         self.noupdate = None
@@ -474,7 +480,8 @@
                 self.write_records(model, *actions)
             self.grouped_write.clear()
         if name == 'data' and self.grouped_model_data:
-            self.ModelData.save(self.grouped_model_data)
+            if self.store_model_data:
+                self.ModelData.save(self.grouped_model_data)
             self.grouped_model_data.clear()
 
         # Closing tag found, if we are in a delegation the handler
@@ -531,11 +538,15 @@
         process. The records that are not encountered are deleted from the
         database in post_import."""
 
-        # Fetch the data in id descending order to avoid depedendcy
-        # problem when the corresponding recordds will be deleted:
-        module_data = self.ModelData.search([
-                ('module', '=', self.module),
-                ], order=[('id', 'DESC')])
+        if self.store_model_data:
+            # Fetch the data in id descending order to avoid depedendcy
+            # problem when the corresponding recordds will be deleted:
+            module_data = self.ModelData.search([
+                    ('module', '=', self.module),
+                    ], order=[('id', 'DESC')])
+        else:
+            module_data = []
+
         return set(rec.fs_id for rec in module_data)
 
     def import_record(self, model, values, fs_id, domain=None):
@@ -691,3 +702,27 @@
         transaction.commit()
 
     return True
+
+
+def import_xml(xml):
+    from trytond.pool import Pool
+
+    pool = Pool()
+    Modules = pool.get('ir.module')
+    modules = {m.name for m in Modules.search([
+                ('state', '=', 'activated'),
+                ])}
+    Lang = pool.get('ir.lang')
+    langs = {l.code for l in Lang.search([
+                ('translatable', '=', True),
+                ])}
+    parser = TrytondXmlHandler(
+        pool, None, 'to activate', modules, langs, store_model_data=False)
+
+    if isinstance(xml, str):
+        stream = io.BytesIO(xml.encode('utf8'))
+    elif isinstance(xml, bytes):
+        stream = io.BytesIO(xml)
+    else:
+        stream = xml
+    parser.parse_xmlstream(stream)

Reply via email to