Importable wheels using distlib/distil
I'm not top-posting, but trying to keep GMane happy ...
Since wheels are .zip files, they can sometimes be used to provide functionality without needing to be installed. Whereas .zip files contain no convention for indicating compatibility with a particular Python, wheels do contain this compatibility information. Thus, it is possible to check if a wheel can be directly imported from, and the wheel support in distlib allows you to take advantage of this using the mount() and unmount() methods. When you mount a wheel, its absolute path name is added to sys.path, allowing the Python code in it to be imported. (A DistlibException is raised if the wheel isn't compatible with the Python which calls the mount() method.) You don't need mount() just to add the wheel's name to sys.path, or to import pure-Python wheels. The mount() method goes further than just enabling Python imports - any C extensions in the wheel are also made available for import. For this to be possible, the wheel has to be built with additional metadata about extensions - a JSON file called EXTENSIONS which serialises an extension mapping dictionary. This maps extension module names to the names in the wheel of the shared libraries which implement those modules. Running unmount() on the wheel removes its absolute pathname from sys.path and makes its C extensions, if any, also unavailable for import. Wheels built with distil contain the EXTENSIONS metadata, so can be mounted complete with C extensions: $ distil download -d /tmp simplejson Downloading simplejson-3.1.2.tar.gz to /tmp/simplejson-3.1.2 63KB @ 73 KB/s 100 % Done: 00:00:00 Unpacking ... done. $ distil package --fo=wh -d /tmp /tmp/simplejson-3.1.2/ The following packages were built: /tmp/simplejson-3.1.2-cp27-none-linux_x86_64.whl $ python Python 2.7.2+ (default, Jul 20 2012, 22:15:08) [GCC 4.6.1] on linux2 Type "help", "copyright", "credits" or "license" for more information.
from distlib.wheel import Wheel w = Wheel('/tmp/simplejson-3.1.2-cp27-none-linux_x86_64.whl') w.mount() import simplejson._speedups dir(simplejson._speedups) ['__doc__', '__file__', '__loader__', '__name__', '__package__', 'encode_basestring_ascii', 'make_encoder', 'make_scanner', 'scanstring'] simplejson._speedups.__file__ '/home/vinay/.distlib/dylib-cache/simplejson/_speedups.so'
This, IMO, makes the wheel format more useful than it already is :-) Regards, Vinay Sajip
On Wed, Mar 27, 2013 at 3:21 PM, Vinay Sajip <vinay_sajip@yahoo.co.uk> wrote:
I'm not top-posting, but trying to keep GMane happy ...
Since wheels are .zip files, they can sometimes be used to provide functionality without needing to be installed. Whereas .zip files contain no convention for indicating compatibility with a particular Python, wheels do contain this compatibility information. Thus, it is possible to check if a wheel can be directly imported from, and the wheel support in distlib allows you to take advantage of this using the mount() and unmount() methods. When you mount a wheel, its absolute path name is added to sys.path, allowing the Python code in it to be imported. (A DistlibException is raised if the wheel isn't compatible with the Python which calls the mount() method.)
You don't need mount() just to add the wheel's name to sys.path, or to import pure-Python wheels. The mount() method goes further than just enabling Python imports - any C extensions in the wheel are also made available for import. For this to be possible, the wheel has to be built with additional metadata about extensions - a JSON file called EXTENSIONS which serialises an extension mapping dictionary. This maps extension module names to the names in the wheel of the shared libraries which implement those modules.
Running unmount() on the wheel removes its absolute pathname from sys.path and makes its C extensions, if any, also unavailable for import.
Wheels built with distil contain the EXTENSIONS metadata, so can be mounted complete with C extensions:
$ distil download -d /tmp simplejson Downloading simplejson-3.1.2.tar.gz to /tmp/simplejson-3.1.2 63KB @ 73 KB/s 100 % Done: 00:00:00 Unpacking ... done. $ distil package --fo=wh -d /tmp /tmp/simplejson-3.1.2/ The following packages were built: /tmp/simplejson-3.1.2-cp27-none-linux_x86_64.whl $ python Python 2.7.2+ (default, Jul 20 2012, 22:15:08) [GCC 4.6.1] on linux2 Type "help", "copyright", "credits" or "license" for more information.
from distlib.wheel import Wheel w = Wheel('/tmp/simplejson-3.1.2-cp27-none-linux_x86_64.whl') w.mount() import simplejson._speedups dir(simplejson._speedups) ['__doc__', '__file__', '__loader__', '__name__', '__package__', 'encode_basestring_ascii', 'make_encoder', 'make_scanner', 'scanstring'] simplejson._speedups.__file__ '/home/vinay/.distlib/dylib-cache/simplejson/_speedups.so'
This, IMO, makes the wheel format more useful than it already is :-)
It's a trap! At least on Unix systems: - Extensions in zip files that get magically extracted to a user's home directory lead to tragic deployment failures for services that run as special users. - Zip files are a pain in the ass during development or debugging. - Zip files are slower to import from (at least in my experience) It would be far better IMO to just unzip the wheel and put that in your path. (I'm hoping that wheels used this way are a suitable replacement for eggs.) Jim -- Jim Fulton http://www.linkedin.com/in/jimfulton
Jim Fulton <jim <at> zope.com> writes:
It's a trap!
At least on Unix systems:
- Extensions in zip files that get magically extracted to a user's home directory lead to tragic deployment failures for services that run as special users.
I can see how it would lead to problems, but the home directory location is just as a proof of concept - the cache doesn't need to be in any private place.
- Zip files are a pain in the ass during development or debugging.
Of course, but wheels are for deployment, not development, and this is one possibility for deployment (several people have mentioned wanting to sometimes just add wheels to sys.path rather than installing them, which got me thinking about this functionality).
- Zip files are slower to import from (at least in my experience)
It's just another option for a user of wheels. Caveat emptor, and all that.
It would be far better IMO to just unzip the wheel and put that in your path. (I'm hoping that wheels used this way are a suitable replacement for eggs.)
Well that's tantamount to installing the wheel, which is fine. I was thinking along the line of egg replacement - AFAIK eggs allow you to import extensions from zip in a similar fashion. Regards, Vinay Sajip
On Wed, Mar 27, 2013 at 4:50 PM, Vinay Sajip <vinay_sajip@yahoo.co.uk> wrote:
Jim Fulton <jim <at> zope.com> writes:
It's a trap!
At least on Unix systems:
- Extensions in zip files that get magically extracted to a user's home directory lead to tragic deployment failures for services that run as special users.
I can see how it would lead to problems, but the home directory location is just as a proof of concept - the cache doesn't need to be in any private place.
Anywhere you extract them is likely going to lead to access control or security issues and generally cause pain, IMO.
- Zip files are a pain in the ass during development or debugging.
Of course, but wheels are for deployment, not development, and this is one possibility for deployment (several people have mentioned wanting to sometimes just add wheels to sys.path rather than installing them, which got me thinking about this functionality).
I expect to use wheels during development just like I use eggs now. Not for development of the wheel/egg, but for development of something that uses it. You're in pdb and you land in a zipped egg/wheel that the package under development invoked and now you're screwed.
- Zip files are slower to import from (at least in my experience)
It's just another option for a user of wheels. Caveat emptor, and all that.
It's been tried with eggs. This is not new ground. Encouraging people to do this is going to cause pain and resentment. I think one of the reasons there's so much (IMO mostly irrational) hate for eggs is that people think you can only used zipped eggs, and zipped eggs cause pain and agony.
It would be far better IMO to just unzip the wheel and put that in your path. (I'm hoping that wheels used this way are a suitable replacement for eggs.)
Well that's tantamount to installing the wheel,
Not really. If you just unzip the wheel and add it to your path, you can stop using it by just removing from your path. If you install the wheel, it's contents will be poured into site-packages (and other places). It's much heavier than just adding the wheel (zipped or unzipped) to your path.
which is fine. I was thinking along the line of egg replacement
Me too.
- AFAIK eggs allow you to import extensions from zip in a similar fashion.
Importing from zipped eggs has proved itself to be an anti pattern. Buildout (as of buildout 2) always unzips eggs. It can then generate scripts with just the packages they need by adding (unzipped) eggs to sys.path. Various plugin systems (including buildout itself with extensions and recipes) do this dynamically at run time. It's very useful. Jim -- Jim Fulton http://www.linkedin.com/in/jimfulton
On 27 March 2013 21:06, Jim Fulton <jim@zope.com> wrote:
- AFAIK eggs allow you to import extensions from zip in a similar fashion.
Importing from zipped eggs has proved itself to be an anti pattern.
I don't like the idea of making wheels work like eggs in this respect. As Jim said, (zipped) eggs have a very bad reputation and associating wheels with that type of functionality would be a very bad idea. Wheels are, and should remain, a binary installer format. On the other hand, zipimport is a very cool feature, and seriously under-used. But it has specific benefits and limitations, and in particular zipimport does not support binary extensions for very good reasons. Zip files on sys.path are practical for pure Python code only, IMO. Having said all that, the fact that wheels are zipfiles, and can be used on sys.path, *can* be useful. But it's an incidental benefit and *not* a core feature. Paul.
Jim Fulton <jim <at> zope.com> writes:
It would be far better IMO to just unzip the wheel and put that in your path. (I'm hoping that wheels used this way are a suitable replacement for eggs.)
Well that's tantamount to installing the wheel,
Not really. If you just unzip the wheel and add it to your path, you can stop using it by just removing from your path. If you install the wheel, it's contents will be poured into site-packages (and other places). It's much heavier than just adding the wheel (zipped or unzipped) to your path.
[snip]
by adding (unzipped) eggs to sys.path. Various plugin systems (including buildout itself with extensions and recipes) do this dynamically at run time. It's very useful.
Thanks for the feedback. How about if I change mount()/unmount() to: def mount(self, append=False, destdir=None): """ Unzip the wheel's contents to the specified directory, or to a temporary directory if destdir is None. Add this directory to sys.path, either appending or prepending according to whether append is True or False. Before doing this, check that the wheel is compatible with the Python making the call to mount(). If successful, this makes the contents of the wheel's root directory - both Python packages and C extensions - importable via normal Python import mechanisms. """ def unmount(self): """ Remove the directory that was used for mounting from sys.path, thus making the wheel's code no longer importable. Return this directory. Note that the caller is responsible for deleting this directory and its contents, which might not be possible - e.g. in Windows, if a shared library has been imported and is linked to the running Python process, there will be an open handle to the shared library which will prevent its deletion. """ Regards, Vinay Sajip
On 28 March 2013 16:02, Vinay Sajip <vinay_sajip@yahoo.co.uk> wrote:
Return this directory. Note that the caller is responsible for deleting this directory and its contents, which might not be possible - e.g. in Windows, if a shared library has been imported and is linked to the running Python process, there will be an open handle to the shared library which will prevent its deletion. """
That's the big issue I have with *any* approach like this. It's entirely possible that the directory cannot be deleted, and as a result the user ends up with the problem of managing clutter caused by this mechanism. Even if the directory is in %TEMP% the user still has the issue of clearing up. Consider a buildslave that continually runs tests - temp directory clutter is a definite issue in a situation like that. And of course, if an application user chooses to use this mechanism, I don't have an option to opt out unless we start getting into complex "if the package is installed use it, otherwise mount our internal wheel" logic. I'd like to hold off on this feature until there are actual requests for the functionality. It's not easy to argue against the idea purely on a "it might go wrong" basis without actual use cases to look at and see if/how they would handle the problem situations. Paul.
Am 28.03.2013 17:42, schrieb Paul Moore:
On 28 March 2013 16:02, Vinay Sajip <vinay_sajip@yahoo.co.uk> wrote:
Return this directory. Note that the caller is responsible for deleting this directory and its contents, which might not be possible - e.g. in Windows, if a shared library has been imported and is linked to the running Python process, there will be an open handle to the shared library which will prevent its deletion. """
That's the big issue I have with *any* approach like this. It's entirely possible that the directory cannot be deleted, and as a result the user ends up with the problem of managing clutter caused by this mechanism. Even if the directory is in %TEMP% the user still has the issue of clearing up. Consider a buildslave that continually runs tests - temp directory clutter is a definite issue in a situation like that.
I made an experiment some time ago: It is possible to delete shared libs containing extension modules imported by Python if the Python process (after Py_Finalize()) calls FreeLibrary(hmod) in a loop for every extension until FreeLibrary returns zero; then the shared lib file can be deleted. Thomas
Paul Moore <p.f.moore <at> gmail.com> writes:
That's the big issue I have with *any* approach like this. It's entirely possible that the directory cannot be deleted, and as a result the user ends up with the problem of managing clutter caused by this mechanism. Even if the directory is in %TEMP% the user still has the issue of clearing up. Consider a buildslave that continually runs tests - temp directory clutter is a definite issue in a situation like that.
And of course, if an application user chooses to use this mechanism, I don't have an option to opt out unless we start getting into complex "if the package is installed use it, otherwise mount our internal wheel" logic.
Well, if you use the feature because it has its uses, you have to work around any costs that it has. At least the problem isn't being ducked. Plus, given that the wheel format is open, it's not a lot of work for an application developer to do a zipfile.extractall() followed by a sys.path.append(), whether distlib's Wheel has a mount() or not. Having mount() might be facilitating a useful feature in a (slightly more) controlled fashion.
I'd like to hold off on this feature until there are actual requests for the functionality. It's not easy to argue against the idea purely on a "it might go wrong" basis without actual use cases to look at and see if/how they would handle the problem situations.
Didn't Jim Fulton say in a post in this thread that it was a useful feature? I'm presuming he based this on real-world experience, but perhaps he would care to clarify. In terms of "actual requests" - there haven't been any actual requests for anything, other than suggestions to improve features that I've unilaterally introduced. This is another instance of the same thing, it seems to me. This is a feature that eggs have but nothing else does, so it seems reasonable to see if we can have alternatives. AFAICT, your worries would apply to eggs too, it's nothing to do with wheels or distlib in particular ... Regards, Vinay Sajip
On Thu, Mar 28, 2013 at 12:02 PM, Vinay Sajip <vinay_sajip@yahoo.co.uk> wrote:
Jim Fulton <jim <at> zope.com> writes:
It would be far better IMO to just unzip the wheel and put that in your path. (I'm hoping that wheels used this way are a suitable replacement for eggs.)
Well that's tantamount to installing the wheel,
Not really. If you just unzip the wheel and add it to your path, you can stop using it by just removing from your path. If you install the wheel, it's contents will be poured into site-packages (and other places). It's much heavier than just adding the wheel (zipped or unzipped) to your path.
[snip]
by adding (unzipped) eggs to sys.path. Various plugin systems (including buildout itself with extensions and recipes) do this dynamically at run time. It's very useful.
Thanks for the feedback.
Thanks for trying to provide a useful feature. I hope my comments aren't too much of a downer.
How about if I change mount()/unmount() to:
def mount(self, append=False, destdir=None): """ Unzip the wheel's contents to the specified directory, or to a temporary directory if destdir is None. Add this directory to sys.path, either appending or prepending according to whether append is True or False.
Before doing this, check that the wheel is compatible with the Python making the call to mount().
If successful, this makes the contents of the wheel's root directory - both Python packages and C extensions - importable via normal Python import mechanisms. """
def unmount(self): """ Remove the directory that was used for mounting from sys.path, thus making the wheel's code no longer importable.
Return this directory. Note that the caller is responsible for deleting this directory and its contents, which might not be possible - e.g. in Windows, if a shared library has been imported and is linked to the running Python process, there will be an open handle to the shared library which will prevent its deletion. """
I'm not sure which users or use cases you're trying to serve here, so I'm not sure what to think of this. For buildout users, buildout would download and extract the wheel the first time it's used and keep it in a cache and then add it to a path at script generation time. For buildout's own uses (extensions and recipes) it would simply add the extracted wheel's location to sys.path at run time (downloading and extracting it first if necessary). So the win for buildout and it's users is to be able to have extracted (but not "installed" wheels) around to be mixed and matched either for script generation or run-time use. If I wasn't using buildout, I kinda doubt I'd want to use something like this rather than just installing wheels with pip. Jim P.S. I'm happy to see all the work you've done on distlib. I'm sorry to say I haven't had time to dig into it yet. I assume that buildout 3 will be based on it at some point. -- Jim Fulton http://www.linkedin.com/in/jimfulton
Jim Fulton <jim <at> zope.com> writes:
So the win for buildout and it's users is to be able to have extracted (but not "installed" wheels) around to be mixed and matched either for script generation or run-time use.
If I wasn't using buildout, I kinda doubt I'd want to use something like this rather than just installing wheels with pip.
Ok, thanks for the clarification. Regards, Vinay Sajip
On Fri, Mar 29, 2013 at 2:02 AM, Vinay Sajip <vinay_sajip@yahoo.co.uk> wrote:
Thanks for the feedback. How about if I change mount()/unmount() to:
def mount(self, append=False, destdir=None): """ Unzip the wheel's contents to the specified directory, or to a temporary directory if destdir is None. Add this directory to sys.path, either appending or prepending according to whether append is True or False.
No, mutating sys.path for versioned imports is a broken design. You end up with two possibilities: * If you append, then you can't override modules that have a default version available on sys.path. This is not an acceptable restriction, which is why pkg_resources doesn't do it that way * If you prepend, then you have the existing pkg_resources failure mode where it can accidentally shadow more modules than intended. This is a nightmare to debug when it goes wrong (it took me months to realise this was why a system install of the main application I work on was shadowing the version in source checkout when running the test suite or building the documentation). The correct way to do it is with a path hook that processes a special "<versioned-packages>" marker label in sys.path (probably placed after the standard library but before site-packages by default). Any mounted directories would be tracked by that path hook, but never included directly in sys.path itself. See http://mail.python.org/pipermail/distutils-sig/2013-March/020207.html for more on how this could be handled (consider mount/unmount as the lower level API for actually adding new path entries directly to the dynamic importer). Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
On Fri, Mar 29, 2013 at 3:55 PM, Nick Coghlan <ncoghlan@gmail.com> wrote:
No, mutating sys.path for versioned imports is a broken design. You end up with two possibilities:
* If you append, then you can't override modules that have a default version available on sys.path. This is not an acceptable restriction, which is why pkg_resources doesn't do it that way * If you prepend, then you have the existing pkg_resources failure mode where it can accidentally shadow more modules than intended. This is a nightmare to debug when it goes wrong (it took me months to realise this was why a system install of the main application I work on was shadowing the version in source checkout when running the test suite or building the documentation).
The correct way to do it is with a path hook that processes a special "<versioned-packages>" marker label in sys.path (probably placed after the standard library but before site-packages by default). Any mounted directories would be tracked by that path hook, but never included directly in sys.path itself.
How is that different from replacing "<versioned-packages>" with the path of the versioned package being added?
On Sat, Mar 30, 2013 at 6:42 AM, PJ Eby <pje@telecommunity.com> wrote:
On Fri, Mar 29, 2013 at 3:55 PM, Nick Coghlan <ncoghlan@gmail.com> wrote:
No, mutating sys.path for versioned imports is a broken design. You end up with two possibilities:
* If you append, then you can't override modules that have a default version available on sys.path. This is not an acceptable restriction, which is why pkg_resources doesn't do it that way * If you prepend, then you have the existing pkg_resources failure mode where it can accidentally shadow more modules than intended. This is a nightmare to debug when it goes wrong (it took me months to realise this was why a system install of the main application I work on was shadowing the version in source checkout when running the test suite or building the documentation).
The correct way to do it is with a path hook that processes a special "<versioned-packages>" marker label in sys.path (probably placed after the standard library but before site-packages by default). Any mounted directories would be tracked by that path hook, but never included directly in sys.path itself.
How is that different from replacing "<versioned-packages>" with the path of the versioned package being added?
You don't lose the place where you want the inserts to happen. Without the marker, you end up having to come up with a heuristic for "make insertions here" and that gets messy as you modify the path (particularly since other code may also modify the path without your involvement). Using a path hook to say "process these additional path entries here" cleans all that up and lets you precisely control the relative precedence of the additions as a group, without needing to care about their contents, or the other contents of sys.path. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
On Fri, Mar 29, 2013 at 4:50 PM, Nick Coghlan <ncoghlan@gmail.com> wrote:
On Sat, Mar 30, 2013 at 6:42 AM, PJ Eby <pje@telecommunity.com> wrote:
On Fri, Mar 29, 2013 at 3:55 PM, Nick Coghlan <ncoghlan@gmail.com> wrote:
No, mutating sys.path for versioned imports is a broken design. You end up with two possibilities:
* If you append, then you can't override modules that have a default version available on sys.path. This is not an acceptable restriction, which is why pkg_resources doesn't do it that way * If you prepend, then you have the existing pkg_resources failure mode where it can accidentally shadow more modules than intended. This is a nightmare to debug when it goes wrong (it took me months to realise this was why a system install of the main application I work on was shadowing the version in source checkout when running the test suite or building the documentation).
The correct way to do it is with a path hook that processes a special "<versioned-packages>" marker label in sys.path (probably placed after the standard library but before site-packages by default). Any mounted directories would be tracked by that path hook, but never included directly in sys.path itself.
How is that different from replacing "<versioned-packages>" with the path of the versioned package being added?
You don't lose the place where you want the inserts to happen. Without the marker, you end up having to come up with a heuristic for "make insertions here" and that gets messy as you modify the path (particularly since other code may also modify the path without your involvement).
But at least you can tell exactly what the order is by inspecting sys.path.
Using a path hook to say "process these additional path entries here" cleans all that up and lets you precisely control the relative precedence of the additions as a group, without needing to care about their contents, or the other contents of sys.path.
Then can't you just bracket the entries with "<start-versioned>" and "<end-versioned>" then? ;-) (That would actually work right now without a path hook, since strings that refer to non-existent paths are optimized away even in Python 2.5+.) Also, btw, pkg_resources's sys.path munging is far more aggressive than anything with wheels needs to be, because pkg_resources is *always* working with individual eggs, not plain install directories. If you are only using standalone wheels to handle alternate versions, then you *definitely* want those standalone wheels to override other things, so a strategy of always placing them immediately before their containing directory is actually safe. (In effect, treating the containing directory as the insertion marker.) So, if the standalone wheel is in site-packages, then activating it would place it just ahead of site-packages -- i.e., the same place you're saying it should go. And as I believe I mentioned before, a single marker for insertion points doesn't address the user site-packages, app directory or app plugin directories, etc. AFAICT your proposal only addresses the needs of system packages, and punts on everything else. At the least, may I suggest that instead of using a marker, if you must use a path hook, simply install the path hook as a wrapper for a specific directory (e.g. site-packages), and let it process insertions for *that directory only*, rather than having a single global notion of "all the versioned packages".
On Sat, Mar 30, 2013 at 8:52 AM, PJ Eby <pje@telecommunity.com> wrote:
On Fri, Mar 29, 2013 at 4:50 PM, Nick Coghlan <ncoghlan@gmail.com> wrote:
You don't lose the place where you want the inserts to happen. Without the marker, you end up having to come up with a heuristic for "make insertions here" and that gets messy as you modify the path (particularly since other code may also modify the path without your involvement).
But at least you can tell exactly what the order is by inspecting sys.path.
Agreed that introspection support for metapath importers and path hooks is currently lacking.
Using a path hook to say "process these additional path entries here" cleans all that up and lets you precisely control the relative precedence of the additions as a group, without needing to care about their contents, or the other contents of sys.path.
Then can't you just bracket the entries with "<start-versioned>" and "<end-versioned>" then? ;-)
(That would actually work right now without a path hook, since strings that refer to non-existent paths are optimized away even in Python 2.5+.)
Sure, you *could*, but then you're effectively embedding a list inside another list and it would be a lot cleaner to just say "at this point, go consult that other dynamically modified list over there".
Also, btw, pkg_resources's sys.path munging is far more aggressive than anything with wheels needs to be, because pkg_resources is *always* working with individual eggs, not plain install directories. If you are only using standalone wheels to handle alternate versions, then you *definitely* want those standalone wheels to override other things, so a strategy of always placing them immediately before their containing directory is actually safe. (In effect, treating the containing directory as the insertion marker.)
Yes, that was the other notion I had - insert the extra directory immediately before the directory where the metadata was located.
So, if the standalone wheel is in site-packages, then activating it would place it just ahead of site-packages -- i.e., the same place you're saying it should go.
And as I believe I mentioned before, a single marker for insertion points doesn't address the user site-packages, app directory or app plugin directories, etc. AFAICT your proposal only addresses the needs of system packages, and punts on everything else.
No, it doesn't - all it does is provide a clear demarcation between the default system path provided by the interpreter and the ad hoc runtime modifications used to gain access to additional packages that aren't available by default. At the moment, if you print out sys.path, you have *no idea* what it originally looked like before the application started modifying it (process global shared state is always fun that way). *What* directories can be added is then entirely up to the manipulation API. I guess we could easily enough snapshot the path during the interpreter initialisation to help diagnose issues with post-startup modifications.
At the least, may I suggest that instead of using a marker, if you must use a path hook, simply install the path hook as a wrapper for a specific directory (e.g. site-packages), and let it process insertions for *that directory only*, rather than having a single global notion of "all the versioned packages".
It's not really "all the versioned packages", it's "all the packages found through this particular path manipulation API". Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
participants (7)
-
Jim Fulton
-
Nick Coghlan
-
Paul Moore
-
PJ Eby
-
Ralf Schmitt
-
Thomas Heller
-
Vinay Sajip