Gentoo Archives: gentoo-portage-dev

From: Jason Stubbs <jstubbs@g.o>
To: gentoo-portage-dev@l.g.o
Subject: Re: [gentoo-portage-dev] Plugin backport PATCH (1/2)/(2/2)
Date: Sat, 19 Nov 2005 04:11:35
Message-Id: 200511191312.46486.jstubbs@gentoo.org
In Reply to: Re: [gentoo-portage-dev] Plugin backport PATCH (1/2)/(2/2) by Brian Harring
1 On Saturday 19 November 2005 08:47, Brian Harring wrote:
2 > On Sat, Nov 19, 2005 at 12:43:43AM +0900, Jason Stubbs wrote:
3 > > +++ ./pym/portage_file.py       2005-11-16 01:56:19.157073160 -0500
4 > >
5 > > +                       # if the dir perms would lack +wx, we have to force it
6 > > +                       force_temp_perms = ((mode & 0300) != 0300)
7 > > +                       resets = []
8 > > +                       apath = normpath(os.path.abspath(path))
9 > > +                       sticky_parent = False
10 > > +                       creating = False
11 > > +                       
12 > > +                       for dir in apath.split(os.path.sep):
13 > > +                               base = os.path.join(base,dir)
14 > > +                               try:
15 > > +                                       st = os.stat(base)
16 > > +                                       # something exists.  why are we doing isdir?
17 > > +                                       if not stat.S_ISDIR(st.st_mode):
18 > > +                                               return False
19 > >
20 > > What's with the comment here?
21 > I blame crack.
22 > Rhetorical question in the code, which isn't very clear in hindsight
23
24 Dunno.. The code makes complete sense to me by itself. The comment only
25 confuses.
26
27 > >
28 > > +                                       # if it's a subdir, we need +wx at least
29 > > +                                       if apath != base:
30 > > +                                               if ((st.st_mode & 0300) != 0300):
31 > > +                                                       try:                    os.chmod(base, (st.st_mode | 0300))
32 > > +                                                       except OSError: return False
33 > > +                                                       resets.append((base, st.st_mode))
34 > > +                                               sticky_parent = (st.st_gid & stat.S_ISGID)
35 > >
36 > > <snip>
37 > >
38 > > +                       try:
39 > > +                               for base, m in reversed(resets):
40 > > +                                       os.chmod(base, m)
41 > > +                               if uid != -1 or gid != -1:
42 > > +                                       os.chown(base, uid, gid)
43 > > +                       except OSError:
44 > > +                               return False
45 > >
46 > > If for some reason the parent dir originally has perms 0000, this code will
47 > > incorrectly change its uid and gid.
48 > That's buggy anyways, since it won't play well with /blah
49
50 Yep...
51
52 > > +++ ./pym/portage_locks.py      2005-11-16 01:56:25.152161768 -0500
53 > > @@ -358,3 +359,91 @@
54 > > +# should the fd be left open indefinitely?
55 > > +# IMO, it shouldn't, but opening/closing everytime around is expensive
56 > >
57 > > It should be closed when locks are released, I think. Otherwise the file may
58 > > be deleted and recreated while one FsLock instance has it open which could
59 > > create situations where two FsLocks are trying to lock the same path but
60 > > using different fds.
61 > Leave it open, on locking do a inode verification of the existing
62 > handle- if it's behind, close the fd, open, resume locking (offhand).
63 >
64 > Race potential, but that can be addressed by getting the read lock,
65 > then doing a second (or first, if it's not re-upping a lock) inode
66 > verification
67
68 Umm.. getting complicated. How often are locks actually obtained, released
69 and then obtained a second time? Sacrificing readability for performance in
70 corner cases doesn't seem quite right. Also, with a long running portage
71 process and badly written code, it'd be quite easy to run out of fds...
72
73 > >
74 > > +               if not blocking:
75 > > +                       try:    fcntl.flock(self.fd, flags|fcntl.LOCK_NB)
76 > > +                       except IOError, ie:
77 > > +                               if ie.errno == 11:
78 > >
79 > > 11 ?
80 > 42 is better? ;)
81 >
82 > Should be using errno.EAGAIN instead of hardcoding though...
83
84 Yep.
85
86 > > +       def __del__(self):
87 > > +               # alright, it's 5:45am, yes this is weird code.
88 > > +               try:
89 > > +                       if self.fd != None:
90 > > +                               self.release_read_lock()
91 > > +               finally:
92 > > +                       if self.fd != None:
93 > > +                               os.close(self.fd)
94 > >
95 > > Allowing an exception to be raised during __del__ is not a good thing...
96 >
97 > The reason it's left in is that-
98 > A) try/finally ensures despite whatever crazyness might occur with
99 > locking, the fd gets waxed
100 > B) blow ups in the code are visible, rather then silently swallowed
101 > C) exceptions in __del__ python complains about, but swallows.
102 >
103 > So... rather then silently ignoring any crazyness, it allows some
104 > noise to be made if the locking code does something weird, and also
105 > ensures the fd is closed.
106
107 No traceback though... Something like the following (on one line):
108
109 Exception exceptions.Exception:
110 <exceptions.Exception instance at 0x2aaaaab2db48> in
111 <bound method foo.__del__ of <__main__.foo instance at 0x2aaaaab2d5f0>>
112 ignored
113
114 I guess it's enough to point out where debugging needs to start...
115
116 > Should check self.fd.closed on a sidenote.
117 >
118 > > Any held lock should be cleaned up too otherwise misbehaving callers could
119 > > deadlock.
120 >
121 > Closing the fd == release of any locks that are bound to that fd.
122 > Hence the anal measures to make damn sure the fd gets closed :)
123
124 Ok. That works then.
125
126 > > +++ ./pym/portage_modules.py    2005-11-16 01:56:28.269687832 -0500
127
128 > > +               # * ferringb|afk hit it at some point, sure of that
129 > > +               # but no idea how, so commenting out to see if things break...
130 > > +               # except AttributeError, e:
131 > > +               #       raise FailedImport(name, e)
132 > >
133 > > I can't see how either, but comments like these don't really help. Please
134 > > leave them out unless they describe how the code works.
135 > Leaving comments regarding "weirdness occured via here, but went away"
136 > isn't a bad thing- if it reappears, you know that at least it was hit
137 > once prior.
138 >
139 > I know how it was hit also (just dawned on me how it would happen)-
140 > echo $'import os;print os.dar' > test.py
141 > python -c'import test'
142 >
143 > AttributeError...
144 >
145 > Should nuke the module imo in that case still, since it's a failed
146 > import.
147
148 Yep, so it shouldn't be commented then - which is my point. If code is to be
149 going into stable, things like this should be resolved prior to.
150
151 > > +++ ./pym/portage_plugins.py    2005-11-16 01:56:51.419168576 -0500
152 > > @@ -0,0 +1,174 @@
153 > > +# Copyright: 2005 Gentoo Foundation
154 > > +# License: GPL2
155 > > +# $Id:$
156 > > +
157 > > +# I don't like this.
158 > > +# doesn't seem clean/right.
159 > >
160 > > ???
161 > Someone didn't clean up the files prior to posting this...
162
163 Still not understanding, but as long as whatever it is it's addressed and
164 the comment removed...
165
166 > > +               # this could be fine grained down to per plugin_type
167 > > +               plug_lock = FsLock(plugins_dir)
168 > >
169 > > Then it probably should be. ;)
170 > It's not needed though. Need a write lock on the plugins dir for when
171 > new plugin types are added; the only gain from it is when read locks
172 > are held long term, and parallel registration is occuring (for savior,
173 > it's possible, for stable, it isn't, not within the portage process at
174 > least).
175
176 Hrmm.. Then there should be a comment why the top-level plugins dir is
177 locked instead. Or perhaps there should be a global lock only while creating
178 the plugin_type subdir and then just locking the subdir afterward...
179
180 > >
181 > > +       def query_plugins(self, plugin_type=None, locking=True, raw=False):
182 > >
183 > > Not sure about allowing the external callers to disable locking... That
184 > > should only be used by register and deregister, right? Perhaps breaking
185 > > the unlocked version into a "protected" method?
186 >
187 > Debatable; any protection used people can stomp right past. Not much
188 > for the "sitting on the porch with the shotgun" approach.
189 >
190 > Not really seeing the gain in hiding the locking tbh; shoving it into
191 > the instance namespace would require locking the attribute to maintain
192 > thread safety; the only gain is just an extra hoop to jump through if
193 > I'm after doing something stupid. :)
194
195 Should query_plugins be called without no locking in place? In the code so
196 far it is only called when a lock is being maintained outside of the method
197 call. I'm not sure what you are referring to with regard to instance
198 namespace. To be clear, I'm suggesting:
199
200 def register_plugins(...):
201 lock_stuff()
202 do_stuff()
203 internal_query_plugins()
204 unlock_stuff()
205
206 def query_plugins(...):
207 lock_stuff()
208 internal_query_plugins()
209 unlock_stuff()
210
211 def internal_query_plugins(...):
212 do_stuff()
213
214 There's no namespace stuff there...
215
216 > > +                                       d[x[:-len_exten]] = dict([(y, dict(c.items(y))) for y in c.sections()])
217 > >
218 > > Same here on the 2.2 bit... Although that's pretty hard to read either way.
219 > Prefer a map call? :)
220
221 Whatever is most readable (and supported).
222
223 > >
224 > > +               finally:
225 > > +                       if locking:
226 > > +                               plug_lock.release_read_lock()
227 > > +               if plugin_type != None:
228 > > +                       return d[plugin_type]
229 > > +               return d
230 > >
231 > > Not sure about having different return types...
232 > Agreed.
233
234 So one method for querying what plugin types are available and another for
235 querying plugins of a given type?
236
237 > > +registry = None
238 > > +from portage.util.currying import pre_curry, pretty_docs
239 > > +
240 > > +def proxy_it(method, *a, **kw):
241 > > +       global registry
242 > > +       if registry == None:
243 > > +               registry = GlobalPluginRegistry()
244 > > +       return getattr(registry, method)(*a, **kw)
245 > > +
246 > > +for x in ["register", "deregister", "query_plugins", "get_plugin"]:
247 > > +       v = pre_curry(proxy_it, x)
248 > > +       doc = getattr(GlobalPluginRegistry, x).__doc__
249 > > +       if doc == None:
250 > > +               doc = ''
251 > > +       else:
252 > > +               # do this so indentation on pydoc __doc__ is sane
253 > > +               doc = "\n".join(map(lambda x:x.lstrip(), doc.split("\n"))) +"\n"
254 > > +       doc += "proxied call to module level registry instances %s method" % x
255 > > +       globals()[x] = pretty_docs(v, doc)
256 > > +
257 > > +del x, v, proxy_it, doc
258 > >
259 > > What's this currying and pretty_docs stuff?
260 > currying == arg binding to a func- in this case, proxy_it gets first
261 > arg of the attr requested; the proxy_it func instantiates a
262 > GlobalPluginRegistry instance if it's missing.
263
264 This stuff seems to be missing from the patch then.
265
266 --
267 Jason Stubbs
268
269 --
270 gentoo-portage-dev@g.o mailing list

Replies

Subject Author
Re: [gentoo-portage-dev] Plugin backport PATCH (1/2)/(2/2) Alec Warner <warnera6@×××××××.edu>
Re: [gentoo-portage-dev] Plugin backport PATCH (1/2)/(2/2) Brian Harring <ferringb@g.o>