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 |