Merge lp:~jml/pkgme-devportal/entry_point into lp:pkgme-devportal
- entry_point
- Merge into trunk
Proposed by
Jonathan Lange
Status: | Merged |
---|---|
Merged at revision: | 14 |
Proposed branch: | lp:~jml/pkgme-devportal/entry_point |
Merge into: | lp:pkgme-devportal |
Diff against target: |
783 lines (+702/-20) 9 files modified
.bzrignore (+3/-0) MANIFEST.in (+2/-0) bin/guess-deps (+2/-9) bin/guess-executable (+2/-11) devportalbinary/__init__.py (+14/-0) devportalbinary/binary.py (+13/-0) distribute_setup.py (+477/-0) setup.py (+42/-0) setup_helpers.py (+147/-0) |
To merge this branch: | bzr merge lp:~jml/pkgme-devportal/entry_point |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
pkgme binary committers | Pending | ||
Review via email: mp+82399@code.launchpad.net |
Commit message
Description of the change
This makes the binary plugin setup.py-
To post a comment you must log in.
- 16. By Jonathan Lange
-
Export the two other scripts as endpoints.
- 17. By Jonathan Lange
-
Don't think we actually need this.
- 18. By Jonathan Lange
-
Tweaks.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === added file '.bzrignore' |
2 | --- .bzrignore 1970-01-01 00:00:00 +0000 |
3 | +++ .bzrignore 2011-11-16 16:53:24 +0000 |
4 | @@ -0,0 +1,3 @@ |
5 | +build |
6 | +dist |
7 | +pkgme_binary.egg-info |
8 | |
9 | === added file 'MANIFEST.in' |
10 | --- MANIFEST.in 1970-01-01 00:00:00 +0000 |
11 | +++ MANIFEST.in 2011-11-16 16:53:24 +0000 |
12 | @@ -0,0 +1,2 @@ |
13 | +include *.py |
14 | +graft devportalbinary/backends |
15 | |
16 | === modified file 'bin/guess-deps' |
17 | --- bin/guess-deps 2011-08-23 17:21:54 +0000 |
18 | +++ bin/guess-deps 2011-11-16 16:53:24 +0000 |
19 | @@ -4,15 +4,8 @@ |
20 | |
21 | import sys |
22 | |
23 | -from devportalbinary.binary import guess_dependencies |
24 | - |
25 | - |
26 | -def main(): |
27 | - deps = guess_dependencies('.') |
28 | - for dep in deps: |
29 | - print dep |
30 | - return 0 |
31 | +from devportalbinary.binary import print_dependencies |
32 | |
33 | |
34 | if __name__ == '__main__': |
35 | - sys.exit(main()) |
36 | + sys.exit(print_dependencies()) |
37 | |
38 | === modified file 'bin/guess-executable' |
39 | --- bin/guess-executable 2011-08-23 17:21:54 +0000 |
40 | +++ bin/guess-executable 2011-11-16 16:53:24 +0000 |
41 | @@ -5,17 +5,8 @@ |
42 | import os |
43 | import sys |
44 | |
45 | -from devportalbinary.binary import ( |
46 | - guess_executable, |
47 | - iter_executables, |
48 | - ) |
49 | - |
50 | - |
51 | -def main(): |
52 | - cwd = os.getcwd() |
53 | - print guess_executable(os.path.dirname(cwd), iter_executables(cwd)) |
54 | - return 0 |
55 | +from devportalbinary.binary import print_executable |
56 | |
57 | |
58 | if __name__ == '__main__': |
59 | - sys.exit(main()) |
60 | + sys.exit(print_executable()) |
61 | |
62 | === modified file 'devportalbinary/__init__.py' |
63 | --- devportalbinary/__init__.py 2011-08-23 17:21:54 +0000 |
64 | +++ devportalbinary/__init__.py 2011-11-16 16:53:24 +0000 |
65 | @@ -1,2 +1,16 @@ |
66 | # Copyright 2011 Canonical Ltd. This software is licensed under the |
67 | # GNU Affero General Public License version 3 (see the file LICENSE). |
68 | + |
69 | +from pkg_resources import resource_filename |
70 | + |
71 | +__all__ = [ |
72 | + '__version__', |
73 | + 'get_backends_path', |
74 | + ] |
75 | + |
76 | + |
77 | +__version__ = '0.0.1' |
78 | + |
79 | + |
80 | +def get_backends_path(): |
81 | + return resource_filename(__name__, 'backends') |
82 | |
83 | === modified file 'devportalbinary/binary.py' |
84 | --- devportalbinary/binary.py 2011-10-24 22:02:09 +0000 |
85 | +++ devportalbinary/binary.py 2011-11-16 16:53:24 +0000 |
86 | @@ -279,3 +279,16 @@ |
87 | libraries = get_shared_library_dependencies(binaries, library_finder) |
88 | deps = libraries_to_deps(libraries, 'i386') |
89 | return deps |
90 | + |
91 | + |
92 | +def print_dependencies(): |
93 | + deps = guess_dependencies('.') |
94 | + for dep in deps: |
95 | + print dep |
96 | + return 0 |
97 | + |
98 | + |
99 | +def print_executable(): |
100 | + cwd = os.getcwd() |
101 | + print guess_executable(os.path.dirname(cwd), iter_executables(cwd)) |
102 | + return 0 |
103 | |
104 | === added file 'distribute_setup.py' |
105 | --- distribute_setup.py 1970-01-01 00:00:00 +0000 |
106 | +++ distribute_setup.py 2011-11-16 16:53:24 +0000 |
107 | @@ -0,0 +1,477 @@ |
108 | +#!python |
109 | +"""Bootstrap distribute installation |
110 | + |
111 | +If you want to use setuptools in your package's setup.py, just include this |
112 | +file in the same directory with it, and add this to the top of your setup.py:: |
113 | + |
114 | + from distribute_setup import use_setuptools |
115 | + use_setuptools() |
116 | + |
117 | +If you want to require a specific version of setuptools, set a download |
118 | +mirror, or use an alternate download directory, you can do so by supplying |
119 | +the appropriate options to ``use_setuptools()``. |
120 | + |
121 | +This file can also be run as a script to install or upgrade setuptools. |
122 | +""" |
123 | +import os |
124 | +import sys |
125 | +import time |
126 | +import fnmatch |
127 | +import tempfile |
128 | +import tarfile |
129 | +from distutils import log |
130 | + |
131 | +try: |
132 | + from site import USER_SITE |
133 | +except ImportError: |
134 | + USER_SITE = None |
135 | + |
136 | +try: |
137 | + import subprocess |
138 | + |
139 | + def _python_cmd(*args): |
140 | + args = (sys.executable,) + args |
141 | + return subprocess.call(args) == 0 |
142 | + |
143 | +except ImportError: |
144 | + # will be used for python 2.3 |
145 | + def _python_cmd(*args): |
146 | + args = (sys.executable,) + args |
147 | + # quoting arguments if windows |
148 | + if sys.platform == 'win32': |
149 | + def quote(arg): |
150 | + if ' ' in arg: |
151 | + return '"%s"' % arg |
152 | + return arg |
153 | + args = [quote(arg) for arg in args] |
154 | + return os.spawnl(os.P_WAIT, sys.executable, *args) == 0 |
155 | + |
156 | +DEFAULT_VERSION = "0.6.10" |
157 | +DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/" |
158 | +SETUPTOOLS_FAKED_VERSION = "0.6c11" |
159 | + |
160 | +SETUPTOOLS_PKG_INFO = """\ |
161 | +Metadata-Version: 1.0 |
162 | +Name: setuptools |
163 | +Version: %s |
164 | +Summary: xxxx |
165 | +Home-page: xxx |
166 | +Author: xxx |
167 | +Author-email: xxx |
168 | +License: xxx |
169 | +Description: xxx |
170 | +""" % SETUPTOOLS_FAKED_VERSION |
171 | + |
172 | + |
173 | +def _install(tarball): |
174 | + # extracting the tarball |
175 | + tmpdir = tempfile.mkdtemp() |
176 | + log.warn('Extracting in %s', tmpdir) |
177 | + old_wd = os.getcwd() |
178 | + try: |
179 | + os.chdir(tmpdir) |
180 | + tar = tarfile.open(tarball) |
181 | + _extractall(tar) |
182 | + tar.close() |
183 | + |
184 | + # going in the directory |
185 | + subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) |
186 | + os.chdir(subdir) |
187 | + log.warn('Now working in %s', subdir) |
188 | + |
189 | + # installing |
190 | + log.warn('Installing Distribute') |
191 | + if not _python_cmd('setup.py', 'install'): |
192 | + log.warn('Something went wrong during the installation.') |
193 | + log.warn('See the error message above.') |
194 | + finally: |
195 | + os.chdir(old_wd) |
196 | + |
197 | + |
198 | +def _build_egg(egg, tarball, to_dir): |
199 | + # extracting the tarball |
200 | + tmpdir = tempfile.mkdtemp() |
201 | + log.warn('Extracting in %s', tmpdir) |
202 | + old_wd = os.getcwd() |
203 | + try: |
204 | + os.chdir(tmpdir) |
205 | + tar = tarfile.open(tarball) |
206 | + _extractall(tar) |
207 | + tar.close() |
208 | + |
209 | + # going in the directory |
210 | + subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) |
211 | + os.chdir(subdir) |
212 | + log.warn('Now working in %s', subdir) |
213 | + |
214 | + # building an egg |
215 | + log.warn('Building a Distribute egg in %s', to_dir) |
216 | + _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir) |
217 | + |
218 | + finally: |
219 | + os.chdir(old_wd) |
220 | + # returning the result |
221 | + log.warn(egg) |
222 | + if not os.path.exists(egg): |
223 | + raise IOError('Could not build the egg.') |
224 | + |
225 | + |
226 | +def _do_download(version, download_base, to_dir, download_delay): |
227 | + egg = os.path.join(to_dir, 'distribute-%s-py%d.%d.egg' |
228 | + % (version, sys.version_info[0], sys.version_info[1])) |
229 | + if not os.path.exists(egg): |
230 | + tarball = download_setuptools(version, download_base, |
231 | + to_dir, download_delay) |
232 | + _build_egg(egg, tarball, to_dir) |
233 | + sys.path.insert(0, egg) |
234 | + import setuptools |
235 | + setuptools.bootstrap_install_from = egg |
236 | + |
237 | + |
238 | +def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, |
239 | + to_dir=os.curdir, download_delay=15, no_fake=True): |
240 | + # making sure we use the absolute path |
241 | + to_dir = os.path.abspath(to_dir) |
242 | + was_imported = 'pkg_resources' in sys.modules or \ |
243 | + 'setuptools' in sys.modules |
244 | + try: |
245 | + try: |
246 | + import pkg_resources |
247 | + if not hasattr(pkg_resources, '_distribute'): |
248 | + if not no_fake: |
249 | + _fake_setuptools() |
250 | + raise ImportError |
251 | + except ImportError: |
252 | + return _do_download(version, download_base, to_dir, download_delay) |
253 | + try: |
254 | + pkg_resources.require("distribute>="+version) |
255 | + return |
256 | + except pkg_resources.VersionConflict: |
257 | + e = sys.exc_info()[1] |
258 | + if was_imported: |
259 | + sys.stderr.write( |
260 | + "The required version of distribute (>=%s) is not available,\n" |
261 | + "and can't be installed while this script is running. Please\n" |
262 | + "install a more recent version first, using\n" |
263 | + "'easy_install -U distribute'." |
264 | + "\n\n(Currently using %r)\n" % (version, e.args[0])) |
265 | + sys.exit(2) |
266 | + else: |
267 | + del pkg_resources, sys.modules['pkg_resources'] # reload ok |
268 | + return _do_download(version, download_base, to_dir, |
269 | + download_delay) |
270 | + except pkg_resources.DistributionNotFound: |
271 | + return _do_download(version, download_base, to_dir, |
272 | + download_delay) |
273 | + finally: |
274 | + if not no_fake: |
275 | + _create_fake_setuptools_pkg_info(to_dir) |
276 | + |
277 | +def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, |
278 | + to_dir=os.curdir, delay=15): |
279 | + """Download distribute from a specified location and return its filename |
280 | + |
281 | + `version` should be a valid distribute version number that is available |
282 | + as an egg for download under the `download_base` URL (which should end |
283 | + with a '/'). `to_dir` is the directory where the egg will be downloaded. |
284 | + `delay` is the number of seconds to pause before an actual download |
285 | + attempt. |
286 | + """ |
287 | + # making sure we use the absolute path |
288 | + to_dir = os.path.abspath(to_dir) |
289 | + try: |
290 | + from urllib.request import urlopen |
291 | + except ImportError: |
292 | + from urllib2 import urlopen |
293 | + tgz_name = "distribute-%s.tar.gz" % version |
294 | + url = download_base + tgz_name |
295 | + saveto = os.path.join(to_dir, tgz_name) |
296 | + src = dst = None |
297 | + if not os.path.exists(saveto): # Avoid repeated downloads |
298 | + try: |
299 | + log.warn("Downloading %s", url) |
300 | + src = urlopen(url) |
301 | + # Read/write all in one block, so we don't create a corrupt file |
302 | + # if the download is interrupted. |
303 | + data = src.read() |
304 | + dst = open(saveto, "wb") |
305 | + dst.write(data) |
306 | + finally: |
307 | + if src: |
308 | + src.close() |
309 | + if dst: |
310 | + dst.close() |
311 | + return os.path.realpath(saveto) |
312 | + |
313 | + |
314 | +def _patch_file(path, content): |
315 | + """Will backup the file then patch it""" |
316 | + existing_content = open(path).read() |
317 | + if existing_content == content: |
318 | + # already patched |
319 | + log.warn('Already patched.') |
320 | + return False |
321 | + log.warn('Patching...') |
322 | + _rename_path(path) |
323 | + f = open(path, 'w') |
324 | + try: |
325 | + f.write(content) |
326 | + finally: |
327 | + f.close() |
328 | + return True |
329 | + |
330 | + |
331 | +def _same_content(path, content): |
332 | + return open(path).read() == content |
333 | + |
334 | +def _no_sandbox(function): |
335 | + def __no_sandbox(*args, **kw): |
336 | + try: |
337 | + from setuptools.sandbox import DirectorySandbox |
338 | + def violation(*args): |
339 | + pass |
340 | + DirectorySandbox._old = DirectorySandbox._violation |
341 | + DirectorySandbox._violation = violation |
342 | + patched = True |
343 | + except ImportError: |
344 | + patched = False |
345 | + |
346 | + try: |
347 | + return function(*args, **kw) |
348 | + finally: |
349 | + if patched: |
350 | + DirectorySandbox._violation = DirectorySandbox._old |
351 | + del DirectorySandbox._old |
352 | + |
353 | + return __no_sandbox |
354 | + |
355 | +@_no_sandbox |
356 | +def _rename_path(path): |
357 | + new_name = path + '.OLD.%s' % time.time() |
358 | + log.warn('Renaming %s into %s', path, new_name) |
359 | + os.rename(path, new_name) |
360 | + return new_name |
361 | + |
362 | +def _remove_flat_installation(placeholder): |
363 | + if not os.path.isdir(placeholder): |
364 | + log.warn('Unkown installation at %s', placeholder) |
365 | + return False |
366 | + found = False |
367 | + for file in os.listdir(placeholder): |
368 | + if fnmatch.fnmatch(file, 'setuptools*.egg-info'): |
369 | + found = True |
370 | + break |
371 | + if not found: |
372 | + log.warn('Could not locate setuptools*.egg-info') |
373 | + return |
374 | + |
375 | + log.warn('Removing elements out of the way...') |
376 | + pkg_info = os.path.join(placeholder, file) |
377 | + if os.path.isdir(pkg_info): |
378 | + patched = _patch_egg_dir(pkg_info) |
379 | + else: |
380 | + patched = _patch_file(pkg_info, SETUPTOOLS_PKG_INFO) |
381 | + |
382 | + if not patched: |
383 | + log.warn('%s already patched.', pkg_info) |
384 | + return False |
385 | + # now let's move the files out of the way |
386 | + for element in ('setuptools', 'pkg_resources.py', 'site.py'): |
387 | + element = os.path.join(placeholder, element) |
388 | + if os.path.exists(element): |
389 | + _rename_path(element) |
390 | + else: |
391 | + log.warn('Could not find the %s element of the ' |
392 | + 'Setuptools distribution', element) |
393 | + return True |
394 | + |
395 | + |
396 | +def _after_install(dist): |
397 | + log.warn('After install bootstrap.') |
398 | + placeholder = dist.get_command_obj('install').install_purelib |
399 | + _create_fake_setuptools_pkg_info(placeholder) |
400 | + |
401 | +@_no_sandbox |
402 | +def _create_fake_setuptools_pkg_info(placeholder): |
403 | + if not placeholder or not os.path.exists(placeholder): |
404 | + log.warn('Could not find the install location') |
405 | + return |
406 | + pyver = '%s.%s' % (sys.version_info[0], sys.version_info[1]) |
407 | + setuptools_file = 'setuptools-%s-py%s.egg-info' % \ |
408 | + (SETUPTOOLS_FAKED_VERSION, pyver) |
409 | + pkg_info = os.path.join(placeholder, setuptools_file) |
410 | + if os.path.exists(pkg_info): |
411 | + log.warn('%s already exists', pkg_info) |
412 | + return |
413 | + |
414 | + log.warn('Creating %s', pkg_info) |
415 | + f = open(pkg_info, 'w') |
416 | + try: |
417 | + f.write(SETUPTOOLS_PKG_INFO) |
418 | + finally: |
419 | + f.close() |
420 | + |
421 | + pth_file = os.path.join(placeholder, 'setuptools.pth') |
422 | + log.warn('Creating %s', pth_file) |
423 | + f = open(pth_file, 'w') |
424 | + try: |
425 | + f.write(os.path.join(os.curdir, setuptools_file)) |
426 | + finally: |
427 | + f.close() |
428 | + |
429 | +def _patch_egg_dir(path): |
430 | + # let's check if it's already patched |
431 | + pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO') |
432 | + if os.path.exists(pkg_info): |
433 | + if _same_content(pkg_info, SETUPTOOLS_PKG_INFO): |
434 | + log.warn('%s already patched.', pkg_info) |
435 | + return False |
436 | + _rename_path(path) |
437 | + os.mkdir(path) |
438 | + os.mkdir(os.path.join(path, 'EGG-INFO')) |
439 | + pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO') |
440 | + f = open(pkg_info, 'w') |
441 | + try: |
442 | + f.write(SETUPTOOLS_PKG_INFO) |
443 | + finally: |
444 | + f.close() |
445 | + return True |
446 | + |
447 | + |
448 | +def _before_install(): |
449 | + log.warn('Before install bootstrap.') |
450 | + _fake_setuptools() |
451 | + |
452 | + |
453 | +def _under_prefix(location): |
454 | + if 'install' not in sys.argv: |
455 | + return True |
456 | + args = sys.argv[sys.argv.index('install')+1:] |
457 | + for index, arg in enumerate(args): |
458 | + for option in ('--root', '--prefix'): |
459 | + if arg.startswith('%s=' % option): |
460 | + top_dir = arg.split('root=')[-1] |
461 | + return location.startswith(top_dir) |
462 | + elif arg == option: |
463 | + if len(args) > index: |
464 | + top_dir = args[index+1] |
465 | + return location.startswith(top_dir) |
466 | + elif option == '--user' and USER_SITE is not None: |
467 | + return location.startswith(USER_SITE) |
468 | + return True |
469 | + |
470 | + |
471 | +def _fake_setuptools(): |
472 | + log.warn('Scanning installed packages') |
473 | + try: |
474 | + import pkg_resources |
475 | + except ImportError: |
476 | + # we're cool |
477 | + log.warn('Setuptools or Distribute does not seem to be installed.') |
478 | + return |
479 | + ws = pkg_resources.working_set |
480 | + try: |
481 | + setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools', |
482 | + replacement=False)) |
483 | + except TypeError: |
484 | + # old distribute API |
485 | + setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools')) |
486 | + |
487 | + if setuptools_dist is None: |
488 | + log.warn('No setuptools distribution found') |
489 | + return |
490 | + # detecting if it was already faked |
491 | + setuptools_location = setuptools_dist.location |
492 | + log.warn('Setuptools installation detected at %s', setuptools_location) |
493 | + |
494 | + # if --root or --preix was provided, and if |
495 | + # setuptools is not located in them, we don't patch it |
496 | + if not _under_prefix(setuptools_location): |
497 | + log.warn('Not patching, --root or --prefix is installing Distribute' |
498 | + ' in another location') |
499 | + return |
500 | + |
501 | + # let's see if its an egg |
502 | + if not setuptools_location.endswith('.egg'): |
503 | + log.warn('Non-egg installation') |
504 | + res = _remove_flat_installation(setuptools_location) |
505 | + if not res: |
506 | + return |
507 | + else: |
508 | + log.warn('Egg installation') |
509 | + pkg_info = os.path.join(setuptools_location, 'EGG-INFO', 'PKG-INFO') |
510 | + if (os.path.exists(pkg_info) and |
511 | + _same_content(pkg_info, SETUPTOOLS_PKG_INFO)): |
512 | + log.warn('Already patched.') |
513 | + return |
514 | + log.warn('Patching...') |
515 | + # let's create a fake egg replacing setuptools one |
516 | + res = _patch_egg_dir(setuptools_location) |
517 | + if not res: |
518 | + return |
519 | + log.warn('Patched done.') |
520 | + _relaunch() |
521 | + |
522 | + |
523 | +def _relaunch(): |
524 | + log.warn('Relaunching...') |
525 | + # we have to relaunch the process |
526 | + args = [sys.executable] + sys.argv |
527 | + sys.exit(subprocess.call(args)) |
528 | + |
529 | + |
530 | +def _extractall(self, path=".", members=None): |
531 | + """Extract all members from the archive to the current working |
532 | + directory and set owner, modification time and permissions on |
533 | + directories afterwards. `path' specifies a different directory |
534 | + to extract to. `members' is optional and must be a subset of the |
535 | + list returned by getmembers(). |
536 | + """ |
537 | + import copy |
538 | + import operator |
539 | + from tarfile import ExtractError |
540 | + directories = [] |
541 | + |
542 | + if members is None: |
543 | + members = self |
544 | + |
545 | + for tarinfo in members: |
546 | + if tarinfo.isdir(): |
547 | + # Extract directories with a safe mode. |
548 | + directories.append(tarinfo) |
549 | + tarinfo = copy.copy(tarinfo) |
550 | + tarinfo.mode = 448 # decimal for oct 0700 |
551 | + self.extract(tarinfo, path) |
552 | + |
553 | + # Reverse sort directories. |
554 | + if sys.version_info < (2, 4): |
555 | + def sorter(dir1, dir2): |
556 | + return cmp(dir1.name, dir2.name) |
557 | + directories.sort(sorter) |
558 | + directories.reverse() |
559 | + else: |
560 | + directories.sort(key=operator.attrgetter('name'), reverse=True) |
561 | + |
562 | + # Set correct owner, mtime and filemode on directories. |
563 | + for tarinfo in directories: |
564 | + dirpath = os.path.join(path, tarinfo.name) |
565 | + try: |
566 | + self.chown(tarinfo, dirpath) |
567 | + self.utime(tarinfo, dirpath) |
568 | + self.chmod(tarinfo, dirpath) |
569 | + except ExtractError: |
570 | + e = sys.exc_info()[1] |
571 | + if self.errorlevel > 1: |
572 | + raise |
573 | + else: |
574 | + self._dbg(1, "tarfile: %s" % e) |
575 | + |
576 | + |
577 | +def main(argv, version=DEFAULT_VERSION): |
578 | + """Install or upgrade setuptools and EasyInstall""" |
579 | + tarball = download_setuptools() |
580 | + _install(tarball) |
581 | + |
582 | + |
583 | +if __name__ == '__main__': |
584 | + main(sys.argv[1:]) |
585 | |
586 | === added file 'setup.py' |
587 | --- setup.py 1970-01-01 00:00:00 +0000 |
588 | +++ setup.py 2011-11-16 16:53:24 +0000 |
589 | @@ -0,0 +1,42 @@ |
590 | +# Copyright 2011 Canonical Ltd. This software is licensed under the |
591 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
592 | + |
593 | +import distribute_setup |
594 | +distribute_setup.use_setuptools() |
595 | + |
596 | +from setup_helpers import ( |
597 | + description, |
598 | + get_version, |
599 | + ) |
600 | +from setuptools import setup, find_packages |
601 | + |
602 | + |
603 | +__version__ = get_version('devportalbinary/__init__.py') |
604 | + |
605 | +setup( |
606 | + name='pkgme-binary', |
607 | + version=__version__, |
608 | + packages=find_packages(), |
609 | + include_package_data=True, |
610 | + maintainer='pkgme developers', |
611 | + maintainer_email='pkgme-devs@lists.launchpad.net', |
612 | + description=description('README'), |
613 | + license='AGPLv3', |
614 | + url='http://launchpad.net/pkgme-binary', |
615 | + download_url='https://launchpad.net/pkgme-binary/+download', |
616 | + test_suite='devportalbinary.tests', |
617 | + install_requires = [ |
618 | + 'bzr', |
619 | + 'pkgme', |
620 | + ], |
621 | + entry_points = { |
622 | + 'console_scripts': [ |
623 | + 'fetch-symbol-files=devportalbinary.database:main', |
624 | + 'guess-executable=devportalbinary.binary:print_executable', |
625 | + 'guess-deps=devportalbinary.binary:print_dependencies', |
626 | + ], |
627 | + 'pkgme.get_backends_path': ['binary=devportalbinary:get_backends_path'], |
628 | + }, |
629 | + # Auto-conversion to Python 3. |
630 | + use_2to3=True, |
631 | + ) |
632 | |
633 | === added file 'setup_helpers.py' |
634 | --- setup_helpers.py 1970-01-01 00:00:00 +0000 |
635 | +++ setup_helpers.py 2011-11-16 16:53:24 +0000 |
636 | @@ -0,0 +1,147 @@ |
637 | +# setup_helper.py - Some utility functions for setup.py authors. |
638 | +# |
639 | +# Copyright (C) 2009, 2010 by Barry A. Warsaw |
640 | +# |
641 | +# This program is free software: you can redistribute it and/or modify it |
642 | +# under the terms of the GNU Lesser General Public License as published by the |
643 | +# Free Software Foundation, version 3 of the License. |
644 | +# |
645 | +# This program is distributed in the hope that it will be useful, but WITHOUT |
646 | +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
647 | +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License |
648 | +# for more details. |
649 | +# |
650 | +# You should have received a copy of the GNU Lesser General Public License |
651 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
652 | + |
653 | +"""setup.py helper functions.""" |
654 | + |
655 | +from __future__ import absolute_import, unicode_literals |
656 | +from __future__ import print_function |
657 | + |
658 | + |
659 | +__metaclass__ = type |
660 | +__all__ = [ |
661 | + 'description', |
662 | + 'find_doctests', |
663 | + 'get_version', |
664 | + 'long_description', |
665 | + 'require_python', |
666 | + ] |
667 | + |
668 | + |
669 | +import os |
670 | +import re |
671 | +import sys |
672 | + |
673 | + |
674 | +DEFAULT_VERSION_RE = re.compile(r'(?P<version>\d+\.\d(?:\.\d+)?)') |
675 | +NL = '\n' |
676 | + |
677 | + |
678 | + |
679 | |
680 | +def require_python(minimum): |
681 | + """Require at least a minimum Python version. |
682 | + |
683 | + The version number is expressed in terms of `sys.hexversion`. E.g. to |
684 | + require a minimum of Python 2.6, use:: |
685 | + |
686 | + >>> require_python(0x206000f0) |
687 | + |
688 | + :param minimum: Minimum Python version supported. |
689 | + :type minimum: integer |
690 | + """ |
691 | + if sys.hexversion < minimum: |
692 | + hversion = hex(minimum)[2:] |
693 | + if len(hversion) % 2 != 0: |
694 | + hversion = '0' + hversion |
695 | + split = list(hversion) |
696 | + parts = [] |
697 | + while split: |
698 | + parts.append(int(''.join((split.pop(0), split.pop(0))), 16)) |
699 | + major, minor, micro, release = parts |
700 | + if release == 0xf0: |
701 | + print('Python {0}.{1}.{2} or better is required'.format( |
702 | + major, minor, micro)) |
703 | + else: |
704 | + print('Python {0}.{1}.{2} ({3}) or better is required'.format( |
705 | + major, minor, micro, hex(release)[2:])) |
706 | + sys.exit(1) |
707 | + |
708 | + |
709 | + |
710 | |
711 | +def get_version(filename, pattern=None): |
712 | + """Extract the __version__ from a file without importing it. |
713 | + |
714 | + While you could get the __version__ by importing the module, the very act |
715 | + of importing can cause unintended consequences. For example, Distribute's |
716 | + automatic 2to3 support will break. Instead, this searches the file for a |
717 | + line that starts with __version__, and extract the version number by |
718 | + regular expression matching. |
719 | + |
720 | + By default, two or three dot-separated digits are recognized, but by |
721 | + passing a pattern parameter, you can recognize just about anything. Use |
722 | + the `version` group name to specify the match group. |
723 | + |
724 | + :param filename: The name of the file to search. |
725 | + :type filename: string |
726 | + :param pattern: Optional alternative regular expression pattern to use. |
727 | + :type pattern: string |
728 | + :return: The version that was extracted. |
729 | + :rtype: string |
730 | + """ |
731 | + if pattern is None: |
732 | + cre = DEFAULT_VERSION_RE |
733 | + else: |
734 | + cre = re.compile(pattern) |
735 | + with open(filename) as fp: |
736 | + for line in fp: |
737 | + if line.startswith('__version__'): |
738 | + mo = cre.search(line) |
739 | + assert mo, 'No valid __version__ string found' |
740 | + return mo.group('version') |
741 | + raise AssertionError('No __version__ assignment found') |
742 | + |
743 | + |
744 | + |
745 | |
746 | +def find_doctests(start='.', extension='.txt'): |
747 | + """Find separate-file doctests in the package. |
748 | + |
749 | + This is useful for Distribute's automatic 2to3 conversion support. The |
750 | + `setup()` keyword argument `convert_2to3_doctests` requires file names, |
751 | + which may be difficult to track automatically as you add new doctests. |
752 | + |
753 | + :param start: Directory to start searching in (default is cwd) |
754 | + :type start: string |
755 | + :param extension: Doctest file extension (default is .txt) |
756 | + :type extension: string |
757 | + :return: The doctest files found. |
758 | + :rtype: list |
759 | + """ |
760 | + doctests = [] |
761 | + for dirpath, dirnames, filenames in os.walk(start): |
762 | + doctests.extend(os.path.join(dirpath, filename) |
763 | + for filename in filenames |
764 | + if filename.endswith(extension)) |
765 | + return doctests |
766 | + |
767 | + |
768 | + |
769 | |
770 | +def long_description(*filenames): |
771 | + """Provide a long description.""" |
772 | + res = [] |
773 | + for value in filenames: |
774 | + if value.endswith('.txt'): |
775 | + with open(value) as fp: |
776 | + value = fp.read() |
777 | + res.append(value) |
778 | + if not value.endswith(NL): |
779 | + res.append('') |
780 | + return NL.join(res) |
781 | + |
782 | + |
783 | +def description(filename): |
784 | + """Provide a short description.""" |
785 | + with open(filename) as fp: |
786 | + for line in fp: |
787 | + return line.strip() |