[Distutils] How to customize egg_info behavior
P.J. Eby
pje at telecommunity.com
Sun Aug 1 21:09:28 CEST 2010
At 10:36 PM 7/31/2010 -0700, Jason R. Coombs wrote:
>Thanks for the tip, pje. Your suggestion was very helpful. Perhaps I can ask
>a follow-up.
>
>In addition to calculating the tag at package-time (when setup.py is run for
>sdist/bdist*), I am calculating the version at package-time.
>
>The problem I have with this approach is with source distributions (sdists).
>Since the sdists get the same setup.py file, the calculations for version
>and tag are done both at packaging time and at install time. I would like to
>configure my setup.py such that when sdist is run, the version and tag are
>calculated and added to the egg_info, but when a consumer of that sdist goes
>to install it, the version and tag are not calculated and just consumed from
>the egg_info (similar to what happens when installing a bdist_egg).
When setuptools builds an sdist, it includes an updated setup.cfg
containing all the original egg_info settings, so that part is done for you.
What this means is that you can programmatically tell if you're
running from an sdist or a checkout by looking for the tag_build
setting in setup.cfg. That means that you could potentially just
specify version='' in your setup(), and put the *entire* version
number in the tag_build option to egg_info.
In order for that to work correctly, what you'd need to do is:
1. Try to read setup.cfg using ConfigParser, and read out the
'tag_build' from the 'egg_info' section.
2. If it's found, set the version string to that, and force tag_build
to an *empty string* in the options['egg_info'] passed to setup(), so
that tag_build doesn't get stuck back in again (doubling-up the version).
3. If it's not found, do a straight-up calculation.
The main downside to this approach, however, is that you have to put
all this code in setup.py, and you can't import it from anywhere,
unless you bundle the code with every project you're using it on.
So here's what you do instead. Implement this as a setup() argument, e.g.:
def hg_version_calc(dist, attr, value):
if value:
from ConfigParser import ConfigParser
parser = ConfigParser()
parser.read('setup.cfg')
if parser.has_section('egg_info') and 'tag_build' in
parser.options('egg_info'):
version = parser.options('egg_info')['tag_build']
else:
version = calculate_version()
dist.metadata.version = version
And register this as a 'distutils.setup_keywords' entry point in your
plugin project. You then need only include a 'use_hg_version = True'
in your setup() call, along with a setup_requires to ensure the
plugin is available.
Now, there is one minor hitch with this; as shown above, this code
will double-up version strings in certain circumstances;
specifically, if an 'egg_info' is done from an sdist -- which is
going to happen if you build pretty much anything from an sdist, be
it an sdist, egg, RPM, or anything else.
There isn't any easy way to work around this at the moment (although
I will implement a formal fix in later releases of setuptools). In
the meantime, you can monkeypatch thus, in the same plugin where you
implement the above function:
from setuptools.command.egg_info import egg_info
old_tagged_version = egg_info.tagged_version
def tagged_version(self):
if self.distribution.use_hg_version:
return safe_version(self.distribution.get_version())
else:
return old_tagged_version(self)
egg_info.tagged_version = tagged_version
Whew. This was a tough one to figure out, and it's a little
hacky. Probably in 0.7 I'll just factor this into a separate entry
point group for version calculations, so that it's not as much
trouble to do something like this.
More information about the Distutils-SIG
mailing list