| 1 |
PKG_INFO_BASENAME='pkg-info.rst' |
|---|
| 2 |
PKG_INFO_FN=None |
|---|
| 3 |
PKG_INFO=None |
|---|
| 4 |
"""python setup.py / python .egg distribution boiler plate utilities |
|---|
| 5 |
|
|---|
| 6 |
For reasons that should be obvious this can not be in its own package. Some |
|---|
| 7 |
exmples of use, assuming you place a copy of this file allong side your |
|---|
| 8 |
setup.py and use pkg-info.rst for your build meta information. |
|---|
| 9 |
|
|---|
| 10 |
A setup.py that gets its release meta data from a .rst format plain text file. |
|---|
| 11 |
|
|---|
| 12 |
execfile('pkg_info.py', globals()) |
|---|
| 13 |
setup( |
|---|
| 14 |
name=PKG_INFO['Name'][0], |
|---|
| 15 |
version=PKG_INFO['Version'][0], |
|---|
| 16 |
provides=PKG_INFO.get('Provides',PKG_INFO['Name'])[0], |
|---|
| 17 |
install_requires=PKG_INFO.get('Requires',[]), |
|---|
| 18 |
description=PKG_INFO['Summary'][0], |
|---|
| 19 |
license=PKG_INFO['License'][0], |
|---|
| 20 |
author=PKG_INFO['Author'][0], |
|---|
| 21 |
author_email=PKG_INFO['Author-email'][0], |
|---|
| 22 |
package_dir = {'':'lib'}, |
|---|
| 23 |
packages = find_packages('lib', exclude=( |
|---|
| 24 |
'ez_setup','develop', '*.tests','*.tests.*','tests.*', 'tests')) |
|---|
| 25 |
) |
|---|
| 26 |
|
|---|
| 27 |
Activation of a distribution in a particular source tree in preference to |
|---|
| 28 |
an existing install (or python setup.py develop install) with the same name |
|---|
| 29 |
and "better" version:: |
|---|
| 30 |
|
|---|
| 31 |
pkg_info_py_fn = join( |
|---|
| 32 |
dirname(dirname(abspath(normpath(expanduser(__file__))))), |
|---|
| 33 |
'pkg_info.py' |
|---|
| 34 |
) |
|---|
| 35 |
if isfile(pkg_info_py_fn): |
|---|
| 36 |
ns = globals().copy() |
|---|
| 37 |
ns.update(dict( |
|---|
| 38 |
__file__=pkg_info_py_fn, |
|---|
| 39 |
__use_this_source_version__=True) |
|---|
| 40 |
) |
|---|
| 41 |
execfile(pkg_info_py_fn, ns) |
|---|
| 42 |
|
|---|
| 43 |
# go ahead with normal application & dependent imports |
|---|
| 44 |
|
|---|
| 45 |
""" |
|---|
| 46 |
|
|---|
| 47 |
def get_info(data=None, fn=None, fileobj=None): |
|---|
| 48 |
import re |
|---|
| 49 |
info={} |
|---|
| 50 |
data = data or fn and file(fn).read() or fileobj and fileobj.read() |
|---|
| 51 |
for k,v,_ in re.findall( |
|---|
| 52 |
r'(?ims)^:?(?P<key>[a-z][\w\-_ ]*):\s*' |
|---|
| 53 |
'(?P<value>.*?' |
|---|
| 54 |
'(?=(\Z|\n^:?[a-z][\w\-_ ]*:)))', |
|---|
| 55 |
data): |
|---|
| 56 |
info.setdefault(k,[]).append(v) |
|---|
| 57 |
return info |
|---|
| 58 |
|
|---|
| 59 |
def read_info(relativeto=__file__, fn=PKG_INFO_BASENAME): |
|---|
| 60 |
from os.path import expanduser, join, abspath, normpath, dirname |
|---|
| 61 |
from os.path import isfile, isdir |
|---|
| 62 |
relativeto=abspath(normpath(expanduser(relativeto))) |
|---|
| 63 |
if isfile(relativeto): |
|---|
| 64 |
relativeto=dirname(relativeto) |
|---|
| 65 |
assert isdir(relativeto) |
|---|
| 66 |
pkg_info_fn=join(relativeto, fn) |
|---|
| 67 |
pkg_info=get_info(fn=pkg_info_fn) |
|---|
| 68 |
return pkg_info_fn, pkg_info |
|---|
| 69 |
try: |
|---|
| 70 |
PKG_INFO_FN, PKG_INFO = read_info() |
|---|
| 71 |
except (IOError, OSError): |
|---|
| 72 |
from traceback import print_exc |
|---|
| 73 |
print_exc() |
|---|
| 74 |
|
|---|
| 75 |
|
|---|
| 76 |
def use_source_dist(project_name, distdir, ws=None, search_path=None): |
|---|
| 77 |
from os.path import isdir |
|---|
| 78 |
from operator import itemgetter |
|---|
| 79 |
assert isdir(distdir), '"%s" is not a directory' % distdir |
|---|
| 80 |
import pkg_resources |
|---|
| 81 |
if ws is None: |
|---|
| 82 |
ws = pkg_resources.working_set |
|---|
| 83 |
if search_path is None: |
|---|
| 84 |
search_path = sys.path |
|---|
| 85 |
pruned = remove_projects(ws, search_path, |
|---|
| 86 |
pkg_resources.safe_name(project_name) |
|---|
| 87 |
) |
|---|
| 88 |
if pruned: |
|---|
| 89 |
print 'Pruned: %s' % ', '.join(map(itemgetter(0), pruned)) |
|---|
| 90 |
else: |
|---|
| 91 |
print 'No potentialy conflicting versions exist for "%s"' % project_name |
|---|
| 92 |
|
|---|
| 93 |
ws.add_entry(distdir) |
|---|
| 94 |
dist = pkg_resources.get_distribution(project_name) |
|---|
| 95 |
assert dist |
|---|
| 96 |
print 'added:', dist.project_name, dist.location |
|---|
| 97 |
|
|---|
| 98 |
|
|---|
| 99 |
def yield_dist_entries(ws): |
|---|
| 100 |
seen = {} |
|---|
| 101 |
for item in ws.entries: |
|---|
| 102 |
for key in ws.entry_keys[item]: |
|---|
| 103 |
if key not in seen: |
|---|
| 104 |
seen[key]=1 |
|---|
| 105 |
yield item, key, ws.by_key[key] |
|---|
| 106 |
|
|---|
| 107 |
|
|---|
| 108 |
def remove_projects(ws=None, search_path=None, *project_names): |
|---|
| 109 |
"""Explicitly remove all named projects from the working set. |
|---|
| 110 |
|
|---|
| 111 |
With a normal setuptools environment it is dificult to have *both* |
|---|
| 112 |
a python setup.py develop/install of a project and an arbitrary non |
|---|
| 113 |
installed development source tree side by side. |
|---|
| 114 |
|
|---|
| 115 |
In the development source tree, to force use of the python modules in |
|---|
| 116 |
that tree, first call this function to remove all 'normal' installs |
|---|
| 117 |
from both sys.path and pkg_resources.working_set |
|---|
| 118 |
|
|---|
| 119 |
""" |
|---|
| 120 |
import pkg_resources |
|---|
| 121 |
|
|---|
| 122 |
if ws is None: |
|---|
| 123 |
ws = pkg_resources.working_set |
|---|
| 124 |
if search_path is None: |
|---|
| 125 |
search_path = sys.path |
|---|
| 126 |
project_names = set(map(pkg_resources.safe_name, project_names)) |
|---|
| 127 |
seen=set([]) |
|---|
| 128 |
entry_prune = [] |
|---|
| 129 |
pth_prune = set([]) |
|---|
| 130 |
pruned = [] |
|---|
| 131 |
for e, k, d in list(yield_dist_entries(ws)): |
|---|
| 132 |
if e in seen: |
|---|
| 133 |
continue |
|---|
| 134 |
seen.add(e) |
|---|
| 135 |
if d.project_name not in project_names: |
|---|
| 136 |
continue |
|---|
| 137 |
if e in ws.entry_keys: |
|---|
| 138 |
ws.entry_keys[e][:] = [ |
|---|
| 139 |
kk for kk in ws.entry_keys[e] |
|---|
| 140 |
if k != kk |
|---|
| 141 |
] |
|---|
| 142 |
if not ws.entry_keys[e]: |
|---|
| 143 |
del ws.entry_keys[e] |
|---|
| 144 |
if k in ws.by_key: |
|---|
| 145 |
del ws.by_key[k] |
|---|
| 146 |
pruned.append((e, k, d)) |
|---|
| 147 |
pth_prune.add(e) |
|---|
| 148 |
#pth_prune.add(d.location) |
|---|
| 149 |
|
|---|
| 150 |
search_path[:] = [pth for pth in sys.path if pth not in pth_prune] |
|---|
| 151 |
ws.entries[:] = [ent for ent in ws.entries if ent not in pth_prune] |
|---|
| 152 |
return pruned |
|---|
| 153 |
|
|---|
| 154 |
try: |
|---|
| 155 |
if __use_this_source_version__: |
|---|
| 156 |
from os.path import dirname |
|---|
| 157 |
# Assumes __file__ is set appropriately; which causes the |
|---|
| 158 |
# automatic call of read_info (above) to do the "right thing" |
|---|
| 159 |
use_source_dist(PKG_INFO['Name'][0], dirname(__file__)) |
|---|
| 160 |
except NameError: |
|---|
| 161 |
pass |
|---|
| 162 |
# |
|---|