|
1 | | -# -*- coding: utf-8 -*- |
2 | 1 | # Copyright 2016 Camptocamp SA |
3 | 2 | # Copyright 2015 Odoo |
| 3 | +# @author Pierre Verkest <pierre@verkest.fr> |
4 | 4 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html) |
5 | 5 |
|
6 | 6 |
|
7 | 7 | import ast |
8 | 8 | import os |
9 | 9 | import signal |
10 | | -import sys |
11 | 10 | import threading |
12 | 11 | from pathlib import Path |
13 | 12 | from typing import Optional |
14 | 13 |
|
15 | 14 | import _pytest |
16 | | -import _pytest._py.error as error |
17 | 15 | import _pytest.python |
| 16 | +import pytest |
| 17 | + |
18 | 18 | import odoo |
19 | 19 | import odoo.tests |
20 | | -import pytest |
21 | | -from _pytest._code.code import ExceptionInfo |
22 | 20 |
|
23 | 21 |
|
24 | 22 | def pytest_addoption(parser): |
@@ -88,7 +86,7 @@ def pytest_cmdline_main(config): |
88 | 86 | raise Exception( |
89 | 87 | "please provide a database name in the Odoo configuration file" |
90 | 88 | ) |
91 | | - |
| 89 | + monkey_patch_resolve_pkg_root_and_module_name() |
92 | 90 | odoo.service.server.start(preload=[], stop=True) |
93 | 91 | # odoo.service.server.start() modifies the SIGINT signal by its own |
94 | 92 | # one which in fact prevents us to stop anthem with Ctrl-c. |
@@ -139,101 +137,31 @@ def enable_odoo_test_flag(): |
139 | 137 | yield |
140 | 138 | odoo.tools.config['test_enable'] = False |
141 | 139 |
|
| 140 | +def monkey_patch_resolve_pkg_root_and_module_name(): |
| 141 | + original_resolve_pkg_root_and_module_name = _pytest.pathlib.resolve_pkg_root_and_module_name |
142 | 142 |
|
143 | | -# Original code of xmo-odoo: |
144 | | -# https://github.com/odoo-dev/odoo/commit/95a131b7f4eebc6e2c623f936283153d62f9e70f |
145 | | -class OdooTestModule(_pytest.python.Module): |
146 | | - """ Should only be invoked for paths inside Odoo addons |
147 | | - """ |
148 | | - |
149 | | - def _importtestmodule(self): |
150 | | - # copy/paste/modified from original: removed sys.path injection & |
151 | | - # added Odoo module prefixing so import within modules is correct |
152 | | - try: |
153 | | - pypkgpath = self.fspath.pypkgpath() |
154 | | - pkgroot = pypkgpath.dirpath() |
155 | | - sep = self.fspath.sep |
156 | | - names = self.fspath.new(ext="").relto(pkgroot).split(sep) |
157 | | - if names[-1] == "__init__": |
158 | | - names.pop() |
159 | | - modname = ".".join(names) |
160 | | - # for modules in odoo/addons, since there is a __init__ the |
161 | | - # module name is already fully qualified (maybe?) |
162 | | - if (not modname.startswith('odoo.addons.') |
163 | | - and modname != 'odoo.addons' |
164 | | - and modname != 'odoo'): |
165 | | - modname = 'odoo.addons.' + modname |
166 | | - |
167 | | - __import__(modname) |
168 | | - mod = sys.modules[modname] |
169 | | - if self.fspath.basename == "__init__.py": |
170 | | - # we don't check anything as we might |
171 | | - # we in a namespace package ... too icky to check |
172 | | - return mod |
173 | | - modfile = mod.__file__ |
174 | | - if modfile[-4:] in ('.pyc', '.pyo'): |
175 | | - modfile = modfile[:-1] |
176 | | - elif modfile.endswith('$py.class'): |
177 | | - modfile = modfile[:-9] + '.py' |
178 | | - if modfile.endswith(os.path.sep + "__init__.py"): |
179 | | - if self.fspath.basename != "__init__.py": |
180 | | - modfile = modfile[:-12] |
181 | | - try: |
182 | | - issame = self.fspath.samefile(modfile) |
183 | | - except error.ENOENT: |
184 | | - issame = False |
185 | | - if not issame: |
186 | | - raise self.fspath.ImportMismatchError(modname, modfile, self) |
187 | | - except SyntaxError as e: |
188 | | - raise self.CollectError( |
189 | | - ExceptionInfo.from_current().getrepr(style="short") |
190 | | - ) from e |
191 | | - except self.fspath.ImportMismatchError: |
192 | | - e = sys.exc_info()[1] |
193 | | - raise self.CollectError( |
194 | | - "import file mismatch:\n" |
195 | | - "imported module %r has this __file__ attribute:\n" |
196 | | - " %s\n" |
197 | | - "which is not the same as the test file we want to collect:\n" |
198 | | - " %s\n" |
199 | | - "HINT: remove __pycache__ / .pyc files and/or use a " |
200 | | - "unique basename for your test file modules" % e.args |
201 | | - ) |
202 | | - self.config.pluginmanager.consider_module(mod) |
203 | | - return mod |
204 | | - |
205 | | - def __repr__(self): |
206 | | - return "<Module %r>" % (getattr(self, "name", None), ) |
207 | | - |
208 | | - |
209 | | -class OdooTestPackage(_pytest.python.Package, OdooTestModule): |
210 | | - """Package with odoo module lookup. |
211 | | -
|
212 | | - Any python module inside the package will be imported with |
213 | | - the prefix `odoo.addons`. |
| 143 | + def resolve_pkg_root_and_module_name( |
| 144 | + path: Path, *, consider_namespace_packages: bool = False |
| 145 | + ) -> "tuple[Path, str]": |
| 146 | + pkg_root, module_name = original_resolve_pkg_root_and_module_name( |
| 147 | + path, consider_namespace_packages=consider_namespace_packages |
| 148 | + ) |
214 | 149 |
|
215 | | - This class is used to prevent loading odoo modules in duplicate, |
216 | | - which happens if a module is loaded with and without the prefix. |
217 | | - """ |
| 150 | + if not module_name.startswith("odoo.addons"): |
| 151 | + manifest = _find_manifest_path(path) |
| 152 | + if manifest and manifest.parent.name == module_name.split(".",1)[0]: |
| 153 | + module_name = "odoo.addons." + module_name |
| 154 | + return pkg_root, module_name |
218 | 155 |
|
219 | | - def __repr__(self): |
220 | | - return "<Package %r>" % (getattr(self, "name", None), ) |
221 | 156 |
|
222 | | - |
223 | | -def pytest_pycollect_makemodule(module_path, path, parent): |
224 | | - if not _find_manifest_path(module_path): |
225 | | - return None |
226 | | - if path.basename == "__init__.py": |
227 | | - return OdooTestPackage.from_parent(parent, path=Path(path)) |
228 | | - else: |
229 | | - return OdooTestModule.from_parent(parent, path=Path(path)) |
| 157 | + _pytest.pathlib.resolve_pkg_root_and_module_name= resolve_pkg_root_and_module_name |
230 | 158 |
|
231 | 159 |
|
232 | 160 | def _find_manifest_path(collection_path: Path) -> Path: |
233 | 161 | """Try to locate an Odoo manifest file in the collection path.""" |
234 | 162 | # check if collection_path is an addon directory |
235 | 163 | path = collection_path |
236 | | - for _ in range(0, 5): |
| 164 | + for _ in range(5): |
237 | 165 | if (path.parent / "__manifest__.py").is_file(): |
238 | 166 | break |
239 | 167 | path = path.parent |
|
0 commit comments