Issue14657
This issue tracker has been migrated to GitHub,
and is currently read-only.
For more information,
see the GitHub FAQs in the Python's Developer Guide.
Created on 2012-04-23 22:34 by pitrou, last changed 2022-04-11 14:57 by admin. This issue is now closed.
Files | ||||
---|---|---|---|---|
File name | Uploaded | Description | Edit | |
unique_importlib.patch | pitrou, 2012-04-23 22:43 | review | ||
unique_importlib2.patch | pitrou, 2012-04-23 22:50 | review | ||
unique_importlib3.patch | pitrou, 2012-04-23 23:01 | review | ||
issue14657_bootstrap_from_disk.diff | ncoghlan, 2012-04-25 15:14 | Different approach that switches to the on-disk importlib ASAP | ||
issue14657_bootstrap_from_disk_v2.diff | ncoghlan, 2012-04-29 08:24 | Updated in light of switch to fully explicit import machinery | ||
issue14657_safe_bootstrap.diff | eric.snow, 2012-05-06 04:50 | simplifying for the common case | review |
Messages (75) | |||
---|---|---|---|
msg159096 - (view) | Author: Antoine Pitrou (pitrou) * | Date: 2012-04-23 22:34 | |
This patch avoids creating a second copy of importlib._bootstrap when a first one exists as _frozen_importlib. This isn't perfect as it mutates the module when importlib is imported for the first time, but I think it's better than the status quo. Also, importlib itself could be imported somewhere along the startup phase, so that all this is invisible to the user. I'm not sure how to test this, since _frozen_importlib is an implementation detail, and changing that module's name would probably defeat the test already. |
|||
msg159098 - (view) | Author: Antoine Pitrou (pitrou) * | Date: 2012-04-23 22:50 | |
New patch with tests. |
|||
msg159100 - (view) | Author: Antoine Pitrou (pitrou) * | Date: 2012-04-23 23:01 | |
New patch also avoids calling _setup() a second time (which can be annoying since _setup() has a list.append() call somewhere). |
|||
msg159104 - (view) | Author: Brett Cannon (brett.cannon) * | Date: 2012-04-24 00:41 | |
So why the mutation? Are you that worried someone is going to import importlib._bootstrap directly? This also costs in development complexity because not only do you have to run 'make' to get changes to be testable, but it also leads to difficult debugging situations where if you are not totally sure you got something working you won't find out until you see e.g. that the standard I/O streams were not initialized. If you really feel the need to hide _frozen_importlib then it would be better to do the minimum required to get import up and running (should be once the encodings are up in Py_Initialize) and then pull in importlib._bootstrap and have that clear out what _frozen_importlib set like __import__(), sys.path_importer_cache(), and eventually sys.meta_path and sys.path_hooks (I wouldn't touch sys.modules, though, thanks to built-ins and extensions not liking to be reloaded). |
|||
msg159109 - (view) | Author: Brett Cannon (brett.cannon) * | Date: 2012-04-24 00:56 | |
I should also mention that all of this becomes much less important once issue #14605 is finished because at that point sys.meta_path and sys.path_hooks will have _frozen_importlib objects and that will be what importlib works off of directly. But I still understand the desire to eliminate _frozen_importlib from being exposed, it's just a matter of coming up with a reasonable solution. |
|||
msg159116 - (view) | Author: Nick Coghlan (ncoghlan) * | Date: 2012-04-24 04:40 | |
My preference would also be for _frozen_importlib._bootstrap to overwrite as much evidence of itself as it can with the "real" one. This would also mean that changes to importlib._bootstrap would actually take effect for user code almost immediately, *without* rebuilding Python, as the frozen version would *only* be used to get hold of the pure Python version. |
|||
msg159123 - (view) | Author: Antoine Pitrou (pitrou) * | Date: 2012-04-24 08:10 | |
> So why the mutation? Are you that worried someone is going to import > importlib._bootstrap directly? Well, importing importlib *does* import importlib._bootstrap, and creates another copy of the module. importlib.__import__ is then wired to _bootstrap.__import__, which is different from the built-in __import__ (potentially using different globals, for example). > This also costs in development complexity because not only do you have > to run 'make' to get changes to be testable, but it also leads to > difficult debugging situations where if you are not totally sure you > got something working you won't find out until you see e.g. that the > standard I/O streams were not initialized. I'm worried that two different copies of importlib will lead to its own difficult debugging situations. |
|||
msg159127 - (view) | Author: Antoine Pitrou (pitrou) * | Date: 2012-04-24 09:06 | |
> This would also mean that changes to importlib._bootstrap would > actually take effect for user code almost immediately, *without* > rebuilding Python, as the frozen version would *only* be used to get > hold of the pure Python version. Actually, _io, encodings and friends must be loaded before importlib gets imported from Python code, so you will still have __loader__ entries referencing the frozen importlib, unless you also rewrite these attributes. My desire here is not to hide _frozen_importlib, rather to avoid subtle issues with two instances of a module living in memory with separate global states. Whether it's the frozen version or the on-disk Python version that gets the preference is another question (a less important one in my mind). |
|||
msg159128 - (view) | Author: Marc-Andre Lemburg (lemburg) * | Date: 2012-04-24 09:14 | |
Antoine Pitrou wrote: > > Antoine Pitrou <pitrou@free.fr> added the comment: > >> This would also mean that changes to importlib._bootstrap would >> actually take effect for user code almost immediately, *without* >> rebuilding Python, as the frozen version would *only* be used to get >> hold of the pure Python version. > > Actually, _io, encodings and friends must be loaded before importlib > gets imported from Python code, so you will still have __loader__ > entries referencing the frozen importlib, unless you also rewrite these > attributes. > > My desire here is not to hide _frozen_importlib, rather to avoid subtle > issues with two instances of a module living in memory with separate > global states. Whether it's the frozen version or the on-disk Python > version that gets the preference is another question (a less important > one in my mind). Why don't you freeze the whole importlib package to avoid all these issues ? As side effect, it will also load a little faster. |
|||
msg159154 - (view) | Author: Brett Cannon (brett.cannon) * | Date: 2012-04-24 15:10 | |
To start, I'm *not* going to make the final call on this issue's solution. I'm inches away from importlib burnout and general integration frustration with trying to clean up the implicit behaviour. So to prevent me from making a bad decision I will you guys make the final call. Anyway, I see two options here. One is the "let _frozen_importlib be *the* implementation, period" argument put forth by Antoine (MAL's "freeze everything" also falls under this since it suffers from the same issues). This is the easiest solution for the issue of not having overlapping implementations and cause potential mix-ups, etc. The issue becomes development difficulty goes up as now you are adding a compile step where if you screw up you can get really bad error messages (e.g. "standard streams could not be created" kind of stuff). This could theoretically be overcome if the importlib tests all used a manually created module directly from the source code to verify things before rebuilding (as well as making sure sys.path_importer_cache was cleaned out). With a restructuring of importlib's tests to use a common TestCase with the proper setUp()/teardown() for keeping things clean along with class and module fixtures to prevent obscene stuff like re-importing for every test method. Another option is we hide the source as _importlib or something to allow direct importation w/o any tricks under a protected name. Then there is Nick's proposal of using _frozen_importlib to start up and then swap out with a new version created from the source during startup. This keeps development simple since the tests run against the code *almost* all other code will use and thus eliminate the test. The problem here is that startup is a smidgen slower and it requires you blacklist what needs to get swapped out and if you mess up that will be tough to debug as well. Both get the same outcome but with different approaches, it's just a question of which one is easiest to maintain. |
|||
msg159158 - (view) | Author: Antoine Pitrou (pitrou) * | Date: 2012-04-24 15:16 | |
Le mardi 24 avril 2012 à 15:10 +0000, Brett Cannon a écrit : > Both get the same outcome but with different approaches, it's just a > question of which one is easiest to maintain. I don't have any strong preference. Nick's proposal sounds slightly better but Nick hasn't uploaded a patch yet :-) |
|||
msg159173 - (view) | Author: Marc-Andre Lemburg (lemburg) * | Date: 2012-04-24 17:37 | |
test me thod. Another option is we hide the source as _importlib or something to allow direct importation w/o any tricks under a protected name. Using the freeze everything approach you make things easier for the implementation, since you don't have to think about whether certain pieces of code are already available or not. For development, you can also have the package load bytecode or source from an external package instead of running (all of) the module's bytecode that was compiled into the binary. This is fairly easy to do, since the needed exec() does not depend on the import machinery. The only downside is big if statement to isolate the frozen version from the loaded one - would be great if we had a command to stop module execution or code execution for a block to make that more elegant, e.g. "break" at module scope :-) |
|||
msg159186 - (view) | Author: Eric Snow (eric.snow) * | Date: 2012-04-24 18:40 | |
> would be great if we had a > command to stop module execution or code execution for a block to > make that more elegant, e.g. "break" at module scope :-) I floated that proposal on python-list a while back and the reaction was mixed. [1] Maybe it's time to try again. (moving over to python-ideas...) [1] http://mail.python.org/pipermail/python-list/2011-June/1274424.html |
|||
msg159196 - (view) | Author: Brett Cannon (brett.cannon) * | Date: 2012-04-24 19:23 | |
I don't quite follow what you are suggesting, MAL. Are you saying to freeze importlib.__init__ and importlib._bootstrap and somehow have improtlib.__init__ choose what to load, frozen or source? |
|||
msg159198 - (view) | Author: Marc-Andre Lemburg (lemburg) * | Date: 2012-04-24 19:51 | |
Brett Cannon wrote: > > Brett Cannon <brett@python.org> added the comment: > > I don't quite follow what you are suggesting, MAL. Are you saying to freeze importlib.__init__ and importlib._bootstrap and somehow have improtlib.__init__ choose what to load, frozen or source? No, it always loads and runs the frozen code, but at the start of the module code it branches between the frozen bytecode and the code read from an external file. Pseudo-code in every module you wish to be able to host externally: # # MyModule # if operating_in_dev_mode and '<frozen>' in __file__: exec(open('dev-area/MyModule.py', 'r).read(), globals(), globals()) else: # Normal module code class MyClass: ... # hundreds of lines of code... Aside: With a module scope "break", the code would look more elegant: # # MyModule # if operating_in_dev_mode and '<frozen>' in __file__: exec(open('dev-area/MyModule.py', 'r).read(), globals(), globals()) break # Normal module code class MyClass: ... # hundreds of lines of code... |
|||
msg159202 - (view) | Author: Brett Cannon (brett.cannon) * | Date: 2012-04-24 20:25 | |
So basically if you are running in a checkout, grab the source file and compile it manually since its location is essentially hard-coded and thus you don't need to care about sys.path and all the other stuff required to do an import, while using the frozen code for when you are running an installed module since you would otherwise need to do the search for importlib's source file to do a load at startup properly. That's an interesting idea. How do we currently tell that the interpreter is running in a checkout? Is that exposed in any way to Python code? |
|||
msg159203 - (view) | Author: Antoine Pitrou (pitrou) * | Date: 2012-04-24 20:39 | |
> That's an interesting idea. How do we currently tell that the > interpreter is running in a checkout? Is that exposed in any way to > Python code? Look for _BUILDDIR_COOKIE in setup.py. But that's only for non-Windows platforms (I don't think setup.py is invoked under Windows). |
|||
msg159204 - (view) | Author: Marc-Andre Lemburg (lemburg) * | Date: 2012-04-24 20:42 | |
Brett Cannon wrote: > > Brett Cannon <brett@python.org> added the comment: > > So basically if you are running in a checkout, grab the source file and compile it manually since its location is essentially hard-coded and thus you don't need to care about sys.path and all the other stuff required to do an import, while using the frozen code for when you are running an installed module since you would otherwise need to do the search for importlib's source file to do a load at startup properly. Right. > That's an interesting idea. How do we currently tell that the interpreter is running in a checkout? Is that exposed in any way to Python code? There's some magic happening in site.py for checkouts, but I'm not sure whether any of that is persistent or even available at the time these particular imports would happen. Then again, I'm not sure you need to know whether you have a checkout or not. You just need some flag to identify whether you want the search for external module code to take place or not. sys.flags could be used for that. |
|||
msg159209 - (view) | Author: Brett Cannon (brett.cannon) * | Date: 2012-04-24 21:13 | |
Modules/getpath.c seems to be where the C code does it when getting paths for sys.path. So it would be possible to use that same algorithm to set some sys attribute (e.g. in_checkout or something) much like sys.gettotalrefcount is optional and only shown when built with --with-pydebug. Otherwise some directory structure check could be done (e.g. find importlib/_bootstrap.py off of sys.path, and then see if ../Modules/Setup or something also exists that would never show up in an installed CPython). |
|||
msg159211 - (view) | Author: Antoine Pitrou (pitrou) * | Date: 2012-04-24 21:15 | |
> Otherwise some directory structure check could be done (e.g. find > importlib/_bootstrap.py off of sys.path, and then see > if ../Modules/Setup or something also exists that would never show up > in an installed CPython). Well, the directory structure check *is* pybuilddir.txt (under POSIX, again; under Windows, you might want to check for Lib/importlib directly). But, agreed, this could be factored in a sys._private_something attribute. |
|||
msg159214 - (view) | Author: Brett Cannon (brett.cannon) * | Date: 2012-04-24 21:36 | |
That's why I was thinking of tying into Modules/getpath.c because I assume that would work cross-platform. Is that incorrect? |
|||
msg159215 - (view) | Author: Antoine Pitrou (pitrou) * | Date: 2012-04-24 21:37 | |
> That's why I was thinking of tying into Modules/getpath.c because I > assume that would work cross-platform. Is that incorrect? Windows uses PC/getpathp.c, not Modules/getpath.c (with tons of duplicate code)... So you would have to tie into both :) |
|||
msg159217 - (view) | Author: Marc-Andre Lemburg (lemburg) * | Date: 2012-04-24 21:46 | |
Brett Cannon wrote: > > Modules/getpath.c seems to be where the C code does it when getting paths for sys.path. So it would be possible to use that same algorithm to set some sys attribute (e.g. in_checkout or something) much like sys.gettotalrefcount is optional and only shown when built with --with-pydebug. Otherwise some directory structure check could be done (e.g. find importlib/_bootstrap.py off of sys.path, and then see if ../Modules/Setup or something also exists that would never show up in an installed CPython). Why not simply use a flag that get's set based on an environment variable, say PYTHONDEVMODE ? Adding more cruft to getpath.c or similar routines is just going to slow down startup time even more... Python 2.7 has a startup time of 70ms on my machine; compare that to Python 2.1 with 10ms and Perl 5 with just 4ms. |
|||
msg159218 - (view) | Author: Antoine Pitrou (pitrou) * | Date: 2012-04-24 21:52 | |
> Adding more cruft to getpath.c or similar routines is just going to > slow down startup time even more... The code is already there. |
|||
msg159224 - (view) | Author: Marc-Andre Lemburg (lemburg) * | Date: 2012-04-24 22:21 | |
Antoine Pitrou wrote: > >> Adding more cruft to getpath.c or similar routines is just going to >> slow down startup time even more... > > The code is already there. Code to detect whether you're running off a checkout vs. a normal installation by looking at even more directories ? I don't see any in getpath.c (and that's good). |
|||
msg159225 - (view) | Author: Antoine Pitrou (pitrou) * | Date: 2012-04-24 22:29 | |
> Code to detect whether you're running off a checkout vs. a normal > installation by looking at even more directories ? I don't > see any in getpath.c (and that's good). Look for "pybuilddir.txt". |
|||
msg159247 - (view) | Author: Brett Cannon (brett.cannon) * | Date: 2012-04-25 01:13 | |
That solves the "I'm in a checkout" problem but it doesn't tell you necessarily where the Lib directory is if you e.g. build from within another directory like Python/, which places the executable and pybuilddir.txt in the current directory. Now obviously you could argue supporting that case is not worth it for development-sake, but I figured since I knew it existed I should put it out there. |
|||
msg159264 - (view) | Author: Marc-Andre Lemburg (lemburg) * | Date: 2012-04-25 09:07 | |
Antoine Pitrou wrote: > > Antoine Pitrou <pitrou@free.fr> added the comment: > >> Code to detect whether you're running off a checkout vs. a normal >> installation by looking at even more directories ? I don't >> see any in getpath.c (and that's good). > > Look for "pybuilddir.txt". Oh dear. Another one of those hacks... why wasn't this done using constants passed in by the configure script and simple string comparison ? BTW: The startup time of python3.3 is 113ms on my machine, that's more than twice as long as python2.7. Given the history, it looks like no one cares about these things anymore... :-( |
|||
msg159268 - (view) | Author: Antoine Pitrou (pitrou) * | Date: 2012-04-25 11:49 | |
> > Look for "pybuilddir.txt". > > Oh dear. Another one of those hacks... why wasn't this done using > constants passed in by the configure script and simple string > comparison ? How would that help distinguish between an installed Python and a non-installed Python? If you have an idea about that, please open an issue and explain it precisely :) |
|||
msg159269 - (view) | Author: Marc-Andre Lemburg (lemburg) * | Date: 2012-04-25 12:06 | |
Antoine Pitrou wrote: > > Antoine Pitrou <pitrou@free.fr> added the comment: > >>> Look for "pybuilddir.txt". >> >> Oh dear. Another one of those hacks... why wasn't this done using >> constants passed in by the configure script and simple string >> comparison ? > > How would that help distinguish between an installed Python and a > non-installed Python? If you have an idea about that, please open an > issue and explain it precisely :) The question pybuildir.txt apparently tries to solve is whether Python is running from the build dir or not. It's not whether Python was installed or not. Checking for the build dir can be done by looking at the argv[0] of the executable and comparing that to the build dir. This can be compiled into the interpreter using a constant, say BUILDIR. At runtime, you'd simply compare the current argv[0] to the BUILDDIR. If it matches, you know that you can assume the build dir layout with reasonable certainty and proceed accordingly. No need for extra joins, file reads, etc. But given the enormous startup time of Python 3.3, those few stats won't make a difference anyway. This would need a completely different holistic approach. Perhaps something for SoC project. |
|||
msg159270 - (view) | Author: Antoine Pitrou (pitrou) * | Date: 2012-04-25 12:12 | |
> The question pybuildir.txt apparently tries to solve is whether Python > is running from the build dir or not. It's not whether Python was > installed or not. That's the same, for all we're concerned. But pybuilddir.txt does not only solve that problem. It also contains the path to extension modules generated by setup.py, so that sys.path can be setup appropriately at startup. |
|||
msg159272 - (view) | Author: Marc-Andre Lemburg (lemburg) * | Date: 2012-04-25 12:28 | |
Antoine Pitrou wrote: > > Antoine Pitrou <pitrou@free.fr> added the comment: > >> The question pybuildir.txt apparently tries to solve is whether Python >> is running from the build dir or not. It's not whether Python was >> installed or not. > > That's the same, for all we're concerned. > But pybuilddir.txt does not only solve that problem. It also contains > the path to extension modules generated by setup.py, so that sys.path > can be setup appropriately at startup. Would be easier to tell distutils to install the extensions in a fixed name dir (instead of using a platform and version in the name) and then use that getpath.c. distutils is pretty flexible at that :-) |
|||
msg159273 - (view) | Author: Nick Coghlan (ncoghlan) * | Date: 2012-04-25 12:32 | |
Still no patch from me, but I did create the rudiments of a shared script for poking around at the import internals (Tools/scripts/import_diagnostics.py) Looking at Antoine's patch, I'd be happier with it if it *didn't* mutate the attributes of _frozen_importlib, but instead just added importlib._bootstrap as an alias for accessing it. That would bring it in line with the way we handle os.path as being just an alias for the appropriate top level module: >>> import os.path >>> os.path.__name__ 'posixpath' Getting access to the source level _bootstrap implementation for testing purposes would then just require the usual techniques for bypassing C accelerators (specifically, using test.support.import_fresh_module with "_frozen_importlib" blocked). That would address the immediate problem of module duplication, without misrepresenting what is going on in potentially confusing ways. |
|||
msg159274 - (view) | Author: Antoine Pitrou (pitrou) * | Date: 2012-04-25 12:38 | |
> Would be easier to tell distutils to install the extensions > in a fixed name dir (instead of using a platform and version > in the name) and then use that getpath.c. distutils is pretty > flexible at that :-) Look, this is becoming very off-topic and you aren't proposing anything concrete (I see neither patches nor problems being solved). Could you open another issue, if you care so much? |
|||
msg159275 - (view) | Author: Antoine Pitrou (pitrou) * | Date: 2012-04-25 12:46 | |
> Looking at Antoine's patch, I'd be happier with it if it *didn't* > mutate the attributes of _frozen_importlib, but instead just added > importlib._bootstrap as an alias for accessing it. I thought it would be nicer for __file__, __name__ and __package__ to reflect the actual source code metadata (__file__ is always a py file while __cached__ may point to the compiled bytecode). But I don't have any strong feelings about that. Yes, __file__ can end up misleading if you modify the Python source without recompiling, but I think most people would only read the code without modifying it. |
|||
msg159292 - (view) | Author: Brett Cannon (brett.cannon) * | Date: 2012-04-25 14:58 | |
To answer MAL's question about startup, I benchmarked on my machine using the normal_startup benchmark from hg.python.org/benchmarks and the bootstrap work only caused a 5-6% slowdown in a non-debug build. If you do it in a debug build it's much worse (I think it was 12% when I benchmarked). |
|||
msg159296 - (view) | Author: Nick Coghlan (ncoghlan) * | Date: 2012-04-25 15:14 | |
OK, I'm leaning back towards my original preference of getting _frozen_importlib out of the way as quickly as we can. Specifically, I'm thinking of separating out the entry point used by importlib.__init__ from that used by pythonrun.c, such that the latter calls a "_bootstrap_from_frozen" function that returns a reference to "importlib._bootstrap", which pythonrun then places in the interpreter state. There would be a few builtin modules that still end up with loaders from _frozen_importlib (specifically, those referenced from importlib._bootstrap._setup as well as importlib itself), but the vast majority of imported modules would only see the "real" versions from importlib._bootstrap. Attached patch is an initial attempt (the reference counting on the two modules is likely still a bit dodgy - this is my first version that didn't segfault as I got used to the mechanics of dealing with a frozen module, so it errs on the side of leaking references) |
|||
msg159299 - (view) | Author: Antoine Pitrou (pitrou) * | Date: 2012-04-25 15:25 | |
> Attached patch is an initial attempt (the reference counting on the > two modules is likely still a bit dodgy - this is my first version > that didn't segfault as I got used to the mechanics of dealing with a > frozen module, so it errs on the side of leaking references) But does it make debugging any easier? The IO streams are not yet initialized at that point (neither are the codecs), so you are executing _bootstrap.py from a very bare interpreter. |
|||
msg159302 - (view) | Author: Nick Coghlan (ncoghlan) * | Date: 2012-04-25 15:38 | |
Yes, in that you'll be able to pick up changes in _bootstrap.py *without* having to rebuild Python. With this in place, we could then get rid of the automatic regeneration of importlib.h which is a complete nightmare if you ever break your built interpreter while hacking on the bootstrapping (as I now know from experience). With my approach, the experience is instead: - modify _bootstrap.py, hack until any new tests pass - run a new explicit "make freeze_importlib" command - run "make" - check everything still works - commit and push If you forget to run "make freeze_importlib", it doesn't really matter all that much, since the frozen one will only be used to find the real one, so it isn't a disaster if it's a little out of date. (That said, we should still have a test that at least checks the two modules have the same attributes) It does mean that importlib.__init__ also needs to be able to run in a partially initialised interpreter, hence the switch from "import imp" to "import _imp". |
|||
msg159304 - (view) | Author: Nick Coghlan (ncoghlan) * | Date: 2012-04-25 15:41 | |
Actually, rather than a test in test suite, we would just change the current automatic rebuild to a Modules/Setup style "'Lib/importlib._bootstrap.py' is newer than 'Python/importlib.h', you may need to run 'make freeze_importlib'" |
|||
msg159307 - (view) | Author: Éric Araujo (eric.araujo) * | Date: 2012-04-25 15:52 | |
> How do we currently tell that the interpreter is running in a checkout? sysconfig.is_python_build() Someone has to confirm that this works on Windows too, as I’ve been told that not installed vs. installed is less clear on that OS. |
|||
msg159308 - (view) | Author: Antoine Pitrou (pitrou) * | Date: 2012-04-25 15:55 | |
> Actually, rather than a test in test suite, we would just change the > current automatic rebuild to a Modules/Setup style > "'Lib/importlib._bootstrap.py' is newer than 'Python/importlib.h', you > may need to run 'make freeze_importlib'" -1 from me. Nobody pays attention to this kind of warning. (and the Modules/Setup thing is a nuisance) Really, we must unsure that the frozen version of importlib is up-to-date. Also, normally you would write your tests in test_import, so that the builtin import *is* tested. So you have to regenerate importlib before committing (or you break the buildbots). |
|||
msg159310 - (view) | Author: Nick Coghlan (ncoghlan) * | Date: 2012-04-25 15:58 | |
The other advantage of splitting the entry points is that we can tweak Brett's plan to make the import machinery explicit such that it happens in a separate function that's only called from __init__.py. That way the published hooks will always be from the on-disk implementation and never from the frozen one. If you're after the ability to emit debugging messages in a way that doesn't cause fatal errors during system startup, the only way I can see is to have a "do nothing" module level display function in _bootstrap.py that is later replaced with a reference to builtins.print: def _debug(*args, **kwds): pass |
|||
msg159311 - (view) | Author: Nick Coghlan (ncoghlan) * | Date: 2012-04-25 16:02 | |
At the very least, failing to regenerate importlib.h shouldn't be a fatal build error. It should just run with what its got, and hopefully you will get a working interpreter out the other end, such that you can regenerate the frozen module on the next pass. If we change that, then I'm OK with keeping the automatic rebuild. |
|||
msg159316 - (view) | Author: Brett Cannon (brett.cannon) * | Date: 2012-04-25 16:40 | |
So how would you tweak the explicit work I'm doing? The code is going to rely on sys.path_hooks and sys.meta_path being populated. I guess the frozen code can set up initially, and then importlib simply substitutes out classes from the frozen module to the code from the source version (which should be easy based on __class__ and __class__.__name__ or something). Or if you do this before anyone else (e.g. zipimport) gets to sys.path_hooks and sys.meta_path then you could just blow them away without care and simply set them up again. |
|||
msg159318 - (view) | Author: Marc-Andre Lemburg (lemburg) * | Date: 2012-04-25 16:46 | |
Nick Coghlan wrote: > > Nick Coghlan <ncoghlan@gmail.com> added the comment: > > At the very least, failing to regenerate importlib.h shouldn't be a fatal build error. It should just run with what its got, and hopefully you will get a working interpreter out the other end, such that you can regenerate the frozen module on the next pass. > > If we change that, then I'm OK with keeping the automatic rebuild. I fixed that already today. You now get a warning message from make, but no build error across all buildbots like I had run into yesterday when working on the code. |
|||
msg159328 - (view) | Author: Marc-Andre Lemburg (lemburg) * | Date: 2012-04-25 17:46 | |
Marc-Andre Lemburg wrote: > > Marc-Andre Lemburg <mal@egenix.com> added the comment: > > Nick Coghlan wrote: >> >> Nick Coghlan <ncoghlan@gmail.com> added the comment: >> >> At the very least, failing to regenerate importlib.h shouldn't be a fatal build error. It should just run with what its got, and hopefully you will get a working interpreter out the other end, such that you can regenerate the frozen module on the next pass. >> >> If we change that, then I'm OK with keeping the automatic rebuild. > > I fixed that already today. See http://bugs.python.org/issue14605 and http://hg.python.org/lookup/acfdf46b8de1 + http://hg.python.org/cpython/rev/5fea362b92fc > You now get a warning message from make, but no build error across > all buildbots like I had run into yesterday when working on the code. |
|||
msg159348 - (view) | Author: Nick Coghlan (ncoghlan) * | Date: 2012-04-26 01:21 | |
My plan would be for the frozen version to be entirely implicit, and have only the subsequent import of the version from disk actually modify the public hooks. However, I realised today that my current patch would break "stdlib-from-zipfile" approaches, so any bootstrapping of importlib from disk would have to take place after zipimport was put in place. That suggests a few possible changes: - reordering import_init so zipimport is initialised before the bootstrapping step - possibly downgrading failure of the bootstrapping step to a warning rather than a fatal error (i.e. continuing with the frozen version if the source version can't be found) This may still all prove to be too complicated and fragile, but I'm not prepared to give up on it yet - having the interpreter pick up on _bootstrap.py changes for the main import system *without* needing to be rebuilt first seems worth a bit of additional complexity in the bootstrapping mechanism. |
|||
msg159585 - (view) | Author: Nick Coghlan (ncoghlan) * | Date: 2012-04-29 08:24 | |
Uploaded new bootstrapping patch that handles the fully explicit import machinery. I also tweaked a couple of things so it plays nicely in terms of building an initial version with the checked in importlib.h. Specifically: pythonrun still calls _frozen_importlib._install and can tolerate that function returning None. Longer term, we'd give the two hooks different names and returning None will become a fatal error, but for the moment, the current behaviour makes the patch much easier to work with. Order is still wrong relative to the zipimport machinery and I haven't benchmarked the startup time overheads. |
|||
msg159961 - (view) | Author: Roundup Robot (python-dev) | Date: 2012-05-04 19:20 | |
New changeset 257cbd2fac38 by Brett Cannon in branch 'default': Issue #13959: Re-implement imp.get_suffixes() in Lib/imp.py. http://hg.python.org/cpython/rev/257cbd2fac38 |
|||
msg160053 - (view) | Author: Eric Snow (eric.snow) * | Date: 2012-05-06 04:50 | |
Here's my take. No one will care about _frozen_importlib vs. importlib._bootstrap normally, right? If __module__/__file__ says _frozen_importlib, it's no big deal. The only time you care about the distiction for importlib._bootstrap is when you're hacking on _bootstrap.py. So let's keep the common case in sight and go from there. There are two sides to the uncommon case: 1. making sure importlib still works after hacking on _bootstrap.py (test_imp, test_import, test_importlib using your new _bootstrap.py). 2. making sure everything still works after hacking on _bootstrap.py (the whole test suite with a new importlib.h?). For the first part, let's simply ignore the pure Python importlib._bootstrap by default? Then we stick a context manager in importlib.test.util that enables it. When you're hacking on _bootstrap.py, you switch it over. The common path stays pretty clean. I've attached a patch for the first part which has similarities to Antoine's. (I didn't apply the context manager to the importlib test cases though.) For that second part, something along the lines of what Nick has posted would be pretty close, but I'm not sure it's worth it. From what I understand, Nick's patch would add yet another import (importlib) to startup to cover a situation that happens very infrequently (hacking _bootstrap.py). However, I'm torn because... ...dealing with a busted importlib.h is not fun. Is there a different approach we could take for that second part? Perhaps something like this: 1. python starts up normally. 2. we clear out all the entire import state except for builtins. 3. we stick importlib._bootstrap in place. 4. we set builtins.__import__ to importlib.__import__. 5. we re-populate sys.modules by reloading all the modules that were in there before (?). 6. we run the test suite against this new import state. 7. ... 8. profit! I'm probably missing something here, but I expect we could stick something like that in some place like importlib.test.util. Would that be sufficient to mitigate the chance of breaking importlib.h? ------------------------------------ Example of using my patch: >>> import sys >>> import importlib.test.util as util >>> importlib._bootstrap <module '_frozen_importlib' from '<frozen>'> >>> sys.modules['importlib._bootstrap'] <module '_frozen_importlib' from '<frozen>'> >>> with util.bootstrap_context(importlib, importlib._pure_bootstrap): ... importlib._bootstrap ... sys.modules['importlib._bootstrap'] ... <module 'importlib._bootstrap' from '/home/esnow/projects/cpython/Lib/importlib/_bootstrap.py'> <module 'importlib._bootstrap' from '/home/esnow/projects/cpython/Lib/importlib/_bootstrap.py'> >>> importlib._bootstrap <module '_frozen_importlib' from '<frozen>'> >>> sys.modules['importlib._bootstrap'] <module '_frozen_importlib' from '<frozen>'> |
|||
msg160058 - (view) | Author: Nick Coghlan (ncoghlan) * | Date: 2012-05-06 07:04 | |
The piece you're missing is that the interpreter state holds a direct reference to the import machinery in interp->importlib, and *that's* what gets used by the builtin __import__ implementation. I'm beginning to think the thing to do is to simply say "yes, there are two copies of importlib._bootstrap". By default, the compiled in one is used, but you can replace it with the on-disk one by appropriately editing sys.meta_path and sys.path_hooks. Trying to hide it isn't going to eliminate the potential problems - it's just going to move the problems around (and likely make them even more confusing in the process). |
|||
msg160059 - (view) | Author: Nick Coghlan (ncoghlan) * | Date: 2012-05-06 07:06 | |
Forgot to add: in our own tests, we should ensure that both the frozen and on-disk versions get executed. I believe that's already the case, since I don't recall anyone removing the test infrastructure that ensured both import.c and importlib are tested for correct behaviour. |
|||
msg160060 - (view) | Author: Antoine Pitrou (pitrou) * | Date: 2012-05-06 07:23 | |
> I believe that's already the case, since I don't recall anyone > removing the test infrastructure that ensured both import.c and > importlib are tested for correct behaviour. What do you mean? I think test_importlib only tests the on-disk version. |
|||
msg160061 - (view) | Author: Antoine Pitrou (pitrou) * | Date: 2012-05-06 07:28 | |
> Here's my take. No one will care about _frozen_importlib vs. > importlib._bootstrap normally, right? If __module__/__file__ says > _frozen_importlib, it's no big deal. The reason I'd prefer __file__ to point to the actual Python file is so that people reading a traceback can find the source code. Of course that's a bit minor. (and, incidentally, the traceback itself will display the source code lines) > The only time you care about the distiction for importlib._bootstrap > is when you're hacking on _bootstrap.py. So let's keep the common > case in sight and go from there. Agreed. > For the first part, let's simply ignore the pure Python > importlib._bootstrap by default? Then we stick a context manager in > importlib.test.util that enables it. When you're hacking on > _bootstrap.py, you switch it over. The common path stays pretty > clean. Looks good to me. > I've attached a patch for the first part which has similarities to > Antoine's. (I didn't apply the context manager to the importlib test > cases though.) I think set_bootstrap() looks a bit fragile, since we have to manually add any importlib attributes that are exported in importlib/__init__.py. Perhaps we could detect them automatically? |
|||
msg160088 - (view) | Author: Brett Cannon (brett.cannon) * | Date: 2012-05-06 16:13 | |
To respond to Nick's "yes, there are two copies of importlib._bootstrap" leanings, distutils2 has actually run into issues with this because they initially made some assumptions about consistency in what importlib returned vs. what import does (Arfrever can explain better than I can since he keeps pointing it out to me =). If using _frozen_importlib to bootstrap in importlib._bootstrap is looking bad, then I'm fine w/ simply having the tests for importlib and imp use importlib._bootstrap and otherwise just use _frozen_importlib for everything else since I have tried to be diligent to add any and all import-related tests to importlib. Except while developing the code should be exactly the same so hiding the details really won't make much of a difference in the very common case. If we go this route, though, then we really should take this time to do a proper context manager/decorator/whatever that covers all import state (including uncaching modules and sys.path_importer_cache) that we might care about and put the solution into test.support (what issue #14715 is asking for and I think is reasonable). We should also then add to regrtest detection of stuff left in sys.path_importer_cache or sys.modules that do not come from _frozen_importlib (which should help with the sporadic test_imp failure). |
|||
msg160091 - (view) | Author: Arfrever Frehtes Taifersar Arahesis (Arfrever) * | Date: 2012-05-06 16:24 | |
It was distribute (fork of setuptools, with added support for Python 3), not distutils2. distribute has been changed to directly use _frozen_importlib: https://bitbucket.org/tarek/distribute/changeset/a2685f3af854 https://bitbucket.org/tarek/distribute/changeset/77c8b155a07d distribute checks __loader__ and __class__.__mro__ attributes of modules. |
|||
msg160093 - (view) | Author: Antoine Pitrou (pitrou) * | Date: 2012-05-06 16:33 | |
> It was distribute (fork of setuptools, with added support for Python 3), not distutils2. > distribute has been changed to directly use _frozen_importlib: This sounds like a rather good hint we need to avoid duplicate copies. |
|||
msg160104 - (view) | Author: Brett Cannon (brett.cannon) * | Date: 2012-05-06 19:05 | |
I think it's beyond a hint and says we need to find a solution or else other people will run into similar issues. And while I'm thinking about it, there is precedent for exposing modules under a different name than they are actually installed as in the system (e.g. os.path is posixpath), so I don't think we need to bend over backwards to mask every detail if the bootstrap solution is not taken (e.g. if we decided to just paper over _frozen_importlib we don't need to iterate over _frozen_importlib.__dict__ and patch up __module__). But I do think that we need to choose some solution to prevent this "forking" of code in the running interpreter. |
|||
msg160120 - (view) | Author: Nick Coghlan (ncoghlan) * | Date: 2012-05-07 06:50 | |
In that case, how about we go with: 1. By default, importlib._bootstrap is never imported. Instead, it is set to be a reference to _frozen_importlib. However, _frozen_importlib does *not* lie about where it came from (and doesn't assume the on-disk source matches the frozen source). 2. We provide two private functions in importlib.__init__: one that replaces all _frozen_importlib references in the import state with importlib._bootstrap references (retrieving the latter from disk first), and one that reverses the process. Note that the __import__ builtin should be replaced as well, since that will otherwise call in to the frozen version of the module. This is basically the same as Eric Snow's suggestion, just with most of the nuts and bolts kept within importlib, so that the testing context manager doesn't need to know the details - it can just call the appropriate importlib functions to change the active implementation. |
|||
msg160147 - (view) | Author: Brett Cannon (brett.cannon) * | Date: 2012-05-07 15:01 | |
Should we have a separate context manager for this, or just make it a flag for a unified import_state() decorator? Or do we want to *always* force the use of the Python code instead of the frozen code? |
|||
msg160148 - (view) | Author: Antoine Pitrou (pitrou) * | Date: 2012-05-07 15:05 | |
> Should we have a separate context manager for this, or just make it a > flag for a unified import_state() decorator? Or do we want to *always* > force the use of the Python code instead of the frozen code? Ideally, we would want to test both versions, so that any oddity in the freezing mechanism gets exercised and diagnosed properly. |
|||
msg160149 - (view) | Author: Antoine Pitrou (pitrou) * | Date: 2012-05-07 15:11 | |
> Ideally, we would want to test both versions, so that any oddity in the > freezing mechanism gets exercised and diagnosed properly. (not to mention the speedups in import.c) |
|||
msg160150 - (view) | Author: Eric Snow (eric.snow) * | Date: 2012-05-07 15:23 | |
I'm +1 on Nick's recommendation. @Antoine > Ideally, we would want to test both versions, so that any oddity in > the freezing mechanism gets exercised and diagnosed properly. +1 Does this mean that the whole test suite should be run under both (whenever _bootstrap.py is modified)? Would that warrant a new flag for the test suite or even an automated check? That's what I was getting at with this: > 1. python starts up normally. > 2. we clear out all the entire import state except for builtins. > 3. we stick importlib._bootstrap in place. > 4. we set builtins.__import__ to importlib.__import__. > 5. we re-populate sys.modules by reloading all the modules that > were in there before (?). > 6. we run the test suite against this new import state. |
|||
msg160153 - (view) | Author: Arfrever Frehtes Taifersar Arahesis (Arfrever) * | Date: 2012-05-07 15:42 | |
It might be sufficient to only run tests from the following files with both importlib._bootstrap and _frozen_importlib: test_imp.py test_import.py test_importhooks.py test_importlib.py test_pkgimport.py test_threaded_import.py test_zipimport.py test_zipimport_support.py |
|||
msg160154 - (view) | Author: Antoine Pitrou (pitrou) * | Date: 2012-05-07 15:47 | |
> It might be sufficient to only run tests from the following files with > both importlib._bootstrap and _frozen_importlib: I was only thinking about test_importlib myself. |
|||
msg160162 - (view) | Author: Brett Cannon (brett.cannon) * | Date: 2012-05-07 19:31 | |
I would say test_importlib and test_imp (test_import really should just get folded into test_importlib). |
|||
msg160974 - (view) | Author: Brett Cannon (brett.cannon) * | Date: 2012-05-17 15:25 | |
Realized that any decorator or context manager that is created for swapping _frozen_importlib/importlib._bootstrap should also verify no module is left in sys.modules with a bad loader and that sys.path_importer_cache doesn't have a bad finder (I would say that this would go into test.support.regrtest's state checks, but that seems overkill for only two tests). And this might be worth doing as a decorator (method or class) to make it easier to make sure the requisite tests always run with both versions (or copying what test_warnings does). I don't want to do anything in a module's test_main() as that precludes using unittest's test discovery for running tests. |
|||
msg162984 - (view) | Author: Antoine Pitrou (pitrou) * | Date: 2012-06-16 18:33 | |
Unless someone plans to do further work on this, I'd like to commit unique_importlib3.patch. A working solution is better than nothing. |
|||
msg163002 - (view) | Author: Eric Snow (eric.snow) * | Date: 2012-06-17 00:54 | |
It's actually at the top of my list, but may still be a week or two until I can get to it. |
|||
msg163010 - (view) | Author: Nick Coghlan (ncoghlan) * | Date: 2012-06-17 03:56 | |
+1 Antoine - your patch is better than the status quo, so it makes sense to apply it. If Eric can make the other way work in time for 3.3, great, but at least we'll have a solid fallback position without it. |
|||
msg163083 - (view) | Author: Roundup Robot (python-dev) | Date: 2012-06-17 20:36 | |
New changeset e3a984076837 by Antoine Pitrou in branch 'default': Issue #14657: The frozen instance of importlib used for bootstrap is now also the module imported as importlib._bootstrap. http://hg.python.org/cpython/rev/e3a984076837 |
|||
msg163084 - (view) | Author: Antoine Pitrou (pitrou) * | Date: 2012-06-17 20:40 | |
Ok, I've committed the patch. I'm closing this issue, but of course potential improvements can be posted under a new issue. |
|||
msg163804 - (view) | Author: Brett Cannon (brett.cannon) * | Date: 2012-06-24 15:47 | |
Do people feel the need to change the importlib tests to run against both the frozen code and importlib/_bootstrap.py? If so either the test_warnings approach (or a new one using class decorators or something) should be used to run the tests twice. |
|||
msg163878 - (view) | Author: Nick Coghlan (ncoghlan) * | Date: 2012-06-25 00:40 | |
Given Antoine's other change to the build process to fix the bootstrapping problem, I'm OK with leaving it up to anyone hacking on _bootstrap.py to remember that they need to run make if they want the interpreter to see any of their changes. Unlike the C accelerator cases, it's not possible for the two implementations to get out of sync on the buildbots, so running the tests twice would just be a convenience change for anyone hacking on _bootstrap.py rather than being needed for correctness. That said, if someone created a new issue and posted a patch to run the tests twice, I wouldn't object - a classic case of +0 :) |
History | |||
---|---|---|---|
Date | User | Action | Args |
2022-04-11 14:57:29 | admin | set | github: 58862 |
2012-06-25 00:40:03 | ncoghlan | set | messages: + msg163878 |
2012-06-24 15:47:41 | brett.cannon | set | messages: + msg163804 |
2012-06-17 20:40:03 | pitrou | set | status: open -> closed messages: + msg163084 dependencies: - test.support.DirsOnSysPath should be replaced by importlib.test.util.import_state resolution: fixed stage: patch review -> resolved |
2012-06-17 20:36:57 | python-dev | set | messages: + msg163083 |
2012-06-17 03:56:08 | ncoghlan | set | messages: + msg163010 |
2012-06-17 00:54:19 | eric.snow | set | messages: + msg163002 |
2012-06-16 18:33:49 | pitrou | set | messages: + msg162984 |
2012-05-17 15:25:50 | brett.cannon | set | messages: + msg160974 |
2012-05-07 19:31:12 | brett.cannon | set | messages: + msg160162 |
2012-05-07 15:47:04 | pitrou | set | messages: + msg160154 |
2012-05-07 15:42:30 | Arfrever | set | messages: + msg160153 |
2012-05-07 15:23:04 | eric.snow | set | messages: + msg160150 |
2012-05-07 15:11:01 | pitrou | set | messages: + msg160149 |
2012-05-07 15:05:28 | pitrou | set | messages: + msg160148 |
2012-05-07 15:01:37 | brett.cannon | set | messages: + msg160147 |
2012-05-07 06:50:03 | ncoghlan | set | messages: + msg160120 |
2012-05-06 19:05:06 | brett.cannon | set | messages: + msg160104 |
2012-05-06 16:33:09 | pitrou | set | messages: + msg160093 |
2012-05-06 16:24:17 | Arfrever | set | messages: + msg160091 |
2012-05-06 16:13:04 | brett.cannon | set | dependencies:
+ test.support.DirsOnSysPath should be replaced by importlib.test.util.import_state messages: + msg160088 |
2012-05-06 07:28:33 | pitrou | set | messages: + msg160061 |
2012-05-06 07:23:54 | pitrou | set | messages: + msg160060 |
2012-05-06 07:06:46 | ncoghlan | set | messages: + msg160059 |
2012-05-06 07:04:56 | ncoghlan | set | messages: + msg160058 |
2012-05-06 04:50:11 | eric.snow | set | files:
+ issue14657_safe_bootstrap.diff messages: + msg160053 |
2012-05-04 19:23:39 | brett.cannon | link | issue13959 dependencies |
2012-05-04 19:20:48 | python-dev | set | nosy:
+ python-dev messages: + msg159961 |
2012-04-29 08:24:13 | ncoghlan | set | files:
+ issue14657_bootstrap_from_disk_v2.diff messages: + msg159585 |
2012-04-26 01:21:55 | ncoghlan | set | messages: + msg159348 |
2012-04-25 17:46:58 | lemburg | set | messages: + msg159328 |
2012-04-25 16:46:09 | lemburg | set | messages: + msg159318 |
2012-04-25 16:40:44 | brett.cannon | set | messages: + msg159316 |
2012-04-25 16:02:27 | ncoghlan | set | messages: + msg159311 |
2012-04-25 15:58:28 | ncoghlan | set | messages: + msg159310 |
2012-04-25 15:55:44 | pitrou | set | messages: + msg159308 |
2012-04-25 15:52:22 | eric.araujo | set | nosy:
+ eric.araujo messages: + msg159307 |
2012-04-25 15:41:34 | ncoghlan | set | messages: + msg159304 |
2012-04-25 15:38:45 | ncoghlan | set | messages: + msg159302 |
2012-04-25 15:25:48 | pitrou | set | messages: + msg159299 |
2012-04-25 15:14:56 | ncoghlan | set | files:
+ issue14657_bootstrap_from_disk.diff messages: + msg159296 |
2012-04-25 14:58:43 | brett.cannon | set | messages: + msg159292 |
2012-04-25 12:46:58 | pitrou | set | messages: + msg159275 |
2012-04-25 12:38:50 | pitrou | set | messages: + msg159274 |
2012-04-25 12:32:16 | ncoghlan | set | messages: + msg159273 |
2012-04-25 12:28:03 | lemburg | set | messages: + msg159272 |
2012-04-25 12:12:28 | pitrou | set | messages: + msg159270 |
2012-04-25 12:06:40 | lemburg | set | messages: + msg159269 |
2012-04-25 11:49:04 | pitrou | set | messages: + msg159268 |
2012-04-25 09:07:32 | lemburg | set | messages: + msg159264 |
2012-04-25 01:13:42 | brett.cannon | set | messages: + msg159247 |
2012-04-24 22:29:52 | pitrou | set | messages: + msg159225 |
2012-04-24 22:21:52 | lemburg | set | messages: + msg159224 |
2012-04-24 21:52:01 | pitrou | set | messages: + msg159218 |
2012-04-24 21:46:20 | lemburg | set | messages: + msg159217 |
2012-04-24 21:37:58 | pitrou | set | messages: + msg159215 |
2012-04-24 21:36:46 | brett.cannon | set | messages: + msg159214 |
2012-04-24 21:15:28 | pitrou | set | messages: + msg159211 |
2012-04-24 21:13:14 | brett.cannon | set | messages: + msg159209 |
2012-04-24 20:42:23 | lemburg | set | messages: + msg159204 |
2012-04-24 20:39:08 | pitrou | set | messages: + msg159203 |
2012-04-24 20:25:34 | brett.cannon | set | messages: + msg159202 |
2012-04-24 19:51:49 | lemburg | set | messages: + msg159198 |
2012-04-24 19:23:34 | brett.cannon | set | messages: + msg159196 |
2012-04-24 18:40:44 | eric.snow | set | messages: + msg159186 |
2012-04-24 17:37:00 | lemburg | set | messages: + msg159173 |
2012-04-24 15:16:11 | pitrou | set | messages: + msg159158 |
2012-04-24 15:10:18 | brett.cannon | set | messages: + msg159154 |
2012-04-24 09:14:55 | lemburg | set | nosy:
+ lemburg messages: + msg159128 |
2012-04-24 09:06:19 | pitrou | set | messages: + msg159127 |
2012-04-24 08:10:20 | pitrou | set | messages: + msg159123 |
2012-04-24 04:48:06 | Arfrever | set | nosy:
+ Arfrever |
2012-04-24 04:40:17 | ncoghlan | set | messages: + msg159116 |
2012-04-24 00:56:35 | brett.cannon | set | messages: + msg159109 |
2012-04-24 00:41:42 | brett.cannon | set | messages: + msg159104 |
2012-04-24 00:09:19 | eric.snow | set | nosy:
+ eric.snow |
2012-04-23 23:01:27 | pitrou | set | files:
+ unique_importlib3.patch messages: + msg159100 |
2012-04-23 22:50:27 | pitrou | set | files:
+ unique_importlib2.patch messages: + msg159098 |
2012-04-23 22:43:14 | pitrou | set | files: + unique_importlib.patch |
2012-04-23 22:42:35 | eric.smith | set | nosy:
+ eric.smith |
2012-04-23 22:42:05 | pitrou | set | files: - unique_importlib.patch |
2012-04-23 22:34:03 | pitrou | create |