PEP 370 – Per user site-packages, and environment stew

cyber.jpgSo, following up from my hard-hitting rant on the subject of dealing with packaging a portable python version (without hardcoded shebang lines) for OS/X, and later cutting over to a kickstart based virtualenv setup, I thought I’d dig into PEP 370 “a bit” as someone pointed out to me this might just cure some of the heart burn.

I put “a bit” in quotes for a reason – PEP 370 itself was probably one of the simplest discussions around a feature on python-dev. It came in on the 2.6-and-forward boat last year. It’s also only about 2-3 pages long, depending on your font size.

The idea is this – when you run python2.6/3.0 (from now on, I’m sticking with 2.6) you will get a ~/.local directory (for those “not in the know” – ~ is your home directory, e.g. /Users/jesse on OS/X).

This directory is laid out like this:

.local/
    bin/
    lib/
        pythonX.X (wherein X.X is the version number)
            site-packages

Disutils was modified to support the –user argument. This means you can run “python setup.py –user” and your .local directory will get populated with the delicious nougat payload of the app.

pip supports this just fine, for example:

zim:~ jesse$ /Library/Frameworks/Python.framework/Versions/2.6/bin/pip install \
--install-option="--user" yolk

Downloading/unpacking yolk
  Downloading yolk-0.4.1.tar.gz (80Kb): 80Kb downloaded
  Running setup.py egg_info for package yolk
Installing collected packages: setuptools, yolk
  Running setup.py install for yolk
    Installing yolk script to /Users/jesse/.local/bin
Successfully installed yolk

Hooray! Look! Files!

zim:~ jesse$ ls -lr .local/
total 0
drwxr-xr-x@ 6 jesse  jesse  204 Mar 31 18:35 lib
drwxrwxr-x  3 jesse  jesse  102 Jul 18 22:09 bin
zim:~ jesse$ ls -lr .local/lib/python2.6/site-packages/
total 0
drwxrwxr-x   9 jesse  jesse  306 Jul 18 22:09 yolk-0.4.1-py2.6.egg-info
drwxrwxr-x  17 jesse  jesse  578 Jul 18 22:09 yolk
zim:~ jesse$ ls -lr .local/bin/
total 8
-rwxr-xr-x  1 jesse  jesse  323 Jul 18 22:09 yolk
zim:~ jesse$

Yes, this means yolk is now installed into my local directory – not the global directory. I can also add .local/bin to my PATH and gain access to the yolk binary. This is a huge step forward. Oh, wait. There’s only one yolk binary:

zim:~ jesse$ cat .local/bin/yolk
#!/Library/Frameworks/Python.framework/Versions/2.6/Resources/Python.app/Contents/MacOS/Python
# EASY-INSTALL-ENTRY-SCRIPT: 'yolk==0.4.1','console_scripts','yolk'
__requires__ = 'yolk==0.4.1'
import sys
from pkg_resources import load_entry_point

sys.exit(
   load_entry_point('yolk==0.4.1', 'console_scripts', 'yolk')()
)

Hmm. As you can see, the hardcoded shebang line is there – it’s a disutils thing. But this means if I have 3.x installed (and 2.7, and 3.1) and I install yolk into any of those, the yolk binary will get overwritten and have a hardcoded shebang line for the last-installed version.

By default, some packages will also lay down scripts which include the version number, for example:

-rwxr-xr-x   1 jesse  jesse   357B Jul 19 21:44 easy_install
-rwxr-xr-x   1 jesse  jesse   365B Jul 19 21:44 easy_install-2.6
-rwxr-xr-x   1 jesse  jesse   386B Jul 19 21:40 easy_install-3.1

In this example, the hardcoded shebang line is treated as lifo – last in, first out. In this example, I installed the python 3.1 version, and then the 2.6 version. If you look in easy_install, you’ll see that it points to the 2.6 version. Sure – I have version-specific names as well, but good luck remember they’re there (I always forget), and they’re not symlinks.

I think a better way of managing this (and I’m shooting this to python-ideas) is to move the bin directory under a matching python version directory. So that way it mirrors .local/lib/pythonx.x. You would get a .local/bin/pythonx.x directory as well, and wouldn’t need to worry about conflicts. Or we just ditch the versions without the version number in them altogether. (link to python-ideas thread)

In any case, this is great for the simple case: you don’t need to install into the global site-packages directory any longer. You just pass in –user to all of the install scripts, for example:

  • python setup.py install –user FooPackage
  • pip install –install-option=”–user” FooPackage

Notice easy_install isn’t here: that’s because it doesn’t allow the pass-through of the –user command to disutils, favoring setuptools method of doing things. That’s lamesauce, but setuptools/easy_install also pre-dates PEP 370, so we’ll just skip past that.

Alright – so, a per-user site-packages directory, minus some binary issues – well, when poking around I suspected there might be some other un-versioned high level directories, so I went digging for a package on pypi which had a million dependencies – or more than one.

zim:~ jesse$ /Library/Frameworks/Python.framework/Versions/2.6/bin/pip install \
--install-option="--user" paver-templates

  Running setup.py install for Sphinx
  Running setup.py install for paver-templates
  Running setup.py install for Paver
  Running setup.py install for PasteDeploy
  Running setup.py install for docutils
  Running setup.py install for Pygments
  Running setup.py install for Jinja2
  Running setup.py install for Cheetah
  Running setup.py install for Paste
Successfully installed paver-templates

I abbreviated the output a bit – so 8 dependencies in total, which resulted in a large increase of “stuff” in the .local/lib/python2.6/site-packages directory – but also in a new .local/docs directory:

zim:~ jesse$ ls -lah .local/docs/
total 1096
drwxrwxr-x  17 jesse  jesse   578B Jul 19 14:12 .
drwxr-xr-x@  5 jesse  jesse   170B Jul 19 14:12 ..
-rw-rw-r--   1 jesse  jesse   125K Jul 19 14:12 api.html
-rw-rw-r--   1 jesse  jesse   7.2K Jul 19 14:12 changelog.html
-rw-rw-r--   1 jesse  jesse    99K Jul 19 14:12 extensions.html
-rw-rw-r--   1 jesse  jesse    13K Jul 19 14:12 faq.html
...snip...

points.jpg
More top-level un-versioned stuff, which will again conflict if I go and install this in say, python3.1. The same issue could arise with any data files stored in the top-level (although most of the packages plop them into site-packages with the code, which is the correct way to do it).

So where does this leave us? Well, first off, I would say this – this is a huge improvement over the old site-packages method. Huge. Massive. Why? Even with the versioning issues I’ve sort of harped on above, this is simply a better way to install and manage packages a user needs.

That being said – installing into the user’s local site-packages should be the preferred deployment method in distutils, rather than needing to pass in –user, we should pass in the inverse, –global. I know this is flamebait – but really, in a world where more and more operating system critical things are being written in Python and using the installed framework (see Fedora as a prime example), it’s really not smart to go mucking around in the global bin directories, or the global site packages.

I’d also make the argument that even the .local structure outlines in pep 370 doesn’t remove/replace the need for something like virtualenv. Here’s why.

Running my experiments for this, I managed to add 38 directories and files into my .local/lib/python2.6 directory. This includes packages, .pth files, egg-info directories, and actual package code directories. What if I just wanted to use it for a single application? How do I deal with some apps or packages which want versions? Now, instead of running “sudo rm -rf /Library/…/site-packages/xxx” I can easily run “rm -rf ~/.local/lib/python2.6/xxx” – but that’s still equivalent to needing to treat .local/lib/xxx like a bonsai tree.

I’d rather treat it like my girlfriends used me as a teenager; spin it up and then drop it off in the bad part of town never to be heard from again. Meaning, build it, install it, delete it.

Not to mention, something like virtualenv (and it’s integration with pip – or is it pip’s integration with virtualenv?) offers additional niceties above and beyond the use it and delete it use-case. You can build an isolated environment, and then run pip over it to generate a bundle, or requirements file, which you can then share with other people (for example).

It also allows me to keep things compartmentalized in a near OCD-level. Now, I could do this with the features in PEP 370, sort of. It supports the PYTHONUSERBASE environment variable, which means you could make a tree like this:

.local/
    app1/
        bin/
        lib/
            python2.6/...

And then write a quick bash function to say “switch PYTHONUSERBASE to .local/app1″ – if that’s what floats your boat (and swaps scripts-without-versions to symlinks so you can count on it pointing to the right version). But why not use something which does this for you, like virtualenv? It also isolates the interpreter itself, not just the packages you want.

gran-torino-clint-eastwood.jpg
And it works with the features of PEP 370. Meaning, if you create a virtualenv, it will still load the .local directory when you load that virtualenv. However, while some might find this desirable, I don’t, and not in the “clint-eastwood-in-gran-torino-get-off-my-lawn” way. Also add the fact that if .local is exposed in the virtualenv, you’ll still lack access to the scripts outside the virtualenv (more on this in a moment). I end up disabling .local loading in the interpreter by exporting PYTHONNOUSERSITE (see the pep) within virtualenvwrapper whenever I call “workon” for a given environment.

Right now, if you run “virtualenv –no-site-packages flubber” you (purposefully) sandbox yourself away from the global site-packages directory. You however, do not get the option to omit the .local directory (yes, I’m going to file a bug – I’m up to two or three to file so far). If I want a sandbox, I want a sandbox. It’s like owning cats – you want them to go in the litter box, not the litter box + a five foot radius.

Also, using virtualenv compartmentalizes installed binaries. Meaning if I make “flubber” and install say, pylint into it, the pylint binaries stick to that virtualenv. And therein lies a different catch.

In my other post, I griped about hard coded paths in the shebang line (#!). This problem is still here, all I’ve done is outline some of the features of the pep and virtualenv. Let’s talk scenarios. Let’s say I install pylint into my .local directory. It’s shebang will point to the version of the python 2.6 binary I’ve got installed. If I make a virtualenv and try to run pylint on code which depends on a library I’ve sandboxed, it won’t work. Why? Because you need to reinstall it into that virtualenv, so it can point to that interpreter.

If the shebang line instead used “/usr/bin/env python” – you could side step this, as any packages-with-binaries installed into the user directories, or the global dirs could just load the interpreter of the virtualenv instead… except… wait for it… it wouldn’t have it’s needed libraries in that virtualenv, which is why it has the hardcoded shebang line in the first place (whee!).

Back to square one.

Using virtualenv though, you can make a bootstrap script to install common utilities (such as pylint) into the environment during creation. Look at the after_install hook. So this works around the entire script-outside the sandbox (but you still get things from the .local directory). You can also use the .local version of pip (should you have it installed) to install a library into a virtualenv sandbox.

Here’s where we are. Installing packages into the global directories (/usr, site-packages, etc) is considered unsanitary and may lead to bad things. So don’t do it – unless you have to, and the times you have to should be rare.

Installing things into your .local directory makes a lot of sense, and which is what you should do, especially for things like libraries you want to use. Scripts get dumped (unversioned) into .local/bin. Using a virtualenv on top of all this is still useful and a good way to manage things – you get (mostly) isolated environments, you can point it at any interpreter and generate an environment for just that version of python (which is what I do). You can also use it to make sandboxes within sanboxes. For example, I make a “master” python2.6 one, named “python2.6″ – inside it’s directory, I can make a directory named “sandboxes”, install virtualenv within it, and make sub-sandboxes within that.

So, PEP 370 is a great change, and pretty darned useful. It still has some of the drawbacks of the global directories (but makes your life as a user/consumer much easier) but its made better (as in the global case) by adding virtualenv on top of it.

For me, I compile python into it’s own directory (/Users/jesse/slash) and then make a “master” virtual machine for each version, and end up using that 95% of the time for experimentation/coding/etc. I made a custom bootstrap environment, and a pip requirements file to manhandle the additional things I want in every environment I make.

None of this – PEP 370, virtualenv, etc are without their drawbacks, or things I’d like to improve – they’re an improvement on the status quo, and can definitely be made better. Personally, I can’t live without virtualenv and virtualenvwrapper. I don’t think I’d use virtualenv as much without virtualenvwrapper.

For bonus reading, check out this email from Tarek describing the consumer use-cases, I think it’s a good, succinct outline.

  • Aaron
    Isn't ".local" a rather generic name for something that's going to be mysteriously appearing in users' home directories?
  • You can change the name of the directory with the PYTHONUSERBASE environment variable; but I definitely agree with you
  • The hard-coded shebang this time is not because of distutils, but because the pip-installed yolk distribution is generating a setuptools-style script for the yolk console script entry point. If you were to install yolk with Buildout w/ the zc.recipe.egg recipe (alternative recipes might generate different scritps), you would instead get:

    #!/Users/kteague/buildouts/shared/python-2.6.1/bin/python

    import sys
    sys.path[0:0] = [
    '/Users/kteague/buildouts/shared/eggs/yolk-0.4.1-py2.6.egg',
    '/Users/kteague/buildouts/shared/eggs/setuptools-0.6c9-py2.6.egg',
    ]

    import yolk.cli

    if __name__ == '__main__':
    yolk.cli.main()

    Still a hard-coded shebang, but you can control this zc.recipe.egg with the 'python' option. Below is the minimal buildout.cfg for installing yolk:

    [buildout]
    parts = yolk

    [yolk]
    recipe = zc.recipe.egg
    eggs = yolk

    And then installing it with a custom shebang:

    [buildout]
    parts = yolk

    [pythonenv]
    executable = /usr/bin/env python

    [yolk]
    recipe = zc.recipe.egg
    python = pythonenv
    eggs = yolk

    The Buildout-style of declaring package locations at install-time (instead of run-time like pip or easy_install) has the advantage that 'setuptools' is not required to run yolk. OK, yolk is a bad example, since it actually depends upon setuptools at run-time, and so requires setuptools ... but there are lots of other packages out there which declare a dependency on setuptools, but don't actually require setuptools - the wrapper script that is generated by easy_install or pip *does* require setuptools though.
  • Thanks - my big problem with buildout has been one of simplicity, and understanding which recipe does which thing and which one makes sense. Trolling pypi for the "buildout" term results in *cough* quite a few of them.

    That being said, until Tarek can kickass and fix up distutils to be "on par"-ish with setuptools, and push out his fork of it, we play in a setuptools-based world, which is unfortunate in a lot of respects.
  • "we play in a setuptools-based world, which is unfortunate in a lot of respects"

    This is only unfortunate in so much as setuptools hasn't seen a lot of maintenance of late, and that setuptools does way too much stuff. Typically its behavoiur such as how scripts are generated by easy_install which irks people, or how packages are downloaded which bother others, or how package metadata is different between setuptools and distutils. But there is nothing in the python packaging ecosystem which says you need to consume or use the ugly bits of setuptools - none of the ugly bits have been immortalized in a PEP! And the ugly bits of Distutils that have been PEP-immortalized will hopefully be kicked to the curb (PEP 314's Requires and Provides fields) without too much fuss.

    Hopefully the 'Distribute' fork can further clarify what people want to keep and what to seperate out from setuptools. Heck, if someone came up today with yet-another setuptools fork, aside from an outcry of "code duplication", I think this could be a good thing! It would further clarify for folks what parts of packaging are 'standards' and 'interchangle formats' and what parts are just instances of behaviour of a given tool and can easily be changed (by switching tools) and only affect user's of that given tool.

    I also think any truly satisfying solution to library management for a developer with reasonably complex requirements won't involve any site-packages. Obviously site-packages isn't going away any time soon, it caters really well for the "scripters" use-case where they just want to consume 3rd party libs in a more one-off scripting nature. But PEP 370 and VirtualEnv work within the assumption and the constraints of "we already have a Python installation with a shared 'bin' location and shared 'library' location" and then place installed files inside those locations. VirtualEnv is a total hack - but a beautiful one! It's good how virtualenv is backwards compatible with all of the existing stuff - but by it's nature it doesn't try and re-think or re-work how the problem is solved. With any shared location, be it a root-only site-packages, a .local site-packages, or a virtualenv-cloned site-packages, any shared location is an all-or-nothing location. With any all-or-nothing shared location you will always have the potential for conflicts: installing or updating dependencies for one app may break another already installed app. When attempting to sort out version/dependency issues you can't choose just some libs from a shared location, but easily ignore other libs of the wrong version in that location.

    If you start without any notion of using a site-packages, then instead you can do something such as the Buildout approach:

    ~/projects
    /app1
    /buildout.cfg
    /bin
    /yolk
    /app2
    /buildout.cfg
    /bin
    /yolk
    ~/pythonlibs
    /yolk-0.4.1-py2.6.egg
    /yolk-0.4.1-py2.4.egg

    This approach has the benefits of:

    * Don't need to have duplicate installations of the same version of the same library. Each version of each library only has to be installed once.

    * Scripts are installed in a project-specific location, not in a shared space.

    This does mean that if yolk-0.4.1 is used by two different projects, then Buildout will generate two ./[someproj]/bin/yolk script entries. But that's a case where duplication is a good thing! While you may happen to be working on both projects today, and both projects are using the same libraries and python version, tomorrow could be a different story. You might want to update one project up to a cutting-edge dev version of a library but don't want to do that right now for the other project. Or maybe one project is being migrated from Python 2.5 to 2.6, but you want to hold the other project back to 2.5. You might want to have a yolk-0.4.1 and yolk-0.5dev installed side-by-side for one project, where ./bin/yolk-stable and ./bin/yolk-dev are expressed. heck, you might even want two scripts that call into yolk-0.4.1, but one version of the script needs to contain an extra line or two of hard-coded python to handle a specific use case (./bin/yolk and ./bin/yolk-extra-easy). Scripts are always installed to support a specific application or project, so always putting the scripts into a project-specific location makes things a lot simpler and I think is the way to go.

    Using a bootstrap in conjunction with VirtualEnv to bring a suite of dev tools into any new project is a good idea (e.g. pylint, nose, zest.releaser). But why state, "These are the dev tools I use and want to carry around with me", when instead you can state, "This project uses these dev tools for support" and "That project uses those dev tools for support". Each project states it's own requirement's and preferences for what tools (and possibly versions) it needs. In this way two developer's who normally prefer different dev tools can easily collaborate on a project together. They don't need to argue over what the "standard dev tool" suite should to be, instead they can say, "we should switch from Tool X to Tool Y for Project A because if gives use Benefit C". Each project expresses it's preferences of what scripts are powered by which version of which libs and which python interpreter (whew!). Then the only thing remaining is for installation tool to care of the ugly work of expressing those preferences by outputting the gory details of the hard-coded bits of a script into a project's /bin/ directory.

    Many language camps have toyed with the approach of writing out hard-coded scripts that declare library locations up-front, and typically they back away from it because this approach is deemed to be "clunky". But it's only clunky if you have to deal with the hard-coding manually! As soon as you check-in hard-coded scripts into version control, the other developers would rightfully berate you (and so instead you put "#!/usr/bin/env python" as a script header, but then you are only pushing the need to hard-code what python and what libs will be used into a hard-coded shell file ...) But instead if you simply express the requirements of each script (which interpreter, which libraries, where to call into for the main function), then it becomes possible to allow a tool to automate expression of all of the clunky bits and from the developer's perspective "clunky" becomes "effortless". Furthermore if your definition of "clunky" is guided by actual performance benchmarks in a given deployment, then you can use different recipes to express script and package layout (flat install, egg-install, zipped-only install) until you reach a file layout that is optimized for a given deployment.

    Another reason for taking the appraoach of having an install tool layout the gory details of setting up scripts and libs for a project is that you can accomodate more than one language. Sure, python is far and away my favorite language, but I appreciate python the language just fine without appreciate how the files to support a given implementation of python are layed out. Imagine if you had "VirtualRubyEnv" and "VirtualPerlEnv" and you working on some mongrelly project that wanted to combine Python, Ruby and Perl into one place (and in an acedemic/bioinformatics setting, mongrelly projects seem to be more common than pure one language only projects). Which "VirtualLang" would be the master one? What if one languages notion of where it puts stuff is incompatible with how another language lays out it's files?

    So we already have, in one incarnation via Buildout and zc.recipe.egg, ostensibly reached some form of Python packaging nirvana, where libraries are consumed from a multi-version only-installed-once-each repository and subtly different scripts aren't attempting to overwrite each other. You still have the valid criticisms of Buildout being more difficult to approach and learn than it needs to be, and these practices need to be more standardized so that any install method is interchangeable with any other one. And people need to simply be aware where a practice that is causing them grief *is not* a set-in-stone standard (or any standard at all), but merely the behaviour of a given tool.

    For the Buildout problem, this can be solved with either better docs, or simply using a different install tool but taking the same approach to installation (e.g. you could do all this with Paver). It would be be a sign of success if we could get to a place where a Ruby-centric developer who wanted to use a little Python in their project could easily manage Python script generation and library installation from Ruby code.

    For standardizing things so that more and different tools can play and interoperate well (e.g. if Buildout could cherry-pick some, but not all, OS distro installed libraries). This is what the bulk of most of Tarek's PEP writing has been pushing python packaging towards: adding install_requires and entry_points to the official metadata, writing out proper installation metadata so that you can query a location and it can tell you what package and version are installed there, and the mentioned but as-yet-un-pep'ed way to structure and consume a multi-version location.
  • There are 143 at the moment, to be precise:

    http://pypi.python.org/pypi?:action=browse&show...

    But as a general purpose project setup and installation tool, that's kind of the point - different recipes to install different stuff. Although there are lots of recipes that are overly-specific and often a single more generic recipe is later developed which can be used, and other recipes that are near-duplicates and a "canonical" one needs to be chosen: templating of config files is a good example here.

    But usually when people talk about Buildout in the context of managing Python libraries, they actually mean the zc.recipe.egg recipe, which is the most commonly used recipe used to install python packages and scripts.

    As for reaching understanding with Buildout, well, I think there is still lots of room for improvement in making a more user friendly "getting starting with Buildout" document. Learning Buildout still does tend to require more headbanging than should be needed.
  • Interesting thoughts - I've been pondering more and more "hooks" we could target for tarek's refacing of distutils to make things like buildout/virtualenv "more of a first class citizen".

    I *do* like the recipe approach, although I admit that I'm not familiar enough with generating them to "really be keen on them" - also, some part of me wishes all of the recipes were centralized in some way to make them mentally mesh a bit more.

    And I have headbanged on buildout a bit, and bounced off - it's relatively alien to my workflow and personally I found the very simple pip/virtualenv workflow dead simple to use (and rapid to learn). But that's just me.
  • rhodium
    Hi Jesse,

    Good post - very timely. I am also responsible for packaging up scripting languages for my company (EDA space) and while I agree with you on most cases I don't on others. I will point out some of the challenges I see with your post. The point of view I come from is ensuring that all users have a consistent (site) environment. I don't focus too much on the "user-space" because I want them to do that for themselves.

    In general I think that if you can use the system defined (/usr/bin/python) and it's default installed packages you should. As you and I both know the temptation to use external python modules is too great and therefore the only real way of using them it to muck up the default installed python, which in general I agree with you is a bad thing **. Also I am completely sensitive to the needs of developers who like the latest version of python and so I am completely cool with using --prefix and packaging up a base python (rpm) in a specific location for them to use.

    While I agree that "mucking-up" the default install tree is bad, I am ok with using a single *.pth file inside of the default site-packages location to allow the ability to hook into external "site" directories as needed. The benefits of doing this are as follows:
    - I use a single environment variable which is defined in the /etc/profile (/etc/csh.cshrc) which specifies the "site" package directory to look in.
    - Allows me to version control this site tree (we use perforce but any repository could work).
    - Allows me to shift on the fly from a dev branch to a head release branch of installed modules, or those in test.
    - I haven't touched PYTHONPATH and reserved that for the user.
    - Module versions and dependancies are handled via the version control system which houses modules. We use perforce so I can get back to a point in time using back-in-time browsing if I need to.
    - virtualenv is great for developers but when real users have to use it - they are too easily confused.. Sorry ;)

    So how does this work. Assumption: A generic installation of python 2.x in a custom (/usr/local/bin) space.
    - Add a site.pth file. The contents look like this..
    import os, site; smsc = os.environ.get("SMSCTECHROOT", "/smsc/tech"); \
    smsc = smsc if os.path.isdir(os.path.join(smsc, "tools/python/Linux/x86_64/lib/python2.5/site-packages")) \
    else "/smsc/tech"; site.addsitedir(os.path.join(smsc, "tools/python/Linux/x86_64/lib/python2.5/site-packages"))

    Because of the limitations on *.pth this looks much more convoluted than it needs to.. but I digress.
    - So when a user fires up python the tree is resolved and he is looking into the repository (or depot) for python modules. If a developer is testing something prior to committing (or submit) he simply refers to his local area as SMSCTECHROOT. Remember SMSCTECHROOT is defined in /etc/profile so it's a guarantee.

    The problems with this approach:
    - I agree that touching a global directory ("/usr") is a bad idea but I just don't see anyway around it.
    - I don't like touch /etc/profile or /etc/csh.cshrc

    Overall though I think your "user" specific emphasis is good. I also think that PEP370 is very well handled. I don't necessarily agree with you that virtualenv is the best thing since sliced bread for the reason I pointed out. I would say if I was a application developer who wanted to package his own application I would use the mac .app approach and roll my own mini-python inside the .app tree.

    Thanks you made me think on a Monday - Keep up the good work!!
  • Just curious: do you think you're at the point where you could sit down and write out a description of how packaging in python would work if it was “perfect” — or do you think the improvements are going to continue incrementally?
  • Well, kinda-sorta. Watching the distutils and python-dev/ideas threads have made me realized there's a lot of nuances to dealing with problems like versioning/etc. However, I think there are three simple profiles we could outline - the Developer (i.e. me), the App packager (writing things for users, with dependencies) and the OS Packager (building out the OS framework).

    Right now, I think making PEP 370 better in small steps goes a long way to scratching the itch of the Developer, and the OS Packager - for the latter, it means less worrying about globally-installed things and how to manage the permissions. For the former, it allows me as a developer to tightly control what things go where.

    For the App Developer, well - I suspect we're rapidly going towards the OS/X type .app world, where python applications bundle most of an interpreter, and the dependencies they need into a singular bundle. Relying on system installs, or questionable user environments is a bad place to be in. Sure - offer a "no bundled download" which power-users would use, but make a package which contains everything you as an App Dev. need to make the other 90% of users be successful.

    From a different view - I don't know how much of this belongs in Python-core. At the language summit I told tarek that I don't think things like virtualenv or easy_install belong in python core - I still don't. However, I *do* think that distutils or other in-core APIs can make it easier (and "supported") to write tools which do these things.

    So, for example - no RPM support in core, but APIs to make building an RPM easier. No virtualenv in core; but APIs to make virtualenv (and things like it) easier, and "officially supported"
  • I'm happy to see things edge in the right direction. Virtualenv + workon with it's sandboxy, tab-completing benifits is something I'd have a hard time living without.
blog comments powered by Disqus