diff --git a/addons/account_edi/tests/common.py b/addons/account_edi/tests/common.py
index ce8c5084cd5..93e0495b67b 100644
--- a/addons/account_edi/tests/common.py
+++ b/addons/account_edi/tests/common.py
@@ -1,6 +1,5 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from odoo.modules.module import get_module_resource
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
from contextlib import contextmanager
@@ -88,19 +87,6 @@ class AccountEdiTestCommon(AccountTestInvoicingCommon):
def edi_cron(self):
self.env['account.edi.document'].sudo().search([('state', 'in', ('to_send', 'to_cancel'))])._process_documents_web_services(with_commit=False)
- def create_invoice_from_file(self, module_name, subfolder, filename):
- file_path = get_module_resource(module_name, subfolder, filename)
- file = open(file_path, 'rb').read()
-
- attachment = self.env['ir.attachment'].create({
- 'name': filename,
- 'datas': base64.encodebytes(file),
- 'res_model': 'account.move',
- })
- journal_id = self.company_data['default_journal_sale']
- action_vals = journal_id.with_context(default_move_type='in_invoice').create_document_from_attachment(attachment.ids)
- return self.env['account.move'].browse(action_vals['res_id'])
-
def assert_generated_file_equal(self, invoice, expected_values, applied_xpath=None):
invoice.action_post()
invoice.edi_document_ids._process_documents_web_services(with_commit=False) # synchronous are called in post, but there's no CRON in tests for asynchronous
diff --git a/addons/attachment_indexation/tests/test_indexation.py b/addons/attachment_indexation/tests/test_indexation.py
index 36c558cdcc1..bb7eac8038b 100644
--- a/addons/attachment_indexation/tests/test_indexation.py
+++ b/addons/attachment_indexation/tests/test_indexation.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from odoo.tests.common import TransactionCase, tagged
+from odoo.tools.misc import file_open
from unittest import skipIf
import os
@@ -17,7 +18,7 @@ class TestCaseIndexation(TransactionCase):
@skipIf(PDFResourceManager is None, "pdfminer not installed")
def test_attachment_pdf_indexation(self):
- with open(os.path.join(directory, 'files', 'test_content.pdf'), 'rb') as file:
+ with file_open(os.path.join(directory, 'files', 'test_content.pdf'), 'rb') as file:
pdf = file.read()
text = self.env['ir.attachment']._index(pdf, 'application/pdf')
self.assertEqual(text, 'TestContent!!\x0c', 'the index content should be correct')
diff --git a/addons/base_import_module/tests/test_import_module.py b/addons/base_import_module/tests/test_import_module.py
index 69ccb8d06b8..488267d8100 100644
--- a/addons/base_import_module/tests/test_import_module.py
+++ b/addons/base_import_module/tests/test_import_module.py
@@ -3,7 +3,6 @@
import base64
import json
import os
-import tempfile
from io import BytesIO
from zipfile import ZipFile
diff --git a/addons/hr_recruitment/tests/test_recruitment_process.py b/addons/hr_recruitment/tests/test_recruitment_process.py
index 74503b35e61..5cbaab8eef2 100644
--- a/addons/hr_recruitment/tests/test_recruitment_process.py
+++ b/addons/hr_recruitment/tests/test_recruitment_process.py
@@ -1,9 +1,8 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from odoo.tests import common
from odoo.addons.hr.tests.common import TestHrCommon
-from odoo.modules.module import get_module_resource
+from odoo.tools.misc import file_open
class TestRecruitmentProcess(TestHrCommon):
@@ -36,7 +35,7 @@ class TestRecruitmentProcess(TestHrCommon):
# An applicant is interested in the job position. So he sends a resume by email.
# In Order to test process of Recruitment so giving HR officer's rights
- with open(get_module_resource('hr_recruitment', 'tests', 'resume.eml'), 'rb') as request_file:
+ with file_open('hr_recruitment/tests/resume.eml', 'rb') as request_file:
request_message = request_file.read()
self.env['mail.thread'].with_user(self.res_users_hr_recruitment_officer).message_process(
'hr.applicant', request_message, custom_values={"job_id": self.job_developer.id})
diff --git a/addons/hw_drivers/connection_manager.py b/addons/hw_drivers/connection_manager.py
index 24e1142bac8..00a445e3d86 100644
--- a/addons/hw_drivers/connection_manager.py
+++ b/addons/hw_drivers/connection_manager.py
@@ -9,7 +9,6 @@ from threading import Thread
import time
import urllib3
-from odoo.modules.module import get_resource_path
from odoo.addons.hw_drivers.main import iot_devices, manager
from odoo.addons.hw_drivers.tools import helpers
diff --git a/addons/hw_drivers/controllers/driver.py b/addons/hw_drivers/controllers/driver.py
index 3f1c526cb66..ff838a6c09d 100755
--- a/addons/hw_drivers/controllers/driver.py
+++ b/addons/hw_drivers/controllers/driver.py
@@ -9,7 +9,6 @@ import subprocess
import time
from odoo import http, tools
-from odoo.modules.module import get_resource_path
from odoo.addons.hw_drivers.event_manager import event_manager
from odoo.addons.hw_drivers.main import iot_devices, manager
diff --git a/addons/hw_drivers/iot_handlers/drivers/DisplayDriver_L.py b/addons/hw_drivers/iot_handlers/drivers/DisplayDriver_L.py
index 4a3d917cfab..a5819aee910 100644
--- a/addons/hw_drivers/iot_handlers/drivers/DisplayDriver_L.py
+++ b/addons/hw_drivers/iot_handlers/drivers/DisplayDriver_L.py
@@ -18,6 +18,7 @@ from odoo.addons.hw_drivers.driver import Driver
from odoo.addons.hw_drivers.event_manager import event_manager
from odoo.addons.hw_drivers.main import iot_devices
from odoo.addons.hw_drivers.tools import helpers
+from odoo.tools.misc import file_open
path = os.path.realpath(os.path.join(os.path.dirname(__file__), '../../views'))
loader = jinja2.FileSystemLoader(path)
@@ -203,7 +204,7 @@ class DisplayController(http.Controller):
cust_js = None
interfaces = ni.interfaces()
- with open(os.path.join(os.path.dirname(__file__), "../../static/src/js/worker.js")) as js:
+ with file_open("hw_drivers/static/src/js/worker.js") as js:
cust_js = js.read()
display_ifaces = []
diff --git a/addons/hw_drivers/iot_handlers/drivers/PrinterDriver_L.py b/addons/hw_drivers/iot_handlers/drivers/PrinterDriver_L.py
index 809057ee9b9..0ee38a68a03 100644
--- a/addons/hw_drivers/iot_handlers/drivers/PrinterDriver_L.py
+++ b/addons/hw_drivers/iot_handlers/drivers/PrinterDriver_L.py
@@ -11,7 +11,6 @@ import os
from PIL import Image, ImageOps
import re
import subprocess
-import tempfile
from uuid import getnode as get_mac
from odoo import http
diff --git a/addons/hw_drivers/tools/helpers.py b/addons/hw_drivers/tools/helpers.py
index da5ba861568..bc20cb19c42 100644
--- a/addons/hw_drivers/tools/helpers.py
+++ b/addons/hw_drivers/tools/helpers.py
@@ -23,7 +23,7 @@ import secrets
from odoo import _, http, service
from odoo.tools.func import lazy_property
-from odoo.modules.module import get_resource_path
+from odoo.tools.misc import file_path
_logger = logging.getLogger(__name__)
@@ -368,7 +368,7 @@ def load_iot_handlers():
And execute these python drivers and interfaces
"""
for directory in ['interfaces', 'drivers']:
- path = get_resource_path('hw_drivers', 'iot_handlers', directory)
+ path = file_path(f'hw_drivers/iot_handlers/{directory}')
filesList = list_file_by_os(path)
for file in filesList:
spec = util.spec_from_file_location(file, str(Path(path).joinpath(file)))
diff --git a/addons/hw_posbox_homepage/controllers/main.py b/addons/hw_posbox_homepage/controllers/main.py
index 8ed59f4cde6..e3943db114c 100644
--- a/addons/hw_posbox_homepage/controllers/main.py
+++ b/addons/hw_posbox_homepage/controllers/main.py
@@ -14,11 +14,11 @@ import threading
from odoo import http, service
from odoo.http import Response
-from odoo.modules.module import get_resource_path
from odoo.addons.hw_drivers.connection_manager import connection_manager
from odoo.addons.hw_drivers.main import iot_devices
from odoo.addons.hw_drivers.tools import helpers
from odoo.addons.web.controllers.home import Home
+from odoo.tools.misc import file_path
_logger = logging.getLogger(__name__)
@@ -112,8 +112,8 @@ class IoTboxHomepage(Home):
@http.route('/list_handlers', type='http', auth='none', website=True)
def list_handlers(self):
- drivers_list = helpers.list_file_by_os(get_resource_path('hw_drivers', 'iot_handlers', 'drivers'))
- interfaces_list = helpers.list_file_by_os(get_resource_path('hw_drivers', 'iot_handlers', 'interfaces'))
+ drivers_list = helpers.list_file_by_os(file_path('hw_drivers/iot_handlers/drivers'))
+ interfaces_list = helpers.list_file_by_os(file_path('hw_drivers/iot_handlers/interfaces'))
return handler_list_template.render({
'title': "Odoo's IoT Box - Handlers list",
'breadcrumb': 'Handlers list',
@@ -167,7 +167,7 @@ class IoTboxHomepage(Home):
else:
persistent = ""
- subprocess.check_call([get_resource_path('point_of_sale', 'tools/posbox/configuration/connect_to_wifi.sh'), essid, password, persistent])
+ subprocess.check_call([file_path('point_of_sale/tools/posbox/configuration/connect_to_wifi.sh'), essid, password, persistent])
server = helpers.get_odoo_server_url()
res_payload = {
'message': 'Connecting to ' + essid,
@@ -198,7 +198,7 @@ class IoTboxHomepage(Home):
@http.route('/handlers_clear', type='http', auth='none', cors='*', csrf=False)
def clear_handlers_list(self):
for directory in ['drivers', 'interfaces']:
- for file in list(Path(get_resource_path('hw_drivers', 'iot_handlers', directory)).glob('*')):
+ for file in list(Path(file_path(f'hw_drivers/iot_handlers/{directory}')).glob('*')):
if file.name != '__pycache__':
helpers.unlink_file(str(file.relative_to(*file.parts[:3])))
return ""
@@ -216,7 +216,7 @@ class IoTboxHomepage(Home):
url = helpers.get_odoo_server_url()
token = helpers.get_token()
if iotname and platform.system() == 'Linux':
- subprocess.check_call([get_resource_path('point_of_sale', 'tools/posbox/configuration/rename_iot.sh'), iotname])
+ subprocess.check_call([file_path('point_of_sale/tools/posbox/configuration/rename_iot.sh'), iotname])
helpers.odoo_restart(5)
return 'http://' + helpers.get_ip() + ':8069'
@@ -238,7 +238,7 @@ class IoTboxHomepage(Home):
token = token.split('|')[1]
else:
url = ''
- subprocess.check_call([get_resource_path('point_of_sale', 'tools/posbox/configuration/connect_to_server_wifi.sh'), url, iotname, token, essid, password, persistent])
+ subprocess.check_call([file_path('point_of_sale/tools/posbox/configuration/connect_to_server_wifi.sh'), url, iotname, token, essid, password, persistent])
return url
# Set server address
diff --git a/addons/l10n_account_edi_ubl_cii_tests/tests/common.py b/addons/l10n_account_edi_ubl_cii_tests/tests/common.py
index d860e4d9827..045ba3f243b 100644
--- a/addons/l10n_account_edi_ubl_cii_tests/tests/common.py
+++ b/addons/l10n_account_edi_ubl_cii_tests/tests/common.py
@@ -3,11 +3,11 @@ import base64
from freezegun import freeze_time
from collections import Counter
+from os.path import join as opj
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
from odoo import fields
from odoo.tools import misc
-from odoo.modules.module import get_resource_path, get_module_resource
from lxml import etree
@@ -117,8 +117,8 @@ class TestUBLCommon(AccountTestInvoicingCommon):
def _update_invoice_from_file(self, module_name, subfolder, filename, invoice):
""" Create an attachment from a file and post it on the invoice
"""
- file_path = get_module_resource(module_name, subfolder, filename)
- with misc.file_open(file_path, 'rb') as file:
+ file_path = opj(module_name, subfolder, filename)
+ with misc.file_open(file_path, 'rb', filter_ext=('.xml',)) as file:
attachment = self.env['ir.attachment'].create({
'name': filename,
'datas': base64.encodebytes(file.read()),
@@ -234,8 +234,7 @@ class TestUBLCommon(AccountTestInvoicingCommon):
xml_content = base64.b64decode(attachment.with_context(bin_size=False).datas)
xml_etree = self.get_xml_tree_from_string(xml_content)
- expected_file_full_path = get_resource_path(self.test_module, 'tests/test_files', expected_file_path)
- self.assertTrue(expected_file_full_path, "expected file not found")
+ expected_file_full_path = misc.file_path(f'{self.test_module}/tests/test_files/{expected_file_path}')
expected_etree = etree.parse(expected_file_full_path).getroot()
modified_etree = self.with_applied_xpath(
diff --git a/addons/l10n_dk_oioubl/tests/test_xml_oioubl_dk.py b/addons/l10n_dk_oioubl/tests/test_xml_oioubl_dk.py
index 9f2629fabc5..14e1e6d19cf 100644
--- a/addons/l10n_dk_oioubl/tests/test_xml_oioubl_dk.py
+++ b/addons/l10n_dk_oioubl/tests/test_xml_oioubl_dk.py
@@ -4,7 +4,6 @@ from odoo import Command, fields
from odoo.addons.l10n_account_edi_ubl_cii_tests.tests.common import TestUBLCommon
from odoo.addons.account.tests.test_account_move_send import TestAccountMoveSendCommon
from odoo.exceptions import UserError
-from odoo.modules.module import get_resource_path
from odoo.tests import tagged
from odoo.tools import file_open
@@ -179,9 +178,8 @@ class TestUBLDK(TestUBLCommon, TestAccountMoveSendCommon):
#########
def import_bill_xml_file_in_purchase_journal(self, file_path):
- full_file_path = get_resource_path(self.test_module, 'tests/test_files', file_path)
- self.assertTrue(full_file_path, f'File not found: {file_path}')
- with file_open(full_file_path, 'rb') as file:
+ file_path = f"{self.test_module}/tests/test_files/{file_path}"
+ with file_open(file_path, 'rb') as file:
xml_attachment = self.env['ir.attachment'].create({
'mimetype': 'application/xml',
'name': 'test_invoice.xml',
diff --git a/addons/l10n_sa_edi/models/account_edi_xml_ubl_21_zatca.py b/addons/l10n_sa_edi/models/account_edi_xml_ubl_21_zatca.py
index ddefa86e0e0..5d81f40a53e 100644
--- a/addons/l10n_sa_edi/models/account_edi_xml_ubl_21_zatca.py
+++ b/addons/l10n_sa_edi/models/account_edi_xml_ubl_21_zatca.py
@@ -3,7 +3,7 @@ from hashlib import sha256
from base64 import b64encode
from lxml import etree
from odoo import models, fields
-from odoo.modules.module import get_module_resource
+from odoo.tools.misc import file_path
import re
TAX_EXEMPTION_CODES = ['VATEX-SA-29', 'VATEX-SA-29-7', 'VATEX-SA-30']
@@ -57,7 +57,7 @@ class AccountEdiXmlUBL21Zatca(models.AbstractModel):
def _transform_and_canonicalize_xml(content):
""" Transform XML content to remove certain elements and signatures using an XSL template """
- invoice_xsl = etree.parse(get_module_resource('l10n_sa_edi', 'data', 'pre-hash_invoice.xsl'))
+ invoice_xsl = etree.parse(file_path('l10n_sa_edi/data/pre-hash_invoice.xsl'))
transform = etree.XSLT(invoice_xsl)
return _canonicalize_xml(transform(content))
diff --git a/addons/l10n_sa_edi/models/account_journal.py b/addons/l10n_sa_edi/models/account_journal.py
index 1afa0d24327..569341c9438 100644
--- a/addons/l10n_sa_edi/models/account_journal.py
+++ b/addons/l10n_sa_edi/models/account_journal.py
@@ -6,7 +6,7 @@ from datetime import datetime
from base64 import b64encode, b64decode
from odoo import models, fields, service, _, api
from odoo.exceptions import UserError
-from odoo.modules.module import get_module_resource
+from odoo.tools.misc import file_open
from requests.exceptions import HTTPError, RequestException
from cryptography import x509
from cryptography.x509 import ObjectIdentifier, load_der_x509_certificate
@@ -288,8 +288,8 @@ class AccountJournal(models.Model):
'simplified/invoice.xml', 'simplified/credit.xml', 'simplified/debit.xml',
], {}
for file in file_names:
- fpath = get_module_resource('l10n_sa_edi', 'tests/compliance', file)
- with open(fpath, 'rb') as ip:
+ fpath = f'l10n_sa_edi/tests/compliance/{file}'
+ with file_open(fpath, 'rb', filter_ext=('.xml',)) as ip:
compliance_files[file] = ip.read().decode()
return compliance_files
diff --git a/addons/lunch/models/lunch_product_category.py b/addons/lunch/models/lunch_product_category.py
index e53b8a89448..be850c5a244 100644
--- a/addons/lunch/models/lunch_product_category.py
+++ b/addons/lunch/models/lunch_product_category.py
@@ -5,7 +5,7 @@ import base64
from odoo import api, fields, models
-from odoo.modules.module import get_module_resource
+from odoo.tools.misc import file_open
class LunchProductCategory(models.Model):
@@ -16,8 +16,7 @@ class LunchProductCategory(models.Model):
@api.model
def _default_image(self):
- image_path = get_module_resource('lunch', 'static/img', 'lunch.png')
- return base64.b64encode(open(image_path, 'rb').read())
+ return base64.b64encode(file_open('lunch/static/img/lunch.png', 'rb').read())
name = fields.Char('Product Category', required=True, translate=True)
company_id = fields.Many2one('res.company')
diff --git a/addons/mail/models/template_reset_mixin.py b/addons/mail/models/template_reset_mixin.py
index aac6a0c6875..120936a05d8 100644
--- a/addons/mail/models/template_reset_mixin.py
+++ b/addons/mail/models/template_reset_mixin.py
@@ -7,10 +7,9 @@ from lxml import etree
from odoo import api, fields, models, tools, _
from odoo.exceptions import UserError
-from odoo.modules import get_module_resource
-from odoo.modules.module import get_resource_from_path, get_resource_path
+from odoo.modules.module import get_resource_from_path
from odoo.tools.convert import xml_import
-from odoo.tools.misc import file_open
+from odoo.tools.misc import file_path
from odoo.tools.translate import TranslationImporter, get_po_paths
@@ -79,7 +78,7 @@ class TemplateResetMixin(models.AbstractModel):
for template in self.filtered('template_fs'):
external_id = template.get_external_id().get(template.id)
module, xml_id = external_id.split('.')
- fullpath = get_resource_path(*template.template_fs.split('/'))
+ fullpath = file_path(template.template_fs)
if fullpath:
for field_name, field in template._fields.items():
if field.translate is True:
diff --git a/addons/mail/tests/test_mail_template.py b/addons/mail/tests/test_mail_template.py
index 621ed9a589c..3436c130459 100644
--- a/addons/mail/tests/test_mail_template.py
+++ b/addons/mail/tests/test_mail_template.py
@@ -5,7 +5,6 @@ from unittest.mock import patch
from odoo.addons.mail.tests.common import MailCommon
from odoo.exceptions import AccessError, ValidationError, UserError
-from odoo.modules.module import get_module_resource
from odoo.tests import Form, tagged, users
from odoo.tools import convert_file
@@ -174,13 +173,14 @@ class TestMailTemplate(MailCommon):
@tagged('mail_template')
class TestMailTemplateReset(MailCommon):
- def _load(self, module, *args):
+ def _load(self, module, filepath):
+ # pylint: disable=no-value-for-parameter
convert_file(self.env, module='mail',
- filename=get_module_resource(module, *args),
+ filename=filepath,
idref={}, mode='init', noupdate=False, kind='test')
def test_mail_template_reset(self):
- self._load('mail', 'tests', 'test_mail_template.xml')
+ self._load('mail', 'tests/test_mail_template.xml')
mail_template = self.env.ref('mail.mail_template_test').with_context(lang=self.env.user.lang)
@@ -212,7 +212,7 @@ class TestMailTemplateReset(MailCommon):
def test_mail_template_reset_translation(self):
""" Test if a translated value can be reset correctly when its translation exists/doesn't exist in the po file of the directory """
- self._load('mail', 'tests', 'test_mail_template.xml')
+ self._load('mail', 'tests/test_mail_template.xml')
self.env['res.lang']._activate_lang('en_UK')
self.env['res.lang']._activate_lang('fr_FR')
diff --git a/addons/pos_self_order/models/pos_config.py b/addons/pos_self_order/models/pos_config.py
index f9f146cc9f3..92028c04b9e 100644
--- a/addons/pos_self_order/models/pos_config.py
+++ b/addons/pos_self_order/models/pos_config.py
@@ -4,13 +4,14 @@
import io
import uuid
import base64
+from os.path import join as opj
from PIL import Image
from typing import Optional, List, Dict
from werkzeug.urls import url_quote
from odoo.exceptions import UserError
from odoo.tools import image_to_base64
-from odoo import api, fields, models, modules, _, service
+from odoo import api, fields, models, _, service
from odoo.tools import file_open, split_every
@@ -123,7 +124,7 @@ class PosConfig(models.Model):
for pos_config_id in pos_config_ids:
for image_name in ['landing_01.jpg', 'landing_02.jpg', 'landing_03.jpg']:
- image_path = modules.get_module_resource("pos_self_order", "static/img", image_name)
+ image_path = opj("pos_self_order/static/img", image_name)
attachment = self.env['ir.attachment'].create({
'name': image_name,
'datas': base64.b64encode(file_open(image_path, "rb").read()),
diff --git a/addons/purchase_stock/tests/common.py b/addons/purchase_stock/tests/common.py
index 34cfdc6ec88..f235d4634c1 100644
--- a/addons/purchase_stock/tests/common.py
+++ b/addons/purchase_stock/tests/common.py
@@ -4,7 +4,6 @@ from datetime import timedelta
from odoo import fields
from odoo.addons.stock.tests.common2 import TestStockCommon
from odoo import tools
-from odoo.modules.module import get_module_resource
class PurchaseTestCommon(TestStockCommon):
diff --git a/addons/rating/models/rating.py b/addons/rating/models/rating.py
index 7b005fb929c..9e740c5faf7 100644
--- a/addons/rating/models/rating.py
+++ b/addons/rating/models/rating.py
@@ -5,7 +5,7 @@ import uuid
from odoo import api, fields, models
from odoo.addons.rating.models import rating_data
-from odoo.modules.module import get_resource_path
+from odoo.tools.misc import file_open
class Rating(models.Model):
@@ -96,12 +96,13 @@ class Rating(models.Model):
self.rating_image_url = False
self.rating_image = False
for rating in self:
+ image_path = f'rating/static/src/img/{rating._get_rating_image_filename()}'
+ rating.rating_image_url = f'/{image_path}'
try:
- image_path = get_resource_path('rating', 'static/src/img', rating._get_rating_image_filename())
- rating.rating_image_url = '/rating/static/src/img/%s' % rating._get_rating_image_filename()
- rating.rating_image = base64.b64encode(open(image_path, 'rb').read()) if image_path else False
- except (IOError, OSError):
- pass
+ rating.rating_image = base64.b64encode(
+ file_open(image_path, 'rb', filter_ext=('.png',)).read())
+ except (IOError, OSError, FileNotFoundError):
+ rating.rating_image = False
@api.depends('rating')
def _compute_rating_text(self):
diff --git a/addons/sale/models/onboarding_onboarding_step.py b/addons/sale/models/onboarding_onboarding_step.py
index 68d34759929..5cb752115df 100644
--- a/addons/sale/models/onboarding_onboarding_step.py
+++ b/addons/sale/models/onboarding_onboarding_step.py
@@ -3,7 +3,6 @@
import base64
from odoo import _, api, Command, models
-from odoo.modules.module import get_resource_path
from odoo.tools import file_open
@@ -39,8 +38,7 @@ class OnboardingStep(models.Model):
# take any existing product or create one
product = self.env['product.product'].search([], limit=1)
if not product:
- default_image_path = get_resource_path('product', 'static/img', 'product_product_13-image.jpg')
- with file_open(default_image_path, 'rb') as default_image_stream:
+ with file_open('product/static/img/product_product_13-image.jpg', 'rb') as default_image_stream:
product = self.env['product.product'].create({
'name': _('Sample Product'),
'active': False,
diff --git a/addons/sale_product_configurator/tests/common.py b/addons/sale_product_configurator/tests/common.py
index 8088df922a4..e2ef4f1c82c 100644
--- a/addons/sale_product_configurator/tests/common.py
+++ b/addons/sale_product_configurator/tests/common.py
@@ -2,8 +2,7 @@
import base64
from odoo.tests.common import TransactionCase
-from odoo.modules.module import get_module_resource
-
+from odoo.tools.misc import file_open
class TestProductConfiguratorCommon(TransactionCase):
@@ -78,8 +77,8 @@ class TestProductConfiguratorCommon(TransactionCase):
cls.product_product_custo_desk.product_variant_ids[3].active = False
# Setup a first optional product
- img_path = get_module_resource('product', 'static', 'img', 'product_product_11-image.png')
- img_content = base64.b64encode(open(img_path, "rb").read())
+ img_path = 'product/static/img/product_product_11-image.png'
+ img_content = base64.b64encode(file_open(img_path, "rb").read())
cls.product_product_conf_chair = cls.env['product.template'].create({
'name': 'Conference Chair (TEST)',
'image_1920': img_content,
diff --git a/addons/sms/tests/test_sms_template.py b/addons/sms/tests/test_sms_template.py
index 5802fd62cd6..09dc00e77c7 100644
--- a/addons/sms/tests/test_sms_template.py
+++ b/addons/sms/tests/test_sms_template.py
@@ -7,7 +7,6 @@ from odoo.addons.mail.tests.common import mail_new_test_user
from odoo.exceptions import AccessError
from odoo.tests import tagged
from odoo.tools import mute_logger, convert_file
-from odoo.modules.module import get_module_resource
@tagged('post_install', '-at_install')
@@ -113,13 +112,14 @@ class TestSmsTemplateAccessRights(TransactionCase):
@tagged('post_install', '-at_install')
class TestSMSTemplateReset(TransactionCase):
- def _load(self, module, *args):
+ def _load(self, module, filepath):
+ # pylint: disable=no-value-for-parameter
convert_file(self.env, module='sms',
- filename=get_module_resource(module, *args),
+ filename=filepath,
idref={}, mode='init', noupdate=False, kind='test')
def test_sms_template_reset(self):
- self._load('sms', 'tests', 'test_sms_template.xml')
+ self._load('sms', 'tests/test_sms_template.xml')
sms_template = self.env.ref('sms.sms_template_test').with_context(lang=self.env.user.lang)
diff --git a/addons/test_base_import/tests/test_base_import.py b/addons/test_base_import/tests/test_base_import.py
index d5d407dbf99..345fa49ca84 100644
--- a/addons/test_base_import/tests/test_base_import.py
+++ b/addons/test_base_import/tests/test_base_import.py
@@ -7,8 +7,8 @@ import pprint
import unittest
from odoo.tests.common import TransactionCase, can_import, RecordCapturer
-from odoo.modules.module import get_module_resource
from odoo.tools import mute_logger, pycompat
+from odoo.tools.misc import file_open
from odoo.addons.base_import.models.base_import import ImportValidationError
from odoo.addons.test_base_import.models.test_base_import import model as base_import_model
@@ -358,8 +358,7 @@ class TestPreview(TransactionCase):
@unittest.skipUnless(can_import('xlrd'), "XLRD module not available")
def test_xls_success(self):
- xls_file_path = get_module_resource('test_base_import', 'data', 'test.xls')
- file_content = open(xls_file_path, 'rb').read()
+ file_content = file_open('test_base_import/data/test.xls', 'rb').read()
import_wizard = self.env['base_import.import'].create({
'res_model': base_import_model('preview'),
'file': file_content,
@@ -382,8 +381,7 @@ class TestPreview(TransactionCase):
@unittest.skipUnless(can_import('xlrd.xlsx'), "XLRD/XLSX not available")
def test_xlsx_success(self):
- xlsx_file_path = get_module_resource('test_base_import', 'data', 'test.xlsx')
- file_content = open(xlsx_file_path, 'rb').read()
+ file_content = file_open('test_base_import/data/test.xlsx', 'rb').read()
import_wizard = self.env['base_import.import'].create({
'res_model': base_import_model('preview'),
'file': file_content,
@@ -406,8 +404,7 @@ class TestPreview(TransactionCase):
@unittest.skipUnless(can_import('odf'), "ODFPY not available")
def test_ods_success(self):
- ods_file_path = get_module_resource('test_base_import', 'data', 'test.ods')
- file_content = open(ods_file_path, 'rb').read()
+ file_content = file_open('test_base_import/data/test.ods', 'rb').read()
import_wizard = self.env['base_import.import'].create({
'res_model': base_import_model('preview'),
'file': file_content,
diff --git a/addons/test_website/tests/test_qweb.py b/addons/test_website/tests/test_qweb.py
index 3f22322fad0..7e62156d815 100644
--- a/addons/test_website/tests/test_qweb.py
+++ b/addons/test_website/tests/test_qweb.py
@@ -6,19 +6,18 @@ import re
from odoo import tools
from odoo.addons.base.tests.common import TransactionCaseWithUserDemo
-from odoo.modules.module import get_module_resource
class TestQweb(TransactionCaseWithUserDemo):
- def _load(self, module, *args):
+ def _load(self, module, filepath):
tools.convert_file(
- self.env, 'test_website',
- get_module_resource(module, *args),
+ self.env, module,
+ filepath,
{}, 'init', False, 'test'
)
def test_qweb_cdn(self):
- self._load('test_website', 'tests', 'template_qweb_test.xml')
+ self._load('test_website', 'tests/template_qweb_test.xml')
website = self.env.ref('website.default_website')
website.write({
diff --git a/addons/web/controllers/binary.py b/addons/web/controllers/binary.py
index 08a1462e3b0..0a51a304b74 100644
--- a/addons/web/controllers/binary.py
+++ b/addons/web/controllers/binary.py
@@ -18,7 +18,6 @@ import odoo.modules.registry
from odoo import http, _
from odoo.exceptions import AccessError, UserError
from odoo.http import request, Response
-from odoo.modules import get_resource_path
from odoo.tools import file_open, file_path, replace_exceptions
from odoo.tools.mimetypes import guess_mimetype
from odoo.tools.image import image_guess_size_from_field_name
@@ -214,12 +213,11 @@ class Binary(http.Controller):
def company_logo(self, dbname=None, **kw):
imgname = 'logo'
imgext = '.png'
- placeholder = functools.partial(get_resource_path, 'web', 'static', 'img')
dbname = request.db
uid = (request.session.uid if dbname else None) or odoo.SUPERUSER_ID
if not dbname:
- response = http.Stream.from_path(placeholder(imgname + imgext)).get_response()
+ response = http.Stream.from_path(file_path('web/static/img/logo.png')).get_response()
else:
try:
# create an empty registry
@@ -255,9 +253,9 @@ class Binary(http.Controller):
response_class=Response,
)
else:
- response = http.Stream.from_path(placeholder('nologo.png')).get_response()
+ response = http.Stream.from_path(file_path('web/static/img/nologo.png')).get_response()
except Exception:
- response = http.Stream.from_path(placeholder(imgname + imgext)).get_response()
+ response = http.Stream.from_path(file_path(f'web/static/img/{imgname}{imgext}')).get_response()
return response
@@ -273,7 +271,7 @@ class Binary(http.Controller):
"""
supported_exts = ('.ttf', '.otf', '.woff', '.woff2')
fonts = []
- fonts_directory = file_path(os.path.join('web', 'static', 'fonts', 'sign'))
+ fonts_directory = file_path('web/static/fonts/sign')
if fontname:
font_path = os.path.join(fonts_directory, fontname)
with file_open(font_path, 'rb', filter_ext=supported_exts) as font_file:
diff --git a/addons/web/controllers/webclient.py b/addons/web/controllers/webclient.py
index e6d8783393f..47dcad879fb 100644
--- a/addons/web/controllers/webclient.py
+++ b/addons/web/controllers/webclient.py
@@ -13,10 +13,10 @@ import werkzeug.wsgi
import odoo
import odoo.modules.registry
from odoo import http
-from odoo.modules import get_manifest, get_resource_path
+from odoo.modules import get_manifest
from odoo.http import request
from odoo.tools import lazy
-from odoo.tools.misc import file_open
+from odoo.tools.misc import file_open, file_path
from .utils import _local_web_translations
@@ -80,7 +80,7 @@ class WebClient(http.Controller):
for addon_name in mods:
manifest = get_manifest(addon_name)
if manifest and manifest['bootstrap']:
- f_name = get_resource_path(addon_name, 'i18n', f'{lang}.po')
+ f_name = file_path(f'{addon_name}/i18n/{lang}.po')
if not f_name:
continue
translations_per_module[addon_name] = {'messages': _local_web_translations(f_name)}
diff --git a/addons/web/models/base_document_layout.py b/addons/web/models/base_document_layout.py
index 7483339a310..2c40c6b8c11 100644
--- a/addons/web/models/base_document_layout.py
+++ b/addons/web/models/base_document_layout.py
@@ -5,8 +5,8 @@ from markupsafe import Markup
from odoo import api, fields, models, tools
from odoo.addons.base.models.ir_qweb_fields import nl2br
-from odoo.modules import get_resource_path
from odoo.tools import html2plaintext, is_html_empty
+from odoo.tools.misc import file_path
try:
import sass as libsass
@@ -289,7 +289,7 @@ class BaseDocumentLayout(models.TransientModel):
precision = 8
output_style = 'expanded'
- bootstrap_path = get_resource_path('web', 'static', 'lib', 'bootstrap', 'scss')
+ bootstrap_path = file_path('web/static/lib/bootstrap/scss')
try:
return libsass.compile(
diff --git a/addons/web/tests/test_js.py b/addons/web/tests/test_js.py
index 4cef8ec460c..a62817ce65a 100644
--- a/addons/web/tests/test_js.py
+++ b/addons/web/tests/test_js.py
@@ -2,7 +2,10 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import re
+from contextlib import suppress
+
import odoo.tests
+from odoo.tools.misc import file_open
RE_ONLY = re.compile(r'QUnit\.(only|debug)\(')
@@ -45,11 +48,12 @@ class WebSuite(odoo.tests.HttpCase):
for asset in assets:
filename = asset['filename']
- if not filename or not filename.endswith('.js'):
+ if not filename.endswith('.js'):
continue
- with open(filename, 'rb') as fp:
- if RE_ONLY.search(fp.read().decode('utf-8')):
- self.fail("`QUnit.only()` or `QUnit.debug()` used in file %r" % asset['url'])
+ with suppress(FileNotFoundError):
+ with file_open(filename, 'rb', filter_ext=('.js',)) as fp:
+ if RE_ONLY.search(fp.read().decode('utf-8')):
+ self.fail("`QUnit.only()` or `QUnit.debug()` used in file %r" % asset['url'])
@odoo.tests.tagged('post_install', '-at_install')
diff --git a/addons/web_editor/controllers/main.py b/addons/web_editor/controllers/main.py
index 4224f7471f5..1fd886ee95d 100644
--- a/addons/web_editor/controllers/main.py
+++ b/addons/web_editor/controllers/main.py
@@ -14,14 +14,14 @@ from lxml import etree
from base64 import b64decode, b64encode
from datetime import datetime
from math import floor
+from os.path import join as opj
from odoo.http import request, Response
from odoo import http, tools, _, SUPERUSER_ID
from odoo.addons.http_routing.models.ir_http import slug, unslug
from odoo.addons.web_editor.tools import get_video_url_data
from odoo.exceptions import UserError, MissingError, ValidationError
-from odoo.modules.module import get_resource_path
-from odoo.tools import file_open
+from odoo.tools.misc import file_open
from odoo.tools.mimetypes import guess_mimetype
from odoo.tools.image import image_data_uri, binary_to_image
from odoo.addons.base.models.assetsbundle import AssetsBundle
@@ -569,11 +569,12 @@ class Web_Editor(http.Controller):
return '%s?access_token=%s' % (attachment.image_src, attachment.access_token)
def _get_shape_svg(self, module, *segments):
- shape_path = get_resource_path(module, 'static', *segments)
- if not shape_path:
+ shape_path = opj(module, 'static', *segments)
+ try:
+ with file_open(shape_path, 'r', filter_ext=('.svg',)) as file:
+ return file.read()
+ except FileNotFoundError:
raise werkzeug.exceptions.NotFound()
- with tools.file_open(shape_path, 'r', filter_ext=('.svg',)) as file:
- return file.read()
def _update_svg_colors(self, options, svg):
user_colors = []
diff --git a/addons/web_editor/models/ir_qweb_fields.py b/addons/web_editor/models/ir_qweb_fields.py
index 98b5ea89d4a..7df43391ca6 100644
--- a/addons/web_editor/models/ir_qweb_fields.py
+++ b/addons/web_editor/models/ir_qweb_fields.py
@@ -29,7 +29,7 @@ from odoo import _, api, models, fields
from odoo.exceptions import UserError, ValidationError
from odoo.tools import ustr, posix_to_ldml, pycompat
from odoo.tools import html_escape as escape
-from odoo.tools.misc import get_lang, babel_locale_parse
+from odoo.tools.misc import file_open, get_lang, babel_locale_parse
REMOTE_CONNECTION_TIMEOUT = 2.5
@@ -479,20 +479,13 @@ class Image(models.AbstractModel):
def load_local_url(self, url):
match = self.local_url_re.match(urls.url_parse(url).path)
-
rest = match.group('rest')
- for sep in os.sep, os.altsep:
- if sep and sep != '/':
- rest.replace(sep, '/')
- path = odoo.modules.get_module_resource(
- match.group('module'), 'static', *(rest.split('/')))
-
- if not path:
- return None
+ path = os.path.join(
+ match.group('module'), 'static', rest)
try:
- with open(path, 'rb') as f:
+ with file_open(path, 'rb') as f:
# force complete image load to ensure it's valid image data
image = I.open(f)
image.load()
diff --git a/addons/website/models/website.py b/addons/website/models/website.py
index 84d802998de..00a49c1d17d 100644
--- a/addons/website/models/website.py
+++ b/addons/website/models/website.py
@@ -23,7 +23,7 @@ from odoo.addons.portal.controllers.portal import pager
from odoo.addons.iap.tools import iap_tools
from odoo.exceptions import AccessError, MissingError, UserError, ValidationError
from odoo.http import request
-from odoo.modules.module import get_resource_path, get_manifest
+from odoo.modules.module import get_manifest
from odoo.osv.expression import AND, OR, FALSE_DOMAIN, get_unaccent_wrapper
from odoo.tools.translate import _, xml_translate
from odoo.tools import escape_psql, pycompat
@@ -98,8 +98,7 @@ class Website(models.Model):
return self.env.ref('base.main_company').social_tiktok
def _default_logo(self):
- image_path = get_resource_path('website', 'static/src/img', 'website_logo.svg')
- with tools.file_open(image_path, 'rb') as f:
+ with tools.file_open('website/static/src/img/website_logo.svg', 'rb') as f:
return base64.b64encode(f.read())
logo = fields.Binary('Website Logo', default=_default_logo, help="Display this logo on the website.")
@@ -134,8 +133,7 @@ class Website(models.Model):
robots_txt = fields.Html('Robots.txt', translate=False, groups='website.group_website_designer', sanitize=False)
def _default_favicon(self):
- img_path = get_resource_path('web', 'static/img/favicon.ico')
- with tools.file_open(img_path, 'rb') as f:
+ with tools.file_open('web/static/img/favicon.ico', 'rb') as f:
return base64.b64encode(f.read())
favicon = fields.Binary(string="Website Favicon", help="This field holds the image used to display a favicon on the website.", default=_default_favicon)
diff --git a/addons/website/models/website_configurator_feature.py b/addons/website/models/website_configurator_feature.py
index ce59015dd6f..fb9071a1545 100644
--- a/addons/website/models/website_configurator_feature.py
+++ b/addons/website/models/website_configurator_feature.py
@@ -5,7 +5,6 @@ import re
from odoo import api, fields, models, tools, _
from odoo.exceptions import ValidationError
-from odoo.modules.module import get_resource_path
class WebsiteConfiguratorFeature(models.Model):
@@ -34,11 +33,11 @@ class WebsiteConfiguratorFeature(models.Model):
@staticmethod
def _process_svg(theme, colors, image_mapping):
svg = None
- preview_svg = get_resource_path(theme, 'static', 'description', theme + '.svg')
- if not preview_svg:
+ try:
+ with tools.file_open(f'{theme}/static/description/{theme}.svg', 'r') as file:
+ svg = file.read()
+ except FileNotFoundError:
return False
- with tools.file_open(preview_svg, 'r') as file:
- svg = file.read()
default_colors = {
'color1': '#3AADAA',
diff --git a/addons/website_event_exhibitor/models/event_sponsor.py b/addons/website_event_exhibitor/models/event_sponsor.py
index 9a08a723e09..91af79d7edd 100644
--- a/addons/website_event_exhibitor/models/event_sponsor.py
+++ b/addons/website_event_exhibitor/models/event_sponsor.py
@@ -7,7 +7,6 @@ from pytz import timezone, utc
from odoo import api, fields, models, _
from odoo.addons.http_routing.models.ir_http import slug
from odoo.addons.resource.models.utils import float_to_time
-from odoo.modules.module import get_resource_path
from odoo.tools import is_html_empty
from odoo.tools.translate import html_translate
@@ -116,7 +115,7 @@ class Sponsor(models.Model):
elif sponsor.partner_id.image_256:
sponsor.website_image_url = self.env['website'].image_url(sponsor.partner_id, 'image_256', size=256)
else:
- sponsor.website_image_url = get_resource_path('website_event_exhibitor', 'static/src/img', 'event_sponsor_default_%d.png' % (sponsor.id % 1))
+ sponsor.website_image_url = 'website_event_exhibitor/static/src/img/event_sponsor_default_0.jpeg'
def _synchronize_with_partner(self, fname):
""" Synchronize with partner if not set. Setting a value does not write
diff --git a/addons/website_event_track/controllers/webmanifest.py b/addons/website_event_track/controllers/webmanifest.py
index 627c2581577..deee466bef1 100644
--- a/addons/website_event_track/controllers/webmanifest.py
+++ b/addons/website_event_track/controllers/webmanifest.py
@@ -7,8 +7,8 @@ import pytz
from odoo import http
from odoo.addons.http_routing.models.ir_http import url_for
from odoo.http import request
-from odoo.modules.module import get_module_resource
from odoo.tools import ustr
+from odoo.tools.misc import file_open
from odoo.tools.translate import _
@@ -47,8 +47,7 @@ class TrackManifest(http.Controller):
def service_worker(self):
""" Returns a ServiceWorker javascript file scoped for website_event
"""
- sw_file = get_module_resource('website_event_track', 'static/src/js/service_worker.js')
- with open(sw_file, 'r') as fp:
+ with file_open('website_event_track/static/src/js/service_worker.js', 'r') as fp:
body = fp.read()
js_cdn_url = 'undefined'
if request.website.cdn_activated:
diff --git a/addons/website_sale/tests/test_customize.py b/addons/website_sale/tests/test_customize.py
index 652f688fa47..81e694c64fb 100644
--- a/addons/website_sale/tests/test_customize.py
+++ b/addons/website_sale/tests/test_customize.py
@@ -4,8 +4,9 @@ import base64
from odoo.addons.base.tests.common import HttpCaseWithUserDemo, HttpCaseWithUserPortal
from odoo.fields import Command
-from odoo.modules.module import get_module_resource
from odoo.tests import tagged
+from odoo.tools.misc import file_open
+
@tagged('post_install', '-at_install')
class TestUi(HttpCaseWithUserDemo, HttpCaseWithUserPortal):
@@ -125,8 +126,7 @@ class TestUi(HttpCaseWithUserDemo, HttpCaseWithUserPortal):
})
self.product_product_4_product_template.attribute_line_ids[0].write({'value_ids': [(4, product_attribute_value_7.id)]})
- img_path = get_module_resource('product', 'static', 'img', 'product_product_11-image.png')
- img_content = base64.b64encode(open(img_path, "rb").read())
+ img_content = base64.b64encode(file_open('product/static/img/product_product_11-image.png', "rb").read())
self.product_product_11_product_template = self.env['product.template'].create({
'name': 'Conference Chair (TEST)',
'website_sequence': 9999, # laule
diff --git a/addons/website_slides/tests/test_ui_wslides.py b/addons/website_slides/tests/test_ui_wslides.py
index 4178f364da0..cff48fb183f 100644
--- a/addons/website_slides/tests/test_ui_wslides.py
+++ b/addons/website_slides/tests/test_ui_wslides.py
@@ -5,9 +5,9 @@ import base64
from dateutil.relativedelta import relativedelta
from odoo import tests
from odoo.fields import Datetime
-from odoo.modules.module import get_module_resource
from odoo.tools import mute_logger
from odoo.addons.base.tests.common import HttpCaseWithUserDemo, HttpCaseWithUserPortal
+from odoo.tools.misc import file_open
class TestUICommon(HttpCaseWithUserDemo, HttpCaseWithUserPortal):
@@ -15,10 +15,8 @@ class TestUICommon(HttpCaseWithUserDemo, HttpCaseWithUserPortal):
def setUp(self):
super(TestUICommon, self).setUp()
# Load pdf and img contents
- pdf_path = get_module_resource('website_slides', 'static', 'src', 'img', 'presentation.pdf')
- pdf_content = base64.b64encode(open(pdf_path, "rb").read())
- img_path = get_module_resource('website_slides', 'static', 'src', 'img', 'slide_demo_gardening_1.jpg')
- img_content = base64.b64encode(open(img_path, "rb").read())
+ pdf_content = base64.b64encode(file_open('website_slides/static/src/img/presentation.pdf', "rb").read())
+ img_content = base64.b64encode(file_open('website_slides/static/src/img/slide_demo_gardening_1.jpg', "rb").read())
self.channel = self.env['slide.channel'].create({
'name': 'Basics of Gardening - Test',
diff --git a/odoo/addons/base/models/assetsbundle.py b/odoo/addons/base/models/assetsbundle.py
index afbd947082e..2226261c7ce 100644
--- a/odoo/addons/base/models/assetsbundle.py
+++ b/odoo/addons/base/models/assetsbundle.py
@@ -28,12 +28,11 @@ from rjsmin import jsmin as rjsmin
from odoo import release, SUPERUSER_ID, _
from odoo.http import request
-from odoo.modules.module import get_resource_path
from odoo.tools import (func, misc, transpile_javascript,
is_odoo_module, SourceMapGenerator, profiler,
apply_inheritance_specs)
from odoo.tools.constants import SCRIPT_EXTENSIONS, STYLE_EXTENSIONS
-from odoo.tools.misc import file_open, html_escape as escape
+from odoo.tools.misc import file_open, file_path
from odoo.tools.pycompat import to_text
_logger = logging.getLogger(__name__)
@@ -186,8 +185,8 @@ class AssetsBundle(object):
self.env.cr.execute(f"""DELETE FROM {attachments._table} WHERE id IN (
SELECT id FROM {attachments._table} WHERE id in %s FOR NO KEY UPDATE SKIP LOCKED
)""", [tuple(attachments.ids)])
- for file_path in to_delete:
- attachments._file_delete(file_path)
+ for fpath in to_delete:
+ attachments._file_delete(fpath)
def clean_attachments(self, extension):
""" Takes care of deleting any outdated ir.attachment records associated to a bundle before
@@ -719,7 +718,7 @@ class AssetsBundle(object):
except IOError:
rtlcss = 'rtlcss'
- cmd = [rtlcss, '-c', get_resource_path("base", "data/rtlcss.json"), '-']
+ cmd = [rtlcss, '-c', file_path("base/data/rtlcss.json"), '-']
try:
rtlcss = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
@@ -1091,7 +1090,7 @@ class SassStylesheetAsset(PreprocessedCSS):
class ScssStylesheetAsset(PreprocessedCSS):
@property
def bootstrap_path(self):
- return get_resource_path('web', 'static', 'lib', 'bootstrap', 'scss')
+ return file_path('web/static/lib/bootstrap/scss')
precision = 8
output_style = 'expanded'
diff --git a/odoo/addons/base/models/ir_http.py b/odoo/addons/base/models/ir_http.py
index 56a28edfb7b..48f938ab1f3 100644
--- a/odoo/addons/base/models/ir_http.py
+++ b/odoo/addons/base/models/ir_http.py
@@ -31,7 +31,6 @@ from odoo.modules.registry import Registry
from odoo.service import security
from odoo.tools import get_lang, submap
from odoo.tools.translate import code_translations
-from odoo.modules.module import get_resource_path, get_module_path
_logger = logging.getLogger(__name__)
diff --git a/odoo/addons/base/models/ir_module.py b/odoo/addons/base/models/ir_module.py
index cf5d5c8e550..8d18e2c015a 100644
--- a/odoo/addons/base/models/ir_module.py
+++ b/odoo/addons/base/models/ir_module.py
@@ -9,7 +9,6 @@ import io
import logging
import os
import shutil
-import tempfile
import threading
import zipfile
@@ -32,7 +31,7 @@ from odoo.tools.parse_version import parse_version
from odoo.tools.misc import topological_sort, get_flag
from odoo.tools.translate import TranslationImporter, get_po_paths
from odoo.http import request
-from odoo.modules import get_module_path, get_module_resource
+from odoo.modules import get_module_path
_logger = logging.getLogger(__name__)
@@ -162,10 +161,8 @@ class Module(models.Model):
if not module.name:
module.description_html = False
continue
- module_path = modules.get_module_path(module.name, display_warning=False) # avoid to log warning for fake community module
- if module_path:
- path = modules.check_resource_path(module_path, 'static/description/index.html')
- if module_path and path:
+ path = os.path.join(module.name, '/static/description/index.html')
+ try:
with tools.file_open(path, 'rb') as desc_file:
doc = desc_file.read()
if doc.startswith(XML_DECLARATION):
@@ -191,7 +188,7 @@ class Module(models.Model):
if element.get('src') and not '//' in element.get('src') and not 'static/' in element.get('src'):
element.set('src', "/%s/static/description/%s" % (module.name, element.get('src')))
module.description_html = tools.html_sanitize(lxml.html.tostring(html))
- else:
+ except FileNotFoundError:
overrides = {
'embed_stylesheet': False,
'doctitle_xform': False,
@@ -246,7 +243,7 @@ class Module(models.Model):
module.icon_image = ''
if module.icon:
path_parts = module.icon.split('/')
- path = modules.get_module_resource(path_parts[1], *path_parts[2:])
+ path = os.path.join(path_parts[1], *path_parts[2:])
elif module.id:
path = modules.module.get_module_icon_path(module)
else:
diff --git a/odoo/addons/base/models/ir_ui_menu.py b/odoo/addons/base/models/ir_ui_menu.py
index e35f6ff4d31..56533906471 100644
--- a/odoo/addons/base/models/ir_ui_menu.py
+++ b/odoo/addons/base/models/ir_ui_menu.py
@@ -3,13 +3,13 @@
import base64
from collections import defaultdict
+from os.path import join as opj
import operator
import re
from odoo import api, fields, models, tools, _
from odoo.exceptions import ValidationError
from odoo.http import request
-from odoo.modules import get_module_resource
from odoo.osv import expression
MENU_ITEM_SEPARATOR = "/"
@@ -56,16 +56,16 @@ class IrUiMenu(models.Model):
else:
return self.name
- def read_image(self, path):
+ def _read_image(self, path):
if not path:
return False
path_info = path.split(',')
- icon_path = get_module_resource(path_info[0], path_info[1])
- icon_image = False
- if icon_path:
- with tools.file_open(icon_path, 'rb') as icon_file:
- icon_image = base64.encodebytes(icon_file.read())
- return icon_image
+ icon_path = opj(path_info[0], path_info[1])
+ try:
+ with tools.file_open(icon_path, 'rb', filter_ext=('.png',)) as icon_file:
+ return base64.encodebytes(icon_file.read())
+ except FileNotFoundError:
+ return False
@api.constrains('parent_id')
def _check_parent_id(self):
@@ -179,10 +179,10 @@ class IrUiMenu(models.Model):
`web_icon` can either be:
- an image icon [module, path]
- a built icon [icon_class, icon_color, background_color]
- and it only has to call `read_image` if it's an image.
+ and it only has to call `_read_image` if it's an image.
"""
if web_icon and len(web_icon.split(',')) == 2:
- return self.read_image(web_icon)
+ return self._read_image(web_icon)
def unlink(self):
# Detach children and promote them to top-level, because it would be unwise to
diff --git a/odoo/addons/base/models/ir_ui_view.py b/odoo/addons/base/models/ir_ui_view.py
index 21247415d6a..9e248f40bc4 100644
--- a/odoo/addons/base/models/ir_ui_view.py
+++ b/odoo/addons/base/models/ir_ui_view.py
@@ -21,12 +21,13 @@ import odoo
from odoo import api, fields, models, tools, _
from odoo.exceptions import ValidationError, AccessError, UserError
from odoo.http import request
-from odoo.modules.module import get_resource_from_path, get_resource_path
+from odoo.modules.module import get_resource_from_path
from odoo.tools import config, ConstantMapping, get_diff, pycompat, apply_inheritance_specs, locate_node, str2bool
-from odoo.tools.convert import _fix_multiple_roots
from odoo.tools import safe_eval, lazy, lazy_property, frozendict
-from odoo.tools.view_validation import valid_view, get_domain_value_names, get_expression_field_names, get_dict_asts
+from odoo.tools.convert import _fix_multiple_roots
+from odoo.tools.misc import file_path
from odoo.tools.translate import xml_translate, TRANSLATED_ATTRS
+from odoo.tools.view_validation import valid_view, get_domain_value_names, get_expression_field_names, get_dict_asts
from odoo.models import check_method_name
from odoo.osv.expression import expression
@@ -234,22 +235,24 @@ actual arch.
if read_file and view.arch_fs and (view.xml_id or view.key):
xml_id = view.xml_id or view.key
# It is safe to split on / herebelow because arch_fs is explicitely stored with '/'
- fullpath = get_resource_path(*view.arch_fs.split('/'))
- if fullpath:
- arch_fs = get_view_arch_from_file(fullpath, xml_id)
- # replace %(xml_id)s, %(xml_id)d, %%(xml_id)s, %%(xml_id)d by the res_id
- if arch_fs:
- arch_fs = resolve_external_ids(arch_fs, xml_id).replace('%%', '%')
- translation_dictionary = field_arch_db.get_translation_dictionary(
- view.with_env(env_en).arch_db, {lang: view.with_env(env_lang).arch_db}
- )
- arch_fs = field_arch_db.translate(
- lambda term: translation_dictionary[term][lang],
- arch_fs
- )
- else:
+ try:
+ fullpath = file_path(view.arch_fs)
+ except FileNotFoundError:
_logger.warning("View %s: Full path [%s] cannot be found.", xml_id, view.arch_fs)
arch_fs = False
+ continue
+
+ arch_fs = get_view_arch_from_file(fullpath, xml_id)
+ # replace %(xml_id)s, %(xml_id)d, %%(xml_id)s, %%(xml_id)d by the res_id
+ if arch_fs:
+ arch_fs = resolve_external_ids(arch_fs, xml_id).replace('%%', '%')
+ translation_dictionary = field_arch_db.get_translation_dictionary(
+ view.with_env(env_en).arch_db, {lang: view.with_env(env_lang).arch_db}
+ )
+ arch_fs = field_arch_db.translate(
+ lambda term: translation_dictionary[term][lang],
+ arch_fs
+ )
view.arch = pycompat.to_text(arch_fs or view.arch_db)
def _inverse_arch(self):
diff --git a/odoo/addons/base/tests/test_pdf.py b/odoo/addons/base/tests/test_pdf.py
index 6f1368e40d0..96d63ca71fa 100644
--- a/odoo/addons/base/tests/test_pdf.py
+++ b/odoo/addons/base/tests/test_pdf.py
@@ -3,7 +3,7 @@
from odoo.tests.common import TransactionCase
from odoo.tools import pdf
-from odoo.modules.module import get_module_resource
+from odoo.tools.misc import file_open
import io
@@ -12,8 +12,7 @@ class TestPdf(TransactionCase):
def setUp(self):
super().setUp()
- file_path = get_module_resource('base', 'tests', 'minimal.pdf')
- self.file = open(file_path, 'rb').read()
+ self.file = file_open('base/tests/minimal.pdf', 'rb').read()
self.minimal_reader_buffer = io.BytesIO(self.file)
self.minimal_pdf_reader = pdf.OdooPdfFileReader(self.minimal_reader_buffer)
diff --git a/odoo/addons/base/tests/test_qweb.py b/odoo/addons/base/tests/test_qweb.py
index 5f9a22c5722..a57193dfe6c 100644
--- a/odoo/addons/base/tests/test_qweb.py
+++ b/odoo/addons/base/tests/test_qweb.py
@@ -12,7 +12,6 @@ from lxml.builder import E
from copy import deepcopy
from textwrap import dedent
-from odoo.modules import get_module_resource
from odoo.tests.common import TransactionCase
from odoo.addons.base.models.ir_qweb import QWebException, render
from odoo.tools import misc, mute_logger
diff --git a/odoo/addons/test_assetsbundle/tests/test_assetsbundle.py b/odoo/addons/test_assetsbundle/tests/test_assetsbundle.py
index 96213e3ab13..a202d1f0c41 100644
--- a/odoo/addons/test_assetsbundle/tests/test_assetsbundle.py
+++ b/odoo/addons/test_assetsbundle/tests/test_assetsbundle.py
@@ -18,12 +18,12 @@ from odoo.addons import __path__ as ADDONS_PATH
from odoo.addons.base.models.assetsbundle import AssetsBundle
from odoo.addons.base.models.ir_asset import AssetPaths
from odoo.addons.base.models.ir_attachment import IrAttachment
-from odoo.modules.module import get_resource_path, get_manifest
+from odoo.modules.module import get_manifest
from odoo.tests import HttpCase, tagged
from odoo.tests.common import TransactionCase
from odoo.addons.base.models.ir_qweb import QWebException
from odoo.tools import mute_logger, func
-
+from odoo.tools.misc import file_path
GETMTINE = os.path.getmtime
@@ -241,7 +241,7 @@ class TestJavascriptAssetsBundle(FileTouchable):
last_modified0 = bundle0.get_checksum('js')
version0 = bundle0.get_version('js')
- path = get_resource_path('test_assetsbundle', 'static', 'src', 'js', 'test_jsfile1.js')
+ path = file_path('test_assetsbundle/static/src/js/test_jsfile1.js')
bundle1 = self._get_asset(self.jsbundle_name, debug_assets=True)
with self._touch(path):
@@ -504,7 +504,7 @@ class TestJavascriptAssetsBundle(FileTouchable):
# Touch test_cssfile1.css
# Note: No lang specific context given while calling _get_asset so it will load assets for en_US
- path = get_resource_path('test_assetsbundle', 'static', 'src', 'css', 'test_cssfile1.css')
+ path = file_path('test_assetsbundle/static/src/css/test_cssfile1.css')
ltr_bundle1 = self._get_asset(self.cssbundle_name, debug_assets=True)
with self._touch(path):
@@ -768,7 +768,7 @@ class TestAssetsBundleWithIRAMock(FileTouchable):
self._bundle(self._get_asset(), False, False, '(Second access, no change)')
# Touch the file and compile a third time
- path = get_resource_path('test_assetsbundle', 'static', 'src', 'scss', 'test_file1.scss')
+ path = file_path('test_assetsbundle/static/src/scss/test_file1.scss')
t = time.time() + 5
asset = self._get_asset()
with self._touch(path, t):
diff --git a/odoo/addons/test_convert/tests/test_convert.py b/odoo/addons/test_convert/tests/test_convert.py
index 549b19acd69..528fe355b4f 100644
--- a/odoo/addons/test_convert/tests/test_convert.py
+++ b/odoo/addons/test_convert/tests/test_convert.py
@@ -8,9 +8,9 @@ from lxml import etree as ET
from lxml.builder import E
import odoo
-from odoo.modules.module import get_resource_path
from odoo.tests import common
from odoo.tools.convert import convert_file, xml_import, _eval_xml
+from odoo.tools.misc import file_path
Field = E.field
Value = E.value
@@ -237,7 +237,7 @@ class TestEvalXML(common.TransactionCase):
self.assertEqual(record.name, 'bar')
# Reset the value to the one from the XML data file,
# with a lang passed in the environment.
- filepath = get_resource_path('test_convert', 'data/test_translated_field/test_model_data.xml')
+ filepath = file_path('test_convert/data/test_translated_field/test_model_data.xml')
doc = ET.parse(filepath)
obj = xml_import(env_fr, 'test_convert', {}, mode='init', xml_filename=filepath)
obj.parse(doc.getroot())
diff --git a/odoo/addons/test_converter/tests/test_html.py b/odoo/addons/test_converter/tests/test_html.py
index 694f4e725b0..510a9524e29 100644
--- a/odoo/addons/test_converter/tests/test_html.py
+++ b/odoo/addons/test_converter/tests/test_html.py
@@ -7,6 +7,7 @@ import re
from odoo.tests import common
from odoo.tools import html_escape as e
+from odoo.tools.misc import file_open
directory = os.path.dirname(__file__)
@@ -216,7 +217,7 @@ class TestBinaryExport(TestBasicExport):
def test_image(self):
converter = self.env['ir.qweb.field.image']
- with open(os.path.join(directory, 'test_vectors', 'image'), 'rb') as f:
+ with file_open(os.path.join(directory, 'test_vectors', 'image'), 'rb') as f:
content = f.read()
encoded_content = base64.b64encode(content)
@@ -225,13 +226,13 @@ class TestBinaryExport(TestBasicExport):
self.assertEqual(
value, u'
' % encoded_content.decode('ascii'))
- with open(os.path.join(directory, 'test_vectors', 'pdf'), 'rb') as f:
+ with file_open(os.path.join(directory, 'test_vectors', 'pdf'), 'rb') as f:
content = f.read()
with self.assertRaises(ValueError):
converter.value_to_html(base64.b64encode(content), {})
- with open(os.path.join(directory, 'test_vectors', 'pptx'), 'rb') as f:
+ with file_open(os.path.join(directory, 'test_vectors', 'pptx'), 'rb') as f:
content = f.read()
with self.assertRaises(ValueError):
diff --git a/odoo/addons/test_lint/tests/test_eslint.py b/odoo/addons/test_lint/tests/test_eslint.py
index a7a5bfc68eb..35ffdf4ceb4 100644
--- a/odoo/addons/test_lint/tests/test_eslint.py
+++ b/odoo/addons/test_lint/tests/test_eslint.py
@@ -6,8 +6,8 @@ import re
import subprocess
from unittest import skipIf
from odoo import tools
-from odoo.modules.module import get_resource_path
from odoo.tests import tagged
+from odoo.tools.misc import file_path
from . import lint_case
@@ -32,7 +32,7 @@ class TestESLint(lint_case.LintCase):
if not re.match('.*/libs?/.*', p) # don't check libraries
if not re.match('.*/o_spreadsheet/o_spreadsheet.js', p) # don't check generated code
]
- eslintrc_path = get_resource_path('test_lint', 'tests', 'eslintrc')
+ eslintrc_path = file_path('test_lint/tests/eslintrc')
_logger.info('Testing %s js files', len(files_to_check))
# https://eslint.org/docs/user-guide/command-line-interface
diff --git a/odoo/addons/test_lint/tests/test_l10n.py b/odoo/addons/test_lint/tests/test_l10n.py
index 225ce9d2bcc..9d6f141613b 100644
--- a/odoo/addons/test_lint/tests/test_l10n.py
+++ b/odoo/addons/test_lint/tests/test_l10n.py
@@ -6,7 +6,7 @@ import itertools
import os
from . import lint_case
-
+from odoo.tools.misc import file_open
class L10nChecker(lint_case.NodeVisitor):
def matches_tagged(self, node):
@@ -44,7 +44,7 @@ class L10nLinter(lint_case.LintCase):
checker = L10nChecker()
rs = []
for path in self.iter_module_files('**/l10n_*/tests/*.py'):
- with open(path, 'rb') as f:
+ with file_open(path, 'rb') as f:
t = ast.parse(f.read(), path)
rs.extend(zip(itertools.repeat(os.path.relpath(path)), checker.visit(t)))
diff --git a/odoo/addons/test_lint/tests/test_manifests.py b/odoo/addons/test_lint/tests/test_manifests.py
index d5fd176e518..77a6adde81f 100644
--- a/odoo/addons/test_lint/tests/test_manifests.py
+++ b/odoo/addons/test_lint/tests/test_manifests.py
@@ -2,11 +2,12 @@
import logging
from ast import literal_eval
+from os.path import join as opj
from odoo.modules import get_modules
-from odoo.modules.module import _DEFAULT_MANIFEST, module_manifest, get_module_path, get_module_resource
+from odoo.modules.module import _DEFAULT_MANIFEST, module_manifest, get_module_path
from odoo.tests import BaseCase
-from odoo.tools import file_open
+from odoo.tools.misc import file_open, file_path
_logger = logging.getLogger(__name__)
@@ -105,8 +106,9 @@ class ManifestLinter(BaseCase):
module)
else:
path_parts = value.split('/')
- path = get_module_resource(path_parts[1], *path_parts[2:])
- if not path:
+ try:
+ file_path(opj(*path_parts[1:]))
+ except FileNotFoundError:
_logger.warning(
"Icon value specified in manifest of module %s wasn't found in given path."
" Please specify a correct value or remove this key from the manifest.",
diff --git a/odoo/addons/test_lint/tests/test_onchange_domains.py b/odoo/addons/test_lint/tests/test_onchange_domains.py
index b67472e7541..44a521f901b 100644
--- a/odoo/addons/test_lint/tests/test_onchange_domains.py
+++ b/odoo/addons/test_lint/tests/test_onchange_domains.py
@@ -3,7 +3,7 @@ import itertools
import os
from . import lint_case
-
+from odoo.tools.misc import file_open
class OnchangeChecker(lint_case.NodeVisitor):
def matches_onchange(self, node):
@@ -37,7 +37,7 @@ class TestOnchangeDomains(lint_case.LintCase):
checker = OnchangeChecker()
rs = []
for path in self.iter_module_files('*.py'):
- with open(path, 'rb') as f:
+ with file_open(path, 'rb') as f:
t = ast.parse(f.read(), path)
rs.extend(zip(itertools.repeat(os.path.relpath(path)), checker.visit(t)))
diff --git a/odoo/addons/test_lint/tests/test_pofile.py b/odoo/addons/test_lint/tests/test_pofile.py
index 61f34a4cc94..f9f8e7baa72 100644
--- a/odoo/addons/test_lint/tests/test_pofile.py
+++ b/odoo/addons/test_lint/tests/test_pofile.py
@@ -3,9 +3,10 @@
from collections import Counter
-from odoo.modules import get_modules, get_resource_path
+from odoo.modules import get_modules
from odoo.tests.common import TransactionCase
from odoo.tools.translate import TranslationFileReader
+from odoo.tools.misc import file_path
class PotLinter(TransactionCase):
@@ -21,8 +22,9 @@ class PotLinter(TransactionCase):
# retrieve all modules, and their corresponding POT file
for module in get_modules():
- filename = get_resource_path(module, 'i18n', module + '.pot')
- if not filename:
+ try:
+ filename = file_path(f'{module}/i18n/{module}.pot')
+ except FileNotFoundError:
continue
counts = Counter(map(format, TranslationFileReader(filename)))
duplicates = [key for key, count in counts.items() if count > 1]
diff --git a/odoo/addons/test_mimetypes/tests/test_guess_mimetypes.py b/odoo/addons/test_mimetypes/tests/test_guess_mimetypes.py
index 2568cd21db9..74f66268469 100644
--- a/odoo/addons/test_mimetypes/tests/test_guess_mimetypes.py
+++ b/odoo/addons/test_mimetypes/tests/test_guess_mimetypes.py
@@ -2,10 +2,11 @@
import os.path
from odoo.tests.common import BaseCase
+from odoo.tools.misc import file_open
from odoo.tools.mimetypes import guess_mimetype
def contents(extension):
- with open(os.path.join(
+ with file_open(os.path.join(
os.path.dirname(__file__),
'testfiles',
'case.{}'.format(extension)
diff --git a/odoo/modules/db.py b/odoo/modules/db.py
index 872c54177b0..fc7e6989996 100644
--- a/odoo/modules/db.py
+++ b/odoo/modules/db.py
@@ -25,8 +25,9 @@ def initialize(cr):
and ir_model_data entries.
"""
- f = odoo.modules.get_module_resource('base', 'data', 'base_data.sql')
- if not f:
+ try:
+ f = odoo.tools.misc.file_path('base/data/base_data.sql')
+ except FileNotFoundError:
m = "File not found: 'base.sql' (provided by module 'base')."
_logger.critical(m)
raise IOError(m)
diff --git a/odoo/modules/migration.py b/odoo/modules/migration.py
index 37081264f02..e55980aef7b 100644
--- a/odoo/modules/migration.py
+++ b/odoo/modules/migration.py
@@ -11,10 +11,10 @@ import os
import re
from os.path import join as opj
-from odoo.modules.module import get_resource_path
import odoo.release as release
import odoo.upgrade
from odoo.tools.parse_version import parse_version
+from odoo.tools.misc import file_path
_logger = logging.getLogger(__name__)
@@ -47,7 +47,7 @@ VERSION_RE = re.compile(
def load_script(path, module_name):
- full_path = get_resource_path(*path.split(os.path.sep)) if not os.path.isabs(path) else path
+ full_path = file_path(path) if not os.path.isabs(path) else path
spec = importlib.util.spec_from_file_location(module_name, full_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
@@ -122,14 +122,21 @@ class MigrationManager(object):
if _verify_upgrade_version(path, version)
}
+ def check_path(path):
+ try:
+ return file_path(path)
+ except FileNotFoundError:
+ return False
+
for pkg in self.graph:
if not (hasattr(pkg, 'update') or pkg.state == 'to upgrade' or
getattr(pkg, 'load_state', None) == 'to upgrade'):
continue
+
self.migrations[pkg.name] = {
- 'module': get_scripts(get_resource_path(pkg.name, 'migrations')),
- 'module_upgrades': get_scripts(get_resource_path(pkg.name, 'upgrades')),
+ 'module': get_scripts(check_path(pkg.name + '/migrations')),
+ 'module_upgrades': get_scripts(check_path(pkg.name + '/upgrades')),
}
scripts = defaultdict(list)
diff --git a/odoo/modules/module.py b/odoo/modules/module.py
index 79d6a9f3511..659a93e5509 100644
--- a/odoo/modules/module.py
+++ b/odoo/modules/module.py
@@ -243,17 +243,18 @@ def get_resource_from_path(path):
return None
def get_module_icon(module):
- iconpath = ['static', 'description', 'icon.png']
- if get_module_resource(module, *iconpath):
- return ('/' + module + '/') + '/'.join(iconpath)
- return '/base/' + '/'.join(iconpath)
+ fpath = f"{module}/static/description/icon.png"
+ try:
+ file_path(fpath)
+ return "/" + fpath
+ except FileNotFoundError:
+ return "/base/static/description/icon.png"
def get_module_icon_path(module):
- iconpath = ['static', 'description', 'icon.png']
- path = get_module_resource(module.name, *iconpath)
- if not path:
- path = get_module_resource('base', *iconpath)
- return path
+ try:
+ return file_path(f"{module}/static/description/icon.png")
+ except FileNotFoundError:
+ return file_path("base/static/description/icon.png")
def module_manifest(path):
"""Returns path to module manifest if one can be found under `path`, else `None`."""
diff --git a/odoo/modules/neutralize.py b/odoo/modules/neutralize.py
index 38b38eb5ef0..e8a08338142 100644
--- a/odoo/modules/neutralize.py
+++ b/odoo/modules/neutralize.py
@@ -1,6 +1,8 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
+from contextlib import suppress
+
import odoo
import logging
@@ -17,8 +19,8 @@ def get_installed_modules(cursor):
def get_neutralization_queries(modules):
# neutralization for each module
for module in modules:
- filename = odoo.modules.get_module_resource(module, 'data/neutralize.sql')
- if filename:
+ filename = f'{module}/data/neutralize.sql'
+ with suppress(FileNotFoundError):
with odoo.tools.misc.file_open(filename) as file:
yield file.read().strip()
diff --git a/odoo/tools/convert.py b/odoo/tools/convert.py
index 2df1c5a5ccf..c3b1588e429 100644
--- a/odoo/tools/convert.py
+++ b/odoo/tools/convert.py
@@ -28,7 +28,7 @@ except ImportError:
import odoo
from . import pycompat
from .config import config
-from .misc import file_open, unquote, ustr, SKIPPED_ELEMENT_TYPES
+from .misc import file_open, file_path, SKIPPED_ELEMENT_TYPES
from .translate import _
from odoo import SUPERUSER_ID, api
from odoo.exceptions import ValidationError
@@ -155,9 +155,10 @@ def _eval_xml(self, node, env):
# after that, only text content makes sense
data = pycompat.to_text(data)
if t == 'file':
- from ..modules import module
path = data.strip()
- if not module.get_module_resource(self.module, path):
+ try:
+ file_path(os.path.join(self.module, path))
+ except FileNotFoundError:
raise IOError("No such file or directory: '%s' in %s" % (
path, self.module))
return '%s,%s' % (self.module, path)
diff --git a/odoo/tools/misc.py b/odoo/tools/misc.py
index ee2dfe3d9bd..8968a77cb80 100644
--- a/odoo/tools/misc.py
+++ b/odoo/tools/misc.py
@@ -428,10 +428,9 @@ def scan_languages():
:returns: a list of (lang_code, lang_name) pairs
:rtype: [(str, unicode)]
"""
- csvpath = odoo.modules.module.get_resource_path('base', 'data', 'res.lang.csv')
try:
# read (code, name) from languages in base/data/res.lang.csv
- with open(csvpath, 'rb') as csvfile:
+ with file_open('base/data/res.lang.csv', 'rb') as csvfile:
reader = pycompat.csv_reader(csvfile, delimiter=',', quotechar='"')
fields = next(reader)
code_index = fields.index("code")
@@ -441,7 +440,7 @@ def scan_languages():
for row in reader
]
except Exception:
- _logger.error("Could not read %s", csvpath)
+ _logger.error("Could not read res.lang.csv")
result = []
return sorted(result or [('en_US', u'English')], key=itemgetter(1))
diff --git a/odoo/tools/translate.py b/odoo/tools/translate.py
index d5228d3f6ed..745ec634bbc 100644
--- a/odoo/tools/translate.py
+++ b/odoo/tools/translate.py
@@ -17,6 +17,7 @@ import tarfile
import threading
import warnings
from collections import defaultdict, namedtuple
+from contextlib import suppress
from datetime import datetime
from os.path import join
@@ -27,9 +28,8 @@ from psycopg2.extras import Json
import odoo
from odoo.exceptions import UserError
-from odoo.modules.module import get_resource_path
from . import config, pycompat
-from .misc import file_open, get_iso_codes, SKIPPED_ELEMENT_TYPES
+from .misc import file_open, file_path, get_iso_codes, SKIPPED_ELEMENT_TYPES
_logger = logging.getLogger(__name__)
@@ -1146,7 +1146,7 @@ class TranslationModuleReader:
if not module:
return
extra_comments = extra_comments or []
- src_file = open(fabsolutepath, 'rb')
+ src_file = file_open(fabsolutepath, 'rb')
options = {}
if extract_method == 'python':
options['encoding'] = 'UTF-8'
@@ -1239,7 +1239,8 @@ class TranslationImporter:
the language must be present and activated in the database
:param xmlids: if given, only translations for records with xmlid in xmlids will be loaded
"""
- with file_open(filepath, mode='rb') as fileobj:
+ with suppress(FileNotFoundError), file_open(filepath, mode='rb') as fileobj:
+ _logger.info('loading base translation file %s for language %s', filepath, lang)
fileformat = os.path.splitext(filepath)[-1][1:].lower()
self.load(fileobj, fileformat, lang, xmlids=xmlids)
@@ -1484,12 +1485,15 @@ def get_po_paths(module_name: str, lang: str):
else:
langs = [lang_base, lang]
- return [
+ po_paths = [
path
for lang_ in langs
for dir_ in ('i18n', 'i18n_extra')
- if (path := get_resource_path(module_name, dir_, lang_ + '.po'))
+ if (path := join(module_name, dir_, lang_ + '.po'))
]
+ for path in po_paths:
+ with suppress(FileNotFoundError):
+ yield file_path(path)
class CodeTranslations: