Skip to content

Commit 25d4d2f

Browse files
committed
[IMP] load_data: allow to modify xml file before loading
1 parent 8643680 commit 25d4d2f

2 files changed

Lines changed: 151 additions & 28 deletions

File tree

openupgradelib/openupgrade.py

Lines changed: 84 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,48 @@ def check_values_selection_field(cr, table_name, field_name, allowed_values):
293293
return res
294294

295295

296-
def load_data(env_or_cr, module_name, filename, idref=None, mode="init"):
296+
def _file_open(module_name, filename):
297+
"""
298+
Search addons paths and upgrade paths for filename and return the opened file,
299+
raise if not found
300+
"""
301+
pathname = os.path.join(module_name, filename)
302+
try:
303+
if version_info[0] >= 11:
304+
fp = tools.file_open(pathname, "rb")
305+
else:
306+
fp = tools.file_open(pathname)
307+
except OSError:
308+
if tools.config.get("upgrade_path"):
309+
if version_info[0] >= 19:
310+
upgrade_paths = tools.config["upgrade_path"]
311+
else:
312+
upgrade_paths = tools.config["upgrade_path"].split(",")
313+
for path in upgrade_paths:
314+
pathname = os.path.join(path, module_name, filename)
315+
try:
316+
fp = open(pathname)
317+
break
318+
except OSError: # pylint: disable=W7938
319+
pass
320+
else:
321+
raise OSError(
322+
"Couldn't find file %s in the upgrade paths (%s)"
323+
% (filename, tools.config["upgrade_path"])
324+
)
325+
else:
326+
raise
327+
return fp
328+
329+
330+
def load_data(
331+
env_or_cr,
332+
module_name,
333+
filename,
334+
idref=None,
335+
mode="init",
336+
xml_transformation_filename=None,
337+
):
297338
"""
298339
Load an xml, csv or yml data file from your post script. The usual case for
299340
this is the
@@ -327,6 +368,18 @@ def load_data(env_or_cr, module_name, filename, idref=None, mode="init"):
327368
update, standard Odoo would recreate the record if it was deleted,
328369
but this will fail in cases where there are required fields to be
329370
filled which are not contained in the data file.
371+
:param xml_transformation_filename:
372+
if filename is an xml file (like the noupdate_changes.xml file generated
373+
by upgrade_analysis), you can pass the name of a transformation file with
374+
view inheritance instructions to transform the file being loaded in-place.
375+
376+
Ie if you pass a file containing::
377+
378+
<odoo>
379+
<xpath expr="//field[@name='uom_id']" position="replace" />
380+
</odoo>
381+
382+
here, you deactivate the first update on a field called `uom_id`.
330383
"""
331384

332385
cr = env_or_cr
@@ -342,33 +395,8 @@ def load_data(env_or_cr, module_name, filename, idref=None, mode="init"):
342395
idref = {}
343396
logger.info("%s: loading %s" % (module_name, filename))
344397
_, ext = os.path.splitext(filename)
345-
pathname = os.path.join(module_name, filename)
346398

347-
try:
348-
if version_info[0] >= 11:
349-
fp = tools.file_open(pathname, "rb")
350-
else:
351-
fp = tools.file_open(pathname)
352-
except OSError:
353-
if tools.config.get("upgrade_path"):
354-
if version_info[0] >= 19:
355-
upgrade_paths = tools.config["upgrade_path"]
356-
else:
357-
upgrade_paths = tools.config["upgrade_path"].split(",")
358-
for path in upgrade_paths:
359-
pathname = os.path.join(path, module_name, filename)
360-
try:
361-
fp = open(pathname)
362-
break
363-
except OSError: # pylint: disable=W7938
364-
pass
365-
else:
366-
raise OSError(
367-
"Couldn't find file %s in the upgrade paths (%s)"
368-
% (filename, tools.config["upgrade_path"])
369-
)
370-
else:
371-
raise
399+
fp = _file_open(module_name, filename)
372400

373401
try:
374402
if ext == ".csv":
@@ -388,7 +416,17 @@ def load_data(env_or_cr, module_name, filename, idref=None, mode="init"):
388416
mode="init",
389417
)
390418
else:
391-
tools.convert_xml_import(env_or_cr, module_name, fp, idref, mode=mode)
419+
tools.convert_xml_import(
420+
env_or_cr,
421+
module_name,
422+
fp
423+
if not xml_transformation_filename
424+
else _load_data_xml_transformation(
425+
fp, _file_open(module_name, xml_transformation_filename)
426+
),
427+
idref,
428+
mode=mode,
429+
)
392430
finally:
393431
fp.close()
394432

@@ -428,6 +466,24 @@ def yield_element(node, path=None):
428466
return yield_element(etree.parse(fp).getroot())
429467

430468

469+
def _load_data_xml_transformation(fp, transformation_fp):
470+
"""
471+
Apply xml_transformation (string of view inheritance style snippets) to xml file
472+
fp, return file-like object that can be loaded with etree.parse
473+
"""
474+
from odoo.tools.template_inheritance import apply_inheritance_specs
475+
476+
transformation_arch = etree.parse(transformation_fp)
477+
478+
modified = apply_inheritance_specs(
479+
etree.parse(fp).getroot(),
480+
transformation_arch.getroot().getchildren(),
481+
)
482+
result = StringIO(etree.tostring(modified, encoding="unicode"))
483+
result.name = None
484+
return result
485+
486+
431487
# for backwards compatibility
432488
load_xml = load_data
433489
table_exists = openupgrade_tools.table_exists

tests/test_openupgradelib.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import os
22
import unittest
3+
from io import StringIO
4+
from unittest import mock
35

46
import psycopg2
57

@@ -121,6 +123,71 @@ def test_lift_constraints(self):
121123
self.assertFalse(admin_partner.exists())
122124
self.env.cr.execute("ROLLBACK TO SAVEPOINT test")
123125

126+
def test_load_data(self):
127+
admin_user = self.env.ref("base.user_admin")
128+
129+
def patched_file_open(path, *args, **kwargs):
130+
if path == "dummy_module/dummy.xml":
131+
result = StringIO(
132+
"""
133+
<odoo>
134+
<record id="base.user_admin" model="res.users">
135+
<field name="name">Not Administrator</field>
136+
<field name="signature">changed signature</field>
137+
</record>
138+
</odoo>
139+
"""
140+
)
141+
elif path == "dummy_module/dummy-transformation.xml":
142+
result = StringIO(
143+
"""
144+
<odoo>
145+
<xpath expr="//field[@name='name']" position="replace" />
146+
</odoo>
147+
"""
148+
)
149+
elif path == "dummy_module/dummy-transformation2.xml":
150+
result = StringIO(
151+
"""
152+
<odoo>
153+
<xpath expr="//field[@name='name']" position="replace" />
154+
<xpath expr="//field[@name='signature']" position="replace" />
155+
</odoo>
156+
"""
157+
)
158+
result.name = "dummy.xml"
159+
return result
160+
161+
env_or_cr = self.env if openupgrade.version_info[0] > 16 else self.cr
162+
163+
with mock.patch("odoo.tools.file_open") as file_open:
164+
file_open.side_effect = patched_file_open
165+
166+
openupgrade.load_data(env_or_cr, "dummy_module", "dummy.xml")
167+
self.assertEqual(admin_user.name, "Not Administrator")
168+
self.assertIn("changed signature", admin_user.signature)
169+
170+
admin_user.name = "Administrator"
171+
admin_user.signature = "original signature"
172+
openupgrade.load_data(
173+
env_or_cr,
174+
"dummy_module",
175+
"dummy.xml",
176+
xml_transformation_filename="dummy-transformation.xml",
177+
)
178+
self.assertEqual(admin_user.name, "Administrator")
179+
self.assertIn("changed signature", admin_user.signature)
180+
181+
admin_user.signature = "original signature"
182+
openupgrade.load_data(
183+
env_or_cr,
184+
"dummy_module",
185+
"dummy.xml",
186+
xml_transformation_filename="dummy-transformation2.xml",
187+
)
188+
self.assertEqual(admin_user.name, "Administrator")
189+
self.assertIn("original signature", admin_user.signature)
190+
124191
def tearDown(self):
125192
super().tearDown()
126193
self.cr.close()

0 commit comments

Comments
 (0)