Merge lp:~florent.x/openobject-server/trunk-tools-file_open into lp:openobject-server

Proposed by Florent
Status: Merged
Approved by: Olivier Dony (Odoo)
Approved revision: 4018
Merged at revision: 4386
Proposed branch: lp:~florent.x/openobject-server/trunk-tools-file_open
Merge into: lp:openobject-server
Diff against target: 185 lines (+68/-54)
2 files modified
openerp/modules/module.py (+14/-11)
openerp/tools/misc.py (+54/-43)
To merge this branch: bzr merge lp:~florent.x/openobject-server/trunk-tools-file_open
Reviewer Review Type Date Requested Status
Florent (community) Approve
Olivier Dony (Odoo) Approve
Review via email: mp+92057@code.launchpad.net

Description of the change

Rewrite the file_open tool to manage better all kind of paths:
 - relative paths 'hr/report/timesheer.xsl'
 - absolute paths '/srv/openerp/addons/hr/report/timesheer.xsl'

If the path name is below "root_path" or below one of the "ad_paths", the zipfile lookup stops at the root of the folder managed by OpenERP.
If the path name is not below an OpenERP folder, the utility does not try to find a zip file.

It should fix the issues, until the next release, when the support for zip modules will be dropped completely.

To post a comment you must log in.
Revision history for this message
Olivier Dony (Odoo) (odo-openerp) wrote :

Ouch, thanks for taking a stab at that [awful] legacy code (dating back from 2007[1]).

As I mentioned on the bug, I have a work in progress to change the file location mechanism to give higher priority to extracted/directory modules rather than zip files, for the various reasons exposed in comment #3.

This might cause a bit of conflict in your branch, but not too much normally.
I should be done very soon, and will notify you, so we can then proceed to review/merge the second part of the fix for bug 928376 - your patch.

Thanks!

[1] rev.632 revid:ced-96fb1d71e2b7282f46324766dd733aa1181e9e00 and related ones

4018. By Florent

[MERGE] file_open: give precedence to directory before zip.

Revision history for this message
Florent (florent.x) wrote :

>
> As I mentioned on the bug, I have a work in progress to change the file
> location mechanism to give higher priority to extracted/directory modules
> rather than zip files, for the various reasons exposed in comment #3.
>
> This might cause a bit of conflict in your branch, but not too much normally.
> I should be done very soon, and will notify you, so we can then proceed to
> review/merge the second part of the fix for bug 928376 - your patch.
>

I did some tests, and I have merged your changes in this branch.

Revision history for this message
Olivier Dony (Odoo) (odo-openerp) wrote :

For the record I merged this branch in an ~openerp-dev branch to have it executed by our Continuous Integration system, and the total time needed to install all standard modules and execute all the tests was not noticeably different.
I imagine this is because the .zip files that are looked up are almost always the same, and are cached as negative dentries by the kernel - causing no disk access after the first.

As the performance difference was the most important reason for this change, I would be in favor of merging it in trunk right after the release of 6.1, because it is a change of behavior and could still cause some unforeseen side-effect - and we're very late in the release process of 6.1.

Thanks!

review: Approve
Revision history for this message
Florent (florent.x) wrote :

AFAIK 6.1 is released now...
and this patch is still candidate for merge in trunk.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'openerp/modules/module.py'
2--- openerp/modules/module.py 2012-02-08 10:59:13 +0000
3+++ openerp/modules/module.py 2012-02-08 22:32:18 +0000
4@@ -280,25 +280,28 @@
5 def get_module_resource(module, *args):
6 """Return the full path of a resource of the given module.
7
8- @param module: the module
9- @param args: the resource path components
10+ :param module: module name
11+ :param list(str) args: resource path components within module
12
13- @return: absolute path to the resource
14+ :rtype: str
15+ :return: absolute path to the resource
16
17 TODO name it get_resource_path
18 TODO make it available inside on osv object (self.get_resource_path)
19 """
20- a = get_module_path(module)
21- if not a: return False
22- resource_path = opj(a, *args)
23- if zipfile.is_zipfile( a +'.zip') :
24- zip = zipfile.ZipFile( a + ".zip")
25+ mod_path = get_module_path(module)
26+ if not mod_path: return False
27+ resource_path = opj(mod_path, *args)
28+ if os.path.isdir(mod_path):
29+ # the module is a directory - ignore zip behavior
30+ if os.path.exists(resource_path):
31+ return resource_path
32+ elif zipfile.is_zipfile(mod_path + '.zip'):
33+ zip = zipfile.ZipFile( mod_path + ".zip")
34 files = ['/'.join(f.split('/')[1:]) for f in zip.namelist()]
35 resource_path = '/'.join(args)
36 if resource_path in files:
37- return opj(a, resource_path)
38- elif os.path.exists(resource_path):
39- return resource_path
40+ return opj(mod_path, resource_path)
41 return False
42
43 def get_module_icon(module):
44
45=== modified file 'openerp/tools/misc.py'
46--- openerp/tools/misc.py 2012-01-25 13:24:07 +0000
47+++ openerp/tools/misc.py 2012-02-08 22:32:18 +0000
48@@ -134,7 +134,7 @@
49 @param name name of the file
50 @param mode file open mode
51 @param subdir subdirectory
52- @param pathinfo if True returns tupple (fileobject, filepath)
53+ @param pathinfo if True returns tuple (fileobject, filepath)
54
55 @return fileobject if pathinfo is False else (fileobject, filepath)
56 """
57@@ -142,44 +142,60 @@
58 adps = addons.module.ad_paths
59 rtp = os.path.normcase(os.path.abspath(config['root_path']))
60
61- if name.replace(os.path.sep, '/').startswith('addons/'):
62+ if os.path.isabs(name):
63+ # It is an absolute path
64+ # Is it below 'addons_path' or 'root_path'?
65+ name = os.path.normcase(os.path.normpath(name))
66+ for root in adps + [rtp]:
67+ if name.startswith(root):
68+ base = root.rstrip(os.sep)
69+ name = name[len(base) + 1:]
70+ break
71+ else:
72+ # It is outside the OpenERP root: skip zipfile lookup.
73+ base, name = os.path.split(name)
74+ return _fileopen(name, mode=mode, basedir=base, pathinfo=pathinfo)
75+
76+ if name.replace(os.sep, '/').startswith('addons/'):
77 subdir = 'addons'
78- name = name[7:]
79+ name2 = name[7:]
80+ elif subdir:
81+ name = os.path.join(subdir, name)
82+ if name.replace(os.sep, '/').startswith('addons/'):
83+ subdir = 'addons'
84+ name2 = name[7:]
85+ else:
86+ name2 = name
87
88- # First try to locate in addons_path
89+ # First, try to locate in addons_path
90 if subdir:
91- subdir2 = subdir
92- if subdir2.replace(os.path.sep, '/').startswith('addons/'):
93- subdir2 = subdir2[7:]
94-
95- subdir2 = (subdir2 != 'addons' or None) and subdir2
96-
97 for adp in adps:
98 try:
99- if subdir2:
100- fn = os.path.join(adp, subdir2, name)
101- else:
102- fn = os.path.join(adp, name)
103- fn = os.path.normpath(fn)
104- fo = file_open(fn, mode=mode, subdir=None, pathinfo=pathinfo)
105- if pathinfo:
106- return fo, fn
107- return fo
108+ return _fileopen(name2, mode=mode, basedir=adp,
109+ pathinfo=pathinfo)
110 except IOError:
111 pass
112
113- if subdir:
114- name = os.path.join(rtp, subdir, name)
115- else:
116- name = os.path.join(rtp, name)
117-
118- name = os.path.normpath(name)
119-
120- # Check for a zipfile in the path
121- head = name
122+ # Second, try to locate in root_path
123+ return _fileopen(name, mode=mode, basedir=rtp, pathinfo=pathinfo)
124+
125+
126+def _fileopen(path, mode, basedir, pathinfo):
127+ name = os.path.normpath(os.path.join(basedir, path))
128+ # Give higher priority to module directories, which is
129+ # a more common case than zipped modules.
130+ if os.path.isfile(name):
131+ fo = open(name, mode)
132+ if pathinfo:
133+ return (fo, name)
134+ return fo
135+
136+ # Support for loading modules in zipped form.
137+ # This will not work for zipped modules that are sitting
138+ # outside of known addons paths.
139+ head = os.path.normpath(path)
140 zipname = False
141- name2 = False
142- while True:
143+ while os.sep in head:
144 head, tail = os.path.split(head)
145 if not tail:
146 break
147@@ -187,9 +203,10 @@
148 zipname = os.path.join(tail, zipname)
149 else:
150 zipname = tail
151- if zipfile.is_zipfile(head+'.zip'):
152+ zpath = os.path.join(basedir, head + '.zip')
153+ if zipfile.is_zipfile(zpath):
154 from cStringIO import StringIO
155- zfile = zipfile.ZipFile(head+'.zip')
156+ zfile = zipfile.ZipFile(zpath)
157 try:
158 fo = StringIO()
159 fo.write(zfile.read(os.path.join(
160@@ -197,20 +214,14 @@
161 os.sep, '/')))
162 fo.seek(0)
163 if pathinfo:
164- return fo, name
165+ return (fo, name)
166 return fo
167 except Exception:
168- name2 = os.path.normpath(os.path.join(head + '.zip', zipname))
169 pass
170- for i in (name2, name):
171- if i and os.path.isfile(i):
172- fo = file(i, mode)
173- if pathinfo:
174- return fo, i
175- return fo
176- if os.path.splitext(name)[1] == '.rml':
177- raise IOError, 'Report %s doesn\'t exist or deleted : ' %str(name)
178- raise IOError, 'File not found : %s' % name
179+ # Not found
180+ if name.endswith('.rml'):
181+ raise IOError('Report %r doesn\'t exist or deleted' % name)
182+ raise IOError('File not found: %s' % name)
183
184
185 #----------------------------------------------------------